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