00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include "asterisk.h"
00038
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 350023 $")
00040
00041 #include <dahdi/user.h>
00042
00043 #include "asterisk/lock.h"
00044 #include "asterisk/file.h"
00045 #include "asterisk/channel.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/app.h"
00050 #include "asterisk/dsp.h"
00051 #include "asterisk/musiconhold.h"
00052 #include "asterisk/manager.h"
00053 #include "asterisk/cli.h"
00054 #include "asterisk/say.h"
00055 #include "asterisk/utils.h"
00056 #include "asterisk/translate.h"
00057 #include "asterisk/ulaw.h"
00058 #include "asterisk/astobj2.h"
00059 #include "asterisk/devicestate.h"
00060 #include "asterisk/dial.h"
00061 #include "asterisk/causes.h"
00062 #include "asterisk/paths.h"
00063 #include "asterisk/data.h"
00064 #include "asterisk/test.h"
00065
00066 #include "enter.h"
00067 #include "leave.h"
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515 #define CONFIG_FILE_NAME "meetme.conf"
00516 #define SLA_CONFIG_FILE "sla.conf"
00517
00518
00519 #define DEFAULT_AUDIO_BUFFERS 32
00520
00521
00522 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
00523
00524 enum {
00525 ADMINFLAG_MUTED = (1 << 1),
00526 ADMINFLAG_SELFMUTED = (1 << 2),
00527 ADMINFLAG_KICKME = (1 << 3),
00528
00529 ADMINFLAG_T_REQUEST = (1 << 4),
00530 };
00531
00532 #define MEETME_DELAYDETECTTALK 300
00533 #define MEETME_DELAYDETECTENDTALK 1000
00534
00535 #define AST_FRAME_BITS 32
00536
00537 enum volume_action {
00538 VOL_UP,
00539 VOL_DOWN
00540 };
00541
00542 enum entrance_sound {
00543 ENTER,
00544 LEAVE
00545 };
00546
00547 enum recording_state {
00548 MEETME_RECORD_OFF,
00549 MEETME_RECORD_STARTED,
00550 MEETME_RECORD_ACTIVE,
00551 MEETME_RECORD_TERMINATE
00552 };
00553
00554 #define CONF_SIZE 320
00555
00556 enum {
00557
00558 CONFFLAG_ADMIN = (1 << 0),
00559
00560 CONFFLAG_MONITOR = (1 << 1),
00561
00562 CONFFLAG_KEYEXIT = (1 << 2),
00563
00564 CONFFLAG_STARMENU = (1 << 3),
00565
00566 CONFFLAG_TALKER = (1 << 4),
00567
00568 CONFFLAG_QUIET = (1 << 5),
00569
00570
00571 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
00572
00573 CONFFLAG_AGI = (1 << 7),
00574
00575 CONFFLAG_MOH = (1 << 8),
00576
00577 CONFFLAG_MARKEDEXIT = (1 << 9),
00578
00579 CONFFLAG_WAITMARKED = (1 << 10),
00580
00581 CONFFLAG_EXIT_CONTEXT = (1 << 11),
00582
00583 CONFFLAG_MARKEDUSER = (1 << 12),
00584
00585 CONFFLAG_INTROUSER = (1 << 13),
00586
00587 CONFFLAG_RECORDCONF = (1<< 14),
00588
00589 CONFFLAG_MONITORTALKER = (1 << 15),
00590 CONFFLAG_DYNAMIC = (1 << 16),
00591 CONFFLAG_DYNAMICPIN = (1 << 17),
00592 CONFFLAG_EMPTY = (1 << 18),
00593 CONFFLAG_EMPTYNOPIN = (1 << 19),
00594 CONFFLAG_ALWAYSPROMPT = (1 << 20),
00595
00596 CONFFLAG_OPTIMIZETALKER = (1 << 21),
00597
00598
00599 CONFFLAG_NOONLYPERSON = (1 << 22),
00600
00601
00602 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
00603
00604 CONFFLAG_STARTMUTED = (1 << 24),
00605
00606 CONFFLAG_PASS_DTMF = (1 << 25),
00607 CONFFLAG_SLA_STATION = (1 << 26),
00608 CONFFLAG_SLA_TRUNK = (1 << 27),
00609
00610 CONFFLAG_KICK_CONTINUE = (1 << 28),
00611 CONFFLAG_DURATION_STOP = (1 << 29),
00612 CONFFLAG_DURATION_LIMIT = (1 << 30),
00613 };
00614
00615
00616 #define CONFFLAG_NO_AUDIO_UNTIL_UP (1ULL << 31)
00617
00618 #define CONFFLAG_INTROMSG (1ULL << 32)
00619
00620 enum {
00621 OPT_ARG_WAITMARKED = 0,
00622 OPT_ARG_EXITKEYS = 1,
00623 OPT_ARG_DURATION_STOP = 2,
00624 OPT_ARG_DURATION_LIMIT = 3,
00625 OPT_ARG_MOH_CLASS = 4,
00626 OPT_ARG_INTROMSG = 5,
00627 OPT_ARG_ARRAY_SIZE = 6,
00628 };
00629
00630 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
00631 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
00632 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
00633 AST_APP_OPTION('b', CONFFLAG_AGI ),
00634 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
00635 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
00636 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
00637 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
00638 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
00639 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
00640 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
00641 AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
00642 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
00643 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
00644 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
00645 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
00646 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
00647 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
00648 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
00649 AST_APP_OPTION('q', CONFFLAG_QUIET ),
00650 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
00651 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
00652 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
00653 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
00654 AST_APP_OPTION('t', CONFFLAG_TALKER ),
00655 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
00656 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
00657 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
00658 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
00659 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
00660 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
00661 END_OPTIONS );
00662
00663 static const char * const app = "MeetMe";
00664 static const char * const app2 = "MeetMeCount";
00665 static const char * const app3 = "MeetMeAdmin";
00666 static const char * const app4 = "MeetMeChannelAdmin";
00667 static const char * const slastation_app = "SLAStation";
00668 static const char * const slatrunk_app = "SLATrunk";
00669
00670
00671 static int rt_schedule;
00672 static int fuzzystart;
00673 static int earlyalert;
00674 static int endalert;
00675 static int extendby;
00676
00677
00678 static int rt_log_members;
00679
00680 #define MAX_CONFNUM 80
00681 #define MAX_PIN 80
00682 #define OPTIONS_LEN 100
00683
00684
00685 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
00686
00687 enum announcetypes {
00688 CONF_HASJOIN,
00689 CONF_HASLEFT
00690 };
00691
00692 struct announce_listitem {
00693 AST_LIST_ENTRY(announce_listitem) entry;
00694 char namerecloc[PATH_MAX];
00695 char language[MAX_LANGUAGE];
00696 struct ast_channel *confchan;
00697 int confusers;
00698 enum announcetypes announcetype;
00699 };
00700
00701
00702 struct ast_conference {
00703 ast_mutex_t playlock;
00704 ast_mutex_t listenlock;
00705 char confno[MAX_CONFNUM];
00706 struct ast_channel *chan;
00707 struct ast_channel *lchan;
00708 int fd;
00709 int dahdiconf;
00710 int users;
00711 int markedusers;
00712 int maxusers;
00713 int endalert;
00714 time_t start;
00715 int refcount;
00716 enum recording_state recording:2;
00717 unsigned int isdynamic:1;
00718 unsigned int locked:1;
00719 unsigned int gmuted:1;
00720 pthread_t recordthread;
00721 ast_mutex_t recordthreadlock;
00722 pthread_attr_t attr;
00723 char *recordingfilename;
00724 char *recordingformat;
00725 char pin[MAX_PIN];
00726 char pinadmin[MAX_PIN];
00727 char uniqueid[32];
00728 long endtime;
00729 const char *useropts;
00730 const char *adminopts;
00731 const char *bookid;
00732 struct ast_frame *transframe[32];
00733 struct ast_frame *origframe;
00734 struct ast_trans_pvt *transpath[32];
00735 struct ao2_container *usercontainer;
00736 AST_LIST_ENTRY(ast_conference) list;
00737
00738 pthread_t announcethread;
00739 ast_mutex_t announcethreadlock;
00740 unsigned int announcethread_stop:1;
00741 ast_cond_t announcelist_addition;
00742 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
00743 ast_mutex_t announcelistlock;
00744 };
00745
00746 static AST_LIST_HEAD_STATIC(confs, ast_conference);
00747
00748 static unsigned int conf_map[1024] = {0, };
00749
00750 struct volume {
00751 int desired;
00752 int actual;
00753 };
00754
00755
00756 struct ast_conf_user {
00757 int user_no;
00758 struct ast_flags64 userflags;
00759 int adminflags;
00760 struct ast_channel *chan;
00761 int talking;
00762 int dahdichannel;
00763 char usrvalue[50];
00764 char namerecloc[PATH_MAX];
00765 time_t jointime;
00766 time_t kicktime;
00767 struct timeval start_time;
00768 long timelimit;
00769 long play_warning;
00770 long warning_freq;
00771 const char *warning_sound;
00772 const char *end_sound;
00773 struct volume talk;
00774 struct volume listen;
00775 AST_LIST_ENTRY(ast_conf_user) list;
00776 };
00777
00778 enum sla_which_trunk_refs {
00779 ALL_TRUNK_REFS,
00780 INACTIVE_TRUNK_REFS,
00781 };
00782
00783 enum sla_trunk_state {
00784 SLA_TRUNK_STATE_IDLE,
00785 SLA_TRUNK_STATE_RINGING,
00786 SLA_TRUNK_STATE_UP,
00787 SLA_TRUNK_STATE_ONHOLD,
00788 SLA_TRUNK_STATE_ONHOLD_BYME,
00789 };
00790
00791 enum sla_hold_access {
00792
00793
00794 SLA_HOLD_OPEN,
00795
00796
00797 SLA_HOLD_PRIVATE,
00798 };
00799
00800 struct sla_trunk_ref;
00801
00802 struct sla_station {
00803 AST_RWLIST_ENTRY(sla_station) entry;
00804 AST_DECLARE_STRING_FIELDS(
00805 AST_STRING_FIELD(name);
00806 AST_STRING_FIELD(device);
00807 AST_STRING_FIELD(autocontext);
00808 );
00809 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
00810 struct ast_dial *dial;
00811
00812
00813
00814 unsigned int ring_timeout;
00815
00816
00817
00818 unsigned int ring_delay;
00819
00820
00821 unsigned int hold_access:1;
00822
00823 unsigned int ref_count;
00824 };
00825
00826 struct sla_station_ref {
00827 AST_LIST_ENTRY(sla_station_ref) entry;
00828 struct sla_station *station;
00829 };
00830
00831 struct sla_trunk {
00832 AST_RWLIST_ENTRY(sla_trunk) entry;
00833 AST_DECLARE_STRING_FIELDS(
00834 AST_STRING_FIELD(name);
00835 AST_STRING_FIELD(device);
00836 AST_STRING_FIELD(autocontext);
00837 );
00838 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
00839
00840 unsigned int num_stations;
00841
00842 unsigned int active_stations;
00843
00844 unsigned int hold_stations;
00845 struct ast_channel *chan;
00846 unsigned int ring_timeout;
00847
00848
00849 unsigned int barge_disabled:1;
00850
00851
00852 unsigned int hold_access:1;
00853
00854
00855 unsigned int on_hold:1;
00856
00857 unsigned int ref_count;
00858 };
00859
00860 struct sla_trunk_ref {
00861 AST_LIST_ENTRY(sla_trunk_ref) entry;
00862 struct sla_trunk *trunk;
00863 enum sla_trunk_state state;
00864 struct ast_channel *chan;
00865
00866
00867
00868 unsigned int ring_timeout;
00869
00870
00871
00872 unsigned int ring_delay;
00873 };
00874
00875 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
00876 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
00877
00878 static const char sla_registrar[] = "SLA";
00879
00880
00881 enum sla_event_type {
00882
00883 SLA_EVENT_HOLD,
00884
00885 SLA_EVENT_DIAL_STATE,
00886
00887 SLA_EVENT_RINGING_TRUNK,
00888
00889 SLA_EVENT_RELOAD,
00890
00891 SLA_EVENT_CHECK_RELOAD,
00892 };
00893
00894 struct sla_event {
00895 enum sla_event_type type;
00896 struct sla_station *station;
00897 struct sla_trunk_ref *trunk_ref;
00898 AST_LIST_ENTRY(sla_event) entry;
00899 };
00900
00901
00902
00903 struct sla_failed_station {
00904 struct sla_station *station;
00905 struct timeval last_try;
00906 AST_LIST_ENTRY(sla_failed_station) entry;
00907 };
00908
00909
00910 struct sla_ringing_trunk {
00911 struct sla_trunk *trunk;
00912
00913 struct timeval ring_begin;
00914 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
00915 AST_LIST_ENTRY(sla_ringing_trunk) entry;
00916 };
00917
00918 enum sla_station_hangup {
00919 SLA_STATION_HANGUP_NORMAL,
00920 SLA_STATION_HANGUP_TIMEOUT,
00921 };
00922
00923
00924 struct sla_ringing_station {
00925 struct sla_station *station;
00926
00927 struct timeval ring_begin;
00928 AST_LIST_ENTRY(sla_ringing_station) entry;
00929 };
00930
00931
00932
00933
00934 static struct {
00935
00936 pthread_t thread;
00937 ast_cond_t cond;
00938 ast_mutex_t lock;
00939 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
00940 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
00941 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
00942 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
00943 unsigned int stop:1;
00944
00945
00946 unsigned int attempt_callerid:1;
00947
00948 unsigned int reload:1;
00949 } sla = {
00950 .thread = AST_PTHREADT_NULL,
00951 };
00952
00953
00954
00955 static int audio_buffers;
00956
00957
00958
00959
00960
00961
00962
00963
00964 static const char gain_map[] = {
00965 -15,
00966 -13,
00967 -10,
00968 -6,
00969 0,
00970 0,
00971 0,
00972 6,
00973 10,
00974 13,
00975 15,
00976 };
00977
00978
00979 static int admin_exec(struct ast_channel *chan, const char *data);
00980 static void *recordthread(void *args);
00981
00982 static const char *istalking(int x)
00983 {
00984 if (x > 0)
00985 return "(talking)";
00986 else if (x < 0)
00987 return "(unmonitored)";
00988 else
00989 return "(not talking)";
00990 }
00991
00992 static int careful_write(int fd, unsigned char *data, int len, int block)
00993 {
00994 int res;
00995 int x;
00996
00997 while (len) {
00998 if (block) {
00999 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
01000 res = ioctl(fd, DAHDI_IOMUX, &x);
01001 } else
01002 res = 0;
01003 if (res >= 0)
01004 res = write(fd, data, len);
01005 if (res < 1) {
01006 if (errno != EAGAIN) {
01007 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
01008 return -1;
01009 } else
01010 return 0;
01011 }
01012 len -= res;
01013 data += res;
01014 }
01015
01016 return 0;
01017 }
01018
01019 static int set_talk_volume(struct ast_conf_user *user, int volume)
01020 {
01021 char gain_adjust;
01022
01023
01024
01025
01026 gain_adjust = gain_map[volume + 5];
01027
01028 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
01029 }
01030
01031 static int set_listen_volume(struct ast_conf_user *user, int volume)
01032 {
01033 char gain_adjust;
01034
01035
01036
01037
01038 gain_adjust = gain_map[volume + 5];
01039
01040 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
01041 }
01042
01043 static void tweak_volume(struct volume *vol, enum volume_action action)
01044 {
01045 switch (action) {
01046 case VOL_UP:
01047 switch (vol->desired) {
01048 case 5:
01049 break;
01050 case 0:
01051 vol->desired = 2;
01052 break;
01053 case -2:
01054 vol->desired = 0;
01055 break;
01056 default:
01057 vol->desired++;
01058 break;
01059 }
01060 break;
01061 case VOL_DOWN:
01062 switch (vol->desired) {
01063 case -5:
01064 break;
01065 case 2:
01066 vol->desired = 0;
01067 break;
01068 case 0:
01069 vol->desired = -2;
01070 break;
01071 default:
01072 vol->desired--;
01073 break;
01074 }
01075 }
01076 }
01077
01078 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
01079 {
01080 tweak_volume(&user->talk, action);
01081
01082
01083
01084 if (!set_talk_volume(user, user->talk.desired))
01085 user->talk.actual = 0;
01086 else
01087 user->talk.actual = user->talk.desired;
01088 }
01089
01090 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
01091 {
01092 tweak_volume(&user->listen, action);
01093
01094
01095
01096 if (!set_listen_volume(user, user->listen.desired))
01097 user->listen.actual = 0;
01098 else
01099 user->listen.actual = user->listen.desired;
01100 }
01101
01102 static void reset_volumes(struct ast_conf_user *user)
01103 {
01104 signed char zero_volume = 0;
01105
01106 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
01107 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
01108 }
01109
01110 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
01111 {
01112 unsigned char *data;
01113 int len;
01114 int res = -1;
01115
01116 if (!ast_check_hangup(chan))
01117 res = ast_autoservice_start(chan);
01118
01119 AST_LIST_LOCK(&confs);
01120
01121 switch(sound) {
01122 case ENTER:
01123 data = enter;
01124 len = sizeof(enter);
01125 break;
01126 case LEAVE:
01127 data = leave;
01128 len = sizeof(leave);
01129 break;
01130 default:
01131 data = NULL;
01132 len = 0;
01133 }
01134 if (data) {
01135 careful_write(conf->fd, data, len, 1);
01136 }
01137
01138 AST_LIST_UNLOCK(&confs);
01139
01140 if (!res)
01141 ast_autoservice_stop(chan);
01142 }
01143
01144 static int user_no_cmp(void *obj, void *arg, int flags)
01145 {
01146 struct ast_conf_user *user = obj;
01147 int *user_no = arg;
01148
01149 if (user->user_no == *user_no) {
01150 return (CMP_MATCH | CMP_STOP);
01151 }
01152
01153 return 0;
01154 }
01155
01156 static int user_max_cmp(void *obj, void *arg, int flags)
01157 {
01158 struct ast_conf_user *user = obj;
01159 int *max_no = arg;
01160
01161 if (user->user_no > *max_no) {
01162 *max_no = user->user_no;
01163 }
01164
01165 return 0;
01166 }
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176
01177
01178
01179
01180
01181
01182 static struct ast_conference *build_conf(const char *confno, const char *pin,
01183 const char *pinadmin, int make, int dynamic, int refcount,
01184 const struct ast_channel *chan, struct ast_test *test)
01185 {
01186 struct ast_conference *cnf;
01187 struct dahdi_confinfo dahdic = { 0, };
01188 int confno_int = 0;
01189
01190 AST_LIST_LOCK(&confs);
01191
01192 AST_LIST_TRAVERSE(&confs, cnf, list) {
01193 if (!strcmp(confno, cnf->confno))
01194 break;
01195 }
01196
01197 if (cnf || (!make && !dynamic))
01198 goto cnfout;
01199
01200
01201 if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
01202 !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
01203 goto cnfout;
01204 }
01205
01206 ast_mutex_init(&cnf->playlock);
01207 ast_mutex_init(&cnf->listenlock);
01208 cnf->recordthread = AST_PTHREADT_NULL;
01209 ast_mutex_init(&cnf->recordthreadlock);
01210 cnf->announcethread = AST_PTHREADT_NULL;
01211 ast_mutex_init(&cnf->announcethreadlock);
01212 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
01213 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
01214 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
01215 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
01216
01217
01218 dahdic.confno = -1;
01219 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01220 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
01221 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
01222 if (test) {
01223
01224
01225
01226 ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
01227 } else {
01228 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
01229 if (cnf->fd >= 0)
01230 close(cnf->fd);
01231 ao2_ref(cnf->usercontainer, -1);
01232 ast_mutex_destroy(&cnf->playlock);
01233 ast_mutex_destroy(&cnf->listenlock);
01234 ast_mutex_destroy(&cnf->recordthreadlock);
01235 ast_mutex_destroy(&cnf->announcethreadlock);
01236 ast_free(cnf);
01237 cnf = NULL;
01238 goto cnfout;
01239 }
01240 }
01241
01242 cnf->dahdiconf = dahdic.confno;
01243
01244
01245 cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL);
01246 if (cnf->chan) {
01247 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
01248 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
01249 dahdic.chan = 0;
01250 dahdic.confno = cnf->dahdiconf;
01251 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01252 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
01253 if (test) {
01254 ast_test_status_update(test, "Error setting conference on pseudo channel\n");
01255 }
01256 ast_log(LOG_WARNING, "Error setting conference\n");
01257 if (cnf->chan)
01258 ast_hangup(cnf->chan);
01259 else
01260 close(cnf->fd);
01261 ao2_ref(cnf->usercontainer, -1);
01262 ast_mutex_destroy(&cnf->playlock);
01263 ast_mutex_destroy(&cnf->listenlock);
01264 ast_mutex_destroy(&cnf->recordthreadlock);
01265 ast_mutex_destroy(&cnf->announcethreadlock);
01266 ast_free(cnf);
01267 cnf = NULL;
01268 goto cnfout;
01269 }
01270 }
01271
01272
01273 cnf->start = time(NULL);
01274 cnf->maxusers = 0x7fffffff;
01275 cnf->isdynamic = dynamic ? 1 : 0;
01276 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
01277 AST_LIST_INSERT_HEAD(&confs, cnf, list);
01278
01279
01280 if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
01281 conf_map[confno_int] = 1;
01282
01283 cnfout:
01284 if (cnf)
01285 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
01286
01287 AST_LIST_UNLOCK(&confs);
01288
01289 return cnf;
01290 }
01291
01292 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
01293 {
01294 static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
01295
01296 int len = strlen(word);
01297 int which = 0;
01298 struct ast_conference *cnf = NULL;
01299 struct ast_conf_user *usr = NULL;
01300 char *confno = NULL;
01301 char usrno[50] = "";
01302 char *myline, *ret = NULL;
01303
01304 if (pos == 1) {
01305 return ast_cli_complete(word, cmds, state);
01306 } else if (pos == 2) {
01307 AST_LIST_LOCK(&confs);
01308 AST_LIST_TRAVERSE(&confs, cnf, list) {
01309 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
01310 ret = cnf->confno;
01311 break;
01312 }
01313 }
01314 ret = ast_strdup(ret);
01315 AST_LIST_UNLOCK(&confs);
01316 return ret;
01317 } else if (pos == 3) {
01318
01319 if (strstr(line, "mute") || strstr(line, "kick")) {
01320 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
01321 return ast_strdup("all");
01322 which++;
01323 AST_LIST_LOCK(&confs);
01324
01325
01326 myline = ast_strdupa(line);
01327 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
01328 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
01329 ;
01330 }
01331
01332 AST_LIST_TRAVERSE(&confs, cnf, list) {
01333 if (!strcmp(confno, cnf->confno))
01334 break;
01335 }
01336
01337 if (cnf) {
01338 struct ao2_iterator user_iter;
01339 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
01340
01341 while((usr = ao2_iterator_next(&user_iter))) {
01342 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
01343 if (!strncasecmp(word, usrno, len) && ++which > state) {
01344 ao2_ref(usr, -1);
01345 break;
01346 }
01347 ao2_ref(usr, -1);
01348 }
01349 ao2_iterator_destroy(&user_iter);
01350 AST_LIST_UNLOCK(&confs);
01351 return usr ? ast_strdup(usrno) : NULL;
01352 }
01353 AST_LIST_UNLOCK(&confs);
01354 }
01355 }
01356
01357 return NULL;
01358 }
01359
01360 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01361 {
01362
01363 struct ast_conf_user *user;
01364 struct ast_conference *cnf;
01365 int hr, min, sec;
01366 int i = 0, total = 0;
01367 time_t now;
01368 struct ast_str *cmdline = NULL;
01369 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
01370 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
01371
01372 switch (cmd) {
01373 case CLI_INIT:
01374 e->command = "meetme list [concise]";
01375 e->usage =
01376 "Usage: meetme list [concise] <confno> \n"
01377 " List all or a specific conference.\n";
01378 return NULL;
01379 case CLI_GENERATE:
01380 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
01381 }
01382
01383
01384 for (i = 0; i < a->argc; i++) {
01385 if (strlen(a->argv[i]) > 100)
01386 ast_cli(a->fd, "Invalid Arguments.\n");
01387 }
01388
01389
01390 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
01391 return CLI_FAILURE;
01392 }
01393
01394 if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
01395
01396 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
01397 now = time(NULL);
01398 AST_LIST_LOCK(&confs);
01399 if (AST_LIST_EMPTY(&confs)) {
01400 if (!concise) {
01401 ast_cli(a->fd, "No active MeetMe conferences.\n");
01402 }
01403 AST_LIST_UNLOCK(&confs);
01404 ast_free(cmdline);
01405 return CLI_SUCCESS;
01406 }
01407 if (!concise) {
01408 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
01409 }
01410 AST_LIST_TRAVERSE(&confs, cnf, list) {
01411 if (cnf->markedusers == 0) {
01412 ast_str_set(&cmdline, 0, "N/A ");
01413 } else {
01414 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
01415 }
01416 hr = (now - cnf->start) / 3600;
01417 min = ((now - cnf->start) % 3600) / 60;
01418 sec = (now - cnf->start) % 60;
01419 if (!concise) {
01420 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, ast_str_buffer(cmdline), hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
01421 } else {
01422 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
01423 cnf->confno,
01424 cnf->users,
01425 cnf->markedusers,
01426 hr, min, sec,
01427 cnf->isdynamic,
01428 cnf->locked);
01429 }
01430
01431 total += cnf->users;
01432 }
01433 AST_LIST_UNLOCK(&confs);
01434 if (!concise) {
01435 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
01436 }
01437 ast_free(cmdline);
01438 return CLI_SUCCESS;
01439 } else if (strcmp(a->argv[1], "list") == 0) {
01440 struct ao2_iterator user_iter;
01441 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
01442
01443 if (AST_LIST_EMPTY(&confs)) {
01444 if (!concise) {
01445 ast_cli(a->fd, "No active MeetMe conferences.\n");
01446 }
01447 ast_free(cmdline);
01448 return CLI_SUCCESS;
01449 }
01450
01451 AST_LIST_LOCK(&confs);
01452 AST_LIST_TRAVERSE(&confs, cnf, list) {
01453 if (strcmp(cnf->confno, a->argv[2]) == 0) {
01454 break;
01455 }
01456 }
01457 if (!cnf) {
01458 if (!concise)
01459 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
01460 AST_LIST_UNLOCK(&confs);
01461 ast_free(cmdline);
01462 return CLI_SUCCESS;
01463 }
01464
01465 time(&now);
01466 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
01467 while((user = ao2_iterator_next(&user_iter))) {
01468 hr = (now - user->jointime) / 3600;
01469 min = ((now - user->jointime) % 3600) / 60;
01470 sec = (now - user->jointime) % 60;
01471 if (!concise) {
01472 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
01473 user->user_no,
01474 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
01475 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
01476 user->chan->name,
01477 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
01478 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
01479 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
01480 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
01481 istalking(user->talking), hr, min, sec);
01482 } else {
01483 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
01484 user->user_no,
01485 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, ""),
01486 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, ""),
01487 user->chan->name,
01488 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
01489 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
01490 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
01491 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
01492 user->talking, hr, min, sec);
01493 }
01494 ao2_ref(user, -1);
01495 }
01496 ao2_iterator_destroy(&user_iter);
01497 if (!concise) {
01498 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
01499 }
01500 AST_LIST_UNLOCK(&confs);
01501 ast_free(cmdline);
01502 return CLI_SUCCESS;
01503 }
01504 if (a->argc < 2) {
01505 ast_free(cmdline);
01506 return CLI_SHOWUSAGE;
01507 }
01508
01509 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
01510
01511 admin_exec(NULL, ast_str_buffer(cmdline));
01512 ast_free(cmdline);
01513
01514 return CLI_SUCCESS;
01515 }
01516
01517
01518 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01519 {
01520
01521 struct ast_str *cmdline = NULL;
01522 int i = 0;
01523
01524 switch (cmd) {
01525 case CLI_INIT:
01526 e->command = "meetme {lock|unlock|mute|unmute|kick}";
01527 e->usage =
01528 "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
01529 " Executes a command for the conference or on a conferee\n";
01530 return NULL;
01531 case CLI_GENERATE:
01532 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
01533 }
01534
01535 if (a->argc > 8)
01536 ast_cli(a->fd, "Invalid Arguments.\n");
01537
01538 for (i = 0; i < a->argc; i++) {
01539 if (strlen(a->argv[i]) > 100)
01540 ast_cli(a->fd, "Invalid Arguments.\n");
01541 }
01542
01543
01544 if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
01545 return CLI_FAILURE;
01546 }
01547
01548 if (a->argc < 1) {
01549 ast_free(cmdline);
01550 return CLI_SHOWUSAGE;
01551 }
01552
01553 ast_str_set(&cmdline, 0, "%s", a->argv[2]);
01554 if (strstr(a->argv[1], "lock")) {
01555 if (strcmp(a->argv[1], "lock") == 0) {
01556
01557 ast_str_append(&cmdline, 0, ",L");
01558 } else {
01559
01560 ast_str_append(&cmdline, 0, ",l");
01561 }
01562 } else if (strstr(a->argv[1], "mute")) {
01563 if (a->argc < 4) {
01564 ast_free(cmdline);
01565 return CLI_SHOWUSAGE;
01566 }
01567 if (strcmp(a->argv[1], "mute") == 0) {
01568
01569 if (strcmp(a->argv[3], "all") == 0) {
01570 ast_str_append(&cmdline, 0, ",N");
01571 } else {
01572 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);
01573 }
01574 } else {
01575
01576 if (strcmp(a->argv[3], "all") == 0) {
01577 ast_str_append(&cmdline, 0, ",n");
01578 } else {
01579 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
01580 }
01581 }
01582 } else if (strcmp(a->argv[1], "kick") == 0) {
01583 if (a->argc < 4) {
01584 ast_free(cmdline);
01585 return CLI_SHOWUSAGE;
01586 }
01587 if (strcmp(a->argv[3], "all") == 0) {
01588
01589 ast_str_append(&cmdline, 0, ",K");
01590 } else {
01591
01592 ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
01593 }
01594 } else {
01595 ast_free(cmdline);
01596 return CLI_SHOWUSAGE;
01597 }
01598
01599 ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
01600
01601 admin_exec(NULL, ast_str_buffer(cmdline));
01602 ast_free(cmdline);
01603
01604 return CLI_SUCCESS;
01605 }
01606
01607 static const char *sla_hold_str(unsigned int hold_access)
01608 {
01609 const char *hold = "Unknown";
01610
01611 switch (hold_access) {
01612 case SLA_HOLD_OPEN:
01613 hold = "Open";
01614 break;
01615 case SLA_HOLD_PRIVATE:
01616 hold = "Private";
01617 default:
01618 break;
01619 }
01620
01621 return hold;
01622 }
01623
01624 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01625 {
01626 const struct sla_trunk *trunk;
01627
01628 switch (cmd) {
01629 case CLI_INIT:
01630 e->command = "sla show trunks";
01631 e->usage =
01632 "Usage: sla show trunks\n"
01633 " This will list all trunks defined in sla.conf\n";
01634 return NULL;
01635 case CLI_GENERATE:
01636 return NULL;
01637 }
01638
01639 ast_cli(a->fd, "\n"
01640 "=============================================================\n"
01641 "=== Configured SLA Trunks ===================================\n"
01642 "=============================================================\n"
01643 "===\n");
01644 AST_RWLIST_RDLOCK(&sla_trunks);
01645 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
01646 struct sla_station_ref *station_ref;
01647 char ring_timeout[16] = "(none)";
01648 if (trunk->ring_timeout)
01649 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
01650 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01651 "=== Trunk Name: %s\n"
01652 "=== ==> Device: %s\n"
01653 "=== ==> AutoContext: %s\n"
01654 "=== ==> RingTimeout: %s\n"
01655 "=== ==> BargeAllowed: %s\n"
01656 "=== ==> HoldAccess: %s\n"
01657 "=== ==> Stations ...\n",
01658 trunk->name, trunk->device,
01659 S_OR(trunk->autocontext, "(none)"),
01660 ring_timeout,
01661 trunk->barge_disabled ? "No" : "Yes",
01662 sla_hold_str(trunk->hold_access));
01663 AST_RWLIST_RDLOCK(&sla_stations);
01664 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
01665 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
01666 AST_RWLIST_UNLOCK(&sla_stations);
01667 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
01668 }
01669 AST_RWLIST_UNLOCK(&sla_trunks);
01670 ast_cli(a->fd, "=============================================================\n\n");
01671
01672 return CLI_SUCCESS;
01673 }
01674
01675 static const char *trunkstate2str(enum sla_trunk_state state)
01676 {
01677 #define S(e) case e: return # e;
01678 switch (state) {
01679 S(SLA_TRUNK_STATE_IDLE)
01680 S(SLA_TRUNK_STATE_RINGING)
01681 S(SLA_TRUNK_STATE_UP)
01682 S(SLA_TRUNK_STATE_ONHOLD)
01683 S(SLA_TRUNK_STATE_ONHOLD_BYME)
01684 }
01685 return "Uknown State";
01686 #undef S
01687 }
01688
01689 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01690 {
01691 const struct sla_station *station;
01692
01693 switch (cmd) {
01694 case CLI_INIT:
01695 e->command = "sla show stations";
01696 e->usage =
01697 "Usage: sla show stations\n"
01698 " This will list all stations defined in sla.conf\n";
01699 return NULL;
01700 case CLI_GENERATE:
01701 return NULL;
01702 }
01703
01704 ast_cli(a->fd, "\n"
01705 "=============================================================\n"
01706 "=== Configured SLA Stations =================================\n"
01707 "=============================================================\n"
01708 "===\n");
01709 AST_RWLIST_RDLOCK(&sla_stations);
01710 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
01711 struct sla_trunk_ref *trunk_ref;
01712 char ring_timeout[16] = "(none)";
01713 char ring_delay[16] = "(none)";
01714 if (station->ring_timeout) {
01715 snprintf(ring_timeout, sizeof(ring_timeout),
01716 "%u", station->ring_timeout);
01717 }
01718 if (station->ring_delay) {
01719 snprintf(ring_delay, sizeof(ring_delay),
01720 "%u", station->ring_delay);
01721 }
01722 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01723 "=== Station Name: %s\n"
01724 "=== ==> Device: %s\n"
01725 "=== ==> AutoContext: %s\n"
01726 "=== ==> RingTimeout: %s\n"
01727 "=== ==> RingDelay: %s\n"
01728 "=== ==> HoldAccess: %s\n"
01729 "=== ==> Trunks ...\n",
01730 station->name, station->device,
01731 S_OR(station->autocontext, "(none)"),
01732 ring_timeout, ring_delay,
01733 sla_hold_str(station->hold_access));
01734 AST_RWLIST_RDLOCK(&sla_trunks);
01735 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01736 if (trunk_ref->ring_timeout) {
01737 snprintf(ring_timeout, sizeof(ring_timeout),
01738 "%u", trunk_ref->ring_timeout);
01739 } else
01740 strcpy(ring_timeout, "(none)");
01741 if (trunk_ref->ring_delay) {
01742 snprintf(ring_delay, sizeof(ring_delay),
01743 "%u", trunk_ref->ring_delay);
01744 } else
01745 strcpy(ring_delay, "(none)");
01746 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
01747 "=== ==> State: %s\n"
01748 "=== ==> RingTimeout: %s\n"
01749 "=== ==> RingDelay: %s\n",
01750 trunk_ref->trunk->name,
01751 trunkstate2str(trunk_ref->state),
01752 ring_timeout, ring_delay);
01753 }
01754 AST_RWLIST_UNLOCK(&sla_trunks);
01755 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01756 "===\n");
01757 }
01758 AST_RWLIST_UNLOCK(&sla_stations);
01759 ast_cli(a->fd, "============================================================\n"
01760 "\n");
01761
01762 return CLI_SUCCESS;
01763 }
01764
01765 static struct ast_cli_entry cli_meetme[] = {
01766 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
01767 AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
01768 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
01769 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
01770 };
01771
01772 static void conf_flush(int fd, struct ast_channel *chan)
01773 {
01774 int x;
01775
01776
01777
01778
01779 if (chan) {
01780 struct ast_frame *f;
01781
01782
01783
01784
01785 while (ast_waitfor(chan, 1)) {
01786 f = ast_read(chan);
01787 if (f)
01788 ast_frfree(f);
01789 else
01790 break;
01791 }
01792 }
01793
01794
01795 x = DAHDI_FLUSH_ALL;
01796 if (ioctl(fd, DAHDI_FLUSH, &x))
01797 ast_log(LOG_WARNING, "Error flushing channel\n");
01798
01799 }
01800
01801
01802
01803
01804 static int conf_free(struct ast_conference *conf)
01805 {
01806 int x;
01807 struct announce_listitem *item;
01808
01809 AST_LIST_REMOVE(&confs, conf, list);
01810 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
01811
01812 if (conf->recording == MEETME_RECORD_ACTIVE) {
01813 conf->recording = MEETME_RECORD_TERMINATE;
01814 AST_LIST_UNLOCK(&confs);
01815 while (1) {
01816 usleep(1);
01817 AST_LIST_LOCK(&confs);
01818 if (conf->recording == MEETME_RECORD_OFF)
01819 break;
01820 AST_LIST_UNLOCK(&confs);
01821 }
01822 }
01823
01824 for (x = 0; x < AST_FRAME_BITS; x++) {
01825 if (conf->transframe[x])
01826 ast_frfree(conf->transframe[x]);
01827 if (conf->transpath[x])
01828 ast_translator_free_path(conf->transpath[x]);
01829 }
01830 if (conf->announcethread != AST_PTHREADT_NULL) {
01831 ast_mutex_lock(&conf->announcelistlock);
01832 conf->announcethread_stop = 1;
01833 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
01834 ast_cond_signal(&conf->announcelist_addition);
01835 ast_mutex_unlock(&conf->announcelistlock);
01836 pthread_join(conf->announcethread, NULL);
01837
01838 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
01839 ast_filedelete(item->namerecloc, NULL);
01840 ao2_ref(item, -1);
01841 }
01842 ast_mutex_destroy(&conf->announcelistlock);
01843 }
01844
01845 if (conf->origframe)
01846 ast_frfree(conf->origframe);
01847 if (conf->lchan)
01848 ast_hangup(conf->lchan);
01849 if (conf->chan)
01850 ast_hangup(conf->chan);
01851 if (conf->fd >= 0)
01852 close(conf->fd);
01853 if (conf->recordingfilename) {
01854 ast_free(conf->recordingfilename);
01855 }
01856 if (conf->usercontainer) {
01857 ao2_ref(conf->usercontainer, -1);
01858 }
01859 if (conf->recordingformat) {
01860 ast_free(conf->recordingformat);
01861 }
01862 ast_mutex_destroy(&conf->playlock);
01863 ast_mutex_destroy(&conf->listenlock);
01864 ast_mutex_destroy(&conf->recordthreadlock);
01865 ast_mutex_destroy(&conf->announcethreadlock);
01866 ast_free(conf);
01867
01868 return 0;
01869 }
01870
01871 static void conf_queue_dtmf(const struct ast_conference *conf,
01872 const struct ast_conf_user *sender, struct ast_frame *f)
01873 {
01874 struct ast_conf_user *user;
01875 struct ao2_iterator user_iter;
01876
01877 user_iter = ao2_iterator_init(conf->usercontainer, 0);
01878 while ((user = ao2_iterator_next(&user_iter))) {
01879 if (user == sender) {
01880 ao2_ref(user, -1);
01881 continue;
01882 }
01883 if (ast_write(user->chan, f) < 0)
01884 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
01885 ao2_ref(user, -1);
01886 }
01887 ao2_iterator_destroy(&user_iter);
01888 }
01889
01890 static void sla_queue_event_full(enum sla_event_type type,
01891 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
01892 {
01893 struct sla_event *event;
01894
01895 if (sla.thread == AST_PTHREADT_NULL) {
01896 return;
01897 }
01898
01899 if (!(event = ast_calloc(1, sizeof(*event))))
01900 return;
01901
01902 event->type = type;
01903 event->trunk_ref = trunk_ref;
01904 event->station = station;
01905
01906 if (!lock) {
01907 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
01908 return;
01909 }
01910
01911 ast_mutex_lock(&sla.lock);
01912 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
01913 ast_cond_signal(&sla.cond);
01914 ast_mutex_unlock(&sla.lock);
01915 }
01916
01917 static void sla_queue_event_nolock(enum sla_event_type type)
01918 {
01919 sla_queue_event_full(type, NULL, NULL, 0);
01920 }
01921
01922 static void sla_queue_event(enum sla_event_type type)
01923 {
01924 sla_queue_event_full(type, NULL, NULL, 1);
01925 }
01926
01927
01928 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
01929 struct ast_conference *conf)
01930 {
01931 struct sla_station *station;
01932 struct sla_trunk_ref *trunk_ref = NULL;
01933 char *trunk_name;
01934
01935 trunk_name = ast_strdupa(conf->confno);
01936 strsep(&trunk_name, "_");
01937 if (ast_strlen_zero(trunk_name)) {
01938 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
01939 return;
01940 }
01941
01942 AST_RWLIST_RDLOCK(&sla_stations);
01943 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
01944 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01945 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
01946 break;
01947 }
01948 if (trunk_ref)
01949 break;
01950 }
01951 AST_RWLIST_UNLOCK(&sla_stations);
01952
01953 if (!trunk_ref) {
01954 ast_debug(1, "Trunk not found for event!\n");
01955 return;
01956 }
01957
01958 sla_queue_event_full(type, trunk_ref, station, 1);
01959 }
01960
01961
01962 static int dispose_conf(struct ast_conference *conf)
01963 {
01964 int res = 0;
01965 int confno_int = 0;
01966
01967 AST_LIST_LOCK(&confs);
01968 if (ast_atomic_dec_and_test(&conf->refcount)) {
01969
01970 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
01971 conf_map[confno_int] = 0;
01972 }
01973 conf_free(conf);
01974 res = 1;
01975 }
01976 AST_LIST_UNLOCK(&confs);
01977
01978 return res;
01979 }
01980
01981 static int rt_extend_conf(const char *confno)
01982 {
01983 char currenttime[32];
01984 char endtime[32];
01985 struct timeval now;
01986 struct ast_tm tm;
01987 struct ast_variable *var, *orig_var;
01988 char bookid[51];
01989
01990 if (!extendby) {
01991 return 0;
01992 }
01993
01994 now = ast_tvnow();
01995
01996 ast_localtime(&now, &tm, NULL);
01997 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
01998
01999 var = ast_load_realtime("meetme", "confno",
02000 confno, "startTime<= ", currenttime,
02001 "endtime>= ", currenttime, NULL);
02002
02003 orig_var = var;
02004
02005
02006 while (var) {
02007 if (!strcasecmp(var->name, "bookid")) {
02008 ast_copy_string(bookid, var->value, sizeof(bookid));
02009 }
02010 if (!strcasecmp(var->name, "endtime")) {
02011 ast_copy_string(endtime, var->value, sizeof(endtime));
02012 }
02013
02014 var = var->next;
02015 }
02016 ast_variables_destroy(orig_var);
02017
02018 ast_strptime(endtime, DATE_FORMAT, &tm);
02019 now = ast_mktime(&tm, NULL);
02020
02021 now.tv_sec += extendby;
02022
02023 ast_localtime(&now, &tm, NULL);
02024 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02025 strcat(currenttime, "0");
02026
02027 var = ast_load_realtime("meetme", "confno",
02028 confno, "startTime<= ", currenttime,
02029 "endtime>= ", currenttime, NULL);
02030
02031
02032 if (!var) {
02033 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
02034 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
02035 return 0;
02036
02037 }
02038
02039 ast_variables_destroy(var);
02040 return -1;
02041 }
02042
02043 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
02044 {
02045 char *original_moh;
02046
02047 ast_channel_lock(chan);
02048 original_moh = ast_strdupa(chan->musicclass);
02049 ast_string_field_set(chan, musicclass, musicclass);
02050 ast_channel_unlock(chan);
02051
02052 ast_moh_start(chan, original_moh, NULL);
02053
02054 ast_channel_lock(chan);
02055 ast_string_field_set(chan, musicclass, original_moh);
02056 ast_channel_unlock(chan);
02057 }
02058
02059 static const char *get_announce_filename(enum announcetypes type)
02060 {
02061 switch (type) {
02062 case CONF_HASLEFT:
02063 return "conf-hasleft";
02064 break;
02065 case CONF_HASJOIN:
02066 return "conf-hasjoin";
02067 break;
02068 default:
02069 return "";
02070 }
02071 }
02072
02073 static void *announce_thread(void *data)
02074 {
02075 struct announce_listitem *current;
02076 struct ast_conference *conf = data;
02077 int res;
02078 char filename[PATH_MAX] = "";
02079 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
02080 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
02081
02082 while (!conf->announcethread_stop) {
02083 ast_mutex_lock(&conf->announcelistlock);
02084 if (conf->announcethread_stop) {
02085 ast_mutex_unlock(&conf->announcelistlock);
02086 break;
02087 }
02088 if (AST_LIST_EMPTY(&conf->announcelist))
02089 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
02090
02091 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
02092 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
02093
02094 ast_mutex_unlock(&conf->announcelistlock);
02095 if (conf->announcethread_stop) {
02096 break;
02097 }
02098
02099 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
02100 ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
02101 if (!ast_fileexists(current->namerecloc, NULL, NULL))
02102 continue;
02103 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
02104 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
02105 res = ast_waitstream(current->confchan, "");
02106 if (!res) {
02107 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
02108 if (!ast_streamfile(current->confchan, filename, current->language))
02109 ast_waitstream(current->confchan, "");
02110 }
02111 }
02112 if (current->announcetype == CONF_HASLEFT) {
02113 ast_filedelete(current->namerecloc, NULL);
02114 }
02115 }
02116 }
02117
02118
02119 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
02120 ast_filedelete(current->namerecloc, NULL);
02121 ao2_ref(current, -1);
02122 }
02123 return NULL;
02124 }
02125
02126 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
02127 {
02128 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
02129 return 1;
02130 }
02131
02132 return (chan->_state == AST_STATE_UP);
02133 }
02134
02135 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
02136 {
02137 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
02138 "Channel: %s\r\n"
02139 "Uniqueid: %s\r\n"
02140 "Meetme: %s\r\n"
02141 "Usernum: %d\r\n"
02142 "Status: %s\r\n",
02143 chan->name, chan->uniqueid, conf->confno, user->user_no, talking ? "on" : "off");
02144 }
02145
02146 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
02147 {
02148 int last_talking = user->talking;
02149 if (last_talking == talking)
02150 return;
02151
02152 user->talking = talking;
02153
02154 if (monitor) {
02155
02156 int was_talking = (last_talking > 0);
02157 int now_talking = (talking > 0);
02158 if (was_talking != now_talking) {
02159 send_talking_event(chan, conf, user, now_talking);
02160 }
02161 }
02162 }
02163
02164 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
02165 {
02166 struct ast_conf_user *user = obj;
02167
02168
02169 if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
02170 user->adminflags |= ADMINFLAG_KICKME;
02171 }
02172 return 0;
02173 }
02174
02175 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
02176 {
02177 struct ast_conf_user *user = obj;
02178
02179
02180 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
02181 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
02182 }
02183 return 0;
02184 }
02185
02186 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
02187 {
02188 struct ast_conf_user *user = obj;
02189
02190
02191 if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
02192 user->adminflags |= ADMINFLAG_MUTED;
02193 }
02194 return 0;
02195 }
02196
02197 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
02198 {
02199 struct ast_conf_user *user = NULL;
02200 int fd;
02201 struct dahdi_confinfo dahdic, dahdic_empty;
02202 struct ast_frame *f;
02203 struct ast_channel *c;
02204 struct ast_frame fr;
02205 int outfd;
02206 int ms;
02207 int nfds;
02208 int res;
02209 int retrydahdi;
02210 int origfd;
02211 int musiconhold = 0, mohtempstopped = 0;
02212 int firstpass = 0;
02213 int lastmarked = 0;
02214 int currentmarked = 0;
02215 int ret = -1;
02216 int x;
02217 int menu_active = 0;
02218 int menu8_active = 0;
02219 int talkreq_manager = 0;
02220 int using_pseudo = 0;
02221 int duration = 20;
02222 int sent_event = 0;
02223 int checked = 0;
02224 int announcement_played = 0;
02225 struct timeval now;
02226 struct ast_dsp *dsp = NULL;
02227 struct ast_app *agi_app;
02228 char *agifile, *mod_speex;
02229 const char *agifiledefault = "conf-background.agi", *tmpvar;
02230 char meetmesecs[30] = "";
02231 char exitcontext[AST_MAX_CONTEXT] = "";
02232 char recordingtmp[AST_MAX_EXTENSION] = "";
02233 char members[10] = "";
02234 int dtmf, opt_waitmarked_timeout = 0;
02235 time_t timeout = 0;
02236 struct dahdi_bufferinfo bi;
02237 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
02238 char *buf = __buf + AST_FRIENDLY_OFFSET;
02239 char *exitkeys = NULL;
02240 unsigned int calldurationlimit = 0;
02241 long timelimit = 0;
02242 long play_warning = 0;
02243 long warning_freq = 0;
02244 const char *warning_sound = NULL;
02245 const char *end_sound = NULL;
02246 char *parse;
02247 long time_left_ms = 0;
02248 struct timeval nexteventts = { 0, };
02249 int to;
02250 int setusercount = 0;
02251 int confsilence = 0, totalsilence = 0;
02252
02253 if (!(user = ao2_alloc(sizeof(*user), NULL))) {
02254 return ret;
02255 }
02256
02257
02258 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
02259 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
02260 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
02261 (opt_waitmarked_timeout > 0)) {
02262 timeout = time(NULL) + opt_waitmarked_timeout;
02263 }
02264
02265 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
02266 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
02267 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
02268 }
02269
02270 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
02271 char *limit_str, *warning_str, *warnfreq_str;
02272 const char *var;
02273
02274 parse = optargs[OPT_ARG_DURATION_LIMIT];
02275 limit_str = strsep(&parse, ":");
02276 warning_str = strsep(&parse, ":");
02277 warnfreq_str = parse;
02278
02279 timelimit = atol(limit_str);
02280 if (warning_str)
02281 play_warning = atol(warning_str);
02282 if (warnfreq_str)
02283 warning_freq = atol(warnfreq_str);
02284
02285 if (!timelimit) {
02286 timelimit = play_warning = warning_freq = 0;
02287 warning_sound = NULL;
02288 } else if (play_warning > timelimit) {
02289 if (!warning_freq) {
02290 play_warning = 0;
02291 } else {
02292 while (play_warning > timelimit)
02293 play_warning -= warning_freq;
02294 if (play_warning < 1)
02295 play_warning = warning_freq = 0;
02296 }
02297 }
02298
02299 ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
02300 if (play_warning) {
02301 ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
02302 }
02303 if (warning_freq) {
02304 ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
02305 }
02306
02307 ast_channel_lock(chan);
02308 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
02309 var = ast_strdupa(var);
02310 }
02311 ast_channel_unlock(chan);
02312
02313 warning_sound = var ? var : "timeleft";
02314
02315 ast_channel_lock(chan);
02316 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
02317 var = ast_strdupa(var);
02318 }
02319 ast_channel_unlock(chan);
02320
02321 end_sound = var ? var : NULL;
02322
02323
02324 calldurationlimit = 0;
02325
02326 if (!play_warning && !end_sound && timelimit) {
02327 calldurationlimit = timelimit / 1000;
02328 timelimit = play_warning = warning_freq = 0;
02329 } else {
02330 ast_debug(2, "Limit Data for this call:\n");
02331 ast_debug(2, "- timelimit = %ld\n", timelimit);
02332 ast_debug(2, "- play_warning = %ld\n", play_warning);
02333 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
02334 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
02335 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
02336 }
02337 }
02338
02339
02340 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
02341 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
02342 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
02343 else
02344 exitkeys = ast_strdupa("#");
02345 }
02346
02347 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
02348 if (!conf->recordingfilename) {
02349 const char *var;
02350 ast_channel_lock(chan);
02351 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
02352 conf->recordingfilename = ast_strdup(var);
02353 }
02354 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
02355 conf->recordingformat = ast_strdup(var);
02356 }
02357 ast_channel_unlock(chan);
02358 if (!conf->recordingfilename) {
02359 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
02360 conf->recordingfilename = ast_strdup(recordingtmp);
02361 }
02362 if (!conf->recordingformat) {
02363 conf->recordingformat = ast_strdup("wav");
02364 }
02365 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
02366 conf->confno, conf->recordingfilename, conf->recordingformat);
02367 }
02368 }
02369
02370 ast_mutex_lock(&conf->recordthreadlock);
02371 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
02372 ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
02373 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
02374 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
02375 dahdic.chan = 0;
02376 dahdic.confno = conf->dahdiconf;
02377 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
02378 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
02379 ast_log(LOG_WARNING, "Error starting listen channel\n");
02380 ast_hangup(conf->lchan);
02381 conf->lchan = NULL;
02382 } else {
02383 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
02384 }
02385 }
02386 ast_mutex_unlock(&conf->recordthreadlock);
02387
02388 ast_mutex_lock(&conf->announcethreadlock);
02389 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
02390 (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
02391 ast_mutex_init(&conf->announcelistlock);
02392 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
02393 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
02394 }
02395 ast_mutex_unlock(&conf->announcethreadlock);
02396
02397 time(&user->jointime);
02398
02399 user->timelimit = timelimit;
02400 user->play_warning = play_warning;
02401 user->warning_freq = warning_freq;
02402 user->warning_sound = warning_sound;
02403 user->end_sound = end_sound;
02404
02405 if (calldurationlimit > 0) {
02406 time(&user->kicktime);
02407 user->kicktime = user->kicktime + calldurationlimit;
02408 }
02409
02410 if (ast_tvzero(user->start_time))
02411 user->start_time = ast_tvnow();
02412 time_left_ms = user->timelimit;
02413
02414 if (user->timelimit) {
02415 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
02416 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
02417 }
02418
02419 if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
02420
02421 if (!ast_streamfile(chan, "conf-locked", chan->language))
02422 ast_waitstream(chan, "");
02423 goto outrun;
02424 }
02425
02426 ast_mutex_lock(&conf->playlock);
02427
02428 if (rt_schedule && conf->maxusers) {
02429 if (conf->users >= conf->maxusers) {
02430
02431 if (!ast_streamfile(chan, "conf-full", chan->language))
02432 ast_waitstream(chan, "");
02433 ast_mutex_unlock(&conf->playlock);
02434 goto outrun;
02435 }
02436 }
02437
02438 ao2_lock(conf->usercontainer);
02439 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
02440 user->user_no++;
02441 ao2_link(conf->usercontainer, user);
02442 ao2_unlock(conf->usercontainer);
02443
02444 user->chan = chan;
02445 user->userflags = *confflags;
02446 user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
02447 user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
02448 user->talking = -1;
02449
02450 ast_mutex_unlock(&conf->playlock);
02451
02452 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
02453 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
02454 char destdir[PATH_MAX];
02455
02456 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
02457
02458 if (ast_mkdir(destdir, 0777) != 0) {
02459 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
02460 goto outrun;
02461 }
02462
02463 snprintf(user->namerecloc, sizeof(user->namerecloc),
02464 "%s/meetme-username-%s-%d", destdir,
02465 conf->confno, user->user_no);
02466 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))
02467 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
02468 else
02469 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
02470 if (res == -1)
02471 goto outrun;
02472 }
02473
02474 ast_mutex_lock(&conf->playlock);
02475
02476 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
02477 conf->markedusers++;
02478 conf->users++;
02479 if (rt_log_members) {
02480
02481 snprintf(members, sizeof(members), "%d", conf->users);
02482 ast_realtime_require_field("meetme",
02483 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
02484 "members", RQ_UINTEGER1, strlen(members),
02485 NULL);
02486 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
02487 }
02488 setusercount = 1;
02489
02490
02491 if (conf->users == 1)
02492 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
02493
02494 ast_mutex_unlock(&conf->playlock);
02495
02496
02497 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
02498
02499 if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
02500 ast_channel_lock(chan);
02501 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
02502 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
02503 } else if (!ast_strlen_zero(chan->macrocontext)) {
02504 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
02505 } else {
02506 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
02507 }
02508 ast_channel_unlock(chan);
02509 }
02510
02511
02512 if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
02513 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
02514 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
02515 ast_waitstream(chan, "");
02516 }
02517 }
02518
02519 if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
02520 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
02521 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
02522 ast_waitstream(chan, "");
02523 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
02524 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
02525 ast_waitstream(chan, "");
02526 }
02527
02528 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
02529 conf->users > 1) {
02530 int keepplaying = 1;
02531
02532 if (conf->users == 2) {
02533 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
02534 res = ast_waitstream(chan, AST_DIGIT_ANY);
02535 ast_stopstream(chan);
02536 if (res > 0)
02537 keepplaying = 0;
02538 else if (res == -1)
02539 goto outrun;
02540 }
02541 } else {
02542 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
02543 res = ast_waitstream(chan, AST_DIGIT_ANY);
02544 ast_stopstream(chan);
02545 if (res > 0)
02546 keepplaying = 0;
02547 else if (res == -1)
02548 goto outrun;
02549 }
02550 if (keepplaying) {
02551 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
02552 if (res > 0)
02553 keepplaying = 0;
02554 else if (res == -1)
02555 goto outrun;
02556 }
02557 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
02558 res = ast_waitstream(chan, AST_DIGIT_ANY);
02559 ast_stopstream(chan);
02560 if (res > 0)
02561 keepplaying = 0;
02562 else if (res == -1)
02563 goto outrun;
02564 }
02565 }
02566 }
02567
02568 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
02569
02570 ast_indicate(chan, -1);
02571 }
02572
02573 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
02574 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
02575 goto outrun;
02576 }
02577
02578 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
02579 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
02580 goto outrun;
02581 }
02582
02583
02584 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
02585 ast_free(mod_speex);
02586 ast_func_write(chan, "DENOISE(rx)", "on");
02587 }
02588
02589 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
02590 user->dahdichannel = !retrydahdi;
02591
02592 dahdiretry:
02593 origfd = chan->fds[0];
02594 if (retrydahdi) {
02595
02596 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
02597 if (fd < 0) {
02598 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
02599 goto outrun;
02600 }
02601 using_pseudo = 1;
02602
02603 memset(&bi, 0, sizeof(bi));
02604 bi.bufsize = CONF_SIZE / 2;
02605 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
02606 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
02607 bi.numbufs = audio_buffers;
02608 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
02609 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
02610 close(fd);
02611 goto outrun;
02612 }
02613 x = 1;
02614 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
02615 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
02616 close(fd);
02617 goto outrun;
02618 }
02619 nfds = 1;
02620 } else {
02621
02622 fd = chan->fds[0];
02623 nfds = 0;
02624 }
02625 memset(&dahdic, 0, sizeof(dahdic));
02626 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
02627
02628 dahdic.chan = 0;
02629 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
02630 ast_log(LOG_WARNING, "Error getting conference\n");
02631 close(fd);
02632 goto outrun;
02633 }
02634 if (dahdic.confmode) {
02635
02636 if (!retrydahdi) {
02637 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
02638 retrydahdi = 1;
02639 goto dahdiretry;
02640 }
02641 }
02642 memset(&dahdic, 0, sizeof(dahdic));
02643
02644 dahdic.chan = 0;
02645 dahdic.confno = conf->dahdiconf;
02646
02647 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
02648 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
02649 struct announce_listitem *item;
02650 if (!(item = ao2_alloc(sizeof(*item), NULL)))
02651 goto outrun;
02652 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
02653 ast_copy_string(item->language, chan->language, sizeof(item->language));
02654 item->confchan = conf->chan;
02655 item->confusers = conf->users;
02656 item->announcetype = CONF_HASJOIN;
02657 ast_mutex_lock(&conf->announcelistlock);
02658 ao2_ref(item, +1);
02659 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
02660 ast_cond_signal(&conf->announcelist_addition);
02661 ast_mutex_unlock(&conf->announcelistlock);
02662
02663 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
02664 ;
02665 }
02666 ao2_ref(item, -1);
02667 }
02668
02669 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
02670 dahdic.confmode = DAHDI_CONF_CONF;
02671 else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
02672 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
02673 else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
02674 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
02675 else
02676 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
02677
02678 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02679 ast_log(LOG_WARNING, "Error setting conference\n");
02680 close(fd);
02681 goto outrun;
02682 }
02683 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
02684
02685 if (!sent_event) {
02686 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
02687 "Channel: %s\r\n"
02688 "Uniqueid: %s\r\n"
02689 "Meetme: %s\r\n"
02690 "Usernum: %d\r\n"
02691 "CallerIDnum: %s\r\n"
02692 "CallerIDname: %s\r\n"
02693 "ConnectedLineNum: %s\r\n"
02694 "ConnectedLineName: %s\r\n",
02695 chan->name, chan->uniqueid, conf->confno,
02696 user->user_no,
02697 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
02698 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>"),
02699 S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
02700 S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<unknown>")
02701 );
02702 sent_event = 1;
02703 }
02704
02705 if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
02706 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
02707 firstpass = 1;
02708 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
02709 if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
02710 (conf->markedusers >= 1))) {
02711 conf_play(chan, conf, ENTER);
02712 }
02713 }
02714
02715 conf_flush(fd, chan);
02716
02717 if (dsp)
02718 ast_dsp_free(dsp);
02719
02720 if (!(dsp = ast_dsp_new())) {
02721 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
02722 res = -1;
02723 }
02724
02725 if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
02726
02727
02728
02729 ast_channel_lock(chan);
02730 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
02731 agifile = ast_strdupa(tmpvar);
02732 } else {
02733 agifile = ast_strdupa(agifiledefault);
02734 }
02735 ast_channel_unlock(chan);
02736
02737 if (user->dahdichannel) {
02738
02739 x = 1;
02740 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02741 }
02742
02743 agi_app = pbx_findapp("agi");
02744 if (agi_app) {
02745 ret = pbx_exec(chan, agi_app, agifile);
02746 } else {
02747 ast_log(LOG_WARNING, "Could not find application (agi)\n");
02748 ret = -2;
02749 }
02750 if (user->dahdichannel) {
02751
02752 x = 0;
02753 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02754 }
02755 } else {
02756 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
02757
02758 x = 1;
02759 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02760 }
02761 for (;;) {
02762 int menu_was_active = 0;
02763
02764 outfd = -1;
02765 ms = -1;
02766 now = ast_tvnow();
02767
02768 if (rt_schedule && conf->endtime) {
02769 char currenttime[32];
02770 long localendtime = 0;
02771 int extended = 0;
02772 struct ast_tm tm;
02773 struct ast_variable *var, *origvar;
02774 struct timeval tmp;
02775
02776 if (now.tv_sec % 60 == 0) {
02777 if (!checked) {
02778 ast_localtime(&now, &tm, NULL);
02779 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02780 var = origvar = ast_load_realtime("meetme", "confno",
02781 conf->confno, "starttime <=", currenttime,
02782 "endtime >=", currenttime, NULL);
02783
02784 for ( ; var; var = var->next) {
02785 if (!strcasecmp(var->name, "endtime")) {
02786 struct ast_tm endtime_tm;
02787 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
02788 tmp = ast_mktime(&endtime_tm, NULL);
02789 localendtime = tmp.tv_sec;
02790 }
02791 }
02792 ast_variables_destroy(origvar);
02793
02794
02795
02796 if (localendtime > conf->endtime){
02797 conf->endtime = localendtime;
02798 extended = 1;
02799 }
02800
02801 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
02802 ast_verbose("Quitting time...\n");
02803 goto outrun;
02804 }
02805
02806 if (!announcement_played && conf->endalert) {
02807 if (now.tv_sec + conf->endalert >= conf->endtime) {
02808 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
02809 ast_waitstream(chan, "");
02810 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
02811 if (!ast_streamfile(chan, "minutes", chan->language))
02812 ast_waitstream(chan, "");
02813 if (musiconhold) {
02814 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02815 }
02816 announcement_played = 1;
02817 }
02818 }
02819
02820 if (extended) {
02821 announcement_played = 0;
02822 }
02823
02824 checked = 1;
02825 }
02826 } else {
02827 checked = 0;
02828 }
02829 }
02830
02831 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
02832 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
02833 ret = 0;
02834 } else {
02835 ret = -1;
02836 }
02837 break;
02838 }
02839
02840 to = -1;
02841 if (user->timelimit) {
02842 int minutes = 0, seconds = 0, remain = 0;
02843
02844 to = ast_tvdiff_ms(nexteventts, now);
02845 if (to < 0) {
02846 to = 0;
02847 }
02848 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
02849 if (time_left_ms < to) {
02850 to = time_left_ms;
02851 }
02852
02853 if (time_left_ms <= 0) {
02854 if (user->end_sound) {
02855 res = ast_streamfile(chan, user->end_sound, chan->language);
02856 res = ast_waitstream(chan, "");
02857 }
02858 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
02859 ret = 0;
02860 } else {
02861 ret = -1;
02862 }
02863 break;
02864 }
02865
02866 if (!to) {
02867 if (time_left_ms >= 5000) {
02868
02869 remain = (time_left_ms + 500) / 1000;
02870 if (remain / 60 >= 1) {
02871 minutes = remain / 60;
02872 seconds = remain % 60;
02873 } else {
02874 seconds = remain;
02875 }
02876
02877
02878 if (user->warning_sound && user->play_warning) {
02879 if (!strcmp(user->warning_sound, "timeleft")) {
02880
02881 res = ast_streamfile(chan, "vm-youhave", chan->language);
02882 res = ast_waitstream(chan, "");
02883 if (minutes) {
02884 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
02885 res = ast_streamfile(chan, "queue-minutes", chan->language);
02886 res = ast_waitstream(chan, "");
02887 }
02888 if (seconds) {
02889 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
02890 res = ast_streamfile(chan, "queue-seconds", chan->language);
02891 res = ast_waitstream(chan, "");
02892 }
02893 } else {
02894 res = ast_streamfile(chan, user->warning_sound, chan->language);
02895 res = ast_waitstream(chan, "");
02896 }
02897 if (musiconhold) {
02898 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02899 }
02900 }
02901 }
02902 if (user->warning_freq) {
02903 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
02904 } else {
02905 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
02906 }
02907 }
02908 }
02909
02910 now = ast_tvnow();
02911 if (timeout && now.tv_sec >= timeout) {
02912 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
02913 ret = 0;
02914 } else {
02915 ret = -1;
02916 }
02917 break;
02918 }
02919
02920
02921
02922
02923 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual) {
02924 set_talk_volume(user, user->listen.desired);
02925 }
02926
02927 menu_was_active = menu_active;
02928
02929 currentmarked = conf->markedusers;
02930 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
02931 ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
02932 ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
02933 lastmarked == 0) {
02934 if (currentmarked == 1 && conf->users > 1) {
02935 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
02936 if (conf->users - 1 == 1) {
02937 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
02938 ast_waitstream(chan, "");
02939 }
02940 } else {
02941 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
02942 ast_waitstream(chan, "");
02943 }
02944 }
02945 }
02946 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
02947 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
02948 ast_waitstream(chan, "");
02949 }
02950 }
02951 }
02952
02953
02954 user->userflags = *confflags;
02955
02956 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
02957 if (currentmarked == 0) {
02958 if (lastmarked != 0) {
02959 if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
02960 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
02961 ast_waitstream(chan, "");
02962 }
02963 }
02964 if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
02965 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
02966 ret = 0;
02967 }
02968 break;
02969 } else {
02970 dahdic.confmode = DAHDI_CONF_CONF;
02971 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02972 ast_log(LOG_WARNING, "Error setting conference\n");
02973 close(fd);
02974 goto outrun;
02975 }
02976 }
02977 }
02978 if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
02979 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02980 musiconhold = 1;
02981 }
02982 } else if (currentmarked >= 1 && lastmarked == 0) {
02983
02984 timeout = 0;
02985 if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
02986 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
02987 } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
02988 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
02989 } else {
02990 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
02991 }
02992 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02993 ast_log(LOG_WARNING, "Error setting conference\n");
02994 close(fd);
02995 goto outrun;
02996 }
02997 if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
02998 ast_moh_stop(chan);
02999 musiconhold = 0;
03000 }
03001 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03002 !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03003 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
03004 ast_waitstream(chan, "");
03005 }
03006 conf_play(chan, conf, ENTER);
03007 }
03008 }
03009 }
03010
03011
03012 if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03013 if (conf->users == 1) {
03014 if (!musiconhold) {
03015 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03016 musiconhold = 1;
03017 }
03018 } else {
03019 if (musiconhold) {
03020 ast_moh_stop(chan);
03021 musiconhold = 0;
03022 }
03023 }
03024 }
03025
03026
03027 if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03028 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03029 ret = 0;
03030 } else {
03031 ret = -1;
03032 }
03033 break;
03034 }
03035
03036
03037
03038
03039 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
03040 dahdic.confmode ^= DAHDI_CONF_TALKER;
03041 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03042 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03043 ret = -1;
03044 break;
03045 }
03046
03047
03048 if (ast_test_flag64(confflags, (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
03049 set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03050 }
03051
03052 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03053 "Channel: %s\r\n"
03054 "Uniqueid: %s\r\n"
03055 "Meetme: %s\r\n"
03056 "Usernum: %i\r\n"
03057 "Status: on\r\n",
03058 chan->name, chan->uniqueid, conf->confno, user->user_no);
03059 }
03060
03061
03062 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
03063 dahdic.confmode |= DAHDI_CONF_TALKER;
03064 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03065 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03066 ret = -1;
03067 break;
03068 }
03069
03070 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03071 "Channel: %s\r\n"
03072 "Uniqueid: %s\r\n"
03073 "Meetme: %s\r\n"
03074 "Usernum: %i\r\n"
03075 "Status: off\r\n",
03076 chan->name, chan->uniqueid, conf->confno, user->user_no);
03077 }
03078
03079 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
03080 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
03081 talkreq_manager = 1;
03082
03083 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03084 "Channel: %s\r\n"
03085 "Uniqueid: %s\r\n"
03086 "Meetme: %s\r\n"
03087 "Usernum: %i\r\n"
03088 "Status: on\r\n",
03089 chan->name, chan->uniqueid, conf->confno, user->user_no);
03090 }
03091
03092
03093 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
03094 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
03095 talkreq_manager = 0;
03096 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03097 "Channel: %s\r\n"
03098 "Uniqueid: %s\r\n"
03099 "Meetme: %s\r\n"
03100 "Usernum: %i\r\n"
03101 "Status: off\r\n",
03102 chan->name, chan->uniqueid, conf->confno, user->user_no);
03103 }
03104
03105
03106 if (user->adminflags & ADMINFLAG_KICKME) {
03107
03108 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03109 !ast_streamfile(chan, "conf-kicked", chan->language)) {
03110 ast_waitstream(chan, "");
03111 }
03112 ret = 0;
03113 break;
03114 }
03115
03116 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
03117
03118 if (c) {
03119 char dtmfstr[2] = "";
03120
03121 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
03122 if (using_pseudo) {
03123
03124 close(fd);
03125 using_pseudo = 0;
03126 }
03127 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
03128 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
03129 user->dahdichannel = !retrydahdi;
03130 goto dahdiretry;
03131 }
03132 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03133 f = ast_read_noaudio(c);
03134 } else {
03135 f = ast_read(c);
03136 }
03137 if (!f) {
03138 break;
03139 }
03140 if (f->frametype == AST_FRAME_DTMF) {
03141 dtmfstr[0] = f->subclass.integer;
03142 dtmfstr[1] = '\0';
03143 }
03144
03145 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec == AST_FORMAT_SLINEAR)) {
03146 if (user->talk.actual) {
03147 ast_frame_adjust_volume(f, user->talk.actual);
03148 }
03149
03150 if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
03151 if (user->talking == -1) {
03152 user->talking = 0;
03153 }
03154
03155 res = ast_dsp_silence(dsp, f, &totalsilence);
03156 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
03157 set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03158 }
03159
03160 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
03161 set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03162 }
03163 }
03164 if (using_pseudo) {
03165
03166
03167
03168
03169
03170
03171
03172
03173
03174
03175
03176
03177 if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
03178 careful_write(fd, f->data.ptr, f->datalen, 0);
03179 }
03180 }
03181 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
03182 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03183 conf_queue_dtmf(conf, user, f);
03184 }
03185 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
03186 ast_log(LOG_WARNING, "Error setting conference\n");
03187 close(fd);
03188 ast_frfree(f);
03189 goto outrun;
03190 }
03191
03192
03193
03194
03195 if (!menu_active && user->talk.desired && !user->talk.actual) {
03196 set_talk_volume(user, 0);
03197 }
03198
03199 if (musiconhold) {
03200 ast_moh_stop(chan);
03201 }
03202 if (menu8_active) {
03203
03204 dtmf = f->subclass.integer;
03205 if (dtmf) {
03206 int keepplaying;
03207 int playednamerec;
03208 struct ao2_iterator user_iter;
03209 struct ast_conf_user *usr = NULL;
03210 switch(dtmf) {
03211 case '1':
03212 keepplaying = 1;
03213 playednamerec = 0;
03214 if (conf->users == 1) {
03215 if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", chan->language)) {
03216 res = ast_waitstream(chan, AST_DIGIT_ANY);
03217 ast_stopstream(chan);
03218 if (res > 0)
03219 keepplaying = 0;
03220 }
03221 } else if (conf->users == 2) {
03222 if (keepplaying && !ast_streamfile(chan, "conf-onlyone", chan->language)) {
03223 res = ast_waitstream(chan, AST_DIGIT_ANY);
03224 ast_stopstream(chan);
03225 if (res > 0)
03226 keepplaying = 0;
03227 }
03228 } else {
03229 if (keepplaying && !ast_streamfile(chan, "conf-thereare", chan->language)) {
03230 res = ast_waitstream(chan, AST_DIGIT_ANY);
03231 ast_stopstream(chan);
03232 if (res > 0)
03233 keepplaying = 0;
03234 }
03235 if (keepplaying) {
03236 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
03237 if (res > 0)
03238 keepplaying = 0;
03239 }
03240 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
03241 res = ast_waitstream(chan, AST_DIGIT_ANY);
03242 ast_stopstream(chan);
03243 if (res > 0)
03244 keepplaying = 0;
03245 }
03246 }
03247 user_iter = ao2_iterator_init(conf->usercontainer, 0);
03248 while((usr = ao2_iterator_next(&user_iter))) {
03249 if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
03250 if (keepplaying && !ast_streamfile(chan, usr->namerecloc, chan->language)) {
03251 res = ast_waitstream(chan, AST_DIGIT_ANY);
03252 ast_stopstream(chan);
03253 if (res > 0)
03254 keepplaying = 0;
03255 }
03256 playednamerec = 1;
03257 }
03258 ao2_ref(usr, -1);
03259 }
03260 ao2_iterator_destroy(&user_iter);
03261 if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", chan->language)) {
03262 res = ast_waitstream(chan, AST_DIGIT_ANY);
03263 ast_stopstream(chan);
03264 if (res > 0)
03265 keepplaying = 0;
03266 }
03267 break;
03268 case '2':
03269 if (conf->users == 1) {
03270 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
03271 ast_waitstream(chan, "");
03272 } else {
03273 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
03274 }
03275 ast_stopstream(chan);
03276 break;
03277 case '3':
03278 if(conf->gmuted) {
03279 conf->gmuted = 0;
03280 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
03281 if (!ast_streamfile(chan, "conf-now-unmuted", chan->language))
03282 ast_waitstream(chan, "");
03283 } else {
03284 conf->gmuted = 1;
03285 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
03286 if (!ast_streamfile(chan, "conf-now-muted", chan->language))
03287 ast_waitstream(chan, "");
03288 }
03289 ast_stopstream(chan);
03290 break;
03291 case '4':
03292 if (conf->recording != MEETME_RECORD_ACTIVE) {
03293 ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
03294
03295 if (!conf->recordingfilename) {
03296 const char *var;
03297 ast_channel_lock(chan);
03298 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03299 conf->recordingfilename = ast_strdup(var);
03300 }
03301 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03302 conf->recordingformat = ast_strdup(var);
03303 }
03304 ast_channel_unlock(chan);
03305 if (!conf->recordingfilename) {
03306 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
03307 conf->recordingfilename = ast_strdup(recordingtmp);
03308 }
03309 if (!conf->recordingformat) {
03310 conf->recordingformat = ast_strdup("wav");
03311 }
03312 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
03313 conf->confno, conf->recordingfilename, conf->recordingformat);
03314 }
03315
03316 ast_mutex_lock(&conf->recordthreadlock);
03317 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
03318 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
03319 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
03320 dahdic.chan = 0;
03321 dahdic.confno = conf->dahdiconf;
03322 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
03323 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
03324 ast_log(LOG_WARNING, "Error starting listen channel\n");
03325 ast_hangup(conf->lchan);
03326 conf->lchan = NULL;
03327 } else {
03328 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
03329 }
03330 }
03331 ast_mutex_unlock(&conf->recordthreadlock);
03332
03333 if (!ast_streamfile(chan, "conf-now-recording", chan->language))
03334 ast_waitstream(chan, "");
03335
03336 }
03337
03338 ast_stopstream(chan);
03339 break;
03340 default:
03341 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
03342 ast_waitstream(chan, "");
03343 ast_stopstream(chan);
03344 break;
03345 }
03346 }
03347
03348 menu8_active = 0;
03349 menu_active = 0;
03350 } else if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03351
03352 if (!menu_active) {
03353 menu_active = 1;
03354
03355 if (!ast_streamfile(chan, "conf-adminmenu-162", chan->language)) {
03356 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
03357 ast_stopstream(chan);
03358 } else {
03359 dtmf = 0;
03360 }
03361 } else {
03362 dtmf = f->subclass.integer;
03363 }
03364 if (dtmf) {
03365 switch(dtmf) {
03366 case '1':
03367 menu_active = 0;
03368
03369
03370 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
03371 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
03372 } else {
03373 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
03374 }
03375
03376 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03377 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
03378 ast_waitstream(chan, "");
03379 }
03380 } else {
03381 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
03382 ast_waitstream(chan, "");
03383 }
03384 }
03385 break;
03386 case '2':
03387 menu_active = 0;
03388 if (conf->locked) {
03389 conf->locked = 0;
03390 if (!ast_streamfile(chan, "conf-unlockednow", chan->language)) {
03391 ast_waitstream(chan, "");
03392 }
03393 } else {
03394 conf->locked = 1;
03395 if (!ast_streamfile(chan, "conf-lockednow", chan->language)) {
03396 ast_waitstream(chan, "");
03397 }
03398 }
03399 break;
03400 case '3':
03401 {
03402 struct ast_conf_user *usr = NULL;
03403 int max_no = 0;
03404 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
03405 menu_active = 0;
03406 usr = ao2_find(conf->usercontainer, &max_no, 0);
03407 if ((usr->chan->name == chan->name) || ast_test_flag64(&usr->userflags, CONFFLAG_ADMIN)) {
03408 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
03409 ast_waitstream(chan, "");
03410 }
03411 } else {
03412 usr->adminflags |= ADMINFLAG_KICKME;
03413 }
03414 ao2_ref(usr, -1);
03415 ast_stopstream(chan);
03416 break;
03417 }
03418 case '4':
03419 tweak_listen_volume(user, VOL_DOWN);
03420 break;
03421 case '5':
03422
03423 if (rt_schedule) {
03424 if (!rt_extend_conf(conf->confno)) {
03425 if (!ast_streamfile(chan, "conf-extended", chan->language)) {
03426 ast_waitstream(chan, "");
03427 }
03428 } else {
03429 if (!ast_streamfile(chan, "conf-nonextended", chan->language)) {
03430 ast_waitstream(chan, "");
03431 }
03432 }
03433 ast_stopstream(chan);
03434 }
03435 menu_active = 0;
03436 break;
03437 case '6':
03438 tweak_listen_volume(user, VOL_UP);
03439 break;
03440 case '7':
03441 tweak_talk_volume(user, VOL_DOWN);
03442 break;
03443 case '8':
03444 menu8_active = 1;
03445 break;
03446 case '9':
03447 tweak_talk_volume(user, VOL_UP);
03448 break;
03449 default:
03450 menu_active = 0;
03451
03452 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
03453 ast_waitstream(chan, "");
03454 }
03455 break;
03456 }
03457 }
03458 } else {
03459
03460 if (!menu_active) {
03461 menu_active = 1;
03462 if (!ast_streamfile(chan, "conf-usermenu-162", chan->language)) {
03463 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
03464 ast_stopstream(chan);
03465 } else {
03466 dtmf = 0;
03467 }
03468 } else {
03469 dtmf = f->subclass.integer;
03470 }
03471 if (dtmf) {
03472 switch (dtmf) {
03473 case '1':
03474 menu_active = 0;
03475
03476
03477 user->adminflags ^= ADMINFLAG_SELFMUTED;
03478
03479
03480 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03481 if (!ast_streamfile(chan, "conf-muted", chan->language)) {
03482 ast_waitstream(chan, "");
03483 }
03484 } else {
03485 if (!ast_streamfile(chan, "conf-unmuted", chan->language)) {
03486 ast_waitstream(chan, "");
03487 }
03488 }
03489 break;
03490 case '2':
03491 menu_active = 0;
03492 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
03493 user->adminflags |= ADMINFLAG_T_REQUEST;
03494 }
03495
03496 if (user->adminflags & ADMINFLAG_T_REQUEST) {
03497 if (!ast_streamfile(chan, "beep", chan->language)) {
03498 ast_waitstream(chan, "");
03499 }
03500 }
03501 break;
03502 case '4':
03503 tweak_listen_volume(user, VOL_DOWN);
03504 break;
03505 case '5':
03506
03507 if (rt_schedule) {
03508 rt_extend_conf(conf->confno);
03509 }
03510 menu_active = 0;
03511 break;
03512 case '6':
03513 tweak_listen_volume(user, VOL_UP);
03514 break;
03515 case '7':
03516 tweak_talk_volume(user, VOL_DOWN);
03517 break;
03518 case '8':
03519 menu_active = 0;
03520 break;
03521 case '9':
03522 tweak_talk_volume(user, VOL_UP);
03523 break;
03524 default:
03525 menu_active = 0;
03526 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
03527 ast_waitstream(chan, "");
03528 }
03529 break;
03530 }
03531 }
03532 }
03533 if (musiconhold && !menu_active) {
03534 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03535 }
03536
03537 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03538 ast_log(LOG_WARNING, "Error setting conference\n");
03539 close(fd);
03540 ast_frfree(f);
03541 goto outrun;
03542 }
03543
03544 conf_flush(fd, chan);
03545
03546
03547
03548
03549 } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
03550 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03551 conf_queue_dtmf(conf, user, f);
03552 }
03553
03554 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
03555 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
03556 ret = 0;
03557 ast_frfree(f);
03558 break;
03559 } else {
03560 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
03561 }
03562 } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
03563 (strchr(exitkeys, f->subclass.integer))) {
03564 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
03565
03566 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03567 conf_queue_dtmf(conf, user, f);
03568 }
03569 ret = 0;
03570 ast_frfree(f);
03571 break;
03572 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
03573 && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03574 conf_queue_dtmf(conf, user, f);
03575 } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
03576 switch (f->subclass.integer) {
03577 case AST_CONTROL_HOLD:
03578 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
03579 break;
03580 default:
03581 break;
03582 }
03583 } else if (f->frametype == AST_FRAME_NULL) {
03584
03585 } else if (f->frametype == AST_FRAME_CONTROL) {
03586 switch (f->subclass.integer) {
03587 case AST_CONTROL_BUSY:
03588 case AST_CONTROL_CONGESTION:
03589 ast_frfree(f);
03590 goto outrun;
03591 break;
03592 default:
03593 ast_debug(1,
03594 "Got ignored control frame on channel %s, f->frametype=%d,f->subclass=%d\n",
03595 chan->name, f->frametype, f->subclass.integer);
03596 }
03597 } else {
03598 ast_debug(1,
03599 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
03600 chan->name, f->frametype, f->subclass.integer);
03601 }
03602 ast_frfree(f);
03603 } else if (outfd > -1) {
03604 res = read(outfd, buf, CONF_SIZE);
03605 if (res > 0) {
03606 memset(&fr, 0, sizeof(fr));
03607 fr.frametype = AST_FRAME_VOICE;
03608 fr.subclass.codec = AST_FORMAT_SLINEAR;
03609 fr.datalen = res;
03610 fr.samples = res / 2;
03611 fr.data.ptr = buf;
03612 fr.offset = AST_FRIENDLY_OFFSET;
03613 if (!user->listen.actual &&
03614 (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
03615 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
03616 (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
03617 )) {
03618 int idx;
03619 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
03620 if (chan->rawwriteformat & (1 << idx)) {
03621 break;
03622 }
03623 }
03624 if (idx >= AST_FRAME_BITS) {
03625 goto bailoutandtrynormal;
03626 }
03627 ast_mutex_lock(&conf->listenlock);
03628 if (!conf->transframe[idx]) {
03629 if (conf->origframe) {
03630 if (musiconhold && !ast_dsp_silence(dsp, conf->origframe, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
03631 ast_moh_stop(chan);
03632 mohtempstopped = 1;
03633 }
03634 if (!conf->transpath[idx]) {
03635 conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
03636 }
03637 if (conf->transpath[idx]) {
03638 conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
03639 if (!conf->transframe[idx]) {
03640 conf->transframe[idx] = &ast_null_frame;
03641 }
03642 }
03643 }
03644 }
03645 if (conf->transframe[idx]) {
03646 if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
03647 can_write(chan, confflags)) {
03648 struct ast_frame *cur;
03649
03650
03651
03652 for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
03653 if (ast_write(chan, cur)) {
03654 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
03655 break;
03656 }
03657 }
03658 if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
03659 mohtempstopped = 0;
03660 ast_moh_start(chan, NULL, NULL);
03661 }
03662 }
03663 } else {
03664 ast_mutex_unlock(&conf->listenlock);
03665 goto bailoutandtrynormal;
03666 }
03667 ast_mutex_unlock(&conf->listenlock);
03668 } else {
03669 bailoutandtrynormal:
03670 if (musiconhold && !ast_dsp_silence(dsp, &fr, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
03671 ast_moh_stop(chan);
03672 mohtempstopped = 1;
03673 }
03674 if (user->listen.actual) {
03675 ast_frame_adjust_volume(&fr, user->listen.actual);
03676 }
03677 if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
03678 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
03679 }
03680 if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
03681 mohtempstopped = 0;
03682 ast_moh_start(chan, NULL, NULL);
03683 }
03684 }
03685 } else {
03686 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
03687 }
03688 }
03689 lastmarked = currentmarked;
03690 }
03691 }
03692
03693 if (musiconhold) {
03694 ast_moh_stop(chan);
03695 }
03696
03697 if (using_pseudo) {
03698 close(fd);
03699 } else {
03700
03701 dahdic.chan = 0;
03702 dahdic.confno = 0;
03703 dahdic.confmode = 0;
03704 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03705 ast_log(LOG_WARNING, "Error setting conference\n");
03706 }
03707 }
03708
03709 reset_volumes(user);
03710
03711 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03712 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03713 conf_play(chan, conf, LEAVE);
03714 }
03715
03716 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
03717 struct announce_listitem *item;
03718 if (!(item = ao2_alloc(sizeof(*item), NULL)))
03719 goto outrun;
03720 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03721 ast_copy_string(item->language, chan->language, sizeof(item->language));
03722 item->confchan = conf->chan;
03723 item->confusers = conf->users;
03724 item->announcetype = CONF_HASLEFT;
03725 ast_mutex_lock(&conf->announcelistlock);
03726 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
03727 ast_cond_signal(&conf->announcelist_addition);
03728 ast_mutex_unlock(&conf->announcelistlock);
03729 } else if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03730 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
03731
03732 ast_filedelete(user->namerecloc, NULL);
03733 }
03734
03735 outrun:
03736 AST_LIST_LOCK(&confs);
03737
03738 if (dsp) {
03739 ast_dsp_free(dsp);
03740 }
03741
03742 if (user->user_no) {
03743
03744 now = ast_tvnow();
03745
03746 if (sent_event) {
03747 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeLeave",
03748 "Channel: %s\r\n"
03749 "Uniqueid: %s\r\n"
03750 "Meetme: %s\r\n"
03751 "Usernum: %d\r\n"
03752 "CallerIDNum: %s\r\n"
03753 "CallerIDName: %s\r\n"
03754 "ConnectedLineNum: %s\r\n"
03755 "ConnectedLineName: %s\r\n"
03756 "Duration: %ld\r\n",
03757 chan->name, chan->uniqueid, conf->confno,
03758 user->user_no,
03759 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
03760 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>"),
03761 S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
03762 S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<unknown>"),
03763 (long)(now.tv_sec - user->jointime));
03764 }
03765
03766 if (setusercount) {
03767 conf->users--;
03768 if (rt_log_members) {
03769
03770 snprintf(members, sizeof(members), "%d", conf->users);
03771 ast_realtime_require_field("meetme",
03772 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
03773 "members", RQ_UINTEGER1, strlen(members),
03774 NULL);
03775 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
03776 }
03777 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03778 conf->markedusers--;
03779 }
03780 }
03781
03782 ao2_unlink(conf->usercontainer, user);
03783
03784
03785 if (!conf->users) {
03786 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
03787 }
03788
03789
03790 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
03791 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
03792
03793
03794 if (rt_schedule) {
03795 pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
03796 }
03797 }
03798 ao2_ref(user, -1);
03799 AST_LIST_UNLOCK(&confs);
03800
03801 return ret;
03802 }
03803
03804 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
03805 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags, int *too_early, char **optargs)
03806 {
03807 struct ast_variable *var, *origvar;
03808 struct ast_conference *cnf;
03809
03810 *too_early = 0;
03811
03812
03813 AST_LIST_LOCK(&confs);
03814 AST_LIST_TRAVERSE(&confs, cnf, list) {
03815 if (!strcmp(confno, cnf->confno)) {
03816 break;
03817 }
03818 }
03819 if (cnf) {
03820 cnf->refcount += refcount;
03821 }
03822 AST_LIST_UNLOCK(&confs);
03823
03824 if (!cnf) {
03825 char *pin = NULL, *pinadmin = NULL;
03826 int maxusers = 0;
03827 struct timeval now;
03828 char recordingfilename[256] = "";
03829 char recordingformat[11] = "";
03830 char currenttime[32] = "";
03831 char eatime[32] = "";
03832 char bookid[51] = "";
03833 char recordingtmp[AST_MAX_EXTENSION] = "";
03834 char useropts[OPTIONS_LEN + 1] = "";
03835 char adminopts[OPTIONS_LEN + 1] = "";
03836 struct ast_tm tm, etm;
03837 struct timeval endtime = { .tv_sec = 0 };
03838 const char *var2;
03839
03840 if (rt_schedule) {
03841 now = ast_tvnow();
03842
03843 ast_localtime(&now, &tm, NULL);
03844 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03845
03846 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
03847
03848 var = ast_load_realtime("meetme", "confno",
03849 confno, "starttime <= ", currenttime, "endtime >= ",
03850 currenttime, NULL);
03851
03852 if (!var && fuzzystart) {
03853 now = ast_tvnow();
03854 now.tv_sec += fuzzystart;
03855
03856 ast_localtime(&now, &tm, NULL);
03857 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03858 var = ast_load_realtime("meetme", "confno",
03859 confno, "starttime <= ", currenttime, "endtime >= ",
03860 currenttime, NULL);
03861 }
03862
03863 if (!var && earlyalert) {
03864 now = ast_tvnow();
03865 now.tv_sec += earlyalert;
03866 ast_localtime(&now, &etm, NULL);
03867 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
03868 var = ast_load_realtime("meetme", "confno",
03869 confno, "starttime <= ", eatime, "endtime >= ",
03870 currenttime, NULL);
03871 if (var) {
03872 *too_early = 1;
03873 }
03874 }
03875
03876 } else {
03877 var = ast_load_realtime("meetme", "confno", confno, NULL);
03878 }
03879
03880 if (!var) {
03881 return NULL;
03882 }
03883
03884 if (rt_schedule && *too_early) {
03885
03886 if (!ast_streamfile(chan, "conf-has-not-started", chan->language)) {
03887 ast_waitstream(chan, "");
03888 }
03889 ast_variables_destroy(var);
03890 return NULL;
03891 }
03892
03893 for (origvar = var; var; var = var->next) {
03894 if (!strcasecmp(var->name, "pin")) {
03895 pin = ast_strdupa(var->value);
03896 } else if (!strcasecmp(var->name, "adminpin")) {
03897 pinadmin = ast_strdupa(var->value);
03898 } else if (!strcasecmp(var->name, "bookId")) {
03899 ast_copy_string(bookid, var->value, sizeof(bookid));
03900 } else if (!strcasecmp(var->name, "opts")) {
03901 ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
03902 } else if (!strcasecmp(var->name, "maxusers")) {
03903 maxusers = atoi(var->value);
03904 } else if (!strcasecmp(var->name, "adminopts")) {
03905 ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
03906 } else if (!strcasecmp(var->name, "recordingfilename")) {
03907 ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
03908 } else if (!strcasecmp(var->name, "recordingformat")) {
03909 ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
03910 } else if (!strcasecmp(var->name, "endtime")) {
03911 struct ast_tm endtime_tm;
03912 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
03913 endtime = ast_mktime(&endtime_tm, NULL);
03914 }
03915 }
03916
03917 ast_variables_destroy(origvar);
03918
03919 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
03920
03921 if (cnf) {
03922 struct ast_flags64 tmp_flags;
03923
03924 cnf->maxusers = maxusers;
03925 cnf->endalert = endalert;
03926 cnf->endtime = endtime.tv_sec;
03927 cnf->useropts = ast_strdup(useropts);
03928 cnf->adminopts = ast_strdup(adminopts);
03929 cnf->bookid = ast_strdup(bookid);
03930 if (!ast_strlen_zero(recordingfilename)) {
03931 cnf->recordingfilename = ast_strdup(recordingfilename);
03932 }
03933 if (!ast_strlen_zero(recordingformat)) {
03934 cnf->recordingformat = ast_strdup(recordingformat);
03935 }
03936
03937
03938
03939 ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
03940 ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
03941
03942 if (strchr(cnf->useropts, 'r')) {
03943 if (ast_strlen_zero(recordingfilename)) {
03944 ast_channel_lock(chan);
03945 if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03946 ast_free(cnf->recordingfilename);
03947 cnf->recordingfilename = ast_strdup(var2);
03948 }
03949 ast_channel_unlock(chan);
03950 if (ast_strlen_zero(cnf->recordingfilename)) {
03951 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, chan->uniqueid);
03952 ast_free(cnf->recordingfilename);
03953 cnf->recordingfilename = ast_strdup(recordingtmp);
03954 }
03955 }
03956 if (ast_strlen_zero(cnf->recordingformat)) {
03957 ast_channel_lock(chan);
03958 if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03959 ast_free(cnf->recordingformat);
03960 cnf->recordingformat = ast_strdup(var2);
03961 }
03962 ast_channel_unlock(chan);
03963 if (ast_strlen_zero(cnf->recordingformat)) {
03964 ast_free(cnf->recordingformat);
03965 cnf->recordingformat = ast_strdup("wav");
03966 }
03967 }
03968 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
03969 }
03970 }
03971 }
03972
03973 if (cnf) {
03974 if (confflags->flags && !cnf->chan &&
03975 !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03976 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
03977 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
03978 ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
03979 }
03980
03981 if (confflags && !cnf->chan &&
03982 ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
03983 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
03984 ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
03985 }
03986 }
03987
03988 return cnf;
03989 }
03990
03991
03992 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
03993 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags)
03994 {
03995 struct ast_config *cfg;
03996 struct ast_variable *var;
03997 struct ast_flags config_flags = { 0 };
03998 struct ast_conference *cnf;
03999
04000 AST_DECLARE_APP_ARGS(args,
04001 AST_APP_ARG(confno);
04002 AST_APP_ARG(pin);
04003 AST_APP_ARG(pinadmin);
04004 );
04005
04006
04007 ast_debug(1, "The requested confno is '%s'?\n", confno);
04008 AST_LIST_LOCK(&confs);
04009 AST_LIST_TRAVERSE(&confs, cnf, list) {
04010 ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
04011 if (!strcmp(confno, cnf->confno))
04012 break;
04013 }
04014 if (cnf) {
04015 cnf->refcount += refcount;
04016 }
04017 AST_LIST_UNLOCK(&confs);
04018
04019 if (!cnf) {
04020 if (dynamic) {
04021
04022 ast_debug(1, "Building dynamic conference '%s'\n", confno);
04023 if (dynamic_pin) {
04024 if (dynamic_pin[0] == 'q') {
04025
04026 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
04027 return NULL;
04028 }
04029 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
04030 } else {
04031 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
04032 }
04033 } else {
04034
04035 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04036 if (!cfg) {
04037 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
04038 return NULL;
04039 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04040 ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
04041 return NULL;
04042 }
04043
04044 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
04045 char parse[MAX_SETTINGS];
04046
04047 if (strcasecmp(var->name, "conf"))
04048 continue;
04049
04050 ast_copy_string(parse, var->value, sizeof(parse));
04051
04052 AST_STANDARD_APP_ARGS(args, parse);
04053 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
04054 if (!strcasecmp(args.confno, confno)) {
04055
04056 cnf = build_conf(args.confno,
04057 S_OR(args.pin, ""),
04058 S_OR(args.pinadmin, ""),
04059 make, dynamic, refcount, chan, NULL);
04060 break;
04061 }
04062 }
04063 if (!var) {
04064 ast_debug(1, "%s isn't a valid conference\n", confno);
04065 }
04066 ast_config_destroy(cfg);
04067 }
04068 } else if (dynamic_pin) {
04069
04070
04071
04072 if (dynamic_pin[0] == 'q') {
04073 dynamic_pin[0] = '\0';
04074 }
04075 }
04076
04077 if (cnf) {
04078 if (confflags && !cnf->chan &&
04079 !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04080 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
04081 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04082 ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
04083 }
04084
04085 if (confflags && !cnf->chan &&
04086 ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04087 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04088 ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04089 }
04090 }
04091
04092 return cnf;
04093 }
04094
04095
04096 static int count_exec(struct ast_channel *chan, const char *data)
04097 {
04098 int res = 0;
04099 struct ast_conference *conf;
04100 int count;
04101 char *localdata;
04102 char val[80] = "0";
04103 AST_DECLARE_APP_ARGS(args,
04104 AST_APP_ARG(confno);
04105 AST_APP_ARG(varname);
04106 );
04107
04108 if (ast_strlen_zero(data)) {
04109 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
04110 return -1;
04111 }
04112
04113 if (!(localdata = ast_strdupa(data)))
04114 return -1;
04115
04116 AST_STANDARD_APP_ARGS(args, localdata);
04117
04118 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
04119
04120 if (conf) {
04121 count = conf->users;
04122 dispose_conf(conf);
04123 conf = NULL;
04124 } else
04125 count = 0;
04126
04127 if (!ast_strlen_zero(args.varname)) {
04128
04129 snprintf(val, sizeof(val), "%d", count);
04130 pbx_builtin_setvar_helper(chan, args.varname, val);
04131 } else {
04132 if (chan->_state != AST_STATE_UP) {
04133 ast_answer(chan);
04134 }
04135 res = ast_say_number(chan, count, "", chan->language, (char *) NULL);
04136 }
04137
04138 return res;
04139 }
04140
04141
04142 static int conf_exec(struct ast_channel *chan, const char *data)
04143 {
04144 int res = -1;
04145 char confno[MAX_CONFNUM] = "";
04146 int allowretry = 0;
04147 int retrycnt = 0;
04148 struct ast_conference *cnf = NULL;
04149 struct ast_flags64 confflags = {0};
04150 struct ast_flags config_flags = { 0 };
04151 int dynamic = 0;
04152 int empty = 0, empty_no_pin = 0;
04153 int always_prompt = 0;
04154 const char *notdata;
04155 char *info, the_pin[MAX_PIN] = "";
04156 AST_DECLARE_APP_ARGS(args,
04157 AST_APP_ARG(confno);
04158 AST_APP_ARG(options);
04159 AST_APP_ARG(pin);
04160 );
04161 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
04162
04163 if (ast_strlen_zero(data)) {
04164 allowretry = 1;
04165 notdata = "";
04166 } else {
04167 notdata = data;
04168 }
04169
04170 if (chan->_state != AST_STATE_UP)
04171 ast_answer(chan);
04172
04173 info = ast_strdupa(notdata);
04174
04175 AST_STANDARD_APP_ARGS(args, info);
04176
04177 if (args.confno) {
04178 ast_copy_string(confno, args.confno, sizeof(confno));
04179 if (ast_strlen_zero(confno)) {
04180 allowretry = 1;
04181 }
04182 }
04183
04184 if (args.pin)
04185 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
04186
04187 if (args.options) {
04188 ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
04189 dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
04190 if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
04191 strcpy(the_pin, "q");
04192
04193 empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
04194 empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
04195 always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
04196 }
04197
04198 do {
04199 if (retrycnt > 3)
04200 allowretry = 0;
04201 if (empty) {
04202 int i;
04203 struct ast_config *cfg;
04204 struct ast_variable *var;
04205 int confno_int;
04206
04207
04208 if ((empty_no_pin) || (!dynamic)) {
04209 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04210 if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
04211 var = ast_variable_browse(cfg, "rooms");
04212 while (var) {
04213 char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
04214 if (!strcasecmp(var->name, "conf")) {
04215 int found = 0;
04216 ast_copy_string(parse, var->value, sizeof(parse));
04217 confno_tmp = strsep(&stringp, "|,");
04218 if (!dynamic) {
04219
04220 AST_LIST_LOCK(&confs);
04221 AST_LIST_TRAVERSE(&confs, cnf, list) {
04222 if (!strcmp(confno_tmp, cnf->confno)) {
04223
04224 found = 1;
04225 break;
04226 }
04227 }
04228 AST_LIST_UNLOCK(&confs);
04229 if (!found) {
04230
04231 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
04232
04233
04234
04235
04236 ast_copy_string(confno, confno_tmp, sizeof(confno));
04237 break;
04238 }
04239 }
04240 }
04241 }
04242 var = var->next;
04243 }
04244 ast_config_destroy(cfg);
04245 }
04246
04247 if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
04248 const char *catg;
04249 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
04250 const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
04251 const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
04252 if (ast_strlen_zero(confno_tmp)) {
04253 continue;
04254 }
04255 if (!dynamic) {
04256 int found = 0;
04257
04258 AST_LIST_LOCK(&confs);
04259 AST_LIST_TRAVERSE(&confs, cnf, list) {
04260 if (!strcmp(confno_tmp, cnf->confno)) {
04261
04262 found = 1;
04263 break;
04264 }
04265 }
04266 AST_LIST_UNLOCK(&confs);
04267 if (!found) {
04268
04269 if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
04270
04271
04272
04273
04274 ast_copy_string(confno, confno_tmp, sizeof(confno));
04275 break;
04276 }
04277 }
04278 }
04279 }
04280 ast_config_destroy(cfg);
04281 }
04282 }
04283
04284
04285 if (ast_strlen_zero(confno) && dynamic) {
04286 AST_LIST_LOCK(&confs);
04287 for (i = 0; i < ARRAY_LEN(conf_map); i++) {
04288 if (!conf_map[i]) {
04289 snprintf(confno, sizeof(confno), "%d", i);
04290 conf_map[i] = 1;
04291 break;
04292 }
04293 }
04294 AST_LIST_UNLOCK(&confs);
04295 }
04296
04297
04298 if (ast_strlen_zero(confno)) {
04299 res = ast_streamfile(chan, "conf-noempty", chan->language);
04300 if (!res)
04301 ast_waitstream(chan, "");
04302 } else {
04303 if (sscanf(confno, "%30d", &confno_int) == 1) {
04304 if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
04305 res = ast_streamfile(chan, "conf-enteringno", chan->language);
04306 if (!res) {
04307 ast_waitstream(chan, "");
04308 res = ast_say_digits(chan, confno_int, "", chan->language);
04309 }
04310 }
04311 } else {
04312 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
04313 }
04314 }
04315 }
04316
04317 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
04318
04319 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
04320 if (res < 0) {
04321
04322 confno[0] = '\0';
04323 allowretry = 0;
04324 break;
04325 }
04326 }
04327 if (!ast_strlen_zero(confno)) {
04328
04329 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
04330 sizeof(the_pin), 1, &confflags);
04331 if (!cnf) {
04332 int too_early = 0;
04333
04334 cnf = find_conf_realtime(chan, confno, 1, dynamic,
04335 the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
04336 if (rt_schedule && too_early)
04337 allowretry = 0;
04338 }
04339
04340 if (!cnf) {
04341 if (allowretry) {
04342 confno[0] = '\0';
04343 res = ast_streamfile(chan, "conf-invalid", chan->language);
04344 if (!res)
04345 ast_waitstream(chan, "");
04346 res = -1;
04347 }
04348 } else {
04349
04350 int req_pin = !ast_strlen_zero(cnf->pin) ||
04351 (!ast_strlen_zero(cnf->pinadmin) &&
04352 ast_test_flag64(&confflags, CONFFLAG_ADMIN));
04353
04354
04355
04356
04357
04358
04359
04360
04361
04362
04363
04364
04365
04366 int not_exempt = !cnf->isdynamic;
04367 not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
04368 not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
04369 if (req_pin && not_exempt) {
04370 char pin[MAX_PIN] = "";
04371 int j;
04372
04373
04374 for (j = 0; j < 3; j++) {
04375 if (*the_pin && (always_prompt == 0)) {
04376 ast_copy_string(pin, the_pin, sizeof(pin));
04377 res = 0;
04378 } else {
04379
04380 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
04381 }
04382 if (res >= 0) {
04383 if ((!strcasecmp(pin, cnf->pin) &&
04384 (ast_strlen_zero(cnf->pinadmin) ||
04385 !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
04386 (!ast_strlen_zero(cnf->pinadmin) &&
04387 !strcasecmp(pin, cnf->pinadmin))) {
04388
04389 allowretry = 0;
04390 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
04391 if (!ast_strlen_zero(cnf->adminopts)) {
04392 char *opts = ast_strdupa(cnf->adminopts);
04393 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04394 }
04395 } else {
04396 if (!ast_strlen_zero(cnf->useropts)) {
04397 char *opts = ast_strdupa(cnf->useropts);
04398 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04399 }
04400 }
04401
04402 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04403 res = conf_run(chan, cnf, &confflags, optargs);
04404 break;
04405 } else {
04406
04407 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
04408 res = ast_waitstream(chan, AST_DIGIT_ANY);
04409 ast_stopstream(chan);
04410 } else {
04411 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
04412 break;
04413 }
04414 if (res < 0)
04415 break;
04416 pin[0] = res;
04417 pin[1] = '\0';
04418 res = -1;
04419 if (allowretry)
04420 confno[0] = '\0';
04421 }
04422 } else {
04423
04424 res = -1;
04425 allowretry = 0;
04426
04427 break;
04428 }
04429
04430
04431 if (*the_pin && (always_prompt == 0)) {
04432 break;
04433 }
04434 }
04435 } else {
04436
04437 allowretry = 0;
04438
04439
04440
04441
04442 if (!ast_strlen_zero(cnf->useropts)) {
04443 char *opts = ast_strdupa(cnf->useropts);
04444 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04445 }
04446
04447
04448 res = conf_run(chan, cnf, &confflags, optargs);
04449 }
04450 dispose_conf(cnf);
04451 cnf = NULL;
04452 }
04453 }
04454 } while (allowretry);
04455
04456 if (cnf)
04457 dispose_conf(cnf);
04458
04459 return res;
04460 }
04461
04462 static struct ast_conf_user *find_user(struct ast_conference *conf, const char *callerident)
04463 {
04464 struct ast_conf_user *user = NULL;
04465 int cid;
04466
04467 sscanf(callerident, "%30i", &cid);
04468 if (conf && callerident) {
04469 user = ao2_find(conf->usercontainer, &cid, 0);
04470
04471 return user;
04472 }
04473 return NULL;
04474 }
04475
04476 static int user_listen_volup_cb(void *obj, void *unused, int flags)
04477 {
04478 struct ast_conf_user *user = obj;
04479 tweak_listen_volume(user, VOL_UP);
04480 return 0;
04481 }
04482
04483 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
04484 {
04485 struct ast_conf_user *user = obj;
04486 tweak_listen_volume(user, VOL_DOWN);
04487 return 0;
04488 }
04489
04490 static int user_talk_volup_cb(void *obj, void *unused, int flags)
04491 {
04492 struct ast_conf_user *user = obj;
04493 tweak_talk_volume(user, VOL_UP);
04494 return 0;
04495 }
04496
04497 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
04498 {
04499 struct ast_conf_user *user = obj;
04500 tweak_talk_volume(user, VOL_DOWN);
04501 return 0;
04502 }
04503
04504 static int user_reset_vol_cb(void *obj, void *unused, int flags)
04505 {
04506 struct ast_conf_user *user = obj;
04507 reset_volumes(user);
04508 return 0;
04509 }
04510
04511 static int user_chan_cb(void *obj, void *args, int flags)
04512 {
04513 struct ast_conf_user *user = obj;
04514 const char *channel = args;
04515
04516 if (!strcmp(user->chan->name, channel)) {
04517 return (CMP_MATCH | CMP_STOP);
04518 }
04519
04520 return 0;
04521 }
04522
04523
04524
04525
04526 static int admin_exec(struct ast_channel *chan, const char *data) {
04527 char *params;
04528 struct ast_conference *cnf;
04529 struct ast_conf_user *user = NULL;
04530 AST_DECLARE_APP_ARGS(args,
04531 AST_APP_ARG(confno);
04532 AST_APP_ARG(command);
04533 AST_APP_ARG(user);
04534 );
04535 int res = 0;
04536
04537 if (ast_strlen_zero(data)) {
04538 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
04539 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
04540 return -1;
04541 }
04542
04543 params = ast_strdupa(data);
04544 AST_STANDARD_APP_ARGS(args, params);
04545
04546 if (!args.command) {
04547 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
04548 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
04549 return -1;
04550 }
04551
04552 AST_LIST_LOCK(&confs);
04553 AST_LIST_TRAVERSE(&confs, cnf, list) {
04554 if (!strcmp(cnf->confno, args.confno))
04555 break;
04556 }
04557
04558 if (!cnf) {
04559 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
04560 AST_LIST_UNLOCK(&confs);
04561 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
04562 return 0;
04563 }
04564
04565 ast_atomic_fetchadd_int(&cnf->refcount, 1);
04566
04567 if (args.user) {
04568 user = find_user(cnf, args.user);
04569 if (!user) {
04570 ast_log(LOG_NOTICE, "Specified User not found!\n");
04571 res = -2;
04572 goto usernotfound;
04573 }
04574 }
04575
04576 switch (*args.command) {
04577 case 76:
04578 cnf->locked = 1;
04579 break;
04580 case 108:
04581 cnf->locked = 0;
04582 break;
04583 case 75:
04584 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
04585 break;
04586 case 101:
04587 {
04588 int max_no = 0;
04589 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
04590 user = ao2_find(cnf->usercontainer, &max_no, 0);
04591 if (!ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))
04592 user->adminflags |= ADMINFLAG_KICKME;
04593 else {
04594 res = -1;
04595 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
04596 }
04597 ao2_ref(user, -1);
04598 break;
04599 }
04600 case 77:
04601 user->adminflags |= ADMINFLAG_MUTED;
04602 break;
04603 case 78:
04604 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, NULL);
04605 break;
04606 case 109:
04607 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
04608 break;
04609 case 110:
04610 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
04611 break;
04612 case 107:
04613 user->adminflags |= ADMINFLAG_KICKME;
04614 break;
04615 case 118:
04616 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
04617 break;
04618 case 86:
04619 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
04620 break;
04621 case 115:
04622 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
04623 break;
04624 case 83:
04625 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
04626 break;
04627 case 82:
04628 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
04629 break;
04630 case 114:
04631 reset_volumes(user);
04632 break;
04633 case 85:
04634 tweak_listen_volume(user, VOL_UP);
04635 break;
04636 case 117:
04637 tweak_listen_volume(user, VOL_DOWN);
04638 break;
04639 case 84:
04640 tweak_talk_volume(user, VOL_UP);
04641 break;
04642 case 116:
04643 tweak_talk_volume(user, VOL_DOWN);
04644 break;
04645 case 'E':
04646 if (rt_extend_conf(args.confno)) {
04647 res = -1;
04648 }
04649 break;
04650 }
04651
04652 if (args.user) {
04653
04654 ao2_ref(user, -1);
04655 }
04656 usernotfound:
04657 AST_LIST_UNLOCK(&confs);
04658
04659 dispose_conf(cnf);
04660 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
04661
04662 return 0;
04663 }
04664
04665
04666
04667 static int channel_admin_exec(struct ast_channel *chan, const char *data) {
04668 char *params;
04669 struct ast_conference *conf = NULL;
04670 struct ast_conf_user *user = NULL;
04671 AST_DECLARE_APP_ARGS(args,
04672 AST_APP_ARG(channel);
04673 AST_APP_ARG(command);
04674 );
04675
04676 if (ast_strlen_zero(data)) {
04677 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
04678 return -1;
04679 }
04680
04681 params = ast_strdupa(data);
04682 AST_STANDARD_APP_ARGS(args, params);
04683
04684 if (!args.channel) {
04685 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
04686 return -1;
04687 }
04688
04689 if (!args.command) {
04690 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
04691 return -1;
04692 }
04693
04694 AST_LIST_LOCK(&confs);
04695 AST_LIST_TRAVERSE(&confs, conf, list) {
04696 if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
04697 break;
04698 }
04699 }
04700
04701 if (!user) {
04702 ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
04703 AST_LIST_UNLOCK(&confs);
04704 return 0;
04705 }
04706
04707
04708 switch (*args.command) {
04709 case 77:
04710 user->adminflags |= ADMINFLAG_MUTED;
04711 break;
04712 case 109:
04713 user->adminflags &= ~ADMINFLAG_MUTED;
04714 break;
04715 case 107:
04716 user->adminflags |= ADMINFLAG_KICKME;
04717 break;
04718 default:
04719 ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
04720 break;
04721 }
04722 ao2_ref(user, -1);
04723 AST_LIST_UNLOCK(&confs);
04724
04725 return 0;
04726 }
04727
04728 static int meetmemute(struct mansession *s, const struct message *m, int mute)
04729 {
04730 struct ast_conference *conf;
04731 struct ast_conf_user *user;
04732 const char *confid = astman_get_header(m, "Meetme");
04733 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
04734 int userno;
04735
04736 if (ast_strlen_zero(confid)) {
04737 astman_send_error(s, m, "Meetme conference not specified");
04738 return 0;
04739 }
04740
04741 if (ast_strlen_zero(userid)) {
04742 astman_send_error(s, m, "Meetme user number not specified");
04743 return 0;
04744 }
04745
04746 userno = strtoul(userid, &userid, 10);
04747
04748 if (*userid) {
04749 astman_send_error(s, m, "Invalid user number");
04750 return 0;
04751 }
04752
04753
04754 AST_LIST_LOCK(&confs);
04755 AST_LIST_TRAVERSE(&confs, conf, list) {
04756 if (!strcmp(confid, conf->confno))
04757 break;
04758 }
04759
04760 if (!conf) {
04761 AST_LIST_UNLOCK(&confs);
04762 astman_send_error(s, m, "Meetme conference does not exist");
04763 return 0;
04764 }
04765
04766 user = ao2_find(conf->usercontainer, &userno, 0);
04767
04768 if (!user) {
04769 AST_LIST_UNLOCK(&confs);
04770 astman_send_error(s, m, "User number not found");
04771 return 0;
04772 }
04773
04774 if (mute)
04775 user->adminflags |= ADMINFLAG_MUTED;
04776 else
04777 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
04778
04779 AST_LIST_UNLOCK(&confs);
04780
04781 ast_log(LOG_NOTICE, "Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ? "" : "un", conf->confno, user->user_no, user->chan->name, user->chan->uniqueid);
04782
04783 ao2_ref(user, -1);
04784 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
04785 return 0;
04786 }
04787
04788 static int action_meetmemute(struct mansession *s, const struct message *m)
04789 {
04790 return meetmemute(s, m, 1);
04791 }
04792
04793 static int action_meetmeunmute(struct mansession *s, const struct message *m)
04794 {
04795 return meetmemute(s, m, 0);
04796 }
04797
04798 static int action_meetmelist(struct mansession *s, const struct message *m)
04799 {
04800 const char *actionid = astman_get_header(m, "ActionID");
04801 const char *conference = astman_get_header(m, "Conference");
04802 char idText[80] = "";
04803 struct ast_conference *cnf;
04804 struct ast_conf_user *user;
04805 struct ao2_iterator user_iter;
04806 int total = 0;
04807
04808 if (!ast_strlen_zero(actionid))
04809 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
04810
04811 if (AST_LIST_EMPTY(&confs)) {
04812 astman_send_error(s, m, "No active conferences.");
04813 return 0;
04814 }
04815
04816 astman_send_listack(s, m, "Meetme user list will follow", "start");
04817
04818
04819 AST_LIST_LOCK(&confs);
04820 AST_LIST_TRAVERSE(&confs, cnf, list) {
04821
04822 if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
04823 continue;
04824
04825
04826 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
04827 while ((user = ao2_iterator_next(&user_iter))) {
04828 total++;
04829 astman_append(s,
04830 "Event: MeetmeList\r\n"
04831 "%s"
04832 "Conference: %s\r\n"
04833 "UserNumber: %d\r\n"
04834 "CallerIDNum: %s\r\n"
04835 "CallerIDName: %s\r\n"
04836 "ConnectedLineNum: %s\r\n"
04837 "ConnectedLineName: %s\r\n"
04838 "Channel: %s\r\n"
04839 "Admin: %s\r\n"
04840 "Role: %s\r\n"
04841 "MarkedUser: %s\r\n"
04842 "Muted: %s\r\n"
04843 "Talking: %s\r\n"
04844 "\r\n",
04845 idText,
04846 cnf->confno,
04847 user->user_no,
04848 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
04849 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
04850 S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
04851 S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<no name>"),
04852 user->chan->name,
04853 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
04854 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
04855 ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
04856 user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
04857 user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
04858 ao2_ref(user, -1);
04859 }
04860 ao2_iterator_destroy(&user_iter);
04861 }
04862 AST_LIST_UNLOCK(&confs);
04863
04864 astman_append(s,
04865 "Event: MeetmeListComplete\r\n"
04866 "EventList: Complete\r\n"
04867 "ListItems: %d\r\n"
04868 "%s"
04869 "\r\n", total, idText);
04870 return 0;
04871 }
04872
04873 static void *recordthread(void *args)
04874 {
04875 struct ast_conference *cnf = args;
04876 struct ast_frame *f = NULL;
04877 int flags;
04878 struct ast_filestream *s = NULL;
04879 int res = 0;
04880 int x;
04881 const char *oldrecordingfilename = NULL;
04882
04883 if (!cnf || !cnf->lchan) {
04884 pthread_exit(0);
04885 }
04886
04887 ast_stopstream(cnf->lchan);
04888 flags = O_CREAT | O_TRUNC | O_WRONLY;
04889
04890
04891 cnf->recording = MEETME_RECORD_ACTIVE;
04892 while (ast_waitfor(cnf->lchan, -1) > -1) {
04893 if (cnf->recording == MEETME_RECORD_TERMINATE) {
04894 AST_LIST_LOCK(&confs);
04895 AST_LIST_UNLOCK(&confs);
04896 break;
04897 }
04898 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
04899 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
04900 oldrecordingfilename = cnf->recordingfilename;
04901 }
04902
04903 f = ast_read(cnf->lchan);
04904 if (!f) {
04905 res = -1;
04906 break;
04907 }
04908 if (f->frametype == AST_FRAME_VOICE) {
04909 ast_mutex_lock(&cnf->listenlock);
04910 for (x = 0; x < AST_FRAME_BITS; x++) {
04911
04912 if (cnf->transframe[x]) {
04913 ast_frfree(cnf->transframe[x]);
04914 cnf->transframe[x] = NULL;
04915 }
04916 }
04917 if (cnf->origframe)
04918 ast_frfree(cnf->origframe);
04919 cnf->origframe = ast_frdup(f);
04920 ast_mutex_unlock(&cnf->listenlock);
04921 if (s)
04922 res = ast_writestream(s, f);
04923 if (res) {
04924 ast_frfree(f);
04925 break;
04926 }
04927 }
04928 ast_frfree(f);
04929 }
04930 cnf->recording = MEETME_RECORD_OFF;
04931 if (s)
04932 ast_closestream(s);
04933
04934 pthread_exit(0);
04935 }
04936
04937
04938 static enum ast_device_state meetmestate(const char *data)
04939 {
04940 struct ast_conference *conf;
04941
04942
04943 AST_LIST_LOCK(&confs);
04944 AST_LIST_TRAVERSE(&confs, conf, list) {
04945 if (!strcmp(data, conf->confno))
04946 break;
04947 }
04948 AST_LIST_UNLOCK(&confs);
04949 if (!conf)
04950 return AST_DEVICE_INVALID;
04951
04952
04953
04954 if (!conf->users)
04955 return AST_DEVICE_NOT_INUSE;
04956
04957 return AST_DEVICE_INUSE;
04958 }
04959
04960 static void load_config_meetme(void)
04961 {
04962 struct ast_config *cfg;
04963 struct ast_flags config_flags = { 0 };
04964 const char *val;
04965
04966 if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
04967 return;
04968 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04969 ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
04970 return;
04971 }
04972
04973 audio_buffers = DEFAULT_AUDIO_BUFFERS;
04974
04975
04976 rt_schedule = 0;
04977 fuzzystart = 0;
04978 earlyalert = 0;
04979 endalert = 0;
04980 extendby = 0;
04981
04982
04983 rt_log_members = 1;
04984
04985 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
04986 if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
04987 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
04988 audio_buffers = DEFAULT_AUDIO_BUFFERS;
04989 } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
04990 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
04991 DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
04992 audio_buffers = DEFAULT_AUDIO_BUFFERS;
04993 }
04994 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
04995 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
04996 }
04997
04998 if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
04999 rt_schedule = ast_true(val);
05000 if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
05001 rt_log_members = ast_true(val);
05002 if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
05003 if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
05004 ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
05005 fuzzystart = 0;
05006 }
05007 }
05008 if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
05009 if ((sscanf(val, "%30d", &earlyalert) != 1)) {
05010 ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
05011 earlyalert = 0;
05012 }
05013 }
05014 if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
05015 if ((sscanf(val, "%30d", &endalert) != 1)) {
05016 ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
05017 endalert = 0;
05018 }
05019 }
05020 if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
05021 if ((sscanf(val, "%30d", &extendby) != 1)) {
05022 ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
05023 extendby = 0;
05024 }
05025 }
05026
05027 ast_config_destroy(cfg);
05028 }
05029
05030
05031
05032
05033 static struct sla_trunk *sla_find_trunk(const char *name)
05034 {
05035 struct sla_trunk *trunk = NULL;
05036
05037 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
05038 if (!strcasecmp(trunk->name, name))
05039 break;
05040 }
05041
05042 return trunk;
05043 }
05044
05045
05046
05047
05048 static struct sla_station *sla_find_station(const char *name)
05049 {
05050 struct sla_station *station = NULL;
05051
05052 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
05053 if (!strcasecmp(station->name, name))
05054 break;
05055 }
05056
05057 return station;
05058 }
05059
05060 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
05061 const struct sla_station *station)
05062 {
05063 struct sla_station_ref *station_ref;
05064 struct sla_trunk_ref *trunk_ref;
05065
05066
05067 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
05068 AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
05069 if (trunk_ref->trunk != trunk || station_ref->station == station)
05070 continue;
05071 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
05072 station_ref->station->hold_access == SLA_HOLD_PRIVATE)
05073 return 1;
05074 return 0;
05075 }
05076 }
05077
05078 return 0;
05079 }
05080
05081
05082
05083
05084
05085
05086
05087
05088 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
05089 const char *name)
05090 {
05091 struct sla_trunk_ref *trunk_ref = NULL;
05092
05093 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05094 if (strcasecmp(trunk_ref->trunk->name, name))
05095 continue;
05096
05097 if ( (trunk_ref->trunk->barge_disabled
05098 && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
05099 (trunk_ref->trunk->hold_stations
05100 && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
05101 && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
05102 sla_check_station_hold_access(trunk_ref->trunk, station) )
05103 {
05104 trunk_ref = NULL;
05105 }
05106
05107 break;
05108 }
05109
05110 return trunk_ref;
05111 }
05112
05113 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
05114 {
05115 struct sla_station_ref *station_ref;
05116
05117 if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
05118 return NULL;
05119
05120 station_ref->station = station;
05121
05122 return station_ref;
05123 }
05124
05125 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
05126 {
05127 struct sla_ringing_station *ringing_station;
05128
05129 if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
05130 return NULL;
05131
05132 ringing_station->station = station;
05133 ringing_station->ring_begin = ast_tvnow();
05134
05135 return ringing_station;
05136 }
05137
05138 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
05139 {
05140 switch (state) {
05141 case SLA_TRUNK_STATE_IDLE:
05142 return AST_DEVICE_NOT_INUSE;
05143 case SLA_TRUNK_STATE_RINGING:
05144 return AST_DEVICE_RINGING;
05145 case SLA_TRUNK_STATE_UP:
05146 return AST_DEVICE_INUSE;
05147 case SLA_TRUNK_STATE_ONHOLD:
05148 case SLA_TRUNK_STATE_ONHOLD_BYME:
05149 return AST_DEVICE_ONHOLD;
05150 }
05151
05152 return AST_DEVICE_UNKNOWN;
05153 }
05154
05155 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
05156 enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
05157 {
05158 struct sla_station *station;
05159 struct sla_trunk_ref *trunk_ref;
05160
05161 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
05162 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05163 if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
05164 || trunk_ref == exclude)
05165 continue;
05166 trunk_ref->state = state;
05167 ast_devstate_changed(sla_state_to_devstate(state),
05168 "SLA:%s_%s", station->name, trunk->name);
05169 break;
05170 }
05171 }
05172 }
05173
05174 struct run_station_args {
05175 struct sla_station *station;
05176 struct sla_trunk_ref *trunk_ref;
05177 ast_mutex_t *cond_lock;
05178 ast_cond_t *cond;
05179 };
05180
05181 static void answer_trunk_chan(struct ast_channel *chan)
05182 {
05183 ast_answer(chan);
05184 ast_indicate(chan, -1);
05185 }
05186
05187 static void *run_station(void *data)
05188 {
05189 struct sla_station *station;
05190 struct sla_trunk_ref *trunk_ref;
05191 struct ast_str *conf_name = ast_str_create(16);
05192 struct ast_flags64 conf_flags = { 0 };
05193 struct ast_conference *conf;
05194
05195 {
05196 struct run_station_args *args = data;
05197 station = args->station;
05198 trunk_ref = args->trunk_ref;
05199 ast_mutex_lock(args->cond_lock);
05200 ast_cond_signal(args->cond);
05201 ast_mutex_unlock(args->cond_lock);
05202
05203 }
05204
05205 ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
05206 ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
05207 ast_set_flag64(&conf_flags,
05208 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
05209 answer_trunk_chan(trunk_ref->chan);
05210 conf = build_conf(ast_str_buffer(conf_name), "", "", 0, 0, 1, trunk_ref->chan, NULL);
05211 if (conf) {
05212 conf_run(trunk_ref->chan, conf, &conf_flags, NULL);
05213 dispose_conf(conf);
05214 conf = NULL;
05215 }
05216 trunk_ref->chan = NULL;
05217 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
05218 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
05219 ast_str_append(&conf_name, 0, ",K");
05220 admin_exec(NULL, ast_str_buffer(conf_name));
05221 trunk_ref->trunk->hold_stations = 0;
05222 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05223 }
05224
05225 ast_dial_join(station->dial);
05226 ast_dial_destroy(station->dial);
05227 station->dial = NULL;
05228 ast_free(conf_name);
05229
05230 return NULL;
05231 }
05232
05233 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
05234 {
05235 char buf[80];
05236 struct sla_station_ref *station_ref;
05237
05238 snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
05239 admin_exec(NULL, buf);
05240 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05241
05242 while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
05243 ast_free(station_ref);
05244
05245 ast_free(ringing_trunk);
05246 }
05247
05248 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
05249 enum sla_station_hangup hangup)
05250 {
05251 struct sla_ringing_trunk *ringing_trunk;
05252 struct sla_trunk_ref *trunk_ref;
05253 struct sla_station_ref *station_ref;
05254
05255 ast_dial_join(ringing_station->station->dial);
05256 ast_dial_destroy(ringing_station->station->dial);
05257 ringing_station->station->dial = NULL;
05258
05259 if (hangup == SLA_STATION_HANGUP_NORMAL)
05260 goto done;
05261
05262
05263
05264
05265
05266
05267 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05268 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05269 if (ringing_trunk->trunk == trunk_ref->trunk)
05270 break;
05271 }
05272 if (!trunk_ref)
05273 continue;
05274 if (!(station_ref = sla_create_station_ref(ringing_station->station)))
05275 continue;
05276 AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
05277 }
05278
05279 done:
05280 ast_free(ringing_station);
05281 }
05282
05283 static void sla_dial_state_callback(struct ast_dial *dial)
05284 {
05285 sla_queue_event(SLA_EVENT_DIAL_STATE);
05286 }
05287
05288
05289
05290
05291 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
05292 const struct sla_station *station)
05293 {
05294 struct sla_station_ref *timed_out_station;
05295
05296 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
05297 if (station == timed_out_station->station)
05298 return 1;
05299 }
05300
05301 return 0;
05302 }
05303
05304
05305
05306
05307
05308
05309
05310
05311
05312 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
05313 struct sla_trunk_ref **trunk_ref, int rm)
05314 {
05315 struct sla_trunk_ref *s_trunk_ref;
05316 struct sla_ringing_trunk *ringing_trunk = NULL;
05317
05318 AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
05319 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05320
05321 if (s_trunk_ref->trunk != ringing_trunk->trunk)
05322 continue;
05323
05324
05325
05326 if (sla_check_timed_out_station(ringing_trunk, station))
05327 continue;
05328
05329 if (rm)
05330 AST_LIST_REMOVE_CURRENT(entry);
05331
05332 if (trunk_ref)
05333 *trunk_ref = s_trunk_ref;
05334
05335 break;
05336 }
05337 AST_LIST_TRAVERSE_SAFE_END;
05338
05339 if (ringing_trunk)
05340 break;
05341 }
05342
05343 return ringing_trunk;
05344 }
05345
05346 static void sla_handle_dial_state_event(void)
05347 {
05348 struct sla_ringing_station *ringing_station;
05349
05350 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05351 struct sla_trunk_ref *s_trunk_ref = NULL;
05352 struct sla_ringing_trunk *ringing_trunk = NULL;
05353 struct run_station_args args;
05354 enum ast_dial_result dial_res;
05355 pthread_t dont_care;
05356 ast_mutex_t cond_lock;
05357 ast_cond_t cond;
05358
05359 switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
05360 case AST_DIAL_RESULT_HANGUP:
05361 case AST_DIAL_RESULT_INVALID:
05362 case AST_DIAL_RESULT_FAILED:
05363 case AST_DIAL_RESULT_TIMEOUT:
05364 case AST_DIAL_RESULT_UNANSWERED:
05365 AST_LIST_REMOVE_CURRENT(entry);
05366 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
05367 break;
05368 case AST_DIAL_RESULT_ANSWERED:
05369 AST_LIST_REMOVE_CURRENT(entry);
05370
05371 ast_mutex_lock(&sla.lock);
05372 ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
05373 ast_mutex_unlock(&sla.lock);
05374 if (!ringing_trunk) {
05375 ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
05376 break;
05377 }
05378
05379 s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
05380
05381 answer_trunk_chan(ringing_trunk->trunk->chan);
05382 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
05383
05384
05385
05386 args.trunk_ref = s_trunk_ref;
05387 args.station = ringing_station->station;
05388 args.cond = &cond;
05389 args.cond_lock = &cond_lock;
05390 ast_free(ringing_trunk);
05391 ast_free(ringing_station);
05392 ast_mutex_init(&cond_lock);
05393 ast_cond_init(&cond, NULL);
05394 ast_mutex_lock(&cond_lock);
05395 ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
05396 ast_cond_wait(&cond, &cond_lock);
05397 ast_mutex_unlock(&cond_lock);
05398 ast_mutex_destroy(&cond_lock);
05399 ast_cond_destroy(&cond);
05400 break;
05401 case AST_DIAL_RESULT_TRYING:
05402 case AST_DIAL_RESULT_RINGING:
05403 case AST_DIAL_RESULT_PROGRESS:
05404 case AST_DIAL_RESULT_PROCEEDING:
05405 break;
05406 }
05407 if (dial_res == AST_DIAL_RESULT_ANSWERED) {
05408
05409 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
05410 sla_queue_event(SLA_EVENT_DIAL_STATE);
05411 break;
05412 }
05413 }
05414 AST_LIST_TRAVERSE_SAFE_END;
05415 }
05416
05417
05418
05419
05420 static int sla_check_ringing_station(const struct sla_station *station)
05421 {
05422 struct sla_ringing_station *ringing_station;
05423
05424 AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
05425 if (station == ringing_station->station)
05426 return 1;
05427 }
05428
05429 return 0;
05430 }
05431
05432
05433
05434
05435 static int sla_check_failed_station(const struct sla_station *station)
05436 {
05437 struct sla_failed_station *failed_station;
05438 int res = 0;
05439
05440 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
05441 if (station != failed_station->station)
05442 continue;
05443 if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
05444 AST_LIST_REMOVE_CURRENT(entry);
05445 ast_free(failed_station);
05446 break;
05447 }
05448 res = 1;
05449 }
05450 AST_LIST_TRAVERSE_SAFE_END
05451
05452 return res;
05453 }
05454
05455
05456
05457
05458 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
05459 {
05460 char *tech, *tech_data;
05461 struct ast_dial *dial;
05462 struct sla_ringing_station *ringing_station;
05463 enum ast_dial_result res;
05464 int caller_is_saved;
05465 struct ast_party_caller caller;
05466
05467 if (!(dial = ast_dial_create()))
05468 return -1;
05469
05470 ast_dial_set_state_callback(dial, sla_dial_state_callback);
05471 tech_data = ast_strdupa(station->device);
05472 tech = strsep(&tech_data, "/");
05473
05474 if (ast_dial_append(dial, tech, tech_data) == -1) {
05475 ast_dial_destroy(dial);
05476 return -1;
05477 }
05478
05479
05480 caller_is_saved = 0;
05481 if (!sla.attempt_callerid) {
05482 caller_is_saved = 1;
05483 caller = ringing_trunk->trunk->chan->caller;
05484 ast_party_caller_init(&ringing_trunk->trunk->chan->caller);
05485 }
05486
05487 res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
05488
05489
05490 if (caller_is_saved) {
05491 ast_party_caller_free(&ringing_trunk->trunk->chan->caller);
05492 ringing_trunk->trunk->chan->caller = caller;
05493 }
05494
05495 if (res != AST_DIAL_RESULT_TRYING) {
05496 struct sla_failed_station *failed_station;
05497 ast_dial_destroy(dial);
05498 if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
05499 return -1;
05500 failed_station->station = station;
05501 failed_station->last_try = ast_tvnow();
05502 AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
05503 return -1;
05504 }
05505 if (!(ringing_station = sla_create_ringing_station(station))) {
05506 ast_dial_join(dial);
05507 ast_dial_destroy(dial);
05508 return -1;
05509 }
05510
05511 station->dial = dial;
05512
05513 AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
05514
05515 return 0;
05516 }
05517
05518
05519
05520 static int sla_check_inuse_station(const struct sla_station *station)
05521 {
05522 struct sla_trunk_ref *trunk_ref;
05523
05524 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05525 if (trunk_ref->chan)
05526 return 1;
05527 }
05528
05529 return 0;
05530 }
05531
05532 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
05533 const struct sla_trunk *trunk)
05534 {
05535 struct sla_trunk_ref *trunk_ref = NULL;
05536
05537 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05538 if (trunk_ref->trunk == trunk)
05539 break;
05540 }
05541
05542 return trunk_ref;
05543 }
05544
05545
05546
05547
05548
05549
05550 static int sla_check_station_delay(struct sla_station *station,
05551 struct sla_ringing_trunk *ringing_trunk)
05552 {
05553 struct sla_trunk_ref *trunk_ref;
05554 unsigned int delay = UINT_MAX;
05555 int time_left, time_elapsed;
05556
05557 if (!ringing_trunk)
05558 ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
05559 else
05560 trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
05561
05562 if (!ringing_trunk || !trunk_ref)
05563 return delay;
05564
05565
05566
05567
05568 delay = trunk_ref->ring_delay;
05569 if (!delay)
05570 delay = station->ring_delay;
05571 if (!delay)
05572 return INT_MAX;
05573
05574 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05575 time_left = (delay * 1000) - time_elapsed;
05576
05577 return time_left;
05578 }
05579
05580
05581
05582
05583 static void sla_ring_stations(void)
05584 {
05585 struct sla_station_ref *station_ref;
05586 struct sla_ringing_trunk *ringing_trunk;
05587
05588
05589
05590 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05591 AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
05592 int time_left;
05593
05594
05595 if (sla_check_ringing_station(station_ref->station))
05596 continue;
05597
05598
05599 if (sla_check_inuse_station(station_ref->station))
05600 continue;
05601
05602
05603
05604 if (sla_check_failed_station(station_ref->station))
05605 continue;
05606
05607
05608
05609 if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
05610 continue;
05611
05612
05613 time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
05614 if (time_left != INT_MAX && time_left > 0)
05615 continue;
05616
05617
05618 sla_ring_station(ringing_trunk, station_ref->station);
05619 }
05620 }
05621
05622 }
05623
05624 static void sla_hangup_stations(void)
05625 {
05626 struct sla_trunk_ref *trunk_ref;
05627 struct sla_ringing_station *ringing_station;
05628
05629 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05630 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05631 struct sla_ringing_trunk *ringing_trunk;
05632 ast_mutex_lock(&sla.lock);
05633 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05634 if (trunk_ref->trunk == ringing_trunk->trunk)
05635 break;
05636 }
05637 ast_mutex_unlock(&sla.lock);
05638 if (ringing_trunk)
05639 break;
05640 }
05641 if (!trunk_ref) {
05642 AST_LIST_REMOVE_CURRENT(entry);
05643 ast_dial_join(ringing_station->station->dial);
05644 ast_dial_destroy(ringing_station->station->dial);
05645 ringing_station->station->dial = NULL;
05646 ast_free(ringing_station);
05647 }
05648 }
05649 AST_LIST_TRAVERSE_SAFE_END
05650 }
05651
05652 static void sla_handle_ringing_trunk_event(void)
05653 {
05654 ast_mutex_lock(&sla.lock);
05655 sla_ring_stations();
05656 ast_mutex_unlock(&sla.lock);
05657
05658
05659 sla_hangup_stations();
05660 }
05661
05662 static void sla_handle_hold_event(struct sla_event *event)
05663 {
05664 ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
05665 event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
05666 ast_devstate_changed(AST_DEVICE_ONHOLD, "SLA:%s_%s",
05667 event->station->name, event->trunk_ref->trunk->name);
05668 sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
05669 INACTIVE_TRUNK_REFS, event->trunk_ref);
05670
05671 if (event->trunk_ref->trunk->active_stations == 1) {
05672
05673
05674 event->trunk_ref->trunk->on_hold = 1;
05675 ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
05676 }
05677
05678 ast_softhangup(event->trunk_ref->chan, AST_SOFTHANGUP_DEV);
05679 event->trunk_ref->chan = NULL;
05680 }
05681
05682
05683
05684
05685
05686 static int sla_calc_trunk_timeouts(unsigned int *timeout)
05687 {
05688 struct sla_ringing_trunk *ringing_trunk;
05689 int res = 0;
05690
05691 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05692 int time_left, time_elapsed;
05693 if (!ringing_trunk->trunk->ring_timeout)
05694 continue;
05695 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05696 time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
05697 if (time_left <= 0) {
05698 pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
05699 AST_LIST_REMOVE_CURRENT(entry);
05700 sla_stop_ringing_trunk(ringing_trunk);
05701 res = 1;
05702 continue;
05703 }
05704 if (time_left < *timeout)
05705 *timeout = time_left;
05706 }
05707 AST_LIST_TRAVERSE_SAFE_END;
05708
05709 return res;
05710 }
05711
05712
05713
05714
05715
05716 static int sla_calc_station_timeouts(unsigned int *timeout)
05717 {
05718 struct sla_ringing_trunk *ringing_trunk;
05719 struct sla_ringing_station *ringing_station;
05720 int res = 0;
05721
05722 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05723 unsigned int ring_timeout = 0;
05724 int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
05725 struct sla_trunk_ref *trunk_ref;
05726
05727
05728
05729
05730 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05731 struct sla_station_ref *station_ref;
05732 int trunk_time_elapsed, trunk_time_left;
05733
05734 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05735 if (ringing_trunk->trunk == trunk_ref->trunk)
05736 break;
05737 }
05738 if (!ringing_trunk)
05739 continue;
05740
05741
05742
05743 if (!trunk_ref->ring_timeout)
05744 break;
05745
05746
05747
05748
05749 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
05750 if (station_ref->station == ringing_station->station)
05751 break;
05752 }
05753 if (station_ref)
05754 continue;
05755
05756 trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05757 trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
05758 if (trunk_time_left > final_trunk_time_left)
05759 final_trunk_time_left = trunk_time_left;
05760 }
05761
05762
05763 if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
05764 continue;
05765
05766
05767 if (ringing_station->station->ring_timeout) {
05768 ring_timeout = ringing_station->station->ring_timeout;
05769 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
05770 time_left = (ring_timeout * 1000) - time_elapsed;
05771 }
05772
05773
05774
05775 if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
05776 time_left = final_trunk_time_left;
05777
05778
05779 if (time_left <= 0) {
05780 AST_LIST_REMOVE_CURRENT(entry);
05781 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
05782 res = 1;
05783 continue;
05784 }
05785
05786
05787
05788 if (time_left < *timeout)
05789 *timeout = time_left;
05790 }
05791 AST_LIST_TRAVERSE_SAFE_END;
05792
05793 return res;
05794 }
05795
05796
05797
05798
05799 static int sla_calc_station_delays(unsigned int *timeout)
05800 {
05801 struct sla_station *station;
05802 int res = 0;
05803
05804 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
05805 struct sla_ringing_trunk *ringing_trunk;
05806 int time_left;
05807
05808
05809 if (sla_check_ringing_station(station))
05810 continue;
05811
05812
05813 if (sla_check_inuse_station(station))
05814 continue;
05815
05816
05817 if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
05818 continue;
05819
05820 if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
05821 continue;
05822
05823
05824
05825
05826 if (time_left <= 0) {
05827 res = 1;
05828 continue;
05829 }
05830
05831 if (time_left < *timeout)
05832 *timeout = time_left;
05833 }
05834
05835 return res;
05836 }
05837
05838
05839
05840 static int sla_process_timers(struct timespec *ts)
05841 {
05842 unsigned int timeout = UINT_MAX;
05843 struct timeval wait;
05844 unsigned int change_made = 0;
05845
05846
05847 if (sla_calc_trunk_timeouts(&timeout))
05848 change_made = 1;
05849
05850
05851 if (sla_calc_station_timeouts(&timeout))
05852 change_made = 1;
05853
05854
05855 if (sla_calc_station_delays(&timeout))
05856 change_made = 1;
05857
05858
05859 if (change_made)
05860 sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
05861
05862
05863 if (timeout == UINT_MAX)
05864 return 0;
05865
05866 if (ts) {
05867 wait = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
05868 ts->tv_sec = wait.tv_sec;
05869 ts->tv_nsec = wait.tv_usec * 1000;
05870 }
05871
05872 return 1;
05873 }
05874
05875 static int sla_load_config(int reload);
05876
05877
05878 static void sla_check_reload(void)
05879 {
05880 struct sla_station *station;
05881 struct sla_trunk *trunk;
05882
05883 ast_mutex_lock(&sla.lock);
05884
05885 if (!AST_LIST_EMPTY(&sla.event_q) || !AST_LIST_EMPTY(&sla.ringing_trunks)
05886 || !AST_LIST_EMPTY(&sla.ringing_stations)) {
05887 ast_mutex_unlock(&sla.lock);
05888 return;
05889 }
05890
05891 AST_RWLIST_RDLOCK(&sla_stations);
05892 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
05893 if (station->ref_count)
05894 break;
05895 }
05896 AST_RWLIST_UNLOCK(&sla_stations);
05897 if (station) {
05898 ast_mutex_unlock(&sla.lock);
05899 return;
05900 }
05901
05902 AST_RWLIST_RDLOCK(&sla_trunks);
05903 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
05904 if (trunk->ref_count)
05905 break;
05906 }
05907 AST_RWLIST_UNLOCK(&sla_trunks);
05908 if (trunk) {
05909 ast_mutex_unlock(&sla.lock);
05910 return;
05911 }
05912
05913
05914 sla_load_config(1);
05915 sla.reload = 0;
05916
05917 ast_mutex_unlock(&sla.lock);
05918 }
05919
05920 static void *sla_thread(void *data)
05921 {
05922 struct sla_failed_station *failed_station;
05923 struct sla_ringing_station *ringing_station;
05924
05925 ast_mutex_lock(&sla.lock);
05926
05927 while (!sla.stop) {
05928 struct sla_event *event;
05929 struct timespec ts = { 0, };
05930 unsigned int have_timeout = 0;
05931
05932 if (AST_LIST_EMPTY(&sla.event_q)) {
05933 if ((have_timeout = sla_process_timers(&ts)))
05934 ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
05935 else
05936 ast_cond_wait(&sla.cond, &sla.lock);
05937 if (sla.stop)
05938 break;
05939 }
05940
05941 if (have_timeout)
05942 sla_process_timers(NULL);
05943
05944 while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
05945 ast_mutex_unlock(&sla.lock);
05946 switch (event->type) {
05947 case SLA_EVENT_HOLD:
05948 sla_handle_hold_event(event);
05949 break;
05950 case SLA_EVENT_DIAL_STATE:
05951 sla_handle_dial_state_event();
05952 break;
05953 case SLA_EVENT_RINGING_TRUNK:
05954 sla_handle_ringing_trunk_event();
05955 break;
05956 case SLA_EVENT_RELOAD:
05957 sla.reload = 1;
05958 case SLA_EVENT_CHECK_RELOAD:
05959 break;
05960 }
05961 ast_free(event);
05962 ast_mutex_lock(&sla.lock);
05963 }
05964
05965 if (sla.reload) {
05966 sla_check_reload();
05967 }
05968 }
05969
05970 ast_mutex_unlock(&sla.lock);
05971
05972 while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
05973 ast_free(ringing_station);
05974
05975 while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
05976 ast_free(failed_station);
05977
05978 return NULL;
05979 }
05980
05981 struct dial_trunk_args {
05982 struct sla_trunk_ref *trunk_ref;
05983 struct sla_station *station;
05984 ast_mutex_t *cond_lock;
05985 ast_cond_t *cond;
05986 };
05987
05988 static void *dial_trunk(void *data)
05989 {
05990 struct dial_trunk_args *args = data;
05991 struct ast_dial *dial;
05992 char *tech, *tech_data;
05993 enum ast_dial_result dial_res;
05994 char conf_name[MAX_CONFNUM];
05995 struct ast_conference *conf;
05996 struct ast_flags64 conf_flags = { 0 };
05997 struct sla_trunk_ref *trunk_ref = args->trunk_ref;
05998 int caller_is_saved;
05999 struct ast_party_caller caller;
06000
06001 if (!(dial = ast_dial_create())) {
06002 ast_mutex_lock(args->cond_lock);
06003 ast_cond_signal(args->cond);
06004 ast_mutex_unlock(args->cond_lock);
06005 return NULL;
06006 }
06007
06008 tech_data = ast_strdupa(trunk_ref->trunk->device);
06009 tech = strsep(&tech_data, "/");
06010 if (ast_dial_append(dial, tech, tech_data) == -1) {
06011 ast_mutex_lock(args->cond_lock);
06012 ast_cond_signal(args->cond);
06013 ast_mutex_unlock(args->cond_lock);
06014 ast_dial_destroy(dial);
06015 return NULL;
06016 }
06017
06018
06019 caller_is_saved = 0;
06020 if (!sla.attempt_callerid) {
06021 caller_is_saved = 1;
06022 caller = trunk_ref->chan->caller;
06023 ast_party_caller_init(&trunk_ref->chan->caller);
06024 }
06025
06026 dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
06027
06028
06029 if (caller_is_saved) {
06030 ast_party_caller_free(&trunk_ref->chan->caller);
06031 trunk_ref->chan->caller = caller;
06032 }
06033
06034 if (dial_res != AST_DIAL_RESULT_TRYING) {
06035 ast_mutex_lock(args->cond_lock);
06036 ast_cond_signal(args->cond);
06037 ast_mutex_unlock(args->cond_lock);
06038 ast_dial_destroy(dial);
06039 return NULL;
06040 }
06041
06042 for (;;) {
06043 unsigned int done = 0;
06044 switch ((dial_res = ast_dial_state(dial))) {
06045 case AST_DIAL_RESULT_ANSWERED:
06046 trunk_ref->trunk->chan = ast_dial_answered(dial);
06047 case AST_DIAL_RESULT_HANGUP:
06048 case AST_DIAL_RESULT_INVALID:
06049 case AST_DIAL_RESULT_FAILED:
06050 case AST_DIAL_RESULT_TIMEOUT:
06051 case AST_DIAL_RESULT_UNANSWERED:
06052 done = 1;
06053 case AST_DIAL_RESULT_TRYING:
06054 case AST_DIAL_RESULT_RINGING:
06055 case AST_DIAL_RESULT_PROGRESS:
06056 case AST_DIAL_RESULT_PROCEEDING:
06057 break;
06058 }
06059 if (done)
06060 break;
06061 }
06062
06063 if (!trunk_ref->trunk->chan) {
06064 ast_mutex_lock(args->cond_lock);
06065 ast_cond_signal(args->cond);
06066 ast_mutex_unlock(args->cond_lock);
06067 ast_dial_join(dial);
06068 ast_dial_destroy(dial);
06069 return NULL;
06070 }
06071
06072 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06073 ast_set_flag64(&conf_flags,
06074 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
06075 CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
06076 conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);
06077
06078 ast_mutex_lock(args->cond_lock);
06079 ast_cond_signal(args->cond);
06080 ast_mutex_unlock(args->cond_lock);
06081
06082 if (conf) {
06083 conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
06084 dispose_conf(conf);
06085 conf = NULL;
06086 }
06087
06088
06089 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06090
06091 trunk_ref->trunk->chan = NULL;
06092 trunk_ref->trunk->on_hold = 0;
06093
06094 ast_dial_join(dial);
06095 ast_dial_destroy(dial);
06096
06097 return NULL;
06098 }
06099
06100
06101
06102 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
06103 {
06104 struct sla_trunk_ref *trunk_ref = NULL;
06105
06106 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06107 if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
06108 break;
06109 }
06110
06111 return trunk_ref;
06112 }
06113
06114 static int sla_station_exec(struct ast_channel *chan, const char *data)
06115 {
06116 char *station_name, *trunk_name;
06117 struct sla_station *station;
06118 struct sla_trunk_ref *trunk_ref = NULL;
06119 char conf_name[MAX_CONFNUM];
06120 struct ast_flags64 conf_flags = { 0 };
06121 struct ast_conference *conf;
06122
06123 if (ast_strlen_zero(data)) {
06124 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06125 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06126 return 0;
06127 }
06128
06129 trunk_name = ast_strdupa(data);
06130 station_name = strsep(&trunk_name, "_");
06131
06132 if (ast_strlen_zero(station_name)) {
06133 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06134 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06135 return 0;
06136 }
06137
06138 AST_RWLIST_RDLOCK(&sla_stations);
06139 station = sla_find_station(station_name);
06140 if (station)
06141 ast_atomic_fetchadd_int((int *) &station->ref_count, 1);
06142 AST_RWLIST_UNLOCK(&sla_stations);
06143
06144 if (!station) {
06145 ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
06146 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06147 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06148 return 0;
06149 }
06150
06151 AST_RWLIST_RDLOCK(&sla_trunks);
06152 if (!ast_strlen_zero(trunk_name)) {
06153 trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
06154 } else
06155 trunk_ref = sla_choose_idle_trunk(station);
06156 AST_RWLIST_UNLOCK(&sla_trunks);
06157
06158 if (!trunk_ref) {
06159 if (ast_strlen_zero(trunk_name))
06160 ast_log(LOG_NOTICE, "No trunks available for call.\n");
06161 else {
06162 ast_log(LOG_NOTICE, "Can't join existing call on trunk "
06163 "'%s' due to access controls.\n", trunk_name);
06164 }
06165 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06166 ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06167 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06168 return 0;
06169 }
06170
06171 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
06172 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
06173 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06174 else {
06175 trunk_ref->state = SLA_TRUNK_STATE_UP;
06176 ast_devstate_changed(AST_DEVICE_INUSE,
06177 "SLA:%s_%s", station->name, trunk_ref->trunk->name);
06178 }
06179 } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
06180 struct sla_ringing_trunk *ringing_trunk;
06181
06182 ast_mutex_lock(&sla.lock);
06183 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06184 if (ringing_trunk->trunk == trunk_ref->trunk) {
06185 AST_LIST_REMOVE_CURRENT(entry);
06186 break;
06187 }
06188 }
06189 AST_LIST_TRAVERSE_SAFE_END
06190 ast_mutex_unlock(&sla.lock);
06191
06192 if (ringing_trunk) {
06193 answer_trunk_chan(ringing_trunk->trunk->chan);
06194 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06195
06196 free(ringing_trunk);
06197
06198
06199 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06200 sla_queue_event(SLA_EVENT_DIAL_STATE);
06201 }
06202 }
06203
06204 trunk_ref->chan = chan;
06205
06206 if (!trunk_ref->trunk->chan) {
06207 ast_mutex_t cond_lock;
06208 ast_cond_t cond;
06209 pthread_t dont_care;
06210 struct dial_trunk_args args = {
06211 .trunk_ref = trunk_ref,
06212 .station = station,
06213 .cond_lock = &cond_lock,
06214 .cond = &cond,
06215 };
06216 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06217
06218
06219
06220 ast_autoservice_start(chan);
06221 ast_mutex_init(&cond_lock);
06222 ast_cond_init(&cond, NULL);
06223 ast_mutex_lock(&cond_lock);
06224 ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
06225 ast_cond_wait(&cond, &cond_lock);
06226 ast_mutex_unlock(&cond_lock);
06227 ast_mutex_destroy(&cond_lock);
06228 ast_cond_destroy(&cond);
06229 ast_autoservice_stop(chan);
06230 if (!trunk_ref->trunk->chan) {
06231 ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
06232 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06233 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06234 trunk_ref->chan = NULL;
06235 ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06236 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06237 return 0;
06238 }
06239 }
06240
06241 if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
06242 trunk_ref->trunk->on_hold) {
06243 trunk_ref->trunk->on_hold = 0;
06244 ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
06245 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06246 }
06247
06248 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06249 ast_set_flag64(&conf_flags,
06250 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
06251 ast_answer(chan);
06252 conf = build_conf(conf_name, "", "", 0, 0, 1, chan, NULL);
06253 if (conf) {
06254 conf_run(chan, conf, &conf_flags, NULL);
06255 dispose_conf(conf);
06256 conf = NULL;
06257 }
06258 trunk_ref->chan = NULL;
06259 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
06260 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
06261 strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
06262 admin_exec(NULL, conf_name);
06263 trunk_ref->trunk->hold_stations = 0;
06264 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06265 }
06266
06267 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
06268
06269 ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
06270 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06271
06272 return 0;
06273 }
06274
06275 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
06276 {
06277 struct sla_trunk_ref *trunk_ref;
06278
06279 if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
06280 return NULL;
06281
06282 trunk_ref->trunk = trunk;
06283
06284 return trunk_ref;
06285 }
06286
06287 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
06288 {
06289 struct sla_ringing_trunk *ringing_trunk;
06290
06291 if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
06292 return NULL;
06293
06294 ringing_trunk->trunk = trunk;
06295 ringing_trunk->ring_begin = ast_tvnow();
06296
06297 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
06298
06299 ast_mutex_lock(&sla.lock);
06300 AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
06301 ast_mutex_unlock(&sla.lock);
06302
06303 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06304
06305 return ringing_trunk;
06306 }
06307
06308 enum {
06309 SLA_TRUNK_OPT_MOH = (1 << 0),
06310 };
06311
06312 enum {
06313 SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
06314 SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
06315 };
06316
06317 AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
06318 AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
06319 END_OPTIONS );
06320
06321 static int sla_trunk_exec(struct ast_channel *chan, const char *data)
06322 {
06323 char conf_name[MAX_CONFNUM];
06324 struct ast_conference *conf;
06325 struct ast_flags64 conf_flags = { 0 };
06326 struct sla_trunk *trunk;
06327 struct sla_ringing_trunk *ringing_trunk;
06328 AST_DECLARE_APP_ARGS(args,
06329 AST_APP_ARG(trunk_name);
06330 AST_APP_ARG(options);
06331 );
06332 char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
06333 struct ast_flags opt_flags = { 0 };
06334 char *parse;
06335
06336 if (ast_strlen_zero(data)) {
06337 ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
06338 return -1;
06339 }
06340
06341 parse = ast_strdupa(data);
06342 AST_STANDARD_APP_ARGS(args, parse);
06343 if (args.argc == 2) {
06344 if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
06345 ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
06346 return -1;
06347 }
06348 }
06349
06350 AST_RWLIST_RDLOCK(&sla_trunks);
06351 trunk = sla_find_trunk(args.trunk_name);
06352 if (trunk)
06353 ast_atomic_fetchadd_int((int *) &trunk->ref_count, 1);
06354 AST_RWLIST_UNLOCK(&sla_trunks);
06355
06356 if (!trunk) {
06357 ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
06358 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06359 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06360 return 0;
06361 }
06362
06363 if (trunk->chan) {
06364 ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
06365 args.trunk_name);
06366 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06367 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06368 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06369 return 0;
06370 }
06371
06372 trunk->chan = chan;
06373
06374 if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
06375 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06376 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06377 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06378 return 0;
06379 }
06380
06381 snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
06382 conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
06383 if (!conf) {
06384 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06385 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06386 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06387 return 0;
06388 }
06389 ast_set_flag64(&conf_flags,
06390 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
06391
06392 if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
06393 ast_indicate(chan, -1);
06394 ast_set_flag64(&conf_flags, CONFFLAG_MOH);
06395 } else
06396 ast_indicate(chan, AST_CONTROL_RINGING);
06397
06398 conf_run(chan, conf, &conf_flags, opts);
06399 dispose_conf(conf);
06400 conf = NULL;
06401 trunk->chan = NULL;
06402 trunk->on_hold = 0;
06403
06404 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06405
06406 if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
06407 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
06408
06409
06410 ast_mutex_lock(&sla.lock);
06411 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06412 if (ringing_trunk->trunk == trunk) {
06413 AST_LIST_REMOVE_CURRENT(entry);
06414 break;
06415 }
06416 }
06417 AST_LIST_TRAVERSE_SAFE_END;
06418 ast_mutex_unlock(&sla.lock);
06419 if (ringing_trunk) {
06420 ast_free(ringing_trunk);
06421 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
06422
06423
06424 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06425 }
06426
06427 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
06428 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
06429
06430 return 0;
06431 }
06432
06433 static enum ast_device_state sla_state(const char *data)
06434 {
06435 char *buf, *station_name, *trunk_name;
06436 struct sla_station *station;
06437 struct sla_trunk_ref *trunk_ref;
06438 enum ast_device_state res = AST_DEVICE_INVALID;
06439
06440 trunk_name = buf = ast_strdupa(data);
06441 station_name = strsep(&trunk_name, "_");
06442
06443 AST_RWLIST_RDLOCK(&sla_stations);
06444 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
06445 if (strcasecmp(station_name, station->name))
06446 continue;
06447 AST_RWLIST_RDLOCK(&sla_trunks);
06448 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06449 if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
06450 break;
06451 }
06452 if (!trunk_ref) {
06453 AST_RWLIST_UNLOCK(&sla_trunks);
06454 break;
06455 }
06456 res = sla_state_to_devstate(trunk_ref->state);
06457 AST_RWLIST_UNLOCK(&sla_trunks);
06458 }
06459 AST_RWLIST_UNLOCK(&sla_stations);
06460
06461 if (res == AST_DEVICE_INVALID) {
06462 ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
06463 trunk_name, station_name);
06464 }
06465
06466 return res;
06467 }
06468
06469 static void destroy_trunk(struct sla_trunk *trunk)
06470 {
06471 struct sla_station_ref *station_ref;
06472
06473 if (!ast_strlen_zero(trunk->autocontext))
06474 ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
06475
06476 while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
06477 ast_free(station_ref);
06478
06479 ast_string_field_free_memory(trunk);
06480 ast_free(trunk);
06481 }
06482
06483 static void destroy_station(struct sla_station *station)
06484 {
06485 struct sla_trunk_ref *trunk_ref;
06486
06487 if (!ast_strlen_zero(station->autocontext)) {
06488 AST_RWLIST_RDLOCK(&sla_trunks);
06489 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06490 char exten[AST_MAX_EXTENSION];
06491 char hint[AST_MAX_APP];
06492 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
06493 snprintf(hint, sizeof(hint), "SLA:%s", exten);
06494 ast_context_remove_extension(station->autocontext, exten,
06495 1, sla_registrar);
06496 ast_context_remove_extension(station->autocontext, hint,
06497 PRIORITY_HINT, sla_registrar);
06498 }
06499 AST_RWLIST_UNLOCK(&sla_trunks);
06500 }
06501
06502 while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
06503 ast_free(trunk_ref);
06504
06505 ast_string_field_free_memory(station);
06506 ast_free(station);
06507 }
06508
06509 static void sla_destroy(void)
06510 {
06511 struct sla_trunk *trunk;
06512 struct sla_station *station;
06513
06514 AST_RWLIST_WRLOCK(&sla_trunks);
06515 while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
06516 destroy_trunk(trunk);
06517 AST_RWLIST_UNLOCK(&sla_trunks);
06518
06519 AST_RWLIST_WRLOCK(&sla_stations);
06520 while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
06521 destroy_station(station);
06522 AST_RWLIST_UNLOCK(&sla_stations);
06523
06524 if (sla.thread != AST_PTHREADT_NULL) {
06525 ast_mutex_lock(&sla.lock);
06526 sla.stop = 1;
06527 ast_cond_signal(&sla.cond);
06528 ast_mutex_unlock(&sla.lock);
06529 pthread_join(sla.thread, NULL);
06530 }
06531
06532
06533 ast_context_destroy(NULL, sla_registrar);
06534
06535 ast_mutex_destroy(&sla.lock);
06536 ast_cond_destroy(&sla.cond);
06537 }
06538
06539 static int sla_check_device(const char *device)
06540 {
06541 char *tech, *tech_data;
06542
06543 tech_data = ast_strdupa(device);
06544 tech = strsep(&tech_data, "/");
06545
06546 if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
06547 return -1;
06548
06549 return 0;
06550 }
06551
06552 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
06553 {
06554 struct sla_trunk *trunk;
06555 struct ast_variable *var;
06556 const char *dev;
06557
06558 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
06559 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
06560 return -1;
06561 }
06562
06563 if (sla_check_device(dev)) {
06564 ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
06565 cat, dev);
06566 return -1;
06567 }
06568
06569 if (!(trunk = ast_calloc_with_stringfields(1, struct sla_trunk, 32))) {
06570 return -1;
06571 }
06572
06573 ast_string_field_set(trunk, name, cat);
06574 ast_string_field_set(trunk, device, dev);
06575
06576 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
06577 if (!strcasecmp(var->name, "autocontext"))
06578 ast_string_field_set(trunk, autocontext, var->value);
06579 else if (!strcasecmp(var->name, "ringtimeout")) {
06580 if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
06581 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
06582 var->value, trunk->name);
06583 trunk->ring_timeout = 0;
06584 }
06585 } else if (!strcasecmp(var->name, "barge"))
06586 trunk->barge_disabled = ast_false(var->value);
06587 else if (!strcasecmp(var->name, "hold")) {
06588 if (!strcasecmp(var->value, "private"))
06589 trunk->hold_access = SLA_HOLD_PRIVATE;
06590 else if (!strcasecmp(var->value, "open"))
06591 trunk->hold_access = SLA_HOLD_OPEN;
06592 else {
06593 ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
06594 var->value, trunk->name);
06595 }
06596 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
06597 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
06598 var->name, var->lineno, SLA_CONFIG_FILE);
06599 }
06600 }
06601
06602 if (!ast_strlen_zero(trunk->autocontext)) {
06603 struct ast_context *context;
06604 context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
06605 if (!context) {
06606 ast_log(LOG_ERROR, "Failed to automatically find or create "
06607 "context '%s' for SLA!\n", trunk->autocontext);
06608 destroy_trunk(trunk);
06609 return -1;
06610 }
06611 if (ast_add_extension2(context, 0 , "s", 1,
06612 NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
06613 ast_log(LOG_ERROR, "Failed to automatically create extension "
06614 "for trunk '%s'!\n", trunk->name);
06615 destroy_trunk(trunk);
06616 return -1;
06617 }
06618 }
06619
06620 AST_RWLIST_WRLOCK(&sla_trunks);
06621 AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
06622 AST_RWLIST_UNLOCK(&sla_trunks);
06623
06624 return 0;
06625 }
06626
06627 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
06628 {
06629 struct sla_trunk *trunk;
06630 struct sla_trunk_ref *trunk_ref;
06631 struct sla_station_ref *station_ref;
06632 char *trunk_name, *options, *cur;
06633
06634 options = ast_strdupa(var->value);
06635 trunk_name = strsep(&options, ",");
06636
06637 AST_RWLIST_RDLOCK(&sla_trunks);
06638 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
06639 if (!strcasecmp(trunk->name, trunk_name))
06640 break;
06641 }
06642
06643 AST_RWLIST_UNLOCK(&sla_trunks);
06644 if (!trunk) {
06645 ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
06646 return;
06647 }
06648 if (!(trunk_ref = create_trunk_ref(trunk)))
06649 return;
06650 trunk_ref->state = SLA_TRUNK_STATE_IDLE;
06651
06652 while ((cur = strsep(&options, ","))) {
06653 char *name, *value = cur;
06654 name = strsep(&value, "=");
06655 if (!strcasecmp(name, "ringtimeout")) {
06656 if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
06657 ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
06658 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
06659 trunk_ref->ring_timeout = 0;
06660 }
06661 } else if (!strcasecmp(name, "ringdelay")) {
06662 if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
06663 ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
06664 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
06665 trunk_ref->ring_delay = 0;
06666 }
06667 } else {
06668 ast_log(LOG_WARNING, "Invalid option '%s' for "
06669 "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
06670 }
06671 }
06672
06673 if (!(station_ref = sla_create_station_ref(station))) {
06674 ast_free(trunk_ref);
06675 return;
06676 }
06677 ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
06678 AST_RWLIST_WRLOCK(&sla_trunks);
06679 AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
06680 AST_RWLIST_UNLOCK(&sla_trunks);
06681 AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
06682 }
06683
06684 static int sla_build_station(struct ast_config *cfg, const char *cat)
06685 {
06686 struct sla_station *station;
06687 struct ast_variable *var;
06688 const char *dev;
06689
06690 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
06691 ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
06692 return -1;
06693 }
06694
06695 if (!(station = ast_calloc_with_stringfields(1, struct sla_station, 32))) {
06696 return -1;
06697 }
06698
06699 ast_string_field_set(station, name, cat);
06700 ast_string_field_set(station, device, dev);
06701
06702 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
06703 if (!strcasecmp(var->name, "trunk"))
06704 sla_add_trunk_to_station(station, var);
06705 else if (!strcasecmp(var->name, "autocontext"))
06706 ast_string_field_set(station, autocontext, var->value);
06707 else if (!strcasecmp(var->name, "ringtimeout")) {
06708 if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
06709 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
06710 var->value, station->name);
06711 station->ring_timeout = 0;
06712 }
06713 } else if (!strcasecmp(var->name, "ringdelay")) {
06714 if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
06715 ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
06716 var->value, station->name);
06717 station->ring_delay = 0;
06718 }
06719 } else if (!strcasecmp(var->name, "hold")) {
06720 if (!strcasecmp(var->value, "private"))
06721 station->hold_access = SLA_HOLD_PRIVATE;
06722 else if (!strcasecmp(var->value, "open"))
06723 station->hold_access = SLA_HOLD_OPEN;
06724 else {
06725 ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
06726 var->value, station->name);
06727 }
06728
06729 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
06730 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
06731 var->name, var->lineno, SLA_CONFIG_FILE);
06732 }
06733 }
06734
06735 if (!ast_strlen_zero(station->autocontext)) {
06736 struct ast_context *context;
06737 struct sla_trunk_ref *trunk_ref;
06738 context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
06739 if (!context) {
06740 ast_log(LOG_ERROR, "Failed to automatically find or create "
06741 "context '%s' for SLA!\n", station->autocontext);
06742 destroy_station(station);
06743 return -1;
06744 }
06745
06746
06747 if (ast_add_extension2(context, 0 , station->name, 1,
06748 NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
06749 ast_log(LOG_ERROR, "Failed to automatically create extension "
06750 "for trunk '%s'!\n", station->name);
06751 destroy_station(station);
06752 return -1;
06753 }
06754 AST_RWLIST_RDLOCK(&sla_trunks);
06755 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06756 char exten[AST_MAX_EXTENSION];
06757 char hint[AST_MAX_APP];
06758 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
06759 snprintf(hint, sizeof(hint), "SLA:%s", exten);
06760
06761
06762 if (ast_add_extension2(context, 0 , exten, 1,
06763 NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
06764 ast_log(LOG_ERROR, "Failed to automatically create extension "
06765 "for trunk '%s'!\n", station->name);
06766 destroy_station(station);
06767 return -1;
06768 }
06769
06770
06771 if (ast_add_extension2(context, 0 , exten, PRIORITY_HINT,
06772 NULL, NULL, hint, NULL, NULL, sla_registrar)) {
06773 ast_log(LOG_ERROR, "Failed to automatically create hint "
06774 "for trunk '%s'!\n", station->name);
06775 destroy_station(station);
06776 return -1;
06777 }
06778 }
06779 AST_RWLIST_UNLOCK(&sla_trunks);
06780 }
06781
06782 AST_RWLIST_WRLOCK(&sla_stations);
06783 AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
06784 AST_RWLIST_UNLOCK(&sla_stations);
06785
06786 return 0;
06787 }
06788
06789 static int sla_load_config(int reload)
06790 {
06791 struct ast_config *cfg;
06792 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06793 const char *cat = NULL;
06794 int res = 0;
06795 const char *val;
06796
06797 if (!reload) {
06798 ast_mutex_init(&sla.lock);
06799 ast_cond_init(&sla.cond, NULL);
06800 }
06801
06802 if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
06803 return 0;
06804 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06805 return 0;
06806 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06807 ast_log(LOG_ERROR, "Config file " SLA_CONFIG_FILE " is in an invalid format. Aborting.\n");
06808 return 0;
06809 }
06810
06811 if (reload) {
06812 struct sla_station *station;
06813 struct sla_trunk *trunk;
06814
06815
06816 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_stations, station, entry) {
06817 AST_RWLIST_REMOVE_CURRENT(entry);
06818 ast_free(station);
06819 }
06820 AST_RWLIST_TRAVERSE_SAFE_END;
06821
06822 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_trunks, trunk, entry) {
06823 AST_RWLIST_REMOVE_CURRENT(entry);
06824 ast_free(trunk);
06825 }
06826 AST_RWLIST_TRAVERSE_SAFE_END;
06827 }
06828
06829 if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
06830 sla.attempt_callerid = ast_true(val);
06831
06832 while ((cat = ast_category_browse(cfg, cat)) && !res) {
06833 const char *type;
06834 if (!strcasecmp(cat, "general"))
06835 continue;
06836 if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
06837 ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
06838 SLA_CONFIG_FILE);
06839 continue;
06840 }
06841 if (!strcasecmp(type, "trunk"))
06842 res = sla_build_trunk(cfg, cat);
06843 else if (!strcasecmp(type, "station"))
06844 res = sla_build_station(cfg, cat);
06845 else {
06846 ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
06847 SLA_CONFIG_FILE, type);
06848 }
06849 }
06850
06851 ast_config_destroy(cfg);
06852
06853
06854
06855 if (sla.thread == AST_PTHREADT_NULL && (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_trunks))) {
06856 ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
06857 }
06858
06859 return res;
06860 }
06861
06862 static int acf_meetme_info_eval(const char *keyword, const struct ast_conference *conf)
06863 {
06864 if (!strcasecmp("lock", keyword)) {
06865 return conf->locked;
06866 } else if (!strcasecmp("parties", keyword)) {
06867 return conf->users;
06868 } else if (!strcasecmp("activity", keyword)) {
06869 time_t now;
06870 now = time(NULL);
06871 return (now - conf->start);
06872 } else if (!strcasecmp("dynamic", keyword)) {
06873 return conf->isdynamic;
06874 } else {
06875 return -1;
06876 }
06877
06878 }
06879
06880 static int acf_meetme_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
06881 {
06882 struct ast_conference *conf;
06883 char *parse;
06884 int result = -2;
06885 AST_DECLARE_APP_ARGS(args,
06886 AST_APP_ARG(keyword);
06887 AST_APP_ARG(confno);
06888 );
06889
06890 if (ast_strlen_zero(data)) {
06891 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
06892 return -1;
06893 }
06894
06895 parse = ast_strdupa(data);
06896 AST_STANDARD_APP_ARGS(args, parse);
06897
06898 if (ast_strlen_zero(args.keyword)) {
06899 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
06900 return -1;
06901 }
06902
06903 if (ast_strlen_zero(args.confno)) {
06904 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
06905 return -1;
06906 }
06907
06908 AST_LIST_LOCK(&confs);
06909 AST_LIST_TRAVERSE(&confs, conf, list) {
06910 if (!strcmp(args.confno, conf->confno)) {
06911 result = acf_meetme_info_eval(args.keyword, conf);
06912 break;
06913 }
06914 }
06915 AST_LIST_UNLOCK(&confs);
06916
06917 if (result > -1) {
06918 snprintf(buf, len, "%d", result);
06919 } else if (result == -1) {
06920 ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
06921 snprintf(buf, len, "0");
06922 } else if (result == -2) {
06923 ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno);
06924 snprintf(buf, len, "0");
06925 }
06926
06927 return 0;
06928 }
06929
06930
06931 static struct ast_custom_function meetme_info_acf = {
06932 .name = "MEETME_INFO",
06933 .read = acf_meetme_info,
06934 };
06935
06936
06937 static int load_config(int reload)
06938 {
06939 load_config_meetme();
06940
06941 if (reload && sla.thread != AST_PTHREADT_NULL) {
06942 sla_queue_event(SLA_EVENT_RELOAD);
06943 ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
06944 "and will be completed when the system is idle.\n");
06945 return 0;
06946 }
06947
06948 return sla_load_config(0);
06949 }
06950
06951 #define MEETME_DATA_EXPORT(MEMBER) \
06952 MEMBER(ast_conference, confno, AST_DATA_STRING) \
06953 MEMBER(ast_conference, dahdiconf, AST_DATA_INTEGER) \
06954 MEMBER(ast_conference, users, AST_DATA_INTEGER) \
06955 MEMBER(ast_conference, markedusers, AST_DATA_INTEGER) \
06956 MEMBER(ast_conference, maxusers, AST_DATA_INTEGER) \
06957 MEMBER(ast_conference, isdynamic, AST_DATA_BOOLEAN) \
06958 MEMBER(ast_conference, locked, AST_DATA_BOOLEAN) \
06959 MEMBER(ast_conference, recordingfilename, AST_DATA_STRING) \
06960 MEMBER(ast_conference, recordingformat, AST_DATA_STRING) \
06961 MEMBER(ast_conference, pin, AST_DATA_PASSWORD) \
06962 MEMBER(ast_conference, pinadmin, AST_DATA_PASSWORD) \
06963 MEMBER(ast_conference, start, AST_DATA_TIMESTAMP) \
06964 MEMBER(ast_conference, endtime, AST_DATA_TIMESTAMP)
06965
06966 AST_DATA_STRUCTURE(ast_conference, MEETME_DATA_EXPORT);
06967
06968 #define MEETME_USER_DATA_EXPORT(MEMBER) \
06969 MEMBER(ast_conf_user, user_no, AST_DATA_INTEGER) \
06970 MEMBER(ast_conf_user, talking, AST_DATA_BOOLEAN) \
06971 MEMBER(ast_conf_user, dahdichannel, AST_DATA_BOOLEAN) \
06972 MEMBER(ast_conf_user, jointime, AST_DATA_TIMESTAMP) \
06973 MEMBER(ast_conf_user, kicktime, AST_DATA_TIMESTAMP) \
06974 MEMBER(ast_conf_user, timelimit, AST_DATA_MILLISECONDS) \
06975 MEMBER(ast_conf_user, play_warning, AST_DATA_MILLISECONDS) \
06976 MEMBER(ast_conf_user, warning_freq, AST_DATA_MILLISECONDS)
06977
06978 AST_DATA_STRUCTURE(ast_conf_user, MEETME_USER_DATA_EXPORT);
06979
06980 static int user_add_provider_cb(void *obj, void *arg, int flags)
06981 {
06982 struct ast_data *data_meetme_user;
06983 struct ast_data *data_meetme_user_channel;
06984 struct ast_data *data_meetme_user_volume;
06985
06986 struct ast_conf_user *user = obj;
06987 struct ast_data *data_meetme_users = arg;
06988
06989 data_meetme_user = ast_data_add_node(data_meetme_users, "user");
06990 if (!data_meetme_user) {
06991 return 0;
06992 }
06993
06994 ast_data_add_structure(ast_conf_user, data_meetme_user, user);
06995
06996
06997 data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel");
06998 if (!data_meetme_user_channel) {
06999 return 0;
07000 }
07001
07002 ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1);
07003
07004
07005 data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume");
07006 if (!data_meetme_user_volume) {
07007 return 0;
07008 }
07009 ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired);
07010 ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual);
07011
07012 data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume");
07013 if (!data_meetme_user_volume) {
07014 return 0;
07015 }
07016 ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired);
07017 ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual);
07018
07019 return 0;
07020 }
07021
07022
07023
07024
07025
07026 static int meetme_data_provider_get(const struct ast_data_search *search,
07027 struct ast_data *data_root)
07028 {
07029 struct ast_conference *cnf;
07030 struct ast_data *data_meetme, *data_meetme_users;
07031
07032 AST_LIST_LOCK(&confs);
07033 AST_LIST_TRAVERSE(&confs, cnf, list) {
07034 data_meetme = ast_data_add_node(data_root, "meetme");
07035 if (!data_meetme) {
07036 continue;
07037 }
07038
07039 ast_data_add_structure(ast_conference, data_meetme, cnf);
07040
07041 if (ao2_container_count(cnf->usercontainer)) {
07042 data_meetme_users = ast_data_add_node(data_meetme, "users");
07043 if (!data_meetme_users) {
07044 ast_data_remove_node(data_root, data_meetme);
07045 continue;
07046 }
07047
07048 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users);
07049 }
07050
07051 if (!ast_data_search_match(search, data_meetme)) {
07052 ast_data_remove_node(data_root, data_meetme);
07053 }
07054 }
07055 AST_LIST_UNLOCK(&confs);
07056
07057 return 0;
07058 }
07059
07060 static const struct ast_data_handler meetme_data_provider = {
07061 .version = AST_DATA_HANDLER_VERSION,
07062 .get = meetme_data_provider_get
07063 };
07064
07065 static const struct ast_data_entry meetme_data_providers[] = {
07066 AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider),
07067 };
07068
07069 #ifdef TEST_FRAMEWORK
07070 AST_TEST_DEFINE(test_meetme_data_provider)
07071 {
07072 struct ast_channel *chan;
07073 struct ast_conference *cnf;
07074 struct ast_data *node;
07075 struct ast_data_query query = {
07076 .path = "/asterisk/application/meetme/list",
07077 .search = "list/meetme/confno=9898"
07078 };
07079
07080 switch (cmd) {
07081 case TEST_INIT:
07082 info->name = "meetme_get_data_test";
07083 info->category = "/main/data/app_meetme/list/";
07084 info->summary = "Meetme data provider unit test";
07085 info->description =
07086 "Tests whether the Meetme data provider implementation works as expected.";
07087 return AST_TEST_NOT_RUN;
07088 case TEST_EXECUTE:
07089 break;
07090 }
07091
07092 chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest");
07093 if (!chan) {
07094 ast_test_status_update(test, "Channel allocation failed\n");
07095 return AST_TEST_FAIL;
07096 }
07097
07098 cnf = build_conf("9898", "", "1234", 1, 1, 1, chan, test);
07099 if (!cnf) {
07100 ast_test_status_update(test, "Build of test conference 9898 failed\n");
07101 ast_hangup(chan);
07102 return AST_TEST_FAIL;
07103 }
07104
07105 node = ast_data_get(&query);
07106 if (!node) {
07107 ast_test_status_update(test, "Data query for test conference 9898 failed\n");
07108 dispose_conf(cnf);
07109 ast_hangup(chan);
07110 return AST_TEST_FAIL;
07111 }
07112
07113 if (strcmp(ast_data_retrieve_string(node, "meetme/confno"), "9898")) {
07114 ast_test_status_update(test, "Query returned the wrong conference\n");
07115 dispose_conf(cnf);
07116 ast_hangup(chan);
07117 ast_data_free(node);
07118 return AST_TEST_FAIL;
07119 }
07120
07121 ast_data_free(node);
07122 dispose_conf(cnf);
07123 ast_hangup(chan);
07124
07125 return AST_TEST_PASS;
07126 }
07127 #endif
07128
07129 static int unload_module(void)
07130 {
07131 int res = 0;
07132
07133 ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07134 res = ast_manager_unregister("MeetmeMute");
07135 res |= ast_manager_unregister("MeetmeUnmute");
07136 res |= ast_manager_unregister("MeetmeList");
07137 res |= ast_unregister_application(app4);
07138 res |= ast_unregister_application(app3);
07139 res |= ast_unregister_application(app2);
07140 res |= ast_unregister_application(app);
07141 res |= ast_unregister_application(slastation_app);
07142 res |= ast_unregister_application(slatrunk_app);
07143
07144 #ifdef TEST_FRAMEWORK
07145 AST_TEST_UNREGISTER(test_meetme_data_provider);
07146 #endif
07147 ast_data_unregister(NULL);
07148
07149 ast_devstate_prov_del("Meetme");
07150 ast_devstate_prov_del("SLA");
07151
07152 sla_destroy();
07153
07154 res |= ast_custom_function_unregister(&meetme_info_acf);
07155 ast_unload_realtime("meetme");
07156
07157 return res;
07158 }
07159
07160
07161
07162 static int load_module(void)
07163 {
07164 int res = 0;
07165
07166 res |= load_config(0);
07167
07168 ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07169 res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
07170 res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
07171 res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
07172 res |= ast_register_application_xml(app4, channel_admin_exec);
07173 res |= ast_register_application_xml(app3, admin_exec);
07174 res |= ast_register_application_xml(app2, count_exec);
07175 res |= ast_register_application_xml(app, conf_exec);
07176 res |= ast_register_application_xml(slastation_app, sla_station_exec);
07177 res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec);
07178
07179 #ifdef TEST_FRAMEWORK
07180 AST_TEST_REGISTER(test_meetme_data_provider);
07181 #endif
07182 ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers));
07183
07184 res |= ast_devstate_prov_add("Meetme", meetmestate);
07185 res |= ast_devstate_prov_add("SLA", sla_state);
07186
07187 res |= ast_custom_function_register(&meetme_info_acf);
07188 ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
07189
07190 return res;
07191 }
07192
07193 static int reload(void)
07194 {
07195 ast_unload_realtime("meetme");
07196 return load_config(1);
07197 }
07198
07199 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
07200 .load = load_module,
07201 .unload = unload_module,
07202 .reload = reload,
07203 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
07204 );
07205