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: 404742 $")
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
02550 static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size, struct dahdi_confinfo *dahdic)
02551 {
02552 int keepplaying;
02553 int playednamerec;
02554 int res;
02555 struct ao2_iterator user_iter;
02556 struct ast_conf_user *usr = NULL;
02557
02558 switch(*dtmf) {
02559 case '1':
02560 keepplaying = 1;
02561 playednamerec = 0;
02562 if (conf->users == 1) {
02563 if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", chan->language)) {
02564 res = ast_waitstream(chan, AST_DIGIT_ANY);
02565 ast_stopstream(chan);
02566 if (res > 0) {
02567 keepplaying = 0;
02568 }
02569 }
02570 } else if (conf->users == 2) {
02571 if (keepplaying && !ast_streamfile(chan, "conf-onlyone", chan->language)) {
02572 res = ast_waitstream(chan, AST_DIGIT_ANY);
02573 ast_stopstream(chan);
02574 if (res > 0) {
02575 keepplaying = 0;
02576 }
02577 }
02578 } else {
02579 if (keepplaying && !ast_streamfile(chan, "conf-thereare", chan->language)) {
02580 res = ast_waitstream(chan, AST_DIGIT_ANY);
02581 ast_stopstream(chan);
02582 if (res > 0) {
02583 keepplaying = 0;
02584 }
02585 }
02586 if (keepplaying) {
02587 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
02588 ast_stopstream(chan);
02589 if (res > 0) {
02590 keepplaying = 0;
02591 }
02592 }
02593 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
02594 res = ast_waitstream(chan, AST_DIGIT_ANY);
02595 ast_stopstream(chan);
02596 if (res > 0) {
02597 keepplaying = 0;
02598 }
02599 }
02600 }
02601 user_iter = ao2_iterator_init(conf->usercontainer, 0);
02602 while((usr = ao2_iterator_next(&user_iter))) {
02603 if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
02604 if (keepplaying && !ast_streamfile(chan, usr->namerecloc, chan->language)) {
02605 res = ast_waitstream(chan, AST_DIGIT_ANY);
02606 ast_stopstream(chan);
02607 if (res > 0) {
02608 keepplaying = 0;
02609 }
02610 }
02611 playednamerec = 1;
02612 }
02613 ao2_ref(usr, -1);
02614 }
02615 ao2_iterator_destroy(&user_iter);
02616 if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", chan->language)) {
02617 res = ast_waitstream(chan, AST_DIGIT_ANY);
02618 ast_stopstream(chan);
02619 if (res > 0) {
02620 keepplaying = 0;
02621 }
02622 }
02623
02624 *menu_mode = MENU_DISABLED;
02625 break;
02626
02627 case '2':
02628 if (conf->users == 1) {
02629 if(!ast_streamfile(chan, "conf-errormenu", chan->language)) {
02630 ast_waitstream(chan, "");
02631 }
02632 } else {
02633 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
02634 }
02635 ast_stopstream(chan);
02636 *menu_mode = MENU_DISABLED;
02637 break;
02638
02639 case '3':
02640 if(conf->gmuted) {
02641 conf->gmuted = 0;
02642 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
02643 if (!ast_streamfile(chan, "conf-now-unmuted", chan->language)) {
02644 ast_waitstream(chan, "");
02645 }
02646 } else {
02647 conf->gmuted = 1;
02648 ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
02649 if (!ast_streamfile(chan, "conf-now-muted", chan->language)) {
02650 ast_waitstream(chan, "");
02651 }
02652 }
02653 ast_stopstream(chan);
02654 *menu_mode = MENU_DISABLED;
02655 break;
02656
02657 case '4':
02658 if (conf->recording != MEETME_RECORD_ACTIVE) {
02659 ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
02660 if (!conf->recordingfilename) {
02661 const char *var;
02662 ast_channel_lock(chan);
02663 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
02664 conf->recordingfilename = ast_strdup(var);
02665 }
02666 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
02667 conf->recordingformat = ast_strdup(var);
02668 }
02669 ast_channel_unlock(chan);
02670 if (!conf->recordingfilename) {
02671 snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
02672 conf->recordingfilename = ast_strdup(recordingtmp);
02673 }
02674 if (!conf->recordingformat) {
02675 conf->recordingformat = ast_strdup("wav");
02676 }
02677 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
02678 conf->confno, conf->recordingfilename, conf->recordingformat);
02679 }
02680
02681 ast_mutex_lock(&conf->recordthreadlock);
02682 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
02683 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
02684 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
02685 dahdic->chan = 0;
02686 dahdic->confno = conf->dahdiconf;
02687 dahdic->confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
02688 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, dahdic)) {
02689 ast_log(LOG_WARNING, "Error starting listen channel\n");
02690 ast_hangup(conf->lchan);
02691 conf->lchan = NULL;
02692 } else {
02693 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
02694 }
02695 }
02696 ast_mutex_unlock(&conf->recordthreadlock);
02697 if (!ast_streamfile(chan, "conf-now-recording", chan->language)) {
02698 ast_waitstream(chan, "");
02699 }
02700 }
02701
02702 ast_stopstream(chan);
02703 *menu_mode = MENU_DISABLED;
02704 break;
02705
02706 case '8':
02707 ast_stopstream(chan);
02708 *menu_mode = MENU_DISABLED;
02709 break;
02710
02711 default:
02712 if (!ast_streamfile(chan, "conf-errormenu", chan->language)) {
02713 ast_waitstream(chan, "");
02714 }
02715 ast_stopstream(chan);
02716 *menu_mode = MENU_DISABLED;
02717 break;
02718 }
02719 }
02720
02721
02722
02723
02724
02725
02726
02727
02728
02729
02730
02731
02732
02733
02734 static void meetme_menu(enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size, struct dahdi_confinfo *dahdic)
02735 {
02736 switch (*menu_mode) {
02737 case MENU_DISABLED:
02738 break;
02739 case MENU_NORMAL:
02740 meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
02741 break;
02742 case MENU_ADMIN:
02743 meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
02744
02745 if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
02746 break;
02747 }
02748 case MENU_ADMIN_EXTENDED:
02749 meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user, recordingtmp, recordingtmp_size, dahdic);
02750 break;
02751 }
02752 }
02753
02754 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
02755 {
02756 struct ast_conf_user *user = NULL;
02757 int fd;
02758 struct dahdi_confinfo dahdic, dahdic_empty;
02759 struct ast_frame *f;
02760 struct ast_channel *c;
02761 struct ast_frame fr;
02762 int outfd;
02763 int ms;
02764 int nfds;
02765 int res;
02766 int retrydahdi;
02767 int origfd;
02768 int musiconhold = 0, mohtempstopped = 0;
02769 int firstpass = 0;
02770 int lastmarked = 0;
02771 int currentmarked = 0;
02772 int ret = -1;
02773 int x;
02774 enum menu_modes menu_mode = MENU_DISABLED;
02775 int talkreq_manager = 0;
02776 int using_pseudo = 0;
02777 int duration = 20;
02778 int sent_event = 0;
02779 int checked = 0;
02780 int announcement_played = 0;
02781 struct timeval now;
02782 struct ast_dsp *dsp = NULL;
02783 struct ast_app *agi_app;
02784 char *agifile, *mod_speex;
02785 const char *agifiledefault = "conf-background.agi", *tmpvar;
02786 char meetmesecs[30] = "";
02787 char exitcontext[AST_MAX_CONTEXT] = "";
02788 char recordingtmp[AST_MAX_EXTENSION] = "";
02789 char members[10] = "";
02790 int dtmf = 0, opt_waitmarked_timeout = 0;
02791 time_t timeout = 0;
02792 struct dahdi_bufferinfo bi;
02793 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
02794 char *buf = __buf + AST_FRIENDLY_OFFSET;
02795 char *exitkeys = NULL;
02796 unsigned int calldurationlimit = 0;
02797 long timelimit = 0;
02798 long play_warning = 0;
02799 long warning_freq = 0;
02800 const char *warning_sound = NULL;
02801 const char *end_sound = NULL;
02802 char *parse;
02803 long time_left_ms = 0;
02804 struct timeval nexteventts = { 0, };
02805 int to;
02806 int setusercount = 0;
02807 int confsilence = 0, totalsilence = 0;
02808
02809 if (!(user = ao2_alloc(sizeof(*user), NULL))) {
02810 return ret;
02811 }
02812
02813
02814 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
02815 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
02816 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
02817 (opt_waitmarked_timeout > 0)) {
02818 timeout = time(NULL) + opt_waitmarked_timeout;
02819 }
02820
02821 if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
02822 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
02823 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
02824 }
02825
02826 if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
02827 char *limit_str, *warning_str, *warnfreq_str;
02828 const char *var;
02829
02830 parse = optargs[OPT_ARG_DURATION_LIMIT];
02831 limit_str = strsep(&parse, ":");
02832 warning_str = strsep(&parse, ":");
02833 warnfreq_str = parse;
02834
02835 timelimit = atol(limit_str);
02836 if (warning_str)
02837 play_warning = atol(warning_str);
02838 if (warnfreq_str)
02839 warning_freq = atol(warnfreq_str);
02840
02841 if (!timelimit) {
02842 timelimit = play_warning = warning_freq = 0;
02843 warning_sound = NULL;
02844 } else if (play_warning > timelimit) {
02845 if (!warning_freq) {
02846 play_warning = 0;
02847 } else {
02848 while (play_warning > timelimit)
02849 play_warning -= warning_freq;
02850 if (play_warning < 1)
02851 play_warning = warning_freq = 0;
02852 }
02853 }
02854
02855 ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
02856 if (play_warning) {
02857 ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
02858 }
02859 if (warning_freq) {
02860 ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
02861 }
02862
02863 ast_channel_lock(chan);
02864 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
02865 var = ast_strdupa(var);
02866 }
02867 ast_channel_unlock(chan);
02868
02869 warning_sound = var ? var : "timeleft";
02870
02871 ast_channel_lock(chan);
02872 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
02873 var = ast_strdupa(var);
02874 }
02875 ast_channel_unlock(chan);
02876
02877 end_sound = var ? var : NULL;
02878
02879
02880 calldurationlimit = 0;
02881
02882 if (!play_warning && !end_sound && timelimit) {
02883 calldurationlimit = timelimit / 1000;
02884 timelimit = play_warning = warning_freq = 0;
02885 } else {
02886 ast_debug(2, "Limit Data for this call:\n");
02887 ast_debug(2, "- timelimit = %ld\n", timelimit);
02888 ast_debug(2, "- play_warning = %ld\n", play_warning);
02889 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
02890 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
02891 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
02892 }
02893 }
02894
02895
02896 if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
02897 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
02898 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
02899 else
02900 exitkeys = ast_strdupa("#");
02901 }
02902
02903 if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
02904 if (!conf->recordingfilename) {
02905 const char *var;
02906 ast_channel_lock(chan);
02907 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
02908 conf->recordingfilename = ast_strdup(var);
02909 }
02910 if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
02911 conf->recordingformat = ast_strdup(var);
02912 }
02913 ast_channel_unlock(chan);
02914 if (!conf->recordingfilename) {
02915 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
02916 conf->recordingfilename = ast_strdup(recordingtmp);
02917 }
02918 if (!conf->recordingformat) {
02919 conf->recordingformat = ast_strdup("wav");
02920 }
02921 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
02922 conf->confno, conf->recordingfilename, conf->recordingformat);
02923 }
02924 }
02925
02926 ast_mutex_lock(&conf->recordthreadlock);
02927 if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
02928 ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, chan, "pseudo", NULL)))) {
02929 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
02930 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
02931 dahdic.chan = 0;
02932 dahdic.confno = conf->dahdiconf;
02933 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
02934 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
02935 ast_log(LOG_WARNING, "Error starting listen channel\n");
02936 ast_hangup(conf->lchan);
02937 conf->lchan = NULL;
02938 } else {
02939 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
02940 }
02941 }
02942 ast_mutex_unlock(&conf->recordthreadlock);
02943
02944 ast_mutex_lock(&conf->announcethreadlock);
02945 if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
02946 (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
02947 ast_mutex_init(&conf->announcelistlock);
02948 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
02949 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
02950 }
02951 ast_mutex_unlock(&conf->announcethreadlock);
02952
02953 time(&user->jointime);
02954
02955 user->timelimit = timelimit;
02956 user->play_warning = play_warning;
02957 user->warning_freq = warning_freq;
02958 user->warning_sound = warning_sound;
02959 user->end_sound = end_sound;
02960
02961 if (calldurationlimit > 0) {
02962 time(&user->kicktime);
02963 user->kicktime = user->kicktime + calldurationlimit;
02964 }
02965
02966 if (ast_tvzero(user->start_time))
02967 user->start_time = ast_tvnow();
02968 time_left_ms = user->timelimit;
02969
02970 if (user->timelimit) {
02971 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
02972 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
02973 }
02974
02975 if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
02976
02977 if (!ast_streamfile(chan, "conf-locked", chan->language))
02978 ast_waitstream(chan, "");
02979 goto outrun;
02980 }
02981
02982 ast_mutex_lock(&conf->playlock);
02983
02984 if (rt_schedule && conf->maxusers) {
02985 if (conf->users >= conf->maxusers) {
02986
02987 if (!ast_streamfile(chan, "conf-full", chan->language))
02988 ast_waitstream(chan, "");
02989 ast_mutex_unlock(&conf->playlock);
02990 goto outrun;
02991 }
02992 }
02993
02994 ao2_lock(conf->usercontainer);
02995 ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
02996 user->user_no++;
02997 ao2_link(conf->usercontainer, user);
02998 ao2_unlock(conf->usercontainer);
02999
03000 user->chan = chan;
03001 user->userflags = *confflags;
03002 user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
03003 user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
03004 user->talking = -1;
03005
03006 ast_mutex_unlock(&conf->playlock);
03007
03008 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03009 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))) {
03010 char destdir[PATH_MAX];
03011
03012 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
03013
03014 if (ast_mkdir(destdir, 0777) != 0) {
03015 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
03016 goto outrun;
03017 }
03018
03019 snprintf(user->namerecloc, sizeof(user->namerecloc),
03020 "%s/meetme-username-%s-%d", destdir,
03021 conf->confno, user->user_no);
03022 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW))
03023 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);
03024 else
03025 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
03026 if (res == -1)
03027 goto outrun;
03028 }
03029
03030 ast_mutex_lock(&conf->playlock);
03031
03032 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
03033 conf->markedusers++;
03034 conf->users++;
03035 if (rt_log_members) {
03036
03037 snprintf(members, sizeof(members), "%d", conf->users);
03038 ast_realtime_require_field("meetme",
03039 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
03040 "members", RQ_UINTEGER1, strlen(members),
03041 NULL);
03042 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
03043 }
03044 setusercount = 1;
03045
03046
03047 if (conf->users == 1)
03048 ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
03049
03050 ast_mutex_unlock(&conf->playlock);
03051
03052
03053 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
03054
03055 if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
03056 ast_channel_lock(chan);
03057 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
03058 ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
03059 } else if (!ast_strlen_zero(chan->macrocontext)) {
03060 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
03061 } else {
03062 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
03063 }
03064 ast_channel_unlock(chan);
03065 }
03066
03067
03068 if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
03069 !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
03070 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
03071 ast_waitstream(chan, "");
03072 }
03073 }
03074
03075 if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
03076 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
03077 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
03078 ast_waitstream(chan, "");
03079 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
03080 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
03081 ast_waitstream(chan, "");
03082 }
03083
03084 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
03085 conf->users > 1) {
03086 int keepplaying = 1;
03087
03088 if (conf->users == 2) {
03089 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
03090 res = ast_waitstream(chan, AST_DIGIT_ANY);
03091 ast_stopstream(chan);
03092 if (res > 0)
03093 keepplaying = 0;
03094 else if (res == -1)
03095 goto outrun;
03096 }
03097 } else {
03098 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
03099 res = ast_waitstream(chan, AST_DIGIT_ANY);
03100 ast_stopstream(chan);
03101 if (res > 0)
03102 keepplaying = 0;
03103 else if (res == -1)
03104 goto outrun;
03105 }
03106 if (keepplaying) {
03107 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
03108 if (res > 0)
03109 keepplaying = 0;
03110 else if (res == -1)
03111 goto outrun;
03112 }
03113 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
03114 res = ast_waitstream(chan, AST_DIGIT_ANY);
03115 ast_stopstream(chan);
03116 if (res > 0)
03117 keepplaying = 0;
03118 else if (res == -1)
03119 goto outrun;
03120 }
03121 }
03122 }
03123
03124 if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
03125
03126 ast_indicate(chan, -1);
03127 }
03128
03129 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
03130 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
03131 goto outrun;
03132 }
03133
03134 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
03135 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
03136 goto outrun;
03137 }
03138
03139
03140 if (!ast_test_flag64(confflags, CONFFLAG_DONT_DENOISE) &&
03141 (mod_speex = ast_module_helper("", "func_speex", 0, 0, 0, 0))) {
03142 ast_free(mod_speex);
03143 ast_func_write(chan, "DENOISE(rx)", "on");
03144 }
03145
03146 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
03147 user->dahdichannel = !retrydahdi;
03148
03149 dahdiretry:
03150 origfd = chan->fds[0];
03151 if (retrydahdi) {
03152
03153 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
03154 if (fd < 0) {
03155 ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
03156 goto outrun;
03157 }
03158 using_pseudo = 1;
03159
03160 memset(&bi, 0, sizeof(bi));
03161 bi.bufsize = CONF_SIZE / 2;
03162 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
03163 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
03164 bi.numbufs = audio_buffers;
03165 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
03166 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
03167 close(fd);
03168 goto outrun;
03169 }
03170 x = 1;
03171 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
03172 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
03173 close(fd);
03174 goto outrun;
03175 }
03176 nfds = 1;
03177 } else {
03178
03179 fd = chan->fds[0];
03180 nfds = 0;
03181 }
03182 memset(&dahdic, 0, sizeof(dahdic));
03183 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
03184
03185 dahdic.chan = 0;
03186 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
03187 ast_log(LOG_WARNING, "Error getting conference\n");
03188 close(fd);
03189 goto outrun;
03190 }
03191 if (dahdic.confmode) {
03192
03193 if (!retrydahdi) {
03194 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
03195 retrydahdi = 1;
03196 goto dahdiretry;
03197 }
03198 }
03199 memset(&dahdic, 0, sizeof(dahdic));
03200
03201 dahdic.chan = 0;
03202 dahdic.confno = conf->dahdiconf;
03203
03204 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03205 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
03206 struct announce_listitem *item;
03207 if (!(item = ao2_alloc(sizeof(*item), NULL)))
03208 goto outrun;
03209 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03210 ast_copy_string(item->language, chan->language, sizeof(item->language));
03211 item->confchan = conf->chan;
03212 item->confusers = conf->users;
03213 item->announcetype = CONF_HASJOIN;
03214 ast_mutex_lock(&conf->announcelistlock);
03215 ao2_ref(item, +1);
03216 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
03217 ast_cond_signal(&conf->announcelist_addition);
03218 ast_mutex_unlock(&conf->announcelistlock);
03219
03220 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
03221 ;
03222 }
03223 ao2_ref(item, -1);
03224 }
03225
03226 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
03227 dahdic.confmode = DAHDI_CONF_CONF;
03228 else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
03229 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03230 else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
03231 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03232 else
03233 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03234
03235 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03236 ast_log(LOG_WARNING, "Error setting conference\n");
03237 close(fd);
03238 goto outrun;
03239 }
03240 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
03241
03242 if (!sent_event) {
03243 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
03244 "Channel: %s\r\n"
03245 "Uniqueid: %s\r\n"
03246 "Meetme: %s\r\n"
03247 "Usernum: %d\r\n"
03248 "CallerIDnum: %s\r\n"
03249 "CallerIDname: %s\r\n"
03250 "ConnectedLineNum: %s\r\n"
03251 "ConnectedLineName: %s\r\n",
03252 chan->name, chan->uniqueid, conf->confno,
03253 user->user_no,
03254 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
03255 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>"),
03256 S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
03257 S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<unknown>")
03258 );
03259 sent_event = 1;
03260 }
03261
03262 if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03263 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03264 firstpass = 1;
03265 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
03266 if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03267 (conf->markedusers >= 1))) {
03268 conf_play(chan, conf, ENTER);
03269 }
03270 }
03271
03272 conf_flush(fd, chan);
03273
03274 if (dsp)
03275 ast_dsp_free(dsp);
03276
03277 if (!(dsp = ast_dsp_new())) {
03278 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
03279 res = -1;
03280 }
03281
03282 if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
03283
03284
03285
03286 ast_channel_lock(chan);
03287 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
03288 agifile = ast_strdupa(tmpvar);
03289 } else {
03290 agifile = ast_strdupa(agifiledefault);
03291 }
03292 ast_channel_unlock(chan);
03293
03294 if (user->dahdichannel) {
03295
03296 x = 1;
03297 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03298 }
03299
03300 agi_app = pbx_findapp("agi");
03301 if (agi_app) {
03302 ret = pbx_exec(chan, agi_app, agifile);
03303 } else {
03304 ast_log(LOG_WARNING, "Could not find application (agi)\n");
03305 ret = -2;
03306 }
03307 if (user->dahdichannel) {
03308
03309 x = 0;
03310 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03311 }
03312 } else {
03313 int lastusers = conf->users;
03314 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
03315
03316 x = 1;
03317 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03318 }
03319
03320 for (;;) {
03321 int menu_was_active = 0;
03322
03323 outfd = -1;
03324 ms = -1;
03325 now = ast_tvnow();
03326
03327 if (rt_schedule && conf->endtime) {
03328 char currenttime[32];
03329 long localendtime = 0;
03330 int extended = 0;
03331 struct ast_tm tm;
03332 struct ast_variable *var, *origvar;
03333 struct timeval tmp;
03334
03335 if (now.tv_sec % 60 == 0) {
03336 if (!checked) {
03337 ast_localtime(&now, &tm, NULL);
03338 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03339 var = origvar = ast_load_realtime("meetme", "confno",
03340 conf->confno, "starttime <=", currenttime,
03341 "endtime >=", currenttime, NULL);
03342
03343 for ( ; var; var = var->next) {
03344 if (!strcasecmp(var->name, "endtime")) {
03345 struct ast_tm endtime_tm;
03346 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
03347 tmp = ast_mktime(&endtime_tm, NULL);
03348 localendtime = tmp.tv_sec;
03349 }
03350 }
03351 ast_variables_destroy(origvar);
03352
03353
03354
03355 if (localendtime > conf->endtime){
03356 conf->endtime = localendtime;
03357 extended = 1;
03358 }
03359
03360 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
03361 ast_verbose("Quitting time...\n");
03362 goto outrun;
03363 }
03364
03365 if (!announcement_played && conf->endalert) {
03366 if (now.tv_sec + conf->endalert >= conf->endtime) {
03367 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
03368 ast_waitstream(chan, "");
03369 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
03370 if (!ast_streamfile(chan, "minutes", chan->language))
03371 ast_waitstream(chan, "");
03372 if (musiconhold) {
03373 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03374 }
03375 announcement_played = 1;
03376 }
03377 }
03378
03379 if (extended) {
03380 announcement_played = 0;
03381 }
03382
03383 checked = 1;
03384 }
03385 } else {
03386 checked = 0;
03387 }
03388 }
03389
03390 if (user->kicktime && (user->kicktime <= now.tv_sec)) {
03391 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03392 ret = 0;
03393 } else {
03394 ret = -1;
03395 }
03396 break;
03397 }
03398
03399 to = -1;
03400 if (user->timelimit) {
03401 int minutes = 0, seconds = 0, remain = 0;
03402
03403 to = ast_tvdiff_ms(nexteventts, now);
03404 if (to < 0) {
03405 to = 0;
03406 }
03407 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
03408 if (time_left_ms < to) {
03409 to = time_left_ms;
03410 }
03411
03412 if (time_left_ms <= 0) {
03413 if (user->end_sound) {
03414 res = ast_streamfile(chan, user->end_sound, chan->language);
03415 res = ast_waitstream(chan, "");
03416 }
03417 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03418 ret = 0;
03419 } else {
03420 ret = -1;
03421 }
03422 break;
03423 }
03424
03425 if (!to) {
03426 if (time_left_ms >= 5000) {
03427
03428 remain = (time_left_ms + 500) / 1000;
03429 if (remain / 60 >= 1) {
03430 minutes = remain / 60;
03431 seconds = remain % 60;
03432 } else {
03433 seconds = remain;
03434 }
03435
03436
03437 if (user->warning_sound && user->play_warning) {
03438 if (!strcmp(user->warning_sound, "timeleft")) {
03439
03440 res = ast_streamfile(chan, "vm-youhave", chan->language);
03441 res = ast_waitstream(chan, "");
03442 if (minutes) {
03443 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
03444 res = ast_streamfile(chan, "queue-minutes", chan->language);
03445 res = ast_waitstream(chan, "");
03446 }
03447 if (seconds) {
03448 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
03449 res = ast_streamfile(chan, "queue-seconds", chan->language);
03450 res = ast_waitstream(chan, "");
03451 }
03452 } else {
03453 res = ast_streamfile(chan, user->warning_sound, chan->language);
03454 res = ast_waitstream(chan, "");
03455 }
03456 if (musiconhold) {
03457 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03458 }
03459 }
03460 }
03461 if (user->warning_freq) {
03462 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
03463 } else {
03464 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03465 }
03466 }
03467 }
03468
03469 now = ast_tvnow();
03470 if (timeout && now.tv_sec >= timeout) {
03471 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03472 ret = 0;
03473 } else {
03474 ret = -1;
03475 }
03476 break;
03477 }
03478
03479
03480
03481
03482 if (!menu_mode && menu_was_active && user->listen.desired && !user->listen.actual) {
03483 set_talk_volume(user, user->listen.desired);
03484 }
03485
03486 menu_was_active = menu_mode;
03487
03488 currentmarked = conf->markedusers;
03489 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03490 ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03491 ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
03492 lastmarked == 0) {
03493 if (currentmarked == 1 && conf->users > 1) {
03494 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
03495 if (conf->users - 1 == 1) {
03496 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language)) {
03497 ast_waitstream(chan, "");
03498 }
03499 } else {
03500 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language)) {
03501 ast_waitstream(chan, "");
03502 }
03503 }
03504 }
03505 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03506 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
03507 ast_waitstream(chan, "");
03508 }
03509 }
03510 }
03511
03512
03513 user->userflags = *confflags;
03514
03515 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03516 if (currentmarked == 0) {
03517 if (lastmarked != 0) {
03518 if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
03519 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language)) {
03520 ast_waitstream(chan, "");
03521 }
03522 }
03523 if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03524 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03525 ret = 0;
03526 }
03527 break;
03528 } else {
03529 dahdic.confmode = DAHDI_CONF_CONF;
03530 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03531 ast_log(LOG_WARNING, "Error setting conference\n");
03532 close(fd);
03533 goto outrun;
03534 }
03535 }
03536 }
03537 if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03538 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03539 musiconhold = 1;
03540 }
03541 } else if (currentmarked >= 1 && lastmarked == 0) {
03542
03543 timeout = 0;
03544 if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
03545 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03546 } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
03547 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03548 } else {
03549 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03550 }
03551 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03552 ast_log(LOG_WARNING, "Error setting conference\n");
03553 close(fd);
03554 goto outrun;
03555 }
03556 if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03557 ast_moh_stop(chan);
03558 musiconhold = 0;
03559 }
03560 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03561 !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03562 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language)) {
03563 ast_waitstream(chan, "");
03564 }
03565 conf_play(chan, conf, ENTER);
03566 }
03567 }
03568 }
03569
03570
03571 if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03572 if (conf->users == 1) {
03573 if (!musiconhold) {
03574 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03575 musiconhold = 1;
03576 }
03577 } else {
03578 if (musiconhold) {
03579 ast_moh_stop(chan);
03580 musiconhold = 0;
03581 }
03582 }
03583 }
03584
03585
03586 if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03587 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03588 ret = 0;
03589 } else {
03590 ret = -1;
03591 }
03592 break;
03593 }
03594
03595
03596 if (conf->users != lastusers) {
03597 if (conf->users < lastusers) {
03598 ast_test_suite_event_notify("NOEXIT", "Message: CONFFLAG_MARKEDEXIT\r\nLastUsers: %d\r\nUsers: %d", lastusers, conf->users);
03599 }
03600 lastusers = conf->users;
03601 }
03602
03603
03604
03605
03606 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
03607 dahdic.confmode ^= DAHDI_CONF_TALKER;
03608 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03609 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03610 ret = -1;
03611 break;
03612 }
03613
03614
03615 if (ast_test_flag64(confflags, (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
03616 set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03617 }
03618
03619 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03620 "Channel: %s\r\n"
03621 "Uniqueid: %s\r\n"
03622 "Meetme: %s\r\n"
03623 "Usernum: %d\r\n"
03624 "Status: on\r\n",
03625 chan->name, chan->uniqueid, conf->confno, user->user_no);
03626 }
03627
03628
03629 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
03630 dahdic.confmode |= DAHDI_CONF_TALKER;
03631 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03632 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
03633 ret = -1;
03634 break;
03635 }
03636
03637 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
03638 "Channel: %s\r\n"
03639 "Uniqueid: %s\r\n"
03640 "Meetme: %s\r\n"
03641 "Usernum: %d\r\n"
03642 "Status: off\r\n",
03643 chan->name, chan->uniqueid, conf->confno, user->user_no);
03644 }
03645
03646 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
03647 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
03648 talkreq_manager = 1;
03649
03650 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03651 "Channel: %s\r\n"
03652 "Uniqueid: %s\r\n"
03653 "Meetme: %s\r\n"
03654 "Usernum: %d\r\n"
03655 "Status: on\r\n",
03656 chan->name, chan->uniqueid, conf->confno, user->user_no);
03657 }
03658
03659
03660 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
03661 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
03662 talkreq_manager = 0;
03663 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
03664 "Channel: %s\r\n"
03665 "Uniqueid: %s\r\n"
03666 "Meetme: %s\r\n"
03667 "Usernum: %d\r\n"
03668 "Status: off\r\n",
03669 chan->name, chan->uniqueid, conf->confno, user->user_no);
03670 }
03671
03672
03673 if (user->adminflags & ADMINFLAG_KICKME) {
03674
03675 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03676 !ast_streamfile(chan, "conf-kicked", chan->language)) {
03677 ast_waitstream(chan, "");
03678 }
03679 ret = 0;
03680 break;
03681 }
03682
03683
03684 if (ast_check_hangup(chan)) {
03685 break;
03686 }
03687
03688 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
03689
03690 if (c) {
03691 char dtmfstr[2] = "";
03692
03693 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
03694 if (using_pseudo) {
03695
03696 close(fd);
03697 using_pseudo = 0;
03698 }
03699 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
03700 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
03701 user->dahdichannel = !retrydahdi;
03702 goto dahdiretry;
03703 }
03704 if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
03705 f = ast_read_noaudio(c);
03706 } else {
03707 f = ast_read(c);
03708 }
03709 if (!f) {
03710 break;
03711 }
03712 if (f->frametype == AST_FRAME_DTMF) {
03713 dtmfstr[0] = f->subclass.integer;
03714 dtmfstr[1] = '\0';
03715 }
03716
03717 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec == AST_FORMAT_SLINEAR)) {
03718 if (user->talk.actual) {
03719 ast_frame_adjust_volume(f, user->talk.actual);
03720 }
03721
03722 if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
03723 if (user->talking == -1) {
03724 user->talking = 0;
03725 }
03726
03727 res = ast_dsp_silence(dsp, f, &totalsilence);
03728 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
03729 set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03730 }
03731
03732 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
03733 set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
03734 }
03735 }
03736 if (using_pseudo) {
03737
03738
03739
03740
03741
03742
03743
03744
03745
03746
03747
03748
03749 if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
03750 careful_write(fd, f->data.ptr, f->datalen, 0);
03751 }
03752 }
03753 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_mode)) {
03754 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03755 conf_queue_dtmf(conf, user, f);
03756 }
03757 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
03758 ast_log(LOG_WARNING, "Error setting conference\n");
03759 close(fd);
03760 ast_frfree(f);
03761 goto outrun;
03762 }
03763
03764
03765
03766
03767 if (!menu_mode && user->talk.desired && !user->talk.actual) {
03768 set_talk_volume(user, 0);
03769 }
03770
03771 if (musiconhold) {
03772 ast_moh_stop(chan);
03773 } else if (!menu_mode) {
03774 char *menu_to_play;
03775 if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03776 menu_mode = MENU_ADMIN;
03777 menu_to_play = "conf-adminmenu-18";
03778 } else {
03779 menu_mode = MENU_NORMAL;
03780 menu_to_play = "conf-usermenu-162";
03781 }
03782
03783 if (!ast_streamfile(chan, menu_to_play, chan->language)) {
03784 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
03785 ast_stopstream(chan);
03786 } else {
03787 dtmf = 0;
03788 }
03789 } else {
03790 dtmf = f->subclass.integer;
03791 }
03792
03793 if (dtmf > 0) {
03794 meetme_menu(&menu_mode, &dtmf, conf, confflags, chan, user, recordingtmp, sizeof(recordingtmp), &dahdic);
03795 }
03796
03797 if (musiconhold && !menu_mode) {
03798 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03799 }
03800
03801 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03802 ast_log(LOG_WARNING, "Error setting conference\n");
03803 close(fd);
03804 ast_frfree(f);
03805 goto outrun;
03806 }
03807
03808 conf_flush(fd, chan);
03809
03810
03811
03812
03813 } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
03814 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03815 conf_queue_dtmf(conf, user, f);
03816 }
03817
03818 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
03819 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
03820 ret = 0;
03821 ast_frfree(f);
03822 break;
03823 } else {
03824 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
03825 }
03826 } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
03827 (strchr(exitkeys, f->subclass.integer))) {
03828 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
03829
03830 if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03831 conf_queue_dtmf(conf, user, f);
03832 }
03833 ret = 0;
03834 ast_frfree(f);
03835 break;
03836 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
03837 && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
03838 conf_queue_dtmf(conf, user, f);
03839 } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
03840 switch (f->subclass.integer) {
03841 case AST_CONTROL_HOLD:
03842 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
03843 break;
03844 default:
03845 break;
03846 }
03847 } else if (f->frametype == AST_FRAME_NULL) {
03848
03849 } else if (f->frametype == AST_FRAME_CONTROL) {
03850 switch (f->subclass.integer) {
03851 case AST_CONTROL_BUSY:
03852 case AST_CONTROL_CONGESTION:
03853 ast_frfree(f);
03854 goto outrun;
03855 break;
03856 default:
03857 ast_debug(1,
03858 "Got ignored control frame on channel %s, f->frametype=%d,f->subclass=%d\n",
03859 chan->name, f->frametype, f->subclass.integer);
03860 }
03861 } else {
03862 ast_debug(1,
03863 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
03864 chan->name, f->frametype, f->subclass.integer);
03865 }
03866 ast_frfree(f);
03867 } else if (outfd > -1) {
03868 res = read(outfd, buf, CONF_SIZE);
03869 if (res > 0) {
03870 memset(&fr, 0, sizeof(fr));
03871 fr.frametype = AST_FRAME_VOICE;
03872 fr.subclass.codec = AST_FORMAT_SLINEAR;
03873 fr.datalen = res;
03874 fr.samples = res / 2;
03875 fr.data.ptr = buf;
03876 fr.offset = AST_FRIENDLY_OFFSET;
03877 if (!user->listen.actual &&
03878 (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
03879 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
03880 (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
03881 )) {
03882 int idx;
03883 for (idx = 0; idx < AST_FRAME_BITS; idx++) {
03884 if (chan->rawwriteformat & (1 << idx)) {
03885 break;
03886 }
03887 }
03888 if (idx >= AST_FRAME_BITS) {
03889 goto bailoutandtrynormal;
03890 }
03891 ast_mutex_lock(&conf->listenlock);
03892 if (!conf->transframe[idx]) {
03893 if (conf->origframe) {
03894 if (musiconhold && !ast_dsp_silence(dsp, conf->origframe, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
03895 ast_moh_stop(chan);
03896 mohtempstopped = 1;
03897 }
03898 if (!conf->transpath[idx]) {
03899 conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
03900 }
03901 if (conf->transpath[idx]) {
03902 conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
03903 if (!conf->transframe[idx]) {
03904 conf->transframe[idx] = &ast_null_frame;
03905 }
03906 }
03907 }
03908 }
03909 if (conf->transframe[idx]) {
03910 if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
03911 can_write(chan, confflags)) {
03912 struct ast_frame *cur;
03913
03914
03915
03916 for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
03917 if (ast_write(chan, cur)) {
03918 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
03919 break;
03920 }
03921 }
03922 if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
03923 mohtempstopped = 0;
03924 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03925 }
03926 }
03927 } else {
03928 ast_mutex_unlock(&conf->listenlock);
03929 goto bailoutandtrynormal;
03930 }
03931 ast_mutex_unlock(&conf->listenlock);
03932 } else {
03933 bailoutandtrynormal:
03934 if (musiconhold && !ast_dsp_silence(dsp, &fr, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
03935 ast_moh_stop(chan);
03936 mohtempstopped = 1;
03937 }
03938 if (user->listen.actual) {
03939 ast_frame_adjust_volume(&fr, user->listen.actual);
03940 }
03941 if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
03942 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
03943 }
03944 if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
03945 mohtempstopped = 0;
03946 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03947 }
03948 }
03949 } else {
03950 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
03951 }
03952 }
03953 lastmarked = currentmarked;
03954 }
03955 }
03956
03957 if (musiconhold) {
03958 ast_moh_stop(chan);
03959 }
03960
03961 if (using_pseudo) {
03962 close(fd);
03963 } else {
03964
03965 dahdic.chan = 0;
03966 dahdic.confno = 0;
03967 dahdic.confmode = 0;
03968 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03969 ast_log(LOG_WARNING, "Error setting conference\n");
03970 }
03971 }
03972
03973 reset_volumes(user);
03974
03975 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03976 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03977 conf_play(chan, conf, LEAVE);
03978 }
03979
03980 if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) || ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
03981 struct announce_listitem *item;
03982 if (!(item = ao2_alloc(sizeof(*item), NULL)))
03983 goto outrun;
03984 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03985 ast_copy_string(item->language, chan->language, sizeof(item->language));
03986 item->confchan = conf->chan;
03987 item->confusers = conf->users;
03988 item->announcetype = CONF_HASLEFT;
03989 ast_mutex_lock(&conf->announcelistlock);
03990 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
03991 ast_cond_signal(&conf->announcelist_addition);
03992 ast_mutex_unlock(&conf->announcelistlock);
03993 } else if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03994 ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
03995
03996 ast_filedelete(user->namerecloc, NULL);
03997 }
03998
03999 outrun:
04000 AST_LIST_LOCK(&confs);
04001
04002 if (dsp) {
04003 ast_dsp_free(dsp);
04004 }
04005
04006 if (user->user_no) {
04007
04008 now = ast_tvnow();
04009
04010 if (sent_event) {
04011 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeLeave",
04012 "Channel: %s\r\n"
04013 "Uniqueid: %s\r\n"
04014 "Meetme: %s\r\n"
04015 "Usernum: %d\r\n"
04016 "CallerIDNum: %s\r\n"
04017 "CallerIDName: %s\r\n"
04018 "ConnectedLineNum: %s\r\n"
04019 "ConnectedLineName: %s\r\n"
04020 "Duration: %ld\r\n",
04021 chan->name, chan->uniqueid, conf->confno,
04022 user->user_no,
04023 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
04024 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>"),
04025 S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
04026 S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<unknown>"),
04027 (long)(now.tv_sec - user->jointime));
04028 }
04029
04030 if (setusercount) {
04031 conf->users--;
04032 if (rt_log_members) {
04033
04034 snprintf(members, sizeof(members), "%d", conf->users);
04035 ast_realtime_require_field("meetme",
04036 "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
04037 "members", RQ_UINTEGER1, strlen(members),
04038 NULL);
04039 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
04040 }
04041 if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
04042 conf->markedusers--;
04043 }
04044 }
04045
04046 ao2_unlink(conf->usercontainer, user);
04047
04048
04049 if (!conf->users) {
04050 ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
04051 }
04052
04053
04054 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
04055 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
04056
04057
04058 if (rt_schedule) {
04059 pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
04060 }
04061 }
04062 ao2_ref(user, -1);
04063 AST_LIST_UNLOCK(&confs);
04064
04065 return ret;
04066 }
04067
04068 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
04069 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags, int *too_early, char **optargs)
04070 {
04071 struct ast_variable *var, *origvar;
04072 struct ast_conference *cnf;
04073
04074 *too_early = 0;
04075
04076
04077 AST_LIST_LOCK(&confs);
04078 AST_LIST_TRAVERSE(&confs, cnf, list) {
04079 if (!strcmp(confno, cnf->confno)) {
04080 break;
04081 }
04082 }
04083 if (cnf) {
04084 cnf->refcount += refcount;
04085 }
04086 AST_LIST_UNLOCK(&confs);
04087
04088 if (!cnf) {
04089 char *pin = NULL, *pinadmin = NULL;
04090 int maxusers = 0;
04091 struct timeval now;
04092 char recordingfilename[256] = "";
04093 char recordingformat[11] = "";
04094 char currenttime[32] = "";
04095 char eatime[32] = "";
04096 char bookid[51] = "";
04097 char recordingtmp[AST_MAX_EXTENSION] = "";
04098 char useropts[OPTIONS_LEN + 1] = "";
04099 char adminopts[OPTIONS_LEN + 1] = "";
04100 struct ast_tm tm, etm;
04101 struct timeval endtime = { .tv_sec = 0 };
04102 const char *var2;
04103
04104 if (rt_schedule) {
04105 now = ast_tvnow();
04106
04107 ast_localtime(&now, &tm, NULL);
04108 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04109
04110 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
04111
04112 var = ast_load_realtime("meetme", "confno",
04113 confno, "starttime <= ", currenttime, "endtime >= ",
04114 currenttime, NULL);
04115
04116 if (!var && fuzzystart) {
04117 now = ast_tvnow();
04118 now.tv_sec += fuzzystart;
04119
04120 ast_localtime(&now, &tm, NULL);
04121 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04122 var = ast_load_realtime("meetme", "confno",
04123 confno, "starttime <= ", currenttime, "endtime >= ",
04124 currenttime, NULL);
04125 }
04126
04127 if (!var && earlyalert) {
04128 now = ast_tvnow();
04129 now.tv_sec += earlyalert;
04130 ast_localtime(&now, &etm, NULL);
04131 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
04132 var = ast_load_realtime("meetme", "confno",
04133 confno, "starttime <= ", eatime, "endtime >= ",
04134 currenttime, NULL);
04135 if (var) {
04136 *too_early = 1;
04137 }
04138 }
04139
04140 } else {
04141 var = ast_load_realtime("meetme", "confno", confno, NULL);
04142 }
04143
04144 if (!var) {
04145 return NULL;
04146 }
04147
04148 if (rt_schedule && *too_early) {
04149
04150 if (!ast_streamfile(chan, "conf-has-not-started", chan->language)) {
04151 ast_waitstream(chan, "");
04152 }
04153 ast_variables_destroy(var);
04154 return NULL;
04155 }
04156
04157 for (origvar = var; var; var = var->next) {
04158 if (!strcasecmp(var->name, "pin")) {
04159 pin = ast_strdupa(var->value);
04160 } else if (!strcasecmp(var->name, "adminpin")) {
04161 pinadmin = ast_strdupa(var->value);
04162 } else if (!strcasecmp(var->name, "bookId")) {
04163 ast_copy_string(bookid, var->value, sizeof(bookid));
04164 } else if (!strcasecmp(var->name, "opts")) {
04165 ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04166 } else if (!strcasecmp(var->name, "maxusers")) {
04167 maxusers = atoi(var->value);
04168 } else if (!strcasecmp(var->name, "adminopts")) {
04169 ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04170 } else if (!strcasecmp(var->name, "recordingfilename")) {
04171 ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
04172 } else if (!strcasecmp(var->name, "recordingformat")) {
04173 ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
04174 } else if (!strcasecmp(var->name, "endtime")) {
04175 struct ast_tm endtime_tm;
04176 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
04177 endtime = ast_mktime(&endtime_tm, NULL);
04178 }
04179 }
04180
04181 ast_variables_destroy(origvar);
04182
04183 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
04184
04185 if (cnf) {
04186 struct ast_flags64 tmp_flags;
04187
04188 cnf->maxusers = maxusers;
04189 cnf->endalert = endalert;
04190 cnf->endtime = endtime.tv_sec;
04191 cnf->useropts = ast_strdup(useropts);
04192 cnf->adminopts = ast_strdup(adminopts);
04193 cnf->bookid = ast_strdup(bookid);
04194 if (!ast_strlen_zero(recordingfilename)) {
04195 cnf->recordingfilename = ast_strdup(recordingfilename);
04196 }
04197 if (!ast_strlen_zero(recordingformat)) {
04198 cnf->recordingformat = ast_strdup(recordingformat);
04199 }
04200
04201
04202
04203 ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
04204 ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
04205
04206 if (strchr(cnf->useropts, 'r')) {
04207 if (ast_strlen_zero(recordingfilename)) {
04208 ast_channel_lock(chan);
04209 if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
04210 ast_free(cnf->recordingfilename);
04211 cnf->recordingfilename = ast_strdup(var2);
04212 }
04213 ast_channel_unlock(chan);
04214 if (ast_strlen_zero(cnf->recordingfilename)) {
04215 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, chan->uniqueid);
04216 ast_free(cnf->recordingfilename);
04217 cnf->recordingfilename = ast_strdup(recordingtmp);
04218 }
04219 }
04220 if (ast_strlen_zero(cnf->recordingformat)) {
04221 ast_channel_lock(chan);
04222 if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
04223 ast_free(cnf->recordingformat);
04224 cnf->recordingformat = ast_strdup(var2);
04225 }
04226 ast_channel_unlock(chan);
04227 if (ast_strlen_zero(cnf->recordingformat)) {
04228 ast_free(cnf->recordingformat);
04229 cnf->recordingformat = ast_strdup("wav");
04230 }
04231 }
04232 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04233 }
04234 }
04235 }
04236
04237 if (cnf) {
04238 if (confflags->flags && !cnf->chan &&
04239 !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04240 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
04241 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04242 ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
04243 }
04244
04245 if (confflags && !cnf->chan &&
04246 ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04247 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04248 ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04249 }
04250 }
04251
04252 return cnf;
04253 }
04254
04255
04256 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
04257 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags64 *confflags)
04258 {
04259 struct ast_config *cfg;
04260 struct ast_variable *var;
04261 struct ast_flags config_flags = { 0 };
04262 struct ast_conference *cnf;
04263
04264 AST_DECLARE_APP_ARGS(args,
04265 AST_APP_ARG(confno);
04266 AST_APP_ARG(pin);
04267 AST_APP_ARG(pinadmin);
04268 );
04269
04270
04271 ast_debug(1, "The requested confno is '%s'?\n", confno);
04272 AST_LIST_LOCK(&confs);
04273 AST_LIST_TRAVERSE(&confs, cnf, list) {
04274 ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
04275 if (!strcmp(confno, cnf->confno))
04276 break;
04277 }
04278 if (cnf) {
04279 cnf->refcount += refcount;
04280 }
04281 AST_LIST_UNLOCK(&confs);
04282
04283 if (!cnf) {
04284 if (dynamic) {
04285
04286 ast_debug(1, "Building dynamic conference '%s'\n", confno);
04287 if (dynamic_pin) {
04288 if (dynamic_pin[0] == 'q') {
04289
04290 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
04291 return NULL;
04292 }
04293 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
04294 } else {
04295 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
04296 }
04297 } else {
04298
04299 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04300 if (!cfg) {
04301 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
04302 return NULL;
04303 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04304 ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
04305 return NULL;
04306 }
04307
04308 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
04309 char parse[MAX_SETTINGS];
04310
04311 if (strcasecmp(var->name, "conf"))
04312 continue;
04313
04314 ast_copy_string(parse, var->value, sizeof(parse));
04315
04316 AST_STANDARD_APP_ARGS(args, parse);
04317 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
04318 if (!strcasecmp(args.confno, confno)) {
04319
04320 cnf = build_conf(args.confno,
04321 S_OR(args.pin, ""),
04322 S_OR(args.pinadmin, ""),
04323 make, dynamic, refcount, chan, NULL);
04324 break;
04325 }
04326 }
04327 if (!var) {
04328 ast_debug(1, "%s isn't a valid conference\n", confno);
04329 }
04330 ast_config_destroy(cfg);
04331 }
04332 } else if (dynamic_pin) {
04333
04334
04335
04336 if (dynamic_pin[0] == 'q') {
04337 dynamic_pin[0] = '\0';
04338 }
04339 }
04340
04341 if (cnf) {
04342 if (confflags && !cnf->chan &&
04343 !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04344 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
04345 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04346 ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
04347 }
04348
04349 if (confflags && !cnf->chan &&
04350 ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04351 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04352 ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04353 }
04354 }
04355
04356 return cnf;
04357 }
04358
04359
04360 static int count_exec(struct ast_channel *chan, const char *data)
04361 {
04362 int res = 0;
04363 struct ast_conference *conf;
04364 int count;
04365 char *localdata;
04366 char val[80] = "0";
04367 AST_DECLARE_APP_ARGS(args,
04368 AST_APP_ARG(confno);
04369 AST_APP_ARG(varname);
04370 );
04371
04372 if (ast_strlen_zero(data)) {
04373 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
04374 return -1;
04375 }
04376
04377 localdata = ast_strdupa(data);
04378
04379 AST_STANDARD_APP_ARGS(args, localdata);
04380
04381 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
04382
04383 if (conf) {
04384 count = conf->users;
04385 dispose_conf(conf);
04386 conf = NULL;
04387 } else
04388 count = 0;
04389
04390 if (!ast_strlen_zero(args.varname)) {
04391
04392 snprintf(val, sizeof(val), "%d", count);
04393 pbx_builtin_setvar_helper(chan, args.varname, val);
04394 } else {
04395 if (chan->_state != AST_STATE_UP) {
04396 ast_answer(chan);
04397 }
04398 res = ast_say_number(chan, count, "", chan->language, (char *) NULL);
04399 }
04400
04401 return res;
04402 }
04403
04404
04405 static int conf_exec(struct ast_channel *chan, const char *data)
04406 {
04407 int res = -1;
04408 char confno[MAX_CONFNUM] = "";
04409 int allowretry = 0;
04410 int retrycnt = 0;
04411 struct ast_conference *cnf = NULL;
04412 struct ast_flags64 confflags = {0};
04413 struct ast_flags config_flags = { 0 };
04414 int dynamic = 0;
04415 int empty = 0, empty_no_pin = 0;
04416 int always_prompt = 0;
04417 const char *notdata;
04418 char *info, the_pin[MAX_PIN] = "";
04419 AST_DECLARE_APP_ARGS(args,
04420 AST_APP_ARG(confno);
04421 AST_APP_ARG(options);
04422 AST_APP_ARG(pin);
04423 );
04424 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
04425
04426 if (ast_strlen_zero(data)) {
04427 allowretry = 1;
04428 notdata = "";
04429 } else {
04430 notdata = data;
04431 }
04432
04433 if (chan->_state != AST_STATE_UP)
04434 ast_answer(chan);
04435
04436 info = ast_strdupa(notdata);
04437
04438 AST_STANDARD_APP_ARGS(args, info);
04439
04440 if (args.confno) {
04441 ast_copy_string(confno, args.confno, sizeof(confno));
04442 if (ast_strlen_zero(confno)) {
04443 allowretry = 1;
04444 }
04445 }
04446
04447 if (args.pin)
04448 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
04449
04450 if (args.options) {
04451 ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
04452 dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
04453 if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
04454 strcpy(the_pin, "q");
04455
04456 empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
04457 empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
04458 always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
04459 }
04460
04461 do {
04462 if (retrycnt > 3)
04463 allowretry = 0;
04464 if (empty) {
04465 int i;
04466 struct ast_config *cfg;
04467 struct ast_variable *var;
04468 int confno_int;
04469
04470
04471 if ((empty_no_pin) || (!dynamic)) {
04472 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04473 if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
04474 var = ast_variable_browse(cfg, "rooms");
04475 while (var) {
04476 char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
04477 if (!strcasecmp(var->name, "conf")) {
04478 int found = 0;
04479 ast_copy_string(parse, var->value, sizeof(parse));
04480 confno_tmp = strsep(&stringp, "|,");
04481 if (!dynamic) {
04482
04483 AST_LIST_LOCK(&confs);
04484 AST_LIST_TRAVERSE(&confs, cnf, list) {
04485 if (!strcmp(confno_tmp, cnf->confno)) {
04486
04487 found = 1;
04488 break;
04489 }
04490 }
04491 AST_LIST_UNLOCK(&confs);
04492 cnf = NULL;
04493 if (!found) {
04494
04495 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
04496
04497
04498
04499
04500 ast_copy_string(confno, confno_tmp, sizeof(confno));
04501 break;
04502 }
04503 }
04504 }
04505 }
04506 var = var->next;
04507 }
04508 ast_config_destroy(cfg);
04509 }
04510
04511 if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
04512 const char *catg;
04513 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
04514 const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
04515 const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
04516 if (ast_strlen_zero(confno_tmp)) {
04517 continue;
04518 }
04519 if (!dynamic) {
04520 int found = 0;
04521
04522 AST_LIST_LOCK(&confs);
04523 AST_LIST_TRAVERSE(&confs, cnf, list) {
04524 if (!strcmp(confno_tmp, cnf->confno)) {
04525
04526 found = 1;
04527 break;
04528 }
04529 }
04530 AST_LIST_UNLOCK(&confs);
04531 if (!found) {
04532
04533 if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
04534
04535
04536
04537
04538 ast_copy_string(confno, confno_tmp, sizeof(confno));
04539 break;
04540 }
04541 }
04542 }
04543 }
04544 ast_config_destroy(cfg);
04545 }
04546 }
04547
04548
04549 if (ast_strlen_zero(confno) && dynamic) {
04550 AST_LIST_LOCK(&confs);
04551 for (i = 0; i < ARRAY_LEN(conf_map); i++) {
04552 if (!conf_map[i]) {
04553 snprintf(confno, sizeof(confno), "%d", i);
04554 conf_map[i] = 1;
04555 break;
04556 }
04557 }
04558 AST_LIST_UNLOCK(&confs);
04559 }
04560
04561
04562 if (ast_strlen_zero(confno)) {
04563 ast_test_suite_event_notify("PLAYBACK", "Message: conf-noempty");
04564 res = ast_streamfile(chan, "conf-noempty", chan->language);
04565 if (!res)
04566 ast_waitstream(chan, "");
04567 } else {
04568 if (sscanf(confno, "%30d", &confno_int) == 1) {
04569 if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
04570 res = ast_streamfile(chan, "conf-enteringno", chan->language);
04571 if (!res) {
04572 ast_waitstream(chan, "");
04573 res = ast_say_digits(chan, confno_int, "", chan->language);
04574 }
04575 }
04576 } else {
04577 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
04578 }
04579 }
04580 }
04581
04582 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
04583
04584 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
04585 if (res < 0) {
04586
04587 confno[0] = '\0';
04588 allowretry = 0;
04589 break;
04590 }
04591 }
04592 if (!ast_strlen_zero(confno)) {
04593
04594 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
04595 sizeof(the_pin), 1, &confflags);
04596 if (!cnf) {
04597 int too_early = 0;
04598
04599 cnf = find_conf_realtime(chan, confno, 1, dynamic,
04600 the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
04601 if (rt_schedule && too_early)
04602 allowretry = 0;
04603 }
04604
04605 if (!cnf) {
04606 if (allowretry) {
04607 confno[0] = '\0';
04608 res = ast_streamfile(chan, "conf-invalid", chan->language);
04609 if (!res)
04610 ast_waitstream(chan, "");
04611 res = -1;
04612 }
04613 } else {
04614
04615 int req_pin = !ast_strlen_zero(cnf->pin) ||
04616 (!ast_strlen_zero(cnf->pinadmin) &&
04617 ast_test_flag64(&confflags, CONFFLAG_ADMIN));
04618
04619
04620
04621
04622
04623
04624
04625
04626
04627
04628
04629
04630
04631 int not_exempt = !cnf->isdynamic;
04632 not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
04633 not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
04634 if (req_pin && not_exempt) {
04635 char pin[MAX_PIN] = "";
04636 int j;
04637
04638
04639 for (j = 0; j < 3; j++) {
04640 if (*the_pin && (always_prompt == 0)) {
04641 ast_copy_string(pin, the_pin, sizeof(pin));
04642 res = 0;
04643 } else {
04644
04645 ast_test_suite_event_notify("PLAYBACK", "Message: conf-getpin\r\n"
04646 "Channel: %s",
04647 chan->name);
04648 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
04649 }
04650 if (res >= 0) {
04651 if ((!strcasecmp(pin, cnf->pin) &&
04652 (ast_strlen_zero(cnf->pinadmin) ||
04653 !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
04654 (!ast_strlen_zero(cnf->pinadmin) &&
04655 !strcasecmp(pin, cnf->pinadmin))) {
04656
04657 allowretry = 0;
04658 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
04659 if (!ast_strlen_zero(cnf->adminopts)) {
04660 char *opts = ast_strdupa(cnf->adminopts);
04661 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04662 }
04663 } else {
04664 if (!ast_strlen_zero(cnf->useropts)) {
04665 char *opts = ast_strdupa(cnf->useropts);
04666 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04667 }
04668 }
04669
04670 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04671 res = conf_run(chan, cnf, &confflags, optargs);
04672 break;
04673 } else {
04674
04675 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
04676 res = ast_waitstream(chan, AST_DIGIT_ANY);
04677 ast_stopstream(chan);
04678 } else {
04679 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
04680 break;
04681 }
04682 if (res < 0)
04683 break;
04684 pin[0] = res;
04685 pin[1] = '\0';
04686 res = -1;
04687 if (allowretry)
04688 confno[0] = '\0';
04689 }
04690 } else {
04691
04692 res = -1;
04693 allowretry = 0;
04694
04695 break;
04696 }
04697
04698
04699 if (*the_pin && (always_prompt == 0)) {
04700 break;
04701 }
04702 }
04703 } else {
04704
04705 allowretry = 0;
04706
04707
04708
04709
04710 if (!ast_strlen_zero(cnf->useropts)) {
04711 char *opts = ast_strdupa(cnf->useropts);
04712 ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
04713 }
04714
04715
04716 res = conf_run(chan, cnf, &confflags, optargs);
04717 }
04718 dispose_conf(cnf);
04719 cnf = NULL;
04720 }
04721 }
04722 } while (allowretry);
04723
04724 if (cnf)
04725 dispose_conf(cnf);
04726
04727 return res;
04728 }
04729
04730 static struct ast_conf_user *find_user(struct ast_conference *conf, const char *callerident)
04731 {
04732 struct ast_conf_user *user = NULL;
04733 int cid;
04734
04735 if (conf && callerident && sscanf(callerident, "%30d", &cid) == 1) {
04736 user = ao2_find(conf->usercontainer, &cid, 0);
04737
04738 return user;
04739 }
04740 return NULL;
04741 }
04742
04743 static int user_listen_volup_cb(void *obj, void *unused, int flags)
04744 {
04745 struct ast_conf_user *user = obj;
04746 tweak_listen_volume(user, VOL_UP);
04747 return 0;
04748 }
04749
04750 static int user_listen_voldown_cb(void *obj, void *unused, int flags)
04751 {
04752 struct ast_conf_user *user = obj;
04753 tweak_listen_volume(user, VOL_DOWN);
04754 return 0;
04755 }
04756
04757 static int user_talk_volup_cb(void *obj, void *unused, int flags)
04758 {
04759 struct ast_conf_user *user = obj;
04760 tweak_talk_volume(user, VOL_UP);
04761 return 0;
04762 }
04763
04764 static int user_talk_voldown_cb(void *obj, void *unused, int flags)
04765 {
04766 struct ast_conf_user *user = obj;
04767 tweak_talk_volume(user, VOL_DOWN);
04768 return 0;
04769 }
04770
04771 static int user_reset_vol_cb(void *obj, void *unused, int flags)
04772 {
04773 struct ast_conf_user *user = obj;
04774 reset_volumes(user);
04775 return 0;
04776 }
04777
04778 static int user_chan_cb(void *obj, void *args, int flags)
04779 {
04780 struct ast_conf_user *user = obj;
04781 const char *channel = args;
04782
04783 if (!strcmp(user->chan->name, channel)) {
04784 return (CMP_MATCH | CMP_STOP);
04785 }
04786
04787 return 0;
04788 }
04789
04790
04791
04792
04793 static int admin_exec(struct ast_channel *chan, const char *data) {
04794 char *params;
04795 struct ast_conference *cnf;
04796 struct ast_conf_user *user = NULL;
04797 AST_DECLARE_APP_ARGS(args,
04798 AST_APP_ARG(confno);
04799 AST_APP_ARG(command);
04800 AST_APP_ARG(user);
04801 );
04802 int res = 0;
04803
04804 if (ast_strlen_zero(data)) {
04805 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
04806 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
04807 return -1;
04808 }
04809
04810 params = ast_strdupa(data);
04811 AST_STANDARD_APP_ARGS(args, params);
04812
04813 if (!args.command) {
04814 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
04815 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
04816 return -1;
04817 }
04818
04819 AST_LIST_LOCK(&confs);
04820 AST_LIST_TRAVERSE(&confs, cnf, list) {
04821 if (!strcmp(cnf->confno, args.confno))
04822 break;
04823 }
04824
04825 if (!cnf) {
04826 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
04827 AST_LIST_UNLOCK(&confs);
04828 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
04829 return 0;
04830 }
04831
04832 ast_atomic_fetchadd_int(&cnf->refcount, 1);
04833
04834 if (args.user) {
04835 user = find_user(cnf, args.user);
04836 if (!user) {
04837 ast_log(LOG_NOTICE, "Specified User not found!\n");
04838 res = -2;
04839 goto usernotfound;
04840 }
04841 } else {
04842
04843 switch (*args.command) {
04844 case 'm':
04845 case 'M':
04846 case 't':
04847 case 'T':
04848 case 'u':
04849 case 'U':
04850 case 'r':
04851 case 'k':
04852 res = -2;
04853 ast_log(LOG_NOTICE, "No user specified!\n");
04854 goto usernotfound;
04855 default:
04856 break;
04857 }
04858 }
04859
04860 switch (*args.command) {
04861 case 76:
04862 cnf->locked = 1;
04863 break;
04864 case 108:
04865 cnf->locked = 0;
04866 break;
04867 case 75:
04868 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
04869 break;
04870 case 101:
04871 {
04872 int max_no = 0;
04873 struct ast_conf_user *eject_user;
04874
04875 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
04876 eject_user = ao2_find(cnf->usercontainer, &max_no, 0);
04877 if (!eject_user) {
04878 res = -1;
04879 ast_log(LOG_NOTICE, "No last user to kick!\n");
04880 break;
04881 }
04882
04883 if (!ast_test_flag64(&eject_user->userflags, CONFFLAG_ADMIN)) {
04884 eject_user->adminflags |= ADMINFLAG_KICKME;
04885 } else {
04886 res = -1;
04887 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
04888 }
04889
04890 ao2_ref(eject_user, -1);
04891 break;
04892 }
04893 case 77:
04894 user->adminflags |= ADMINFLAG_MUTED;
04895 break;
04896 case 78:
04897 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, &cnf);
04898 break;
04899 case 109:
04900 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
04901 break;
04902 case 110:
04903 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
04904 break;
04905 case 107:
04906 user->adminflags |= ADMINFLAG_KICKME;
04907 break;
04908 case 118:
04909 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
04910 break;
04911 case 86:
04912 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
04913 break;
04914 case 115:
04915 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
04916 break;
04917 case 83:
04918 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
04919 break;
04920 case 82:
04921 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
04922 break;
04923 case 114:
04924 reset_volumes(user);
04925 break;
04926 case 85:
04927 tweak_listen_volume(user, VOL_UP);
04928 break;
04929 case 117:
04930 tweak_listen_volume(user, VOL_DOWN);
04931 break;
04932 case 84:
04933 tweak_talk_volume(user, VOL_UP);
04934 break;
04935 case 116:
04936 tweak_talk_volume(user, VOL_DOWN);
04937 break;
04938 case 'E':
04939 if (rt_extend_conf(args.confno)) {
04940 res = -1;
04941 }
04942 break;
04943 }
04944
04945 if (args.user) {
04946
04947 ao2_ref(user, -1);
04948 }
04949 usernotfound:
04950 AST_LIST_UNLOCK(&confs);
04951
04952 dispose_conf(cnf);
04953 pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
04954
04955 return 0;
04956 }
04957
04958
04959
04960 static int channel_admin_exec(struct ast_channel *chan, const char *data) {
04961 char *params;
04962 struct ast_conference *conf = NULL;
04963 struct ast_conf_user *user = NULL;
04964 AST_DECLARE_APP_ARGS(args,
04965 AST_APP_ARG(channel);
04966 AST_APP_ARG(command);
04967 );
04968
04969 if (ast_strlen_zero(data)) {
04970 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
04971 return -1;
04972 }
04973
04974 params = ast_strdupa(data);
04975 AST_STANDARD_APP_ARGS(args, params);
04976
04977 if (!args.channel) {
04978 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
04979 return -1;
04980 }
04981
04982 if (!args.command) {
04983 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
04984 return -1;
04985 }
04986
04987 AST_LIST_LOCK(&confs);
04988 AST_LIST_TRAVERSE(&confs, conf, list) {
04989 if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
04990 break;
04991 }
04992 }
04993
04994 if (!user) {
04995 ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
04996 AST_LIST_UNLOCK(&confs);
04997 return 0;
04998 }
04999
05000
05001 switch (*args.command) {
05002 case 77:
05003 user->adminflags |= ADMINFLAG_MUTED;
05004 break;
05005 case 109:
05006 user->adminflags &= ~ADMINFLAG_MUTED;
05007 break;
05008 case 107:
05009 user->adminflags |= ADMINFLAG_KICKME;
05010 break;
05011 default:
05012 ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
05013 break;
05014 }
05015 ao2_ref(user, -1);
05016 AST_LIST_UNLOCK(&confs);
05017
05018 return 0;
05019 }
05020
05021 static int meetmemute(struct mansession *s, const struct message *m, int mute)
05022 {
05023 struct ast_conference *conf;
05024 struct ast_conf_user *user;
05025 const char *confid = astman_get_header(m, "Meetme");
05026 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
05027 int userno;
05028
05029 if (ast_strlen_zero(confid)) {
05030 astman_send_error(s, m, "Meetme conference not specified");
05031 return 0;
05032 }
05033
05034 if (ast_strlen_zero(userid)) {
05035 astman_send_error(s, m, "Meetme user number not specified");
05036 return 0;
05037 }
05038
05039 userno = strtoul(userid, &userid, 10);
05040
05041 if (*userid) {
05042 astman_send_error(s, m, "Invalid user number");
05043 return 0;
05044 }
05045
05046
05047 AST_LIST_LOCK(&confs);
05048 AST_LIST_TRAVERSE(&confs, conf, list) {
05049 if (!strcmp(confid, conf->confno))
05050 break;
05051 }
05052
05053 if (!conf) {
05054 AST_LIST_UNLOCK(&confs);
05055 astman_send_error(s, m, "Meetme conference does not exist");
05056 return 0;
05057 }
05058
05059 user = ao2_find(conf->usercontainer, &userno, 0);
05060
05061 if (!user) {
05062 AST_LIST_UNLOCK(&confs);
05063 astman_send_error(s, m, "User number not found");
05064 return 0;
05065 }
05066
05067 if (mute)
05068 user->adminflags |= ADMINFLAG_MUTED;
05069 else
05070 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
05071
05072 AST_LIST_UNLOCK(&confs);
05073
05074 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);
05075
05076 ao2_ref(user, -1);
05077 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
05078 return 0;
05079 }
05080
05081 static int action_meetmemute(struct mansession *s, const struct message *m)
05082 {
05083 return meetmemute(s, m, 1);
05084 }
05085
05086 static int action_meetmeunmute(struct mansession *s, const struct message *m)
05087 {
05088 return meetmemute(s, m, 0);
05089 }
05090
05091 static int action_meetmelist(struct mansession *s, const struct message *m)
05092 {
05093 const char *actionid = astman_get_header(m, "ActionID");
05094 const char *conference = astman_get_header(m, "Conference");
05095 char idText[80] = "";
05096 struct ast_conference *cnf;
05097 struct ast_conf_user *user;
05098 struct ao2_iterator user_iter;
05099 int total = 0;
05100
05101 if (!ast_strlen_zero(actionid))
05102 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05103
05104 if (AST_LIST_EMPTY(&confs)) {
05105 astman_send_error(s, m, "No active conferences.");
05106 return 0;
05107 }
05108
05109 astman_send_listack(s, m, "Meetme user list will follow", "start");
05110
05111
05112 AST_LIST_LOCK(&confs);
05113 AST_LIST_TRAVERSE(&confs, cnf, list) {
05114
05115 if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
05116 continue;
05117
05118
05119 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
05120 while ((user = ao2_iterator_next(&user_iter))) {
05121 total++;
05122 astman_append(s,
05123 "Event: MeetmeList\r\n"
05124 "%s"
05125 "Conference: %s\r\n"
05126 "UserNumber: %d\r\n"
05127 "CallerIDNum: %s\r\n"
05128 "CallerIDName: %s\r\n"
05129 "ConnectedLineNum: %s\r\n"
05130 "ConnectedLineName: %s\r\n"
05131 "Channel: %s\r\n"
05132 "Admin: %s\r\n"
05133 "Role: %s\r\n"
05134 "MarkedUser: %s\r\n"
05135 "Muted: %s\r\n"
05136 "Talking: %s\r\n"
05137 "\r\n",
05138 idText,
05139 cnf->confno,
05140 user->user_no,
05141 S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
05142 S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
05143 S_COR(user->chan->connected.id.number.valid, user->chan->connected.id.number.str, "<unknown>"),
05144 S_COR(user->chan->connected.id.name.valid, user->chan->connected.id.name.str, "<no name>"),
05145 user->chan->name,
05146 ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
05147 ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
05148 ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
05149 user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
05150 user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
05151 ao2_ref(user, -1);
05152 }
05153 ao2_iterator_destroy(&user_iter);
05154 }
05155 AST_LIST_UNLOCK(&confs);
05156
05157 astman_append(s,
05158 "Event: MeetmeListComplete\r\n"
05159 "EventList: Complete\r\n"
05160 "ListItems: %d\r\n"
05161 "%s"
05162 "\r\n", total, idText);
05163 return 0;
05164 }
05165
05166
05167
05168
05169
05170
05171
05172 static void filename_parse(char *filename, char *buffer)
05173 {
05174 char *slash;
05175 if (ast_strlen_zero(filename)) {
05176 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
05177 } else if (filename[0] != '/') {
05178 snprintf(buffer, PATH_MAX, "%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
05179 } else {
05180 ast_copy_string(buffer, filename, PATH_MAX);
05181 }
05182
05183 slash = buffer;
05184 if ((slash = strrchr(slash, '/'))) {
05185 *slash = '\0';
05186 ast_mkdir(buffer, 0777);
05187 *slash = '/';
05188 }
05189 }
05190
05191 static void *recordthread(void *args)
05192 {
05193 struct ast_conference *cnf = args;
05194 struct ast_frame *f = NULL;
05195 int flags;
05196 struct ast_filestream *s = NULL;
05197 int res = 0;
05198 int x;
05199 const char *oldrecordingfilename = NULL;
05200 char filename_buffer[PATH_MAX];
05201
05202 if (!cnf || !cnf->lchan) {
05203 pthread_exit(0);
05204 }
05205
05206 filename_buffer[0] = '\0';
05207 filename_parse(cnf->recordingfilename, filename_buffer);
05208
05209 ast_stopstream(cnf->lchan);
05210 flags = O_CREAT | O_TRUNC | O_WRONLY;
05211
05212
05213 cnf->recording = MEETME_RECORD_ACTIVE;
05214 while (ast_waitfor(cnf->lchan, -1) > -1) {
05215 if (cnf->recording == MEETME_RECORD_TERMINATE) {
05216 AST_LIST_LOCK(&confs);
05217 AST_LIST_UNLOCK(&confs);
05218 break;
05219 }
05220 if (!s && !(ast_strlen_zero(filename_buffer)) && (filename_buffer != oldrecordingfilename)) {
05221 s = ast_writefile(filename_buffer, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
05222 oldrecordingfilename = filename_buffer;
05223 }
05224
05225 f = ast_read(cnf->lchan);
05226 if (!f) {
05227 res = -1;
05228 break;
05229 }
05230 if (f->frametype == AST_FRAME_VOICE) {
05231 ast_mutex_lock(&cnf->listenlock);
05232 for (x = 0; x < AST_FRAME_BITS; x++) {
05233
05234 if (cnf->transframe[x]) {
05235 ast_frfree(cnf->transframe[x]);
05236 cnf->transframe[x] = NULL;
05237 }
05238 }
05239 if (cnf->origframe)
05240 ast_frfree(cnf->origframe);
05241 cnf->origframe = ast_frdup(f);
05242 ast_mutex_unlock(&cnf->listenlock);
05243 if (s)
05244 res = ast_writestream(s, f);
05245 if (res) {
05246 ast_frfree(f);
05247 break;
05248 }
05249 }
05250 ast_frfree(f);
05251 }
05252 cnf->recording = MEETME_RECORD_OFF;
05253 if (s)
05254 ast_closestream(s);
05255
05256 pthread_exit(0);
05257 }
05258
05259
05260 static enum ast_device_state meetmestate(const char *data)
05261 {
05262 struct ast_conference *conf;
05263
05264
05265 AST_LIST_LOCK(&confs);
05266 AST_LIST_TRAVERSE(&confs, conf, list) {
05267 if (!strcmp(data, conf->confno))
05268 break;
05269 }
05270 AST_LIST_UNLOCK(&confs);
05271 if (!conf)
05272 return AST_DEVICE_INVALID;
05273
05274
05275
05276 if (!conf->users)
05277 return AST_DEVICE_NOT_INUSE;
05278
05279 return AST_DEVICE_INUSE;
05280 }
05281
05282 static void load_config_meetme(void)
05283 {
05284 struct ast_config *cfg;
05285 struct ast_flags config_flags = { 0 };
05286 const char *val;
05287
05288 if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
05289 return;
05290 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
05291 ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format. Aborting.\n");
05292 return;
05293 }
05294
05295 audio_buffers = DEFAULT_AUDIO_BUFFERS;
05296
05297
05298 rt_schedule = 0;
05299 fuzzystart = 0;
05300 earlyalert = 0;
05301 endalert = 0;
05302 extendby = 0;
05303
05304
05305 rt_log_members = 1;
05306
05307 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
05308 if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
05309 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
05310 audio_buffers = DEFAULT_AUDIO_BUFFERS;
05311 } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
05312 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
05313 DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
05314 audio_buffers = DEFAULT_AUDIO_BUFFERS;
05315 }
05316 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
05317 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
05318 }
05319
05320 if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
05321 rt_schedule = ast_true(val);
05322 if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
05323 rt_log_members = ast_true(val);
05324 if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
05325 if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
05326 ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
05327 fuzzystart = 0;
05328 }
05329 }
05330 if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
05331 if ((sscanf(val, "%30d", &earlyalert) != 1)) {
05332 ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
05333 earlyalert = 0;
05334 }
05335 }
05336 if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
05337 if ((sscanf(val, "%30d", &endalert) != 1)) {
05338 ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
05339 endalert = 0;
05340 }
05341 }
05342 if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
05343 if ((sscanf(val, "%30d", &extendby) != 1)) {
05344 ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
05345 extendby = 0;
05346 }
05347 }
05348
05349 ast_config_destroy(cfg);
05350 }
05351
05352
05353
05354
05355
05356 static void unref_obj(void *obj)
05357 {
05358 if (obj) {
05359 ao2_ref(obj, -1);
05360 }
05361 }
05362
05363
05364
05365
05366
05367 static struct sla_trunk *sla_find_trunk(const char *name)
05368 {
05369 struct sla_trunk tmp_trunk = {
05370 .name = name,
05371 };
05372
05373 return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
05374 }
05375
05376
05377
05378
05379
05380 static struct sla_station *sla_find_station(const char *name)
05381 {
05382 struct sla_station tmp_station = {
05383 .name = name,
05384 };
05385
05386 return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
05387 }
05388
05389 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
05390 const struct sla_station *station)
05391 {
05392 struct sla_station_ref *station_ref;
05393 struct sla_trunk_ref *trunk_ref;
05394
05395
05396 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
05397 AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
05398 if (trunk_ref->trunk != trunk || station_ref->station == station)
05399 continue;
05400 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
05401 station_ref->station->hold_access == SLA_HOLD_PRIVATE)
05402 return 1;
05403 return 0;
05404 }
05405 }
05406
05407 return 0;
05408 }
05409
05410
05411
05412
05413
05414
05415
05416
05417
05418
05419 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
05420 const char *name)
05421 {
05422 struct sla_trunk_ref *trunk_ref = NULL;
05423
05424 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05425 if (strcasecmp(trunk_ref->trunk->name, name))
05426 continue;
05427
05428 if ( (trunk_ref->trunk->barge_disabled
05429 && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
05430 (trunk_ref->trunk->hold_stations
05431 && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
05432 && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
05433 sla_check_station_hold_access(trunk_ref->trunk, station) )
05434 {
05435 trunk_ref = NULL;
05436 }
05437
05438 break;
05439 }
05440
05441 if (trunk_ref) {
05442 ao2_ref(trunk_ref, 1);
05443 }
05444
05445 return trunk_ref;
05446 }
05447
05448 static void sla_station_ref_destructor(void *obj)
05449 {
05450 struct sla_station_ref *station_ref = obj;
05451
05452 if (station_ref->station) {
05453 ao2_ref(station_ref->station, -1);
05454 station_ref->station = NULL;
05455 }
05456 }
05457
05458 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
05459 {
05460 struct sla_station_ref *station_ref;
05461
05462 if (!(station_ref = ao2_alloc(sizeof(*station_ref), sla_station_ref_destructor))) {
05463 return NULL;
05464 }
05465
05466 ao2_ref(station, 1);
05467 station_ref->station = station;
05468
05469 return station_ref;
05470 }
05471
05472 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
05473 {
05474 struct sla_ringing_station *ringing_station;
05475
05476 if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
05477 return NULL;
05478
05479 ao2_ref(station, 1);
05480 ringing_station->station = station;
05481 ringing_station->ring_begin = ast_tvnow();
05482
05483 return ringing_station;
05484 }
05485
05486 static void sla_ringing_station_destroy(struct sla_ringing_station *ringing_station)
05487 {
05488 if (ringing_station->station) {
05489 ao2_ref(ringing_station->station, -1);
05490 ringing_station->station = NULL;
05491 }
05492
05493 ast_free(ringing_station);
05494 }
05495
05496 static struct sla_failed_station *sla_create_failed_station(struct sla_station *station)
05497 {
05498 struct sla_failed_station *failed_station;
05499
05500 if (!(failed_station = ast_calloc(1, sizeof(*failed_station)))) {
05501 return NULL;
05502 }
05503
05504 ao2_ref(station, 1);
05505 failed_station->station = station;
05506 failed_station->last_try = ast_tvnow();
05507
05508 return failed_station;
05509 }
05510
05511 static void sla_failed_station_destroy(struct sla_failed_station *failed_station)
05512 {
05513 if (failed_station->station) {
05514 ao2_ref(failed_station->station, -1);
05515 failed_station->station = NULL;
05516 }
05517
05518 ast_free(failed_station);
05519 }
05520
05521 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
05522 {
05523 switch (state) {
05524 case SLA_TRUNK_STATE_IDLE:
05525 return AST_DEVICE_NOT_INUSE;
05526 case SLA_TRUNK_STATE_RINGING:
05527 return AST_DEVICE_RINGING;
05528 case SLA_TRUNK_STATE_UP:
05529 return AST_DEVICE_INUSE;
05530 case SLA_TRUNK_STATE_ONHOLD:
05531 case SLA_TRUNK_STATE_ONHOLD_BYME:
05532 return AST_DEVICE_ONHOLD;
05533 }
05534
05535 return AST_DEVICE_UNKNOWN;
05536 }
05537
05538 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
05539 enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
05540 {
05541 struct sla_station *station;
05542 struct sla_trunk_ref *trunk_ref;
05543 struct ao2_iterator i;
05544
05545 i = ao2_iterator_init(sla_stations, 0);
05546 while ((station = ao2_iterator_next(&i))) {
05547 ao2_lock(station);
05548 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05549 if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
05550 || trunk_ref == exclude) {
05551 continue;
05552 }
05553 trunk_ref->state = state;
05554 ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
05555 "SLA:%s_%s", station->name, trunk->name);
05556 break;
05557 }
05558 ao2_unlock(station);
05559 ao2_ref(station, -1);
05560 }
05561 ao2_iterator_destroy(&i);
05562 }
05563
05564 struct run_station_args {
05565 struct sla_station *station;
05566 struct sla_trunk_ref *trunk_ref;
05567 ast_mutex_t *cond_lock;
05568 ast_cond_t *cond;
05569 };
05570
05571 static void answer_trunk_chan(struct ast_channel *chan)
05572 {
05573 ast_answer(chan);
05574 ast_indicate(chan, -1);
05575 }
05576
05577 static void *run_station(void *data)
05578 {
05579 RAII_VAR(struct sla_station *, station, NULL, unref_obj);
05580 RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, unref_obj);
05581 struct ast_str *conf_name = ast_str_create(16);
05582 struct ast_flags64 conf_flags = { 0 };
05583 struct ast_conference *conf;
05584
05585 {
05586 struct run_station_args *args = data;
05587 station = args->station;
05588 trunk_ref = args->trunk_ref;
05589 ast_mutex_lock(args->cond_lock);
05590 ast_cond_signal(args->cond);
05591 ast_mutex_unlock(args->cond_lock);
05592
05593 }
05594
05595 ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
05596 ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
05597 ast_set_flag64(&conf_flags,
05598 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
05599 answer_trunk_chan(trunk_ref->chan);
05600 conf = build_conf(ast_str_buffer(conf_name), "", "", 0, 0, 1, trunk_ref->chan, NULL);
05601 if (conf) {
05602 conf_run(trunk_ref->chan, conf, &conf_flags, NULL);
05603 dispose_conf(conf);
05604 conf = NULL;
05605 }
05606 trunk_ref->chan = NULL;
05607 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
05608 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
05609 ast_str_append(&conf_name, 0, ",K");
05610 admin_exec(NULL, ast_str_buffer(conf_name));
05611 trunk_ref->trunk->hold_stations = 0;
05612 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05613 }
05614
05615 ast_dial_join(station->dial);
05616 ast_dial_destroy(station->dial);
05617 station->dial = NULL;
05618 ast_free(conf_name);
05619
05620 return NULL;
05621 }
05622
05623 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk);
05624
05625 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
05626 {
05627 char buf[80];
05628 struct sla_station_ref *station_ref;
05629
05630 snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
05631 admin_exec(NULL, buf);
05632 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05633
05634 while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
05635 ao2_ref(station_ref, -1);
05636 }
05637
05638 sla_ringing_trunk_destroy(ringing_trunk);
05639 }
05640
05641 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
05642 enum sla_station_hangup hangup)
05643 {
05644 struct sla_ringing_trunk *ringing_trunk;
05645 struct sla_trunk_ref *trunk_ref;
05646 struct sla_station_ref *station_ref;
05647
05648 ast_dial_join(ringing_station->station->dial);
05649 ast_dial_destroy(ringing_station->station->dial);
05650 ringing_station->station->dial = NULL;
05651
05652 if (hangup == SLA_STATION_HANGUP_NORMAL)
05653 goto done;
05654
05655
05656
05657
05658
05659
05660 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05661 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
05662 if (ringing_trunk->trunk == trunk_ref->trunk)
05663 break;
05664 }
05665 if (!trunk_ref)
05666 continue;
05667 if (!(station_ref = sla_create_station_ref(ringing_station->station)))
05668 continue;
05669 AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
05670 }
05671
05672 done:
05673 sla_ringing_station_destroy(ringing_station);
05674 }
05675
05676 static void sla_dial_state_callback(struct ast_dial *dial)
05677 {
05678 sla_queue_event(SLA_EVENT_DIAL_STATE);
05679 }
05680
05681
05682
05683
05684 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
05685 const struct sla_station *station)
05686 {
05687 struct sla_station_ref *timed_out_station;
05688
05689 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
05690 if (station == timed_out_station->station)
05691 return 1;
05692 }
05693
05694 return 0;
05695 }
05696
05697
05698
05699
05700
05701
05702
05703
05704
05705 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
05706 struct sla_trunk_ref **trunk_ref, int rm)
05707 {
05708 struct sla_trunk_ref *s_trunk_ref;
05709 struct sla_ringing_trunk *ringing_trunk = NULL;
05710
05711 AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
05712 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05713
05714 if (s_trunk_ref->trunk != ringing_trunk->trunk)
05715 continue;
05716
05717
05718
05719 if (sla_check_timed_out_station(ringing_trunk, station))
05720 continue;
05721
05722 if (rm)
05723 AST_LIST_REMOVE_CURRENT(entry);
05724
05725 if (trunk_ref) {
05726 ao2_ref(s_trunk_ref, 1);
05727 *trunk_ref = s_trunk_ref;
05728 }
05729
05730 break;
05731 }
05732 AST_LIST_TRAVERSE_SAFE_END;
05733
05734 if (ringing_trunk)
05735 break;
05736 }
05737
05738 return ringing_trunk;
05739 }
05740
05741 static void sla_handle_dial_state_event(void)
05742 {
05743 struct sla_ringing_station *ringing_station;
05744
05745 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
05746 RAII_VAR(struct sla_trunk_ref *, s_trunk_ref, NULL, unref_obj);
05747 struct sla_ringing_trunk *ringing_trunk = NULL;
05748 struct run_station_args args;
05749 enum ast_dial_result dial_res;
05750 pthread_t dont_care;
05751 ast_mutex_t cond_lock;
05752 ast_cond_t cond;
05753
05754 switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
05755 case AST_DIAL_RESULT_HANGUP:
05756 case AST_DIAL_RESULT_INVALID:
05757 case AST_DIAL_RESULT_FAILED:
05758 case AST_DIAL_RESULT_TIMEOUT:
05759 case AST_DIAL_RESULT_UNANSWERED:
05760 AST_LIST_REMOVE_CURRENT(entry);
05761 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
05762 break;
05763 case AST_DIAL_RESULT_ANSWERED:
05764 AST_LIST_REMOVE_CURRENT(entry);
05765
05766 ast_mutex_lock(&sla.lock);
05767 ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
05768 ast_mutex_unlock(&sla.lock);
05769 if (!ringing_trunk) {
05770
05771
05772
05773
05774
05775 ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
05776 ast_dial_join(ringing_station->station->dial);
05777 ast_dial_destroy(ringing_station->station->dial);
05778 ringing_station->station->dial = NULL;
05779 sla_ringing_station_destroy(ringing_station);
05780 break;
05781 }
05782
05783 s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
05784
05785 answer_trunk_chan(ringing_trunk->trunk->chan);
05786 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
05787
05788
05789
05790 ao2_ref(s_trunk_ref, 1);
05791 args.trunk_ref = s_trunk_ref;
05792 ao2_ref(ringing_station->station, 1);
05793 args.station = ringing_station->station;
05794 args.cond = &cond;
05795 args.cond_lock = &cond_lock;
05796 sla_ringing_trunk_destroy(ringing_trunk);
05797 sla_ringing_station_destroy(ringing_station);
05798 ast_mutex_init(&cond_lock);
05799 ast_cond_init(&cond, NULL);
05800 ast_mutex_lock(&cond_lock);
05801 ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
05802 ast_cond_wait(&cond, &cond_lock);
05803 ast_mutex_unlock(&cond_lock);
05804 ast_mutex_destroy(&cond_lock);
05805 ast_cond_destroy(&cond);
05806 break;
05807 case AST_DIAL_RESULT_TRYING:
05808 case AST_DIAL_RESULT_RINGING:
05809 case AST_DIAL_RESULT_PROGRESS:
05810 case AST_DIAL_RESULT_PROCEEDING:
05811 break;
05812 }
05813 if (dial_res == AST_DIAL_RESULT_ANSWERED) {
05814
05815 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
05816 sla_queue_event(SLA_EVENT_DIAL_STATE);
05817 break;
05818 }
05819 }
05820 AST_LIST_TRAVERSE_SAFE_END;
05821 }
05822
05823
05824
05825
05826 static int sla_check_ringing_station(const struct sla_station *station)
05827 {
05828 struct sla_ringing_station *ringing_station;
05829
05830 AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
05831 if (station == ringing_station->station)
05832 return 1;
05833 }
05834
05835 return 0;
05836 }
05837
05838
05839
05840
05841 static int sla_check_failed_station(const struct sla_station *station)
05842 {
05843 struct sla_failed_station *failed_station;
05844 int res = 0;
05845
05846 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
05847 if (station != failed_station->station)
05848 continue;
05849 if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
05850 AST_LIST_REMOVE_CURRENT(entry);
05851 sla_failed_station_destroy(failed_station);
05852 break;
05853 }
05854 res = 1;
05855 }
05856 AST_LIST_TRAVERSE_SAFE_END
05857
05858 return res;
05859 }
05860
05861
05862
05863
05864 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
05865 {
05866 char *tech, *tech_data;
05867 struct ast_dial *dial;
05868 struct sla_ringing_station *ringing_station;
05869 enum ast_dial_result res;
05870 int caller_is_saved;
05871 struct ast_party_caller caller;
05872
05873 if (!(dial = ast_dial_create()))
05874 return -1;
05875
05876 ast_dial_set_state_callback(dial, sla_dial_state_callback);
05877 tech_data = ast_strdupa(station->device);
05878 tech = strsep(&tech_data, "/");
05879
05880 if (ast_dial_append(dial, tech, tech_data) == -1) {
05881 ast_dial_destroy(dial);
05882 return -1;
05883 }
05884
05885
05886 caller_is_saved = 0;
05887 if (!sla.attempt_callerid) {
05888 caller_is_saved = 1;
05889 caller = ringing_trunk->trunk->chan->caller;
05890 ast_party_caller_init(&ringing_trunk->trunk->chan->caller);
05891 }
05892
05893 res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
05894
05895
05896 if (caller_is_saved) {
05897 ast_party_caller_free(&ringing_trunk->trunk->chan->caller);
05898 ringing_trunk->trunk->chan->caller = caller;
05899 }
05900
05901 if (res != AST_DIAL_RESULT_TRYING) {
05902 struct sla_failed_station *failed_station;
05903 ast_dial_destroy(dial);
05904 if ((failed_station = sla_create_failed_station(station))) {
05905 AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
05906 }
05907 return -1;
05908 }
05909 if (!(ringing_station = sla_create_ringing_station(station))) {
05910 ast_dial_join(dial);
05911 ast_dial_destroy(dial);
05912 return -1;
05913 }
05914
05915 station->dial = dial;
05916
05917 AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
05918
05919 return 0;
05920 }
05921
05922
05923
05924 static int sla_check_inuse_station(const struct sla_station *station)
05925 {
05926 struct sla_trunk_ref *trunk_ref;
05927
05928 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05929 if (trunk_ref->chan)
05930 return 1;
05931 }
05932
05933 return 0;
05934 }
05935
05936 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
05937 const struct sla_trunk *trunk)
05938 {
05939 struct sla_trunk_ref *trunk_ref = NULL;
05940
05941 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05942 if (trunk_ref->trunk == trunk)
05943 break;
05944 }
05945
05946 ao2_ref(trunk_ref, 1);
05947
05948 return trunk_ref;
05949 }
05950
05951
05952
05953
05954
05955
05956 static int sla_check_station_delay(struct sla_station *station,
05957 struct sla_ringing_trunk *ringing_trunk)
05958 {
05959 RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, unref_obj);
05960 unsigned int delay = UINT_MAX;
05961 int time_left, time_elapsed;
05962
05963 if (!ringing_trunk)
05964 ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
05965 else
05966 trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
05967
05968 if (!ringing_trunk || !trunk_ref)
05969 return delay;
05970
05971
05972
05973
05974 delay = trunk_ref->ring_delay;
05975 if (!delay)
05976 delay = station->ring_delay;
05977 if (!delay)
05978 return INT_MAX;
05979
05980 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
05981 time_left = (delay * 1000) - time_elapsed;
05982
05983 return time_left;
05984 }
05985
05986
05987
05988
05989 static void sla_ring_stations(void)
05990 {
05991 struct sla_station_ref *station_ref;
05992 struct sla_ringing_trunk *ringing_trunk;
05993
05994
05995
05996 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
05997 AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
05998 int time_left;
05999
06000
06001 if (sla_check_ringing_station(station_ref->station))
06002 continue;
06003
06004
06005 if (sla_check_inuse_station(station_ref->station))
06006 continue;
06007
06008
06009
06010 if (sla_check_failed_station(station_ref->station))
06011 continue;
06012
06013
06014
06015 if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
06016 continue;
06017
06018
06019 time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
06020 if (time_left != INT_MAX && time_left > 0)
06021 continue;
06022
06023
06024 sla_ring_station(ringing_trunk, station_ref->station);
06025 }
06026 }
06027
06028 }
06029
06030 static void sla_hangup_stations(void)
06031 {
06032 struct sla_trunk_ref *trunk_ref;
06033 struct sla_ringing_station *ringing_station;
06034
06035 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06036 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06037 struct sla_ringing_trunk *ringing_trunk;
06038 ast_mutex_lock(&sla.lock);
06039 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06040 if (trunk_ref->trunk == ringing_trunk->trunk)
06041 break;
06042 }
06043 ast_mutex_unlock(&sla.lock);
06044 if (ringing_trunk)
06045 break;
06046 }
06047 if (!trunk_ref) {
06048 AST_LIST_REMOVE_CURRENT(entry);
06049 ast_dial_join(ringing_station->station->dial);
06050 ast_dial_destroy(ringing_station->station->dial);
06051 ringing_station->station->dial = NULL;
06052 sla_ringing_station_destroy(ringing_station);
06053 }
06054 }
06055 AST_LIST_TRAVERSE_SAFE_END
06056 }
06057
06058 static void sla_handle_ringing_trunk_event(void)
06059 {
06060 ast_mutex_lock(&sla.lock);
06061 sla_ring_stations();
06062 ast_mutex_unlock(&sla.lock);
06063
06064
06065 sla_hangup_stations();
06066 }
06067
06068 static void sla_handle_hold_event(struct sla_event *event)
06069 {
06070 ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
06071 event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
06072 ast_devstate_changed(AST_DEVICE_ONHOLD, AST_DEVSTATE_CACHABLE, "SLA:%s_%s",
06073 event->station->name, event->trunk_ref->trunk->name);
06074 sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
06075 INACTIVE_TRUNK_REFS, event->trunk_ref);
06076
06077 if (event->trunk_ref->trunk->active_stations == 1) {
06078
06079
06080 event->trunk_ref->trunk->on_hold = 1;
06081 ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
06082 }
06083
06084 ast_softhangup(event->trunk_ref->chan, AST_SOFTHANGUP_DEV);
06085 event->trunk_ref->chan = NULL;
06086 }
06087
06088
06089
06090
06091
06092 static int sla_calc_trunk_timeouts(unsigned int *timeout)
06093 {
06094 struct sla_ringing_trunk *ringing_trunk;
06095 int res = 0;
06096
06097 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06098 int time_left, time_elapsed;
06099 if (!ringing_trunk->trunk->ring_timeout)
06100 continue;
06101 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06102 time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
06103 if (time_left <= 0) {
06104 pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
06105 AST_LIST_REMOVE_CURRENT(entry);
06106 sla_stop_ringing_trunk(ringing_trunk);
06107 res = 1;
06108 continue;
06109 }
06110 if (time_left < *timeout)
06111 *timeout = time_left;
06112 }
06113 AST_LIST_TRAVERSE_SAFE_END;
06114
06115 return res;
06116 }
06117
06118
06119
06120
06121
06122 static int sla_calc_station_timeouts(unsigned int *timeout)
06123 {
06124 struct sla_ringing_trunk *ringing_trunk;
06125 struct sla_ringing_station *ringing_station;
06126 int res = 0;
06127
06128 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
06129 unsigned int ring_timeout = 0;
06130 int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
06131 struct sla_trunk_ref *trunk_ref;
06132
06133
06134
06135
06136 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
06137 struct sla_station_ref *station_ref;
06138 int trunk_time_elapsed, trunk_time_left;
06139
06140 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
06141 if (ringing_trunk->trunk == trunk_ref->trunk)
06142 break;
06143 }
06144 if (!ringing_trunk)
06145 continue;
06146
06147
06148
06149 if (!trunk_ref->ring_timeout)
06150 break;
06151
06152
06153
06154
06155 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
06156 if (station_ref->station == ringing_station->station)
06157 break;
06158 }
06159 if (station_ref)
06160 continue;
06161
06162 trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
06163 trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
06164 if (trunk_time_left > final_trunk_time_left)
06165 final_trunk_time_left = trunk_time_left;
06166 }
06167
06168
06169 if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
06170 continue;
06171
06172
06173 if (ringing_station->station->ring_timeout) {
06174 ring_timeout = ringing_station->station->ring_timeout;
06175 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
06176 time_left = (ring_timeout * 1000) - time_elapsed;
06177 }
06178
06179
06180
06181 if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
06182 time_left = final_trunk_time_left;
06183
06184
06185 if (time_left <= 0) {
06186 AST_LIST_REMOVE_CURRENT(entry);
06187 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
06188 res = 1;
06189 continue;
06190 }
06191
06192
06193
06194 if (time_left < *timeout)
06195 *timeout = time_left;
06196 }
06197 AST_LIST_TRAVERSE_SAFE_END;
06198
06199 return res;
06200 }
06201
06202
06203
06204
06205 static int sla_calc_station_delays(unsigned int *timeout)
06206 {
06207 struct sla_station *station;
06208 int res = 0;
06209 struct ao2_iterator i;
06210
06211 i = ao2_iterator_init(sla_stations, 0);
06212 for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
06213 struct sla_ringing_trunk *ringing_trunk;
06214 int time_left;
06215
06216
06217 if (sla_check_ringing_station(station))
06218 continue;
06219
06220
06221 if (sla_check_inuse_station(station))
06222 continue;
06223
06224
06225 if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
06226 continue;
06227
06228 if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
06229 continue;
06230
06231
06232
06233
06234 if (time_left <= 0) {
06235 res = 1;
06236 continue;
06237 }
06238
06239 if (time_left < *timeout)
06240 *timeout = time_left;
06241 }
06242 ao2_iterator_destroy(&i);
06243
06244 return res;
06245 }
06246
06247
06248
06249 static int sla_process_timers(struct timespec *ts)
06250 {
06251 unsigned int timeout = UINT_MAX;
06252 struct timeval wait;
06253 unsigned int change_made = 0;
06254
06255
06256 if (sla_calc_trunk_timeouts(&timeout))
06257 change_made = 1;
06258
06259
06260 if (sla_calc_station_timeouts(&timeout))
06261 change_made = 1;
06262
06263
06264 if (sla_calc_station_delays(&timeout))
06265 change_made = 1;
06266
06267
06268 if (change_made)
06269 sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
06270
06271
06272 if (timeout == UINT_MAX)
06273 return 0;
06274
06275 if (ts) {
06276 wait = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
06277 ts->tv_sec = wait.tv_sec;
06278 ts->tv_nsec = wait.tv_usec * 1000;
06279 }
06280
06281 return 1;
06282 }
06283
06284 static void sla_event_destroy(struct sla_event *event)
06285 {
06286 if (event->trunk_ref) {
06287 ao2_ref(event->trunk_ref, -1);
06288 event->trunk_ref = NULL;
06289 }
06290
06291 if (event->station) {
06292 ao2_ref(event->station, -1);
06293 event->station = NULL;
06294 }
06295
06296 ast_free(event);
06297 }
06298
06299 static void *sla_thread(void *data)
06300 {
06301 struct sla_failed_station *failed_station;
06302 struct sla_ringing_station *ringing_station;
06303
06304 ast_mutex_lock(&sla.lock);
06305
06306 while (!sla.stop) {
06307 struct sla_event *event;
06308 struct timespec ts = { 0, };
06309 unsigned int have_timeout = 0;
06310
06311 if (AST_LIST_EMPTY(&sla.event_q)) {
06312 if ((have_timeout = sla_process_timers(&ts)))
06313 ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
06314 else
06315 ast_cond_wait(&sla.cond, &sla.lock);
06316 if (sla.stop)
06317 break;
06318 }
06319
06320 if (have_timeout)
06321 sla_process_timers(NULL);
06322
06323 while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
06324 ast_mutex_unlock(&sla.lock);
06325 switch (event->type) {
06326 case SLA_EVENT_HOLD:
06327 sla_handle_hold_event(event);
06328 break;
06329 case SLA_EVENT_DIAL_STATE:
06330 sla_handle_dial_state_event();
06331 break;
06332 case SLA_EVENT_RINGING_TRUNK:
06333 sla_handle_ringing_trunk_event();
06334 break;
06335 }
06336 sla_event_destroy(event);
06337 ast_mutex_lock(&sla.lock);
06338 }
06339 }
06340
06341 ast_mutex_unlock(&sla.lock);
06342
06343 while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry))) {
06344 sla_ringing_station_destroy(ringing_station);
06345 }
06346
06347 while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) {
06348 sla_failed_station_destroy(failed_station);
06349 }
06350
06351 return NULL;
06352 }
06353
06354 struct dial_trunk_args {
06355 struct sla_trunk_ref *trunk_ref;
06356 struct sla_station *station;
06357 ast_mutex_t *cond_lock;
06358 ast_cond_t *cond;
06359 };
06360
06361 static void *dial_trunk(void *data)
06362 {
06363 struct dial_trunk_args *args = data;
06364 struct ast_dial *dial;
06365 char *tech, *tech_data;
06366 enum ast_dial_result dial_res;
06367 char conf_name[MAX_CONFNUM];
06368 struct ast_conference *conf;
06369 struct ast_flags64 conf_flags = { 0 };
06370 RAII_VAR(struct sla_trunk_ref *, trunk_ref, args->trunk_ref, unref_obj);
06371 RAII_VAR(struct sla_station *, station, args->station, unref_obj);
06372 int caller_is_saved;
06373 struct ast_party_caller caller;
06374 int last_state = 0;
06375 int current_state = 0;
06376
06377 if (!(dial = ast_dial_create())) {
06378 ast_mutex_lock(args->cond_lock);
06379 ast_cond_signal(args->cond);
06380 ast_mutex_unlock(args->cond_lock);
06381 return NULL;
06382 }
06383
06384 tech_data = ast_strdupa(trunk_ref->trunk->device);
06385 tech = strsep(&tech_data, "/");
06386 if (ast_dial_append(dial, tech, tech_data) == -1) {
06387 ast_mutex_lock(args->cond_lock);
06388 ast_cond_signal(args->cond);
06389 ast_mutex_unlock(args->cond_lock);
06390 ast_dial_destroy(dial);
06391 return NULL;
06392 }
06393
06394
06395 caller_is_saved = 0;
06396 if (!sla.attempt_callerid) {
06397 caller_is_saved = 1;
06398 caller = trunk_ref->chan->caller;
06399 ast_party_caller_init(&trunk_ref->chan->caller);
06400 }
06401
06402 dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
06403
06404
06405 if (caller_is_saved) {
06406 ast_party_caller_free(&trunk_ref->chan->caller);
06407 trunk_ref->chan->caller = caller;
06408 }
06409
06410 if (dial_res != AST_DIAL_RESULT_TRYING) {
06411 ast_mutex_lock(args->cond_lock);
06412 ast_cond_signal(args->cond);
06413 ast_mutex_unlock(args->cond_lock);
06414 ast_dial_destroy(dial);
06415 return NULL;
06416 }
06417
06418 for (;;) {
06419 unsigned int done = 0;
06420 switch ((dial_res = ast_dial_state(dial))) {
06421 case AST_DIAL_RESULT_ANSWERED:
06422 trunk_ref->trunk->chan = ast_dial_answered(dial);
06423 case AST_DIAL_RESULT_HANGUP:
06424 case AST_DIAL_RESULT_INVALID:
06425 case AST_DIAL_RESULT_FAILED:
06426 case AST_DIAL_RESULT_TIMEOUT:
06427 case AST_DIAL_RESULT_UNANSWERED:
06428 done = 1;
06429 break;
06430 case AST_DIAL_RESULT_TRYING:
06431 current_state = AST_CONTROL_PROGRESS;
06432 break;
06433 case AST_DIAL_RESULT_RINGING:
06434 case AST_DIAL_RESULT_PROGRESS:
06435 case AST_DIAL_RESULT_PROCEEDING:
06436 current_state = AST_CONTROL_RINGING;
06437 break;
06438 }
06439 if (done)
06440 break;
06441
06442
06443 if (station && ast_device_state(station->device) == AST_DEVICE_NOT_INUSE) {
06444 ast_debug(3, "Originating station device %s no longer active\n", station->device);
06445 trunk_ref->trunk->chan = NULL;
06446 break;
06447 }
06448
06449
06450 if (current_state != last_state) {
06451 ast_debug(3, "Indicating State Change %d to channel %s\n", current_state, trunk_ref->chan->name);
06452 ast_indicate(trunk_ref->chan, current_state);
06453 last_state = current_state;
06454 }
06455
06456
06457 ast_safe_sleep(trunk_ref->chan, 100);
06458 }
06459
06460 if (!trunk_ref->trunk->chan) {
06461 ast_mutex_lock(args->cond_lock);
06462 ast_cond_signal(args->cond);
06463 ast_mutex_unlock(args->cond_lock);
06464 ast_dial_join(dial);
06465 ast_dial_destroy(dial);
06466 return NULL;
06467 }
06468
06469 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06470 ast_set_flag64(&conf_flags,
06471 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
06472 CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
06473 conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);
06474
06475 ast_mutex_lock(args->cond_lock);
06476 ast_cond_signal(args->cond);
06477 ast_mutex_unlock(args->cond_lock);
06478
06479 if (conf) {
06480 conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
06481 dispose_conf(conf);
06482 conf = NULL;
06483 }
06484
06485
06486 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06487
06488 trunk_ref->trunk->chan = NULL;
06489 trunk_ref->trunk->on_hold = 0;
06490
06491 ast_dial_join(dial);
06492 ast_dial_destroy(dial);
06493
06494 return NULL;
06495 }
06496
06497
06498
06499
06500
06501 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
06502 {
06503 struct sla_trunk_ref *trunk_ref = NULL;
06504
06505 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06506 if (trunk_ref->state == SLA_TRUNK_STATE_IDLE) {
06507 ao2_ref(trunk_ref, 1);
06508 break;
06509 }
06510 }
06511
06512 return trunk_ref;
06513 }
06514
06515 static int sla_station_exec(struct ast_channel *chan, const char *data)
06516 {
06517 char *station_name, *trunk_name;
06518 RAII_VAR(struct sla_station *, station, NULL, unref_obj);
06519 RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, unref_obj);
06520 char conf_name[MAX_CONFNUM];
06521 struct ast_flags64 conf_flags = { 0 };
06522 struct ast_conference *conf;
06523
06524 if (ast_strlen_zero(data)) {
06525 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06526 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06527 return 0;
06528 }
06529
06530 trunk_name = ast_strdupa(data);
06531 station_name = strsep(&trunk_name, "_");
06532
06533 if (ast_strlen_zero(station_name)) {
06534 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
06535 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06536 return 0;
06537 }
06538
06539 station = sla_find_station(station_name);
06540
06541 if (!station) {
06542 ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
06543 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
06544 return 0;
06545 }
06546
06547 ao2_lock(station);
06548 if (!ast_strlen_zero(trunk_name)) {
06549 trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
06550 } else {
06551 trunk_ref = sla_choose_idle_trunk(station);
06552 }
06553 ao2_unlock(station);
06554
06555 if (!trunk_ref) {
06556 if (ast_strlen_zero(trunk_name))
06557 ast_log(LOG_NOTICE, "No trunks available for call.\n");
06558 else {
06559 ast_log(LOG_NOTICE, "Can't join existing call on trunk "
06560 "'%s' due to access controls.\n", trunk_name);
06561 }
06562 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06563 return 0;
06564 }
06565
06566 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
06567 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
06568 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06569 else {
06570 trunk_ref->state = SLA_TRUNK_STATE_UP;
06571 ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE,
06572 "SLA:%s_%s", station->name, trunk_ref->trunk->name);
06573 }
06574 } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
06575 struct sla_ringing_trunk *ringing_trunk;
06576
06577 ast_mutex_lock(&sla.lock);
06578 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06579 if (ringing_trunk->trunk == trunk_ref->trunk) {
06580 AST_LIST_REMOVE_CURRENT(entry);
06581 break;
06582 }
06583 }
06584 AST_LIST_TRAVERSE_SAFE_END
06585 ast_mutex_unlock(&sla.lock);
06586
06587 if (ringing_trunk) {
06588 answer_trunk_chan(ringing_trunk->trunk->chan);
06589 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06590
06591 sla_ringing_trunk_destroy(ringing_trunk);
06592
06593
06594 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06595 sla_queue_event(SLA_EVENT_DIAL_STATE);
06596 }
06597 }
06598
06599 trunk_ref->chan = chan;
06600
06601 if (!trunk_ref->trunk->chan) {
06602 ast_mutex_t cond_lock;
06603 ast_cond_t cond;
06604 pthread_t dont_care;
06605 struct dial_trunk_args args = {
06606 .trunk_ref = trunk_ref,
06607 .station = station,
06608 .cond_lock = &cond_lock,
06609 .cond = &cond,
06610 };
06611 ao2_ref(trunk_ref, 1);
06612 ao2_ref(station, 1);
06613 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06614
06615
06616
06617 ast_autoservice_start(chan);
06618 ast_mutex_init(&cond_lock);
06619 ast_cond_init(&cond, NULL);
06620 ast_mutex_lock(&cond_lock);
06621 ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
06622 ast_cond_wait(&cond, &cond_lock);
06623 ast_mutex_unlock(&cond_lock);
06624 ast_mutex_destroy(&cond_lock);
06625 ast_cond_destroy(&cond);
06626 ast_autoservice_stop(chan);
06627 if (!trunk_ref->trunk->chan) {
06628 ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
06629 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
06630 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06631 trunk_ref->chan = NULL;
06632 return 0;
06633 }
06634 }
06635
06636 if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
06637 trunk_ref->trunk->on_hold) {
06638 trunk_ref->trunk->on_hold = 0;
06639 ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
06640 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
06641 }
06642
06643 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06644 ast_set_flag64(&conf_flags,
06645 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
06646 ast_answer(chan);
06647 conf = build_conf(conf_name, "", "", 0, 0, 1, chan, NULL);
06648 if (conf) {
06649 conf_run(chan, conf, &conf_flags, NULL);
06650 dispose_conf(conf);
06651 conf = NULL;
06652 }
06653 trunk_ref->chan = NULL;
06654 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
06655 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
06656 strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
06657 admin_exec(NULL, conf_name);
06658 trunk_ref->trunk->hold_stations = 0;
06659 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06660 }
06661
06662 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
06663
06664 return 0;
06665 }
06666
06667 static void sla_trunk_ref_destructor(void *obj)
06668 {
06669 struct sla_trunk_ref *trunk_ref = obj;
06670
06671 if (trunk_ref->trunk) {
06672 ao2_ref(trunk_ref->trunk, -1);
06673 trunk_ref->trunk = NULL;
06674 }
06675 }
06676
06677 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
06678 {
06679 struct sla_trunk_ref *trunk_ref;
06680
06681 if (!(trunk_ref = ao2_alloc(sizeof(*trunk_ref), sla_trunk_ref_destructor))) {
06682 return NULL;
06683 }
06684
06685 ao2_ref(trunk, 1);
06686 trunk_ref->trunk = trunk;
06687
06688 return trunk_ref;
06689 }
06690
06691 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
06692 {
06693 struct sla_ringing_trunk *ringing_trunk;
06694
06695 if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk)))) {
06696 return NULL;
06697 }
06698
06699 ao2_ref(trunk, 1);
06700 ringing_trunk->trunk = trunk;
06701 ringing_trunk->ring_begin = ast_tvnow();
06702
06703 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
06704
06705 ast_mutex_lock(&sla.lock);
06706 AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
06707 ast_mutex_unlock(&sla.lock);
06708
06709 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06710
06711 return ringing_trunk;
06712 }
06713
06714 static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk)
06715 {
06716 if (ringing_trunk->trunk) {
06717 ao2_ref(ringing_trunk->trunk, -1);
06718 ringing_trunk->trunk = NULL;
06719 }
06720
06721 ast_free(ringing_trunk);
06722 }
06723
06724 enum {
06725 SLA_TRUNK_OPT_MOH = (1 << 0),
06726 };
06727
06728 enum {
06729 SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
06730 SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
06731 };
06732
06733 AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
06734 AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
06735 END_OPTIONS );
06736
06737 static int sla_trunk_exec(struct ast_channel *chan, const char *data)
06738 {
06739 char conf_name[MAX_CONFNUM];
06740 struct ast_conference *conf;
06741 struct ast_flags64 conf_flags = { 0 };
06742 RAII_VAR(struct sla_trunk *, trunk, NULL, unref_obj);
06743 struct sla_ringing_trunk *ringing_trunk;
06744 AST_DECLARE_APP_ARGS(args,
06745 AST_APP_ARG(trunk_name);
06746 AST_APP_ARG(options);
06747 );
06748 char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
06749 struct ast_flags opt_flags = { 0 };
06750 char *parse;
06751
06752 if (ast_strlen_zero(data)) {
06753 ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
06754 return -1;
06755 }
06756
06757 parse = ast_strdupa(data);
06758 AST_STANDARD_APP_ARGS(args, parse);
06759 if (args.argc == 2) {
06760 if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
06761 ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
06762 return -1;
06763 }
06764 }
06765
06766 trunk = sla_find_trunk(args.trunk_name);
06767
06768 if (!trunk) {
06769 ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
06770 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06771 return 0;
06772 }
06773
06774 if (trunk->chan) {
06775 ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
06776 args.trunk_name);
06777 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06778 return 0;
06779 }
06780
06781 trunk->chan = chan;
06782
06783 if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
06784 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06785 return 0;
06786 }
06787
06788 snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
06789 conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
06790 if (!conf) {
06791 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
06792 return 0;
06793 }
06794 ast_set_flag64(&conf_flags,
06795 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
06796
06797 if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
06798 ast_indicate(chan, -1);
06799 ast_set_flag64(&conf_flags, CONFFLAG_MOH);
06800 } else
06801 ast_indicate(chan, AST_CONTROL_RINGING);
06802
06803 conf_run(chan, conf, &conf_flags, opts);
06804 dispose_conf(conf);
06805 conf = NULL;
06806 trunk->chan = NULL;
06807 trunk->on_hold = 0;
06808
06809 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06810
06811 if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
06812 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
06813
06814
06815 ast_mutex_lock(&sla.lock);
06816 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
06817 if (ringing_trunk->trunk == trunk) {
06818 AST_LIST_REMOVE_CURRENT(entry);
06819 break;
06820 }
06821 }
06822 AST_LIST_TRAVERSE_SAFE_END;
06823 ast_mutex_unlock(&sla.lock);
06824 if (ringing_trunk) {
06825 sla_ringing_trunk_destroy(ringing_trunk);
06826 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
06827
06828
06829 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
06830 }
06831
06832 return 0;
06833 }
06834
06835 static enum ast_device_state sla_state(const char *data)
06836 {
06837 char *buf, *station_name, *trunk_name;
06838 RAII_VAR(struct sla_station *, station, NULL, unref_obj);
06839 struct sla_trunk_ref *trunk_ref;
06840 enum ast_device_state res = AST_DEVICE_INVALID;
06841
06842 trunk_name = buf = ast_strdupa(data);
06843 station_name = strsep(&trunk_name, "_");
06844
06845 station = sla_find_station(station_name);
06846 if (station) {
06847 ao2_lock(station);
06848 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06849 if (!strcasecmp(trunk_name, trunk_ref->trunk->name)) {
06850 res = sla_state_to_devstate(trunk_ref->state);
06851 break;
06852 }
06853 }
06854 ao2_unlock(station);
06855 }
06856
06857 if (res == AST_DEVICE_INVALID) {
06858 ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
06859 trunk_name, station_name);
06860 }
06861
06862 return res;
06863 }
06864
06865 static int sla_trunk_release_refs(void *obj, void *arg, int flags)
06866 {
06867 struct sla_trunk *trunk = obj;
06868 struct sla_station_ref *station_ref;
06869
06870 while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry))) {
06871 ao2_ref(station_ref, -1);
06872 }
06873
06874 return 0;
06875 }
06876
06877 static int sla_station_release_refs(void *obj, void *arg, int flags)
06878 {
06879 struct sla_station *station = obj;
06880 struct sla_trunk_ref *trunk_ref;
06881
06882 while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry))) {
06883 ao2_ref(trunk_ref, -1);
06884 }
06885
06886 return 0;
06887 }
06888
06889 static void sla_station_destructor(void *obj)
06890 {
06891 struct sla_station *station = obj;
06892
06893 ast_debug(1, "sla_station destructor for '%s'\n", station->name);
06894
06895 if (!ast_strlen_zero(station->autocontext)) {
06896 struct sla_trunk_ref *trunk_ref;
06897
06898 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
06899 char exten[AST_MAX_EXTENSION];
06900 char hint[AST_MAX_APP];
06901 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
06902 snprintf(hint, sizeof(hint), "SLA:%s", exten);
06903 ast_context_remove_extension(station->autocontext, exten,
06904 1, sla_registrar);
06905 ast_context_remove_extension(station->autocontext, hint,
06906 PRIORITY_HINT, sla_registrar);
06907 }
06908 }
06909
06910 sla_station_release_refs(station, NULL, 0);
06911
06912 ast_string_field_free_memory(station);
06913 }
06914
06915 static int sla_trunk_hash(const void *obj, const int flags)
06916 {
06917 const struct sla_trunk *trunk = obj;
06918
06919 return ast_str_case_hash(trunk->name);
06920 }
06921
06922 static int sla_trunk_cmp(void *obj, void *arg, int flags)
06923 {
06924 struct sla_trunk *trunk = obj, *trunk2 = arg;
06925
06926 return !strcasecmp(trunk->name, trunk2->name) ? CMP_MATCH | CMP_STOP : 0;
06927 }
06928
06929 static int sla_station_hash(const void *obj, const int flags)
06930 {
06931 const struct sla_station *station = obj;
06932
06933 return ast_str_case_hash(station->name);
06934 }
06935
06936 static int sla_station_cmp(void *obj, void *arg, int flags)
06937 {
06938 struct sla_station *station = obj, *station2 = arg;
06939
06940 return !strcasecmp(station->name, station2->name) ? CMP_MATCH | CMP_STOP : 0;
06941 }
06942
06943 static void sla_destroy(void)
06944 {
06945 if (sla.thread != AST_PTHREADT_NULL) {
06946 ast_mutex_lock(&sla.lock);
06947 sla.stop = 1;
06948 ast_cond_signal(&sla.cond);
06949 ast_mutex_unlock(&sla.lock);
06950 pthread_join(sla.thread, NULL);
06951 }
06952
06953
06954 ast_context_destroy(NULL, sla_registrar);
06955
06956 ast_mutex_destroy(&sla.lock);
06957 ast_cond_destroy(&sla.cond);
06958
06959 ao2_callback(sla_trunks, 0, sla_trunk_release_refs, NULL);
06960 ao2_callback(sla_stations, 0, sla_station_release_refs, NULL);
06961
06962 ao2_ref(sla_trunks, -1);
06963 sla_trunks = NULL;
06964
06965 ao2_ref(sla_stations, -1);
06966 sla_stations = NULL;
06967 }
06968
06969 static int sla_check_device(const char *device)
06970 {
06971 char *tech, *tech_data;
06972
06973 tech_data = ast_strdupa(device);
06974 tech = strsep(&tech_data, "/");
06975
06976 if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
06977 return -1;
06978
06979 return 0;
06980 }
06981
06982 static void sla_trunk_destructor(void *obj)
06983 {
06984 struct sla_trunk *trunk = obj;
06985
06986 ast_debug(1, "sla_trunk destructor for '%s'\n", trunk->name);
06987
06988 if (!ast_strlen_zero(trunk->autocontext)) {
06989 ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
06990 }
06991
06992 sla_trunk_release_refs(trunk, NULL, 0);
06993
06994 ast_string_field_free_memory(trunk);
06995 }
06996
06997 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
06998 {
06999 RAII_VAR(struct sla_trunk *, trunk, NULL, unref_obj);
07000 struct ast_variable *var;
07001 const char *dev;
07002 int existing_trunk = 0;
07003
07004 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
07005 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
07006 return -1;
07007 }
07008
07009 if (sla_check_device(dev)) {
07010 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with invalid device '%s'!\n",
07011 cat, dev);
07012 return -1;
07013 }
07014
07015 if ((trunk = sla_find_trunk(cat))) {
07016 trunk->mark = 0;
07017 existing_trunk = 1;
07018 } else if ((trunk = ao2_alloc(sizeof(*trunk), sla_trunk_destructor))) {
07019 if (ast_string_field_init(trunk, 32)) {
07020 return -1;
07021 }
07022 ast_string_field_set(trunk, name, cat);
07023 } else {
07024 return -1;
07025 }
07026
07027 ao2_lock(trunk);
07028
07029 ast_string_field_set(trunk, device, dev);
07030
07031 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
07032 if (!strcasecmp(var->name, "autocontext"))
07033 ast_string_field_set(trunk, autocontext, var->value);
07034 else if (!strcasecmp(var->name, "ringtimeout")) {
07035 if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
07036 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
07037 var->value, trunk->name);
07038 trunk->ring_timeout = 0;
07039 }
07040 } else if (!strcasecmp(var->name, "barge"))
07041 trunk->barge_disabled = ast_false(var->value);
07042 else if (!strcasecmp(var->name, "hold")) {
07043 if (!strcasecmp(var->value, "private"))
07044 trunk->hold_access = SLA_HOLD_PRIVATE;
07045 else if (!strcasecmp(var->value, "open"))
07046 trunk->hold_access = SLA_HOLD_OPEN;
07047 else {
07048 ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
07049 var->value, trunk->name);
07050 }
07051 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
07052 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
07053 var->name, var->lineno, SLA_CONFIG_FILE);
07054 }
07055 }
07056
07057 ao2_unlock(trunk);
07058
07059 if (!ast_strlen_zero(trunk->autocontext)) {
07060 struct ast_context *context;
07061 context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
07062 if (!context) {
07063 ast_log(LOG_ERROR, "Failed to automatically find or create "
07064 "context '%s' for SLA!\n", trunk->autocontext);
07065 return -1;
07066 }
07067 if (ast_add_extension2(context, 0 , "s", 1,
07068 NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
07069 ast_log(LOG_ERROR, "Failed to automatically create extension "
07070 "for trunk '%s'!\n", trunk->name);
07071 return -1;
07072 }
07073 }
07074
07075 if (!existing_trunk) {
07076 ao2_link(sla_trunks, trunk);
07077 }
07078
07079 return 0;
07080 }
07081
07082
07083
07084
07085
07086 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
07087 {
07088 RAII_VAR(struct sla_trunk *, trunk, NULL, unref_obj);
07089 struct sla_trunk_ref *trunk_ref = NULL;
07090 struct sla_station_ref *station_ref;
07091 char *trunk_name, *options, *cur;
07092 int existing_trunk_ref = 0;
07093 int existing_station_ref = 0;
07094
07095 options = ast_strdupa(var->value);
07096 trunk_name = strsep(&options, ",");
07097
07098 trunk = sla_find_trunk(trunk_name);
07099 if (!trunk) {
07100 ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
07101 return;
07102 }
07103
07104 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07105 if (trunk_ref->trunk == trunk) {
07106 trunk_ref->mark = 0;
07107 existing_trunk_ref = 1;
07108 break;
07109 }
07110 }
07111
07112 if (!trunk_ref && !(trunk_ref = create_trunk_ref(trunk))) {
07113 return;
07114 }
07115
07116 trunk_ref->state = SLA_TRUNK_STATE_IDLE;
07117
07118 while ((cur = strsep(&options, ","))) {
07119 char *name, *value = cur;
07120 name = strsep(&value, "=");
07121 if (!strcasecmp(name, "ringtimeout")) {
07122 if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
07123 ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
07124 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
07125 trunk_ref->ring_timeout = 0;
07126 }
07127 } else if (!strcasecmp(name, "ringdelay")) {
07128 if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
07129 ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
07130 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
07131 trunk_ref->ring_delay = 0;
07132 }
07133 } else {
07134 ast_log(LOG_WARNING, "Invalid option '%s' for "
07135 "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
07136 }
07137 }
07138
07139 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
07140 if (station_ref->station == station) {
07141 station_ref->mark = 0;
07142 existing_station_ref = 1;
07143 break;
07144 }
07145 }
07146
07147 if (!station_ref && !(station_ref = sla_create_station_ref(station))) {
07148 if (!existing_trunk_ref) {
07149 ao2_ref(trunk_ref, -1);
07150 } else {
07151 trunk_ref->mark = 1;
07152 }
07153 return;
07154 }
07155
07156 if (!existing_station_ref) {
07157 ao2_lock(trunk);
07158 AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
07159 ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
07160 ao2_unlock(trunk);
07161 }
07162
07163 if (!existing_trunk_ref) {
07164 ao2_lock(station);
07165 AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
07166 ao2_unlock(station);
07167 }
07168 }
07169
07170 static int sla_build_station(struct ast_config *cfg, const char *cat)
07171 {
07172 RAII_VAR(struct sla_station *, station, NULL, unref_obj);
07173 struct ast_variable *var;
07174 const char *dev;
07175 int existing_station = 0;
07176
07177 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
07178 ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
07179 return -1;
07180 }
07181
07182 if ((station = sla_find_station(cat))) {
07183 station->mark = 0;
07184 existing_station = 1;
07185 } else if ((station = ao2_alloc(sizeof(*station), sla_station_destructor))) {
07186 if (ast_string_field_init(station, 32)) {
07187 return -1;
07188 }
07189 ast_string_field_set(station, name, cat);
07190 } else {
07191 return -1;
07192 }
07193
07194 ao2_lock(station);
07195
07196 ast_string_field_set(station, device, dev);
07197
07198 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
07199 if (!strcasecmp(var->name, "trunk")) {
07200 ao2_unlock(station);
07201 sla_add_trunk_to_station(station, var);
07202 ao2_lock(station);
07203 } else if (!strcasecmp(var->name, "autocontext")) {
07204 ast_string_field_set(station, autocontext, var->value);
07205 } else if (!strcasecmp(var->name, "ringtimeout")) {
07206 if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
07207 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
07208 var->value, station->name);
07209 station->ring_timeout = 0;
07210 }
07211 } else if (!strcasecmp(var->name, "ringdelay")) {
07212 if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
07213 ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
07214 var->value, station->name);
07215 station->ring_delay = 0;
07216 }
07217 } else if (!strcasecmp(var->name, "hold")) {
07218 if (!strcasecmp(var->value, "private"))
07219 station->hold_access = SLA_HOLD_PRIVATE;
07220 else if (!strcasecmp(var->value, "open"))
07221 station->hold_access = SLA_HOLD_OPEN;
07222 else {
07223 ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
07224 var->value, station->name);
07225 }
07226
07227 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
07228 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
07229 var->name, var->lineno, SLA_CONFIG_FILE);
07230 }
07231 }
07232
07233 ao2_unlock(station);
07234
07235 if (!ast_strlen_zero(station->autocontext)) {
07236 struct ast_context *context;
07237 struct sla_trunk_ref *trunk_ref;
07238 context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
07239 if (!context) {
07240 ast_log(LOG_ERROR, "Failed to automatically find or create "
07241 "context '%s' for SLA!\n", station->autocontext);
07242 return -1;
07243 }
07244
07245
07246 if (ast_add_extension2(context, 0 , station->name, 1,
07247 NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
07248 ast_log(LOG_ERROR, "Failed to automatically create extension "
07249 "for trunk '%s'!\n", station->name);
07250 return -1;
07251 }
07252 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07253 char exten[AST_MAX_EXTENSION];
07254 char hint[AST_MAX_APP];
07255 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
07256 snprintf(hint, sizeof(hint), "SLA:%s", exten);
07257
07258
07259 if (ast_add_extension2(context, 0 , exten, 1,
07260 NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
07261 ast_log(LOG_ERROR, "Failed to automatically create extension "
07262 "for trunk '%s'!\n", station->name);
07263 return -1;
07264 }
07265
07266
07267 if (ast_add_extension2(context, 0 , exten, PRIORITY_HINT,
07268 NULL, NULL, hint, NULL, NULL, sla_registrar)) {
07269 ast_log(LOG_ERROR, "Failed to automatically create hint "
07270 "for trunk '%s'!\n", station->name);
07271 return -1;
07272 }
07273 }
07274 }
07275
07276 if (!existing_station) {
07277 ao2_link(sla_stations, station);
07278 }
07279
07280 return 0;
07281 }
07282
07283 static int sla_trunk_mark(void *obj, void *arg, int flags)
07284 {
07285 struct sla_trunk *trunk = obj;
07286 struct sla_station_ref *station_ref;
07287
07288 ao2_lock(trunk);
07289
07290 trunk->mark = 1;
07291
07292 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
07293 station_ref->mark = 1;
07294 }
07295
07296 ao2_unlock(trunk);
07297
07298 return 0;
07299 }
07300
07301 static int sla_station_mark(void *obj, void *arg, int flags)
07302 {
07303 struct sla_station *station = obj;
07304 struct sla_trunk_ref *trunk_ref;
07305
07306 ao2_lock(station);
07307
07308 station->mark = 1;
07309
07310 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
07311 trunk_ref->mark = 1;
07312 }
07313
07314 ao2_unlock(station);
07315
07316 return 0;
07317 }
07318
07319 static int sla_trunk_is_marked(void *obj, void *arg, int flags)
07320 {
07321 struct sla_trunk *trunk = obj;
07322
07323 ao2_lock(trunk);
07324
07325 if (trunk->mark) {
07326
07327 sla_trunk_release_refs(trunk, NULL, 0);
07328 } else {
07329 struct sla_station_ref *station_ref;
07330
07331
07332 AST_LIST_TRAVERSE_SAFE_BEGIN(&trunk->stations, station_ref, entry) {
07333 if (!station_ref->mark) {
07334 continue;
07335 }
07336 AST_LIST_REMOVE_CURRENT(entry);
07337 ao2_ref(station_ref, -1);
07338 }
07339 AST_LIST_TRAVERSE_SAFE_END
07340 }
07341
07342 ao2_unlock(trunk);
07343
07344 return trunk->mark ? CMP_MATCH : 0;
07345 }
07346
07347 static int sla_station_is_marked(void *obj, void *arg, int flags)
07348 {
07349 struct sla_station *station = obj;
07350
07351 ao2_lock(station);
07352
07353 if (station->mark) {
07354
07355 sla_station_release_refs(station, NULL, 0);
07356 } else {
07357 struct sla_trunk_ref *trunk_ref;
07358
07359
07360 AST_LIST_TRAVERSE_SAFE_BEGIN(&station->trunks, trunk_ref, entry) {
07361 if (!trunk_ref->mark) {
07362 continue;
07363 }
07364 AST_LIST_REMOVE_CURRENT(entry);
07365 ao2_ref(trunk_ref, -1);
07366 }
07367 AST_LIST_TRAVERSE_SAFE_END
07368 }
07369
07370 ao2_unlock(station);
07371
07372 return station->mark ? CMP_MATCH : 0;
07373 }
07374
07375 static int sla_in_use(void)
07376 {
07377 return ao2_container_count(sla_trunks) || ao2_container_count(sla_stations);
07378 }
07379
07380 static int sla_load_config(int reload)
07381 {
07382 struct ast_config *cfg;
07383 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
07384 const char *cat = NULL;
07385 int res = 0;
07386 const char *val;
07387
07388 if (!reload) {
07389 ast_mutex_init(&sla.lock);
07390 ast_cond_init(&sla.cond, NULL);
07391 sla_trunks = ao2_container_alloc(1, sla_trunk_hash, sla_trunk_cmp);
07392 sla_stations = ao2_container_alloc(1, sla_station_hash, sla_station_cmp);
07393 }
07394
07395 if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
07396 return 0;
07397 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
07398 return 0;
07399 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
07400 ast_log(LOG_ERROR, "Config file " SLA_CONFIG_FILE " is in an invalid format. Aborting.\n");
07401 return 0;
07402 }
07403
07404 if (reload) {
07405 ao2_callback(sla_trunks, 0, sla_trunk_mark, NULL);
07406 ao2_callback(sla_stations, 0, sla_station_mark, NULL);
07407 }
07408
07409 if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
07410 sla.attempt_callerid = ast_true(val);
07411
07412 while ((cat = ast_category_browse(cfg, cat)) && !res) {
07413 const char *type;
07414 if (!strcasecmp(cat, "general"))
07415 continue;
07416 if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
07417 ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
07418 SLA_CONFIG_FILE);
07419 continue;
07420 }
07421 if (!strcasecmp(type, "trunk"))
07422 res = sla_build_trunk(cfg, cat);
07423 else if (!strcasecmp(type, "station"))
07424 res = sla_build_station(cfg, cat);
07425 else {
07426 ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
07427 SLA_CONFIG_FILE, type);
07428 }
07429 }
07430
07431 ast_config_destroy(cfg);
07432
07433 if (reload) {
07434 ao2_callback(sla_trunks, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_trunk_is_marked, NULL);
07435 ao2_callback(sla_stations, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_station_is_marked, NULL);
07436 }
07437
07438
07439 if (sla.thread == AST_PTHREADT_NULL && sla_in_use()) {
07440 ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
07441 }
07442
07443 return res;
07444 }
07445
07446 static int acf_meetme_info_eval(const char *keyword, const struct ast_conference *conf)
07447 {
07448 if (!strcasecmp("lock", keyword)) {
07449 return conf->locked;
07450 } else if (!strcasecmp("parties", keyword)) {
07451 return conf->users;
07452 } else if (!strcasecmp("activity", keyword)) {
07453 time_t now;
07454 now = time(NULL);
07455 return (now - conf->start);
07456 } else if (!strcasecmp("dynamic", keyword)) {
07457 return conf->isdynamic;
07458 } else {
07459 return -1;
07460 }
07461
07462 }
07463
07464 static int acf_meetme_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
07465 {
07466 struct ast_conference *conf;
07467 char *parse;
07468 int result = -2;
07469 AST_DECLARE_APP_ARGS(args,
07470 AST_APP_ARG(keyword);
07471 AST_APP_ARG(confno);
07472 );
07473
07474 if (ast_strlen_zero(data)) {
07475 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
07476 return -1;
07477 }
07478
07479 parse = ast_strdupa(data);
07480 AST_STANDARD_APP_ARGS(args, parse);
07481
07482 if (ast_strlen_zero(args.keyword)) {
07483 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
07484 return -1;
07485 }
07486
07487 if (ast_strlen_zero(args.confno)) {
07488 ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
07489 return -1;
07490 }
07491
07492 AST_LIST_LOCK(&confs);
07493 AST_LIST_TRAVERSE(&confs, conf, list) {
07494 if (!strcmp(args.confno, conf->confno)) {
07495 result = acf_meetme_info_eval(args.keyword, conf);
07496 break;
07497 }
07498 }
07499 AST_LIST_UNLOCK(&confs);
07500
07501 if (result > -1) {
07502 snprintf(buf, len, "%d", result);
07503 } else if (result == -1) {
07504 ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
07505 snprintf(buf, len, "0");
07506 } else if (result == -2) {
07507 ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno);
07508 snprintf(buf, len, "0");
07509 }
07510
07511 return 0;
07512 }
07513
07514
07515 static struct ast_custom_function meetme_info_acf = {
07516 .name = "MEETME_INFO",
07517 .read = acf_meetme_info,
07518 };
07519
07520
07521 static int load_config(int reload)
07522 {
07523 load_config_meetme();
07524 return sla_load_config(reload);
07525 }
07526
07527 #define MEETME_DATA_EXPORT(MEMBER) \
07528 MEMBER(ast_conference, confno, AST_DATA_STRING) \
07529 MEMBER(ast_conference, dahdiconf, AST_DATA_INTEGER) \
07530 MEMBER(ast_conference, users, AST_DATA_INTEGER) \
07531 MEMBER(ast_conference, markedusers, AST_DATA_INTEGER) \
07532 MEMBER(ast_conference, maxusers, AST_DATA_INTEGER) \
07533 MEMBER(ast_conference, isdynamic, AST_DATA_BOOLEAN) \
07534 MEMBER(ast_conference, locked, AST_DATA_BOOLEAN) \
07535 MEMBER(ast_conference, recordingfilename, AST_DATA_STRING) \
07536 MEMBER(ast_conference, recordingformat, AST_DATA_STRING) \
07537 MEMBER(ast_conference, pin, AST_DATA_PASSWORD) \
07538 MEMBER(ast_conference, pinadmin, AST_DATA_PASSWORD) \
07539 MEMBER(ast_conference, start, AST_DATA_TIMESTAMP) \
07540 MEMBER(ast_conference, endtime, AST_DATA_TIMESTAMP)
07541
07542 AST_DATA_STRUCTURE(ast_conference, MEETME_DATA_EXPORT);
07543
07544 #define MEETME_USER_DATA_EXPORT(MEMBER) \
07545 MEMBER(ast_conf_user, user_no, AST_DATA_INTEGER) \
07546 MEMBER(ast_conf_user, talking, AST_DATA_BOOLEAN) \
07547 MEMBER(ast_conf_user, dahdichannel, AST_DATA_BOOLEAN) \
07548 MEMBER(ast_conf_user, jointime, AST_DATA_TIMESTAMP) \
07549 MEMBER(ast_conf_user, kicktime, AST_DATA_TIMESTAMP) \
07550 MEMBER(ast_conf_user, timelimit, AST_DATA_MILLISECONDS) \
07551 MEMBER(ast_conf_user, play_warning, AST_DATA_MILLISECONDS) \
07552 MEMBER(ast_conf_user, warning_freq, AST_DATA_MILLISECONDS)
07553
07554 AST_DATA_STRUCTURE(ast_conf_user, MEETME_USER_DATA_EXPORT);
07555
07556 static int user_add_provider_cb(void *obj, void *arg, int flags)
07557 {
07558 struct ast_data *data_meetme_user;
07559 struct ast_data *data_meetme_user_channel;
07560 struct ast_data *data_meetme_user_volume;
07561
07562 struct ast_conf_user *user = obj;
07563 struct ast_data *data_meetme_users = arg;
07564
07565 data_meetme_user = ast_data_add_node(data_meetme_users, "user");
07566 if (!data_meetme_user) {
07567 return 0;
07568 }
07569
07570 ast_data_add_structure(ast_conf_user, data_meetme_user, user);
07571
07572
07573 data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel");
07574 if (!data_meetme_user_channel) {
07575 return 0;
07576 }
07577
07578 ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1);
07579
07580
07581 data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume");
07582 if (!data_meetme_user_volume) {
07583 return 0;
07584 }
07585 ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired);
07586 ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual);
07587
07588 data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume");
07589 if (!data_meetme_user_volume) {
07590 return 0;
07591 }
07592 ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired);
07593 ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual);
07594
07595 return 0;
07596 }
07597
07598
07599
07600
07601
07602 static int meetme_data_provider_get(const struct ast_data_search *search,
07603 struct ast_data *data_root)
07604 {
07605 struct ast_conference *cnf;
07606 struct ast_data *data_meetme, *data_meetme_users;
07607
07608 AST_LIST_LOCK(&confs);
07609 AST_LIST_TRAVERSE(&confs, cnf, list) {
07610 data_meetme = ast_data_add_node(data_root, "meetme");
07611 if (!data_meetme) {
07612 continue;
07613 }
07614
07615 ast_data_add_structure(ast_conference, data_meetme, cnf);
07616
07617 if (ao2_container_count(cnf->usercontainer)) {
07618 data_meetme_users = ast_data_add_node(data_meetme, "users");
07619 if (!data_meetme_users) {
07620 ast_data_remove_node(data_root, data_meetme);
07621 continue;
07622 }
07623
07624 ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users);
07625 }
07626
07627 if (!ast_data_search_match(search, data_meetme)) {
07628 ast_data_remove_node(data_root, data_meetme);
07629 }
07630 }
07631 AST_LIST_UNLOCK(&confs);
07632
07633 return 0;
07634 }
07635
07636 static const struct ast_data_handler meetme_data_provider = {
07637 .version = AST_DATA_HANDLER_VERSION,
07638 .get = meetme_data_provider_get
07639 };
07640
07641 static const struct ast_data_entry meetme_data_providers[] = {
07642 AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider),
07643 };
07644
07645 #ifdef TEST_FRAMEWORK
07646 AST_TEST_DEFINE(test_meetme_data_provider)
07647 {
07648 struct ast_channel *chan;
07649 struct ast_conference *cnf;
07650 struct ast_data *node;
07651 struct ast_data_query query = {
07652 .path = "/asterisk/application/meetme/list",
07653 .search = "list/meetme/confno=9898"
07654 };
07655
07656 switch (cmd) {
07657 case TEST_INIT:
07658 info->name = "meetme_get_data_test";
07659 info->category = "/main/data/app_meetme/list/";
07660 info->summary = "Meetme data provider unit test";
07661 info->description =
07662 "Tests whether the Meetme data provider implementation works as expected.";
07663 return AST_TEST_NOT_RUN;
07664 case TEST_EXECUTE:
07665 break;
07666 }
07667
07668 chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest");
07669 if (!chan) {
07670 ast_test_status_update(test, "Channel allocation failed\n");
07671 return AST_TEST_FAIL;
07672 }
07673
07674 cnf = build_conf("9898", "", "1234", 1, 1, 1, chan, test);
07675 if (!cnf) {
07676 ast_test_status_update(test, "Build of test conference 9898 failed\n");
07677 ast_hangup(chan);
07678 return AST_TEST_FAIL;
07679 }
07680
07681 node = ast_data_get(&query);
07682 if (!node) {
07683 ast_test_status_update(test, "Data query for test conference 9898 failed\n");
07684 dispose_conf(cnf);
07685 ast_hangup(chan);
07686 return AST_TEST_FAIL;
07687 }
07688
07689 if (strcmp(ast_data_retrieve_string(node, "meetme/confno"), "9898")) {
07690 ast_test_status_update(test, "Query returned the wrong conference\n");
07691 dispose_conf(cnf);
07692 ast_hangup(chan);
07693 ast_data_free(node);
07694 return AST_TEST_FAIL;
07695 }
07696
07697 ast_data_free(node);
07698 dispose_conf(cnf);
07699 ast_hangup(chan);
07700
07701 return AST_TEST_PASS;
07702 }
07703 #endif
07704
07705 static int unload_module(void)
07706 {
07707 int res = 0;
07708
07709 ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07710 res = ast_manager_unregister("MeetmeMute");
07711 res |= ast_manager_unregister("MeetmeUnmute");
07712 res |= ast_manager_unregister("MeetmeList");
07713 res |= ast_unregister_application(app4);
07714 res |= ast_unregister_application(app3);
07715 res |= ast_unregister_application(app2);
07716 res |= ast_unregister_application(app);
07717 res |= ast_unregister_application(slastation_app);
07718 res |= ast_unregister_application(slatrunk_app);
07719
07720 #ifdef TEST_FRAMEWORK
07721 AST_TEST_UNREGISTER(test_meetme_data_provider);
07722 #endif
07723 ast_data_unregister(NULL);
07724
07725 ast_devstate_prov_del("Meetme");
07726 ast_devstate_prov_del("SLA");
07727
07728 sla_destroy();
07729
07730 res |= ast_custom_function_unregister(&meetme_info_acf);
07731 ast_unload_realtime("meetme");
07732
07733 return res;
07734 }
07735
07736 static int load_module(void)
07737 {
07738 int res = 0;
07739
07740 res |= load_config(0);
07741
07742 ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
07743 res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
07744 res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
07745 res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
07746 res |= ast_register_application_xml(app4, channel_admin_exec);
07747 res |= ast_register_application_xml(app3, admin_exec);
07748 res |= ast_register_application_xml(app2, count_exec);
07749 res |= ast_register_application_xml(app, conf_exec);
07750 res |= ast_register_application_xml(slastation_app, sla_station_exec);
07751 res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec);
07752
07753 #ifdef TEST_FRAMEWORK
07754 AST_TEST_REGISTER(test_meetme_data_provider);
07755 #endif
07756 ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers));
07757
07758 res |= ast_devstate_prov_add("Meetme", meetmestate);
07759 res |= ast_devstate_prov_add("SLA", sla_state);
07760
07761 res |= ast_custom_function_register(&meetme_info_acf);
07762 ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
07763
07764 return res;
07765 }
07766
07767 static int reload(void)
07768 {
07769 ast_unload_realtime("meetme");
07770 return load_config(1);
07771 }
07772
07773 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
07774 .load = load_module,
07775 .unload = unload_module,
07776 .reload = reload,
07777 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
07778 );
07779