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