00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "asterisk.h"
00037
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 201093 $")
00039
00040 #include <sys/ioctl.h>
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/astobj.h"
00059 #include "asterisk/astobj2.h"
00060 #include "asterisk/devicestate.h"
00061 #include "asterisk/dial.h"
00062 #include "asterisk/causes.h"
00063 #include "asterisk/paths.h"
00064
00065 #include "enter.h"
00066 #include "leave.h"
00067
00068 #define CONFIG_FILE_NAME "meetme.conf"
00069 #define SLA_CONFIG_FILE "sla.conf"
00070
00071
00072 #define DEFAULT_AUDIO_BUFFERS 32
00073
00074
00075 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
00076
00077 enum {
00078 ADMINFLAG_MUTED = (1 << 1),
00079 ADMINFLAG_SELFMUTED = (1 << 2),
00080 ADMINFLAG_KICKME = (1 << 3),
00081
00082 ADMINFLAG_T_REQUEST = (1 << 4),
00083 };
00084
00085 #define MEETME_DELAYDETECTTALK 300
00086 #define MEETME_DELAYDETECTENDTALK 1000
00087
00088 #define AST_FRAME_BITS 32
00089
00090 enum volume_action {
00091 VOL_UP,
00092 VOL_DOWN
00093 };
00094
00095 enum entrance_sound {
00096 ENTER,
00097 LEAVE
00098 };
00099
00100 enum recording_state {
00101 MEETME_RECORD_OFF,
00102 MEETME_RECORD_STARTED,
00103 MEETME_RECORD_ACTIVE,
00104 MEETME_RECORD_TERMINATE
00105 };
00106
00107 #define CONF_SIZE 320
00108
00109 enum {
00110
00111 CONFFLAG_ADMIN = (1 << 0),
00112
00113 CONFFLAG_MONITOR = (1 << 1),
00114
00115 CONFFLAG_KEYEXIT = (1 << 2),
00116
00117 CONFFLAG_STARMENU = (1 << 3),
00118
00119 CONFFLAG_TALKER = (1 << 4),
00120
00121 CONFFLAG_QUIET = (1 << 5),
00122
00123
00124 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
00125
00126 CONFFLAG_AGI = (1 << 7),
00127
00128 CONFFLAG_MOH = (1 << 8),
00129
00130 CONFFLAG_MARKEDEXIT = (1 << 9),
00131
00132 CONFFLAG_WAITMARKED = (1 << 10),
00133
00134 CONFFLAG_EXIT_CONTEXT = (1 << 11),
00135
00136 CONFFLAG_MARKEDUSER = (1 << 12),
00137
00138 CONFFLAG_INTROUSER = (1 << 13),
00139
00140 CONFFLAG_RECORDCONF = (1<< 14),
00141
00142 CONFFLAG_MONITORTALKER = (1 << 15),
00143 CONFFLAG_DYNAMIC = (1 << 16),
00144 CONFFLAG_DYNAMICPIN = (1 << 17),
00145 CONFFLAG_EMPTY = (1 << 18),
00146 CONFFLAG_EMPTYNOPIN = (1 << 19),
00147 CONFFLAG_ALWAYSPROMPT = (1 << 20),
00148
00149 CONFFLAG_OPTIMIZETALKER = (1 << 21),
00150
00151
00152 CONFFLAG_NOONLYPERSON = (1 << 22),
00153
00154
00155 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
00156
00157 CONFFLAG_STARTMUTED = (1 << 24),
00158
00159 CONFFLAG_PASS_DTMF = (1 << 25),
00160 CONFFLAG_SLA_STATION = (1 << 26),
00161 CONFFLAG_SLA_TRUNK = (1 << 27),
00162
00163 CONFFLAG_KICK_CONTINUE = (1 << 28),
00164 CONFFLAG_DURATION_STOP = (1 << 29),
00165 CONFFLAG_DURATION_LIMIT = (1 << 30),
00166
00167 CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 31),
00168 };
00169
00170 enum {
00171 OPT_ARG_WAITMARKED = 0,
00172 OPT_ARG_EXITKEYS = 1,
00173 OPT_ARG_DURATION_STOP = 2,
00174 OPT_ARG_DURATION_LIMIT = 3,
00175 OPT_ARG_MOH_CLASS = 4,
00176 OPT_ARG_ARRAY_SIZE = 5,
00177 };
00178
00179 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
00180 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
00181 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
00182 AST_APP_OPTION('b', CONFFLAG_AGI ),
00183 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
00184 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
00185 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
00186 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
00187 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
00188 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
00189 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
00190 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
00191 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
00192 AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
00193 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
00194 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
00195 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
00196 AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
00197 AST_APP_OPTION('q', CONFFLAG_QUIET ),
00198 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
00199 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
00200 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
00201 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
00202 AST_APP_OPTION('t', CONFFLAG_TALKER ),
00203 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
00204 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
00205 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
00206 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
00207 AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
00208 AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
00209 END_OPTIONS );
00210
00211 static const char *app = "MeetMe";
00212 static const char *app2 = "MeetMeCount";
00213 static const char *app3 = "MeetMeAdmin";
00214 static const char *app4 = "MeetMeChannelAdmin";
00215 static const char *slastation_app = "SLAStation";
00216 static const char *slatrunk_app = "SLATrunk";
00217
00218 static const char *synopsis = "MeetMe conference bridge";
00219 static const char *synopsis2 = "MeetMe participant count";
00220 static const char *synopsis3 = "MeetMe conference Administration";
00221 static const char *synopsis4 = "MeetMe conference Administration (channel specific)";
00222 static const char *slastation_synopsis = "Shared Line Appearance Station";
00223 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
00224
00225
00226 static int rt_schedule;
00227 static int fuzzystart;
00228 static int earlyalert;
00229 static int endalert;
00230
00231
00232 static int rt_log_members;
00233
00234 static const char *descrip =
00235 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
00236 "conference. If the conference number is omitted, the user will be prompted\n"
00237 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
00238 "is specified, by pressing '#'.\n"
00239 "Please note: The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)\n"
00240 " must be present for conferencing to operate properly. In addition, the chan_dahdi\n"
00241 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
00242 "The option string may contain zero or more of the following characters:\n"
00243 " 'a' -- set admin mode\n"
00244 " 'A' -- set marked mode\n"
00245 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
00246 " Default: conf-background.agi (Note: This does not work with\n"
00247 " non-DAHDI channels in the same conference)\n"
00248 " 'c' -- announce user(s) count on joining a conference\n"
00249 " 'C' -- continue in dialplan when kicked out of conference\n"
00250 " 'd' -- dynamically add conference\n"
00251 " 'D' -- dynamically add conference, prompting for a PIN\n"
00252 " 'e' -- select an empty conference\n"
00253 " 'E' -- select an empty pinless conference\n"
00254 " 'F' -- Pass DTMF through the conference.\n"
00255 " 'i' -- announce user join/leave with review\n"
00256 " 'I' -- announce user join/leave without review\n"
00257 " 'l' -- set listen only mode (Listen only, no talking)\n"
00258 " 'm' -- set initially muted\n"
00259 " 'M[(<class>)]'\n"
00260 " -- enable music on hold when the conference has a single caller.\n"
00261 " Optionally, specify a musiconhold class to use. If one is not\n"
00262 " provided, it will use the channel's currently set music class,\n"
00263 " or \"default\".\n"
00264 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
00265 " being muted, meaning (a) No encode is done on transmission and\n"
00266 " (b) Received audio that is not registered as talking is omitted\n"
00267 " causing no buildup in background noise\n"
00268 " 'p[(<keys>)]'\n"
00269 " -- allow user to exit the conference by pressing '#' (default)\n"
00270 " or any of the defined keys. If keys contain '*' this will override\n"
00271 " option 's'. The key used is set to channel variable MEETME_EXIT_KEY.\n"
00272 " 'P' -- always prompt for the pin even if it is specified\n"
00273 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
00274 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
00275 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
00276 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
00277 " wav.\n"
00278 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
00279 " 't' -- set talk only mode. (Talk only, no listening)\n"
00280 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
00281 " 'w[(<secs>)]'\n"
00282 " -- wait until the marked user enters the conference\n"
00283 " 'x' -- close the conference when last marked user exits\n"
00284 " 'X' -- allow user to exit the conference by entering a valid single\n"
00285 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
00286 " if that variable is not defined.\n"
00287 " '1' -- do not play message when first person enters\n"
00288 " 'S(x)' -- Kick the user 'x' seconds *after* he entered into the conference.\n"
00289 " 'L(x[:y][:z])' - Limit the conference to 'x' ms. Play a warning when 'y' ms are\n"
00290 " left. Repeat the warning every 'z' ms. The following special\n"
00291 " variables can be used with this option:\n"
00292 " * CONF_LIMIT_TIMEOUT_FILE File to play when time is up.\n"
00293 " * CONF_LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
00294 " The default is to say the time remaining.\n"
00295 "";
00296
00297 static const char *descrip2 =
00298 " MeetMeCount(confno[,var]): Plays back the number of users in the specified\n"
00299 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
00300 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
00301 "the channel, unless priority n+1 exists, in which case priority progress will\n"
00302 "continue.\n"
00303 "";
00304
00305 static const char *descrip3 =
00306 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
00307 " 'e' -- Eject last user that joined\n"
00308 " 'k' -- Kick one user out of conference\n"
00309 " 'K' -- Kick all users out of conference\n"
00310 " 'l' -- Unlock conference\n"
00311 " 'L' -- Lock conference\n"
00312 " 'm' -- Unmute one user\n"
00313 " 'M' -- Mute one user\n"
00314 " 'n' -- Unmute all users in the conference\n"
00315 " 'N' -- Mute all non-admin users in the conference\n"
00316 " 'r' -- Reset one user's volume settings\n"
00317 " 'R' -- Reset all users volume settings\n"
00318 " 's' -- Lower entire conference speaking volume\n"
00319 " 'S' -- Raise entire conference speaking volume\n"
00320 " 't' -- Lower one user's talk volume\n"
00321 " 'T' -- Raise one user's talk volume\n"
00322 " 'u' -- Lower one user's listen volume\n"
00323 " 'U' -- Raise one user's listen volume\n"
00324 " 'v' -- Lower entire conference listening volume\n"
00325 " 'V' -- Raise entire conference listening volume\n"
00326 "";
00327
00328 static const char *descrip4 =
00329 " MeetMeChannelAdmin(channel,command): Run admin command for a specific\n"
00330 "channel in any coference.\n"
00331 " 'k' -- Kick the specified user out of the conference he is in\n"
00332 " 'm' -- Unmute the specified user\n"
00333 " 'M' -- Mute the specified user\n"
00334 "";
00335
00336 static const char *slastation_desc =
00337 " SLAStation(<station name>):\n"
00338 "This application should be executed by an SLA station. The argument depends\n"
00339 "on how the call was initiated. If the phone was just taken off hook, then\n"
00340 "the argument \"station\" should be just the station name. If the call was\n"
00341 "initiated by pressing a line key, then the station name should be preceded\n"
00342 "by an underscore and the trunk name associated with that line button.\n"
00343 "For example: \"station1_line1\"."
00344 " On exit, this application will set the variable SLASTATION_STATUS to\n"
00345 "one of the following values:\n"
00346 " FAILURE | CONGESTION | SUCCESS\n"
00347 "";
00348
00349 static const char *slatrunk_desc =
00350 " SLATrunk(<trunk name>[,options]):\n"
00351 "This application should be executed by an SLA trunk on an inbound call.\n"
00352 "The channel calling this application should correspond to the SLA trunk\n"
00353 "with the name \"trunk\" that is being passed as an argument.\n"
00354 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
00355 "one of the following values:\n"
00356 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
00357 " The available options are:\n"
00358 " M[(<class>)] - Play back the specified MOH class instead of ringing\n"
00359 "";
00360
00361 #define MAX_CONFNUM 80
00362 #define MAX_PIN 80
00363
00364 enum announcetypes {
00365 CONF_HASJOIN,
00366 CONF_HASLEFT
00367 };
00368
00369 struct announce_listitem {
00370 AST_LIST_ENTRY(announce_listitem) entry;
00371 char namerecloc[PATH_MAX];
00372 char language[MAX_LANGUAGE];
00373 struct ast_channel *confchan;
00374 int confusers;
00375 enum announcetypes announcetype;
00376 };
00377
00378
00379 struct ast_conference {
00380 ast_mutex_t playlock;
00381 ast_mutex_t listenlock;
00382 char confno[MAX_CONFNUM];
00383 struct ast_channel *chan;
00384 struct ast_channel *lchan;
00385 int fd;
00386 int dahdiconf;
00387 int users;
00388 int markedusers;
00389 int maxusers;
00390 int endalert;
00391 time_t start;
00392 int refcount;
00393 enum recording_state recording:2;
00394 unsigned int isdynamic:1;
00395 unsigned int locked:1;
00396 pthread_t recordthread;
00397 ast_mutex_t recordthreadlock;
00398 pthread_attr_t attr;
00399 const char *recordingfilename;
00400 const char *recordingformat;
00401 char pin[MAX_PIN];
00402 char pinadmin[MAX_PIN];
00403 char uniqueid[32];
00404 long endtime;
00405 struct ast_frame *transframe[32];
00406 struct ast_frame *origframe;
00407 struct ast_trans_pvt *transpath[32];
00408 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
00409 AST_LIST_ENTRY(ast_conference) list;
00410
00411 pthread_t announcethread;
00412 ast_mutex_t announcethreadlock;
00413 unsigned int announcethread_stop:1;
00414 ast_cond_t announcelist_addition;
00415 AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
00416 ast_mutex_t announcelistlock;
00417 };
00418
00419 static AST_LIST_HEAD_STATIC(confs, ast_conference);
00420
00421 static unsigned int conf_map[1024] = {0, };
00422
00423 struct volume {
00424 int desired;
00425 int actual;
00426 };
00427
00428
00429 struct ast_conf_user {
00430 int user_no;
00431 int userflags;
00432 int adminflags;
00433 struct ast_channel *chan;
00434 int talking;
00435 int dahdichannel;
00436 char usrvalue[50];
00437 char namerecloc[PATH_MAX];
00438 time_t jointime;
00439 time_t kicktime;
00440 struct timeval start_time;
00441 long timelimit;
00442 long play_warning;
00443 long warning_freq;
00444 const char *warning_sound;
00445 const char *end_sound;
00446 struct volume talk;
00447 struct volume listen;
00448 AST_LIST_ENTRY(ast_conf_user) list;
00449 };
00450
00451 enum sla_which_trunk_refs {
00452 ALL_TRUNK_REFS,
00453 INACTIVE_TRUNK_REFS,
00454 };
00455
00456 enum sla_trunk_state {
00457 SLA_TRUNK_STATE_IDLE,
00458 SLA_TRUNK_STATE_RINGING,
00459 SLA_TRUNK_STATE_UP,
00460 SLA_TRUNK_STATE_ONHOLD,
00461 SLA_TRUNK_STATE_ONHOLD_BYME,
00462 };
00463
00464 enum sla_hold_access {
00465
00466
00467 SLA_HOLD_OPEN,
00468
00469
00470 SLA_HOLD_PRIVATE,
00471 };
00472
00473 struct sla_trunk_ref;
00474
00475 struct sla_station {
00476 AST_RWLIST_ENTRY(sla_station) entry;
00477 AST_DECLARE_STRING_FIELDS(
00478 AST_STRING_FIELD(name);
00479 AST_STRING_FIELD(device);
00480 AST_STRING_FIELD(autocontext);
00481 );
00482 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
00483 struct ast_dial *dial;
00484
00485
00486
00487 unsigned int ring_timeout;
00488
00489
00490
00491 unsigned int ring_delay;
00492
00493
00494 unsigned int hold_access:1;
00495
00496 unsigned int ref_count;
00497 };
00498
00499 struct sla_station_ref {
00500 AST_LIST_ENTRY(sla_station_ref) entry;
00501 struct sla_station *station;
00502 };
00503
00504 struct sla_trunk {
00505 AST_RWLIST_ENTRY(sla_trunk) entry;
00506 AST_DECLARE_STRING_FIELDS(
00507 AST_STRING_FIELD(name);
00508 AST_STRING_FIELD(device);
00509 AST_STRING_FIELD(autocontext);
00510 );
00511 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
00512
00513 unsigned int num_stations;
00514
00515 unsigned int active_stations;
00516
00517 unsigned int hold_stations;
00518 struct ast_channel *chan;
00519 unsigned int ring_timeout;
00520
00521
00522 unsigned int barge_disabled:1;
00523
00524
00525 unsigned int hold_access:1;
00526
00527
00528 unsigned int on_hold:1;
00529
00530 unsigned int ref_count;
00531 };
00532
00533 struct sla_trunk_ref {
00534 AST_LIST_ENTRY(sla_trunk_ref) entry;
00535 struct sla_trunk *trunk;
00536 enum sla_trunk_state state;
00537 struct ast_channel *chan;
00538
00539
00540
00541 unsigned int ring_timeout;
00542
00543
00544
00545 unsigned int ring_delay;
00546 };
00547
00548 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
00549 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
00550
00551 static const char sla_registrar[] = "SLA";
00552
00553
00554 enum sla_event_type {
00555
00556 SLA_EVENT_HOLD,
00557
00558 SLA_EVENT_DIAL_STATE,
00559
00560 SLA_EVENT_RINGING_TRUNK,
00561
00562 SLA_EVENT_RELOAD,
00563
00564 SLA_EVENT_CHECK_RELOAD,
00565 };
00566
00567 struct sla_event {
00568 enum sla_event_type type;
00569 struct sla_station *station;
00570 struct sla_trunk_ref *trunk_ref;
00571 AST_LIST_ENTRY(sla_event) entry;
00572 };
00573
00574
00575
00576 struct sla_failed_station {
00577 struct sla_station *station;
00578 struct timeval last_try;
00579 AST_LIST_ENTRY(sla_failed_station) entry;
00580 };
00581
00582
00583 struct sla_ringing_trunk {
00584 struct sla_trunk *trunk;
00585
00586 struct timeval ring_begin;
00587 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
00588 AST_LIST_ENTRY(sla_ringing_trunk) entry;
00589 };
00590
00591 enum sla_station_hangup {
00592 SLA_STATION_HANGUP_NORMAL,
00593 SLA_STATION_HANGUP_TIMEOUT,
00594 };
00595
00596
00597 struct sla_ringing_station {
00598 struct sla_station *station;
00599
00600 struct timeval ring_begin;
00601 AST_LIST_ENTRY(sla_ringing_station) entry;
00602 };
00603
00604
00605
00606
00607 static struct {
00608
00609 pthread_t thread;
00610 ast_cond_t cond;
00611 ast_mutex_t lock;
00612 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
00613 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
00614 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
00615 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
00616 unsigned int stop:1;
00617
00618
00619 unsigned int attempt_callerid:1;
00620
00621 unsigned int reload:1;
00622 } sla = {
00623 .thread = AST_PTHREADT_NULL,
00624 };
00625
00626
00627
00628 static int audio_buffers;
00629
00630
00631
00632
00633
00634
00635
00636 static char const gain_map[] = {
00637 -15,
00638 -13,
00639 -10,
00640 -6,
00641 0,
00642 0,
00643 0,
00644 6,
00645 10,
00646 13,
00647 15,
00648 };
00649
00650
00651 static int admin_exec(struct ast_channel *chan, void *data);
00652 static void *recordthread(void *args);
00653
00654 static char *istalking(int x)
00655 {
00656 if (x > 0)
00657 return "(talking)";
00658 else if (x < 0)
00659 return "(unmonitored)";
00660 else
00661 return "(not talking)";
00662 }
00663
00664 static int careful_write(int fd, unsigned char *data, int len, int block)
00665 {
00666 int res;
00667 int x;
00668
00669 while (len) {
00670 if (block) {
00671 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
00672 res = ioctl(fd, DAHDI_IOMUX, &x);
00673 } else
00674 res = 0;
00675 if (res >= 0)
00676 res = write(fd, data, len);
00677 if (res < 1) {
00678 if (errno != EAGAIN) {
00679 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
00680 return -1;
00681 } else
00682 return 0;
00683 }
00684 len -= res;
00685 data += res;
00686 }
00687
00688 return 0;
00689 }
00690
00691 static int set_talk_volume(struct ast_conf_user *user, int volume)
00692 {
00693 char gain_adjust;
00694
00695
00696
00697
00698 gain_adjust = gain_map[volume + 5];
00699
00700 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
00701 }
00702
00703 static int set_listen_volume(struct ast_conf_user *user, int volume)
00704 {
00705 char gain_adjust;
00706
00707
00708
00709
00710 gain_adjust = gain_map[volume + 5];
00711
00712 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
00713 }
00714
00715 static void tweak_volume(struct volume *vol, enum volume_action action)
00716 {
00717 switch (action) {
00718 case VOL_UP:
00719 switch (vol->desired) {
00720 case 5:
00721 break;
00722 case 0:
00723 vol->desired = 2;
00724 break;
00725 case -2:
00726 vol->desired = 0;
00727 break;
00728 default:
00729 vol->desired++;
00730 break;
00731 }
00732 break;
00733 case VOL_DOWN:
00734 switch (vol->desired) {
00735 case -5:
00736 break;
00737 case 2:
00738 vol->desired = 0;
00739 break;
00740 case 0:
00741 vol->desired = -2;
00742 break;
00743 default:
00744 vol->desired--;
00745 break;
00746 }
00747 }
00748 }
00749
00750 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
00751 {
00752 tweak_volume(&user->talk, action);
00753
00754
00755
00756 if (!set_talk_volume(user, user->talk.desired))
00757 user->talk.actual = 0;
00758 else
00759 user->talk.actual = user->talk.desired;
00760 }
00761
00762 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
00763 {
00764 tweak_volume(&user->listen, action);
00765
00766
00767
00768 if (!set_listen_volume(user, user->listen.desired))
00769 user->listen.actual = 0;
00770 else
00771 user->listen.actual = user->listen.desired;
00772 }
00773
00774 static void reset_volumes(struct ast_conf_user *user)
00775 {
00776 signed char zero_volume = 0;
00777
00778 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00779 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
00780 }
00781
00782 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
00783 {
00784 unsigned char *data;
00785 int len;
00786 int res = -1;
00787
00788 if (!ast_check_hangup(chan))
00789 res = ast_autoservice_start(chan);
00790
00791 AST_LIST_LOCK(&confs);
00792
00793 switch(sound) {
00794 case ENTER:
00795 data = enter;
00796 len = sizeof(enter);
00797 break;
00798 case LEAVE:
00799 data = leave;
00800 len = sizeof(leave);
00801 break;
00802 default:
00803 data = NULL;
00804 len = 0;
00805 }
00806 if (data) {
00807 careful_write(conf->fd, data, len, 1);
00808 }
00809
00810 AST_LIST_UNLOCK(&confs);
00811
00812 if (!res)
00813 ast_autoservice_stop(chan);
00814 }
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
00831 {
00832 struct ast_conference *cnf;
00833 struct dahdi_confinfo dahdic = { 0, };
00834 int confno_int = 0;
00835
00836 AST_LIST_LOCK(&confs);
00837
00838 AST_LIST_TRAVERSE(&confs, cnf, list) {
00839 if (!strcmp(confno, cnf->confno))
00840 break;
00841 }
00842
00843 if (cnf || (!make && !dynamic))
00844 goto cnfout;
00845
00846
00847 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
00848 goto cnfout;
00849
00850 ast_mutex_init(&cnf->playlock);
00851 ast_mutex_init(&cnf->listenlock);
00852 cnf->recordthread = AST_PTHREADT_NULL;
00853 ast_mutex_init(&cnf->recordthreadlock);
00854 cnf->announcethread = AST_PTHREADT_NULL;
00855 ast_mutex_init(&cnf->announcethreadlock);
00856 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
00857 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
00858 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
00859 ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
00860
00861
00862 dahdic.confno = -1;
00863 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
00864 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
00865 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
00866 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
00867 if (cnf->fd >= 0)
00868 close(cnf->fd);
00869 ast_free(cnf);
00870 cnf = NULL;
00871 goto cnfout;
00872 }
00873
00874 cnf->dahdiconf = dahdic.confno;
00875
00876
00877 cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL);
00878 if (cnf->chan) {
00879 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
00880 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
00881 dahdic.chan = 0;
00882 dahdic.confno = cnf->dahdiconf;
00883 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
00884 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
00885 ast_log(LOG_WARNING, "Error setting conference\n");
00886 if (cnf->chan)
00887 ast_hangup(cnf->chan);
00888 else
00889 close(cnf->fd);
00890
00891 ast_free(cnf);
00892 cnf = NULL;
00893 goto cnfout;
00894 }
00895 }
00896
00897
00898 cnf->start = time(NULL);
00899 cnf->maxusers = 0x7fffffff;
00900 cnf->isdynamic = dynamic ? 1 : 0;
00901 ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
00902 AST_LIST_INSERT_HEAD(&confs, cnf, list);
00903
00904
00905 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
00906 conf_map[confno_int] = 1;
00907
00908 cnfout:
00909 if (cnf)
00910 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
00911
00912 AST_LIST_UNLOCK(&confs);
00913
00914 return cnf;
00915 }
00916
00917
00918 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
00919 {
00920 static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
00921
00922 int len = strlen(word);
00923 int which = 0;
00924 struct ast_conference *cnf = NULL;
00925 struct ast_conf_user *usr = NULL;
00926 char *confno = NULL;
00927 char usrno[50] = "";
00928 char *myline, *ret = NULL;
00929
00930 if (pos == 1) {
00931 return ast_cli_complete(word, cmds, state);
00932 } else if (pos == 2) {
00933 AST_LIST_LOCK(&confs);
00934 AST_LIST_TRAVERSE(&confs, cnf, list) {
00935 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
00936 ret = cnf->confno;
00937 break;
00938 }
00939 }
00940 ret = ast_strdup(ret);
00941 AST_LIST_UNLOCK(&confs);
00942 return ret;
00943 } else if (pos == 3) {
00944
00945 if (strstr(line, "mute") || strstr(line, "kick")) {
00946 if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
00947 return ast_strdup("all");
00948 which++;
00949 AST_LIST_LOCK(&confs);
00950
00951
00952 myline = ast_strdupa(line);
00953 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
00954 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
00955 ;
00956 }
00957
00958 AST_LIST_TRAVERSE(&confs, cnf, list) {
00959 if (!strcmp(confno, cnf->confno))
00960 break;
00961 }
00962
00963 if (cnf) {
00964
00965 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
00966 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
00967 if (!strncasecmp(word, usrno, len) && ++which > state)
00968 break;
00969 }
00970 }
00971 AST_LIST_UNLOCK(&confs);
00972 return usr ? ast_strdup(usrno) : NULL;
00973 } else if (strstr(line, "list") && (state == 0))
00974 return ast_strdup("concise");
00975 }
00976
00977 return NULL;
00978 }
00979
00980 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00981 {
00982
00983 struct ast_conference *cnf;
00984 struct ast_conf_user *user;
00985 int hr, min, sec;
00986 int i = 0, total = 0;
00987 time_t now;
00988 char cmdline[1024] = "";
00989 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s %-8s %-6s\n"
00990 #define MC_DATA_FORMAT "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
00991
00992 switch (cmd) {
00993 case CLI_INIT:
00994 e->command = "meetme";
00995 e->usage =
00996 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
00997 " Executes a command for the conference or on a conferee\n";
00998 return NULL;
00999 case CLI_GENERATE:
01000 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
01001 }
01002
01003 if (a->argc > 8)
01004 ast_cli(a->fd, "Invalid Arguments.\n");
01005
01006 for (i = 0; i < a->argc; i++) {
01007 if (strlen(a->argv[i]) > 100)
01008 ast_cli(a->fd, "Invalid Arguments.\n");
01009 }
01010 if (a->argc == 1 || (a->argc == 2 && !strcasecmp(a->argv[1], "concise"))) {
01011
01012 int concise = (a->argc == 2 && !strcasecmp(a->argv[1], "concise"));
01013 now = time(NULL);
01014 AST_LIST_LOCK(&confs);
01015 if (AST_LIST_EMPTY(&confs)) {
01016 if (!concise)
01017 ast_cli(a->fd, "No active MeetMe conferences.\n");
01018 AST_LIST_UNLOCK(&confs);
01019 return CLI_SUCCESS;
01020 }
01021 if (!concise)
01022 ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
01023 AST_LIST_TRAVERSE(&confs, cnf, list) {
01024 if (cnf->markedusers == 0)
01025 strcpy(cmdline, "N/A ");
01026 else
01027 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
01028 hr = (now - cnf->start) / 3600;
01029 min = ((now - cnf->start) % 3600) / 60;
01030 sec = (now - cnf->start) % 60;
01031 if (!concise)
01032 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
01033 else {
01034 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
01035 cnf->confno,
01036 cnf->users,
01037 cnf->markedusers,
01038 hr, min, sec,
01039 cnf->isdynamic,
01040 cnf->locked);
01041 }
01042
01043 total += cnf->users;
01044 }
01045 AST_LIST_UNLOCK(&confs);
01046 if (!concise)
01047 ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
01048 return CLI_SUCCESS;
01049 }
01050 if (a->argc < 3)
01051 return CLI_SHOWUSAGE;
01052 ast_copy_string(cmdline, a->argv[2], sizeof(cmdline));
01053 if (strstr(a->argv[1], "lock")) {
01054 if (strcmp(a->argv[1], "lock") == 0) {
01055
01056 strncat(cmdline, ",L", sizeof(cmdline) - strlen(cmdline) - 1);
01057 } else {
01058
01059 strncat(cmdline, ",l", sizeof(cmdline) - strlen(cmdline) - 1);
01060 }
01061 } else if (strstr(a->argv[1], "mute")) {
01062 if (a->argc < 4)
01063 return CLI_SHOWUSAGE;
01064 if (strcmp(a->argv[1], "mute") == 0) {
01065
01066 if (strcmp(a->argv[3], "all") == 0) {
01067 strncat(cmdline, ",N", sizeof(cmdline) - strlen(cmdline) - 1);
01068 } else {
01069 strncat(cmdline, ",M,", sizeof(cmdline) - strlen(cmdline) - 1);
01070 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
01071 }
01072 } else {
01073
01074 if (strcmp(a->argv[3], "all") == 0) {
01075 strncat(cmdline, ",n", sizeof(cmdline) - strlen(cmdline) - 1);
01076 } else {
01077 strncat(cmdline, ",m,", sizeof(cmdline) - strlen(cmdline) - 1);
01078 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
01079 }
01080 }
01081 } else if (strcmp(a->argv[1], "kick") == 0) {
01082 if (a->argc < 4)
01083 return CLI_SHOWUSAGE;
01084 if (strcmp(a->argv[3], "all") == 0) {
01085
01086 strncat(cmdline, ",K", sizeof(cmdline) - strlen(cmdline) - 1);
01087 } else {
01088
01089 strncat(cmdline, ",k,", sizeof(cmdline) - strlen(cmdline) - 1);
01090 strncat(cmdline, a->argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
01091 }
01092 } else if (strcmp(a->argv[1], "list") == 0) {
01093 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
01094
01095 if (AST_LIST_EMPTY(&confs)) {
01096 if (!concise)
01097 ast_cli(a->fd, "No active conferences.\n");
01098 return CLI_SUCCESS;
01099 }
01100
01101 AST_LIST_LOCK(&confs);
01102 AST_LIST_TRAVERSE(&confs, cnf, list) {
01103 if (strcmp(cnf->confno, a->argv[2]) == 0)
01104 break;
01105 }
01106 if (!cnf) {
01107 if (!concise)
01108 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
01109 AST_LIST_UNLOCK(&confs);
01110 return CLI_SUCCESS;
01111 }
01112
01113 time(&now);
01114 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
01115 hr = (now - user->jointime) / 3600;
01116 min = ((now - user->jointime) % 3600) / 60;
01117 sec = (now - user->jointime) % 60;
01118 if (!concise)
01119 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
01120 user->user_no,
01121 S_OR(user->chan->cid.cid_num, "<unknown>"),
01122 S_OR(user->chan->cid.cid_name, "<no name>"),
01123 user->chan->name,
01124 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
01125 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
01126 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
01127 user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
01128 istalking(user->talking), hr, min, sec);
01129 else
01130 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
01131 user->user_no,
01132 S_OR(user->chan->cid.cid_num, ""),
01133 S_OR(user->chan->cid.cid_name, ""),
01134 user->chan->name,
01135 user->userflags & CONFFLAG_ADMIN ? "1" : "",
01136 user->userflags & CONFFLAG_MONITOR ? "1" : "",
01137 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
01138 user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
01139 user->talking, hr, min, sec);
01140
01141 }
01142 if (!concise)
01143 ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
01144 AST_LIST_UNLOCK(&confs);
01145 return CLI_SUCCESS;
01146 } else
01147 return CLI_SHOWUSAGE;
01148
01149 ast_debug(1, "Cmdline: %s\n", cmdline);
01150
01151 admin_exec(NULL, cmdline);
01152
01153 return CLI_SUCCESS;
01154 }
01155
01156 static const char *sla_hold_str(unsigned int hold_access)
01157 {
01158 const char *hold = "Unknown";
01159
01160 switch (hold_access) {
01161 case SLA_HOLD_OPEN:
01162 hold = "Open";
01163 break;
01164 case SLA_HOLD_PRIVATE:
01165 hold = "Private";
01166 default:
01167 break;
01168 }
01169
01170 return hold;
01171 }
01172
01173 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01174 {
01175 const struct sla_trunk *trunk;
01176
01177 switch (cmd) {
01178 case CLI_INIT:
01179 e->command = "sla show trunks";
01180 e->usage =
01181 "Usage: sla show trunks\n"
01182 " This will list all trunks defined in sla.conf\n";
01183 return NULL;
01184 case CLI_GENERATE:
01185 return NULL;
01186 }
01187
01188 ast_cli(a->fd, "\n"
01189 "=============================================================\n"
01190 "=== Configured SLA Trunks ===================================\n"
01191 "=============================================================\n"
01192 "===\n");
01193 AST_RWLIST_RDLOCK(&sla_trunks);
01194 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
01195 struct sla_station_ref *station_ref;
01196 char ring_timeout[16] = "(none)";
01197 if (trunk->ring_timeout)
01198 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
01199 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01200 "=== Trunk Name: %s\n"
01201 "=== ==> Device: %s\n"
01202 "=== ==> AutoContext: %s\n"
01203 "=== ==> RingTimeout: %s\n"
01204 "=== ==> BargeAllowed: %s\n"
01205 "=== ==> HoldAccess: %s\n"
01206 "=== ==> Stations ...\n",
01207 trunk->name, trunk->device,
01208 S_OR(trunk->autocontext, "(none)"),
01209 ring_timeout,
01210 trunk->barge_disabled ? "No" : "Yes",
01211 sla_hold_str(trunk->hold_access));
01212 AST_RWLIST_RDLOCK(&sla_stations);
01213 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
01214 ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
01215 AST_RWLIST_UNLOCK(&sla_stations);
01216 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
01217 }
01218 AST_RWLIST_UNLOCK(&sla_trunks);
01219 ast_cli(a->fd, "=============================================================\n\n");
01220
01221 return CLI_SUCCESS;
01222 }
01223
01224 static const char *trunkstate2str(enum sla_trunk_state state)
01225 {
01226 #define S(e) case e: return # e;
01227 switch (state) {
01228 S(SLA_TRUNK_STATE_IDLE)
01229 S(SLA_TRUNK_STATE_RINGING)
01230 S(SLA_TRUNK_STATE_UP)
01231 S(SLA_TRUNK_STATE_ONHOLD)
01232 S(SLA_TRUNK_STATE_ONHOLD_BYME)
01233 }
01234 return "Uknown State";
01235 #undef S
01236 }
01237
01238 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01239 {
01240 const struct sla_station *station;
01241
01242 switch (cmd) {
01243 case CLI_INIT:
01244 e->command = "sla show stations";
01245 e->usage =
01246 "Usage: sla show stations\n"
01247 " This will list all stations defined in sla.conf\n";
01248 return NULL;
01249 case CLI_GENERATE:
01250 return NULL;
01251 }
01252
01253 ast_cli(a->fd, "\n"
01254 "=============================================================\n"
01255 "=== Configured SLA Stations =================================\n"
01256 "=============================================================\n"
01257 "===\n");
01258 AST_RWLIST_RDLOCK(&sla_stations);
01259 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
01260 struct sla_trunk_ref *trunk_ref;
01261 char ring_timeout[16] = "(none)";
01262 char ring_delay[16] = "(none)";
01263 if (station->ring_timeout) {
01264 snprintf(ring_timeout, sizeof(ring_timeout),
01265 "%u", station->ring_timeout);
01266 }
01267 if (station->ring_delay) {
01268 snprintf(ring_delay, sizeof(ring_delay),
01269 "%u", station->ring_delay);
01270 }
01271 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01272 "=== Station Name: %s\n"
01273 "=== ==> Device: %s\n"
01274 "=== ==> AutoContext: %s\n"
01275 "=== ==> RingTimeout: %s\n"
01276 "=== ==> RingDelay: %s\n"
01277 "=== ==> HoldAccess: %s\n"
01278 "=== ==> Trunks ...\n",
01279 station->name, station->device,
01280 S_OR(station->autocontext, "(none)"),
01281 ring_timeout, ring_delay,
01282 sla_hold_str(station->hold_access));
01283 AST_RWLIST_RDLOCK(&sla_trunks);
01284 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01285 if (trunk_ref->ring_timeout) {
01286 snprintf(ring_timeout, sizeof(ring_timeout),
01287 "%u", trunk_ref->ring_timeout);
01288 } else
01289 strcpy(ring_timeout, "(none)");
01290 if (trunk_ref->ring_delay) {
01291 snprintf(ring_delay, sizeof(ring_delay),
01292 "%u", trunk_ref->ring_delay);
01293 } else
01294 strcpy(ring_delay, "(none)");
01295 ast_cli(a->fd, "=== ==> Trunk Name: %s\n"
01296 "=== ==> State: %s\n"
01297 "=== ==> RingTimeout: %s\n"
01298 "=== ==> RingDelay: %s\n",
01299 trunk_ref->trunk->name,
01300 trunkstate2str(trunk_ref->state),
01301 ring_timeout, ring_delay);
01302 }
01303 AST_RWLIST_UNLOCK(&sla_trunks);
01304 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01305 "===\n");
01306 }
01307 AST_RWLIST_UNLOCK(&sla_stations);
01308 ast_cli(a->fd, "============================================================\n"
01309 "\n");
01310
01311 return CLI_SUCCESS;
01312 }
01313
01314 static struct ast_cli_entry cli_meetme[] = {
01315 AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
01316 AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
01317 AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
01318 };
01319
01320 static void conf_flush(int fd, struct ast_channel *chan)
01321 {
01322 int x;
01323
01324
01325
01326
01327 if (chan) {
01328 struct ast_frame *f;
01329
01330
01331
01332
01333 while (ast_waitfor(chan, 1)) {
01334 f = ast_read(chan);
01335 if (f)
01336 ast_frfree(f);
01337 else
01338 break;
01339 }
01340 }
01341
01342
01343 x = DAHDI_FLUSH_ALL;
01344 if (ioctl(fd, DAHDI_FLUSH, &x))
01345 ast_log(LOG_WARNING, "Error flushing channel\n");
01346
01347 }
01348
01349
01350
01351 static int conf_free(struct ast_conference *conf)
01352 {
01353 int x;
01354 struct announce_listitem *item;
01355
01356 AST_LIST_REMOVE(&confs, conf, list);
01357 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
01358
01359 if (conf->recording == MEETME_RECORD_ACTIVE) {
01360 conf->recording = MEETME_RECORD_TERMINATE;
01361 AST_LIST_UNLOCK(&confs);
01362 while (1) {
01363 usleep(1);
01364 AST_LIST_LOCK(&confs);
01365 if (conf->recording == MEETME_RECORD_OFF)
01366 break;
01367 AST_LIST_UNLOCK(&confs);
01368 }
01369 }
01370
01371 for (x = 0; x < AST_FRAME_BITS; x++) {
01372 if (conf->transframe[x])
01373 ast_frfree(conf->transframe[x]);
01374 if (conf->transpath[x])
01375 ast_translator_free_path(conf->transpath[x]);
01376 }
01377 if (conf->announcethread != AST_PTHREADT_NULL) {
01378 ast_mutex_lock(&conf->announcelistlock);
01379 conf->announcethread_stop = 1;
01380 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
01381 ast_cond_signal(&conf->announcelist_addition);
01382 ast_mutex_unlock(&conf->announcelistlock);
01383 pthread_join(conf->announcethread, NULL);
01384
01385 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
01386 ast_filedelete(item->namerecloc, NULL);
01387 ao2_ref(item, -1);
01388 }
01389 ast_mutex_destroy(&conf->announcelistlock);
01390 }
01391 if (conf->origframe)
01392 ast_frfree(conf->origframe);
01393 if (conf->lchan)
01394 ast_hangup(conf->lchan);
01395 if (conf->chan)
01396 ast_hangup(conf->chan);
01397 if (conf->fd >= 0)
01398 close(conf->fd);
01399
01400 ast_mutex_destroy(&conf->playlock);
01401 ast_mutex_destroy(&conf->listenlock);
01402 ast_mutex_destroy(&conf->recordthreadlock);
01403 ast_mutex_destroy(&conf->announcethreadlock);
01404 ast_free(conf);
01405
01406 return 0;
01407 }
01408
01409 static void conf_queue_dtmf(const struct ast_conference *conf,
01410 const struct ast_conf_user *sender, struct ast_frame *f)
01411 {
01412 struct ast_conf_user *user;
01413
01414 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
01415 if (user == sender)
01416 continue;
01417 if (ast_write(user->chan, f) < 0)
01418 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
01419 }
01420 }
01421
01422 static void sla_queue_event_full(enum sla_event_type type,
01423 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
01424 {
01425 struct sla_event *event;
01426
01427 if (sla.thread == AST_PTHREADT_NULL) {
01428 return;
01429 }
01430
01431 if (!(event = ast_calloc(1, sizeof(*event))))
01432 return;
01433
01434 event->type = type;
01435 event->trunk_ref = trunk_ref;
01436 event->station = station;
01437
01438 if (!lock) {
01439 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
01440 return;
01441 }
01442
01443 ast_mutex_lock(&sla.lock);
01444 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
01445 ast_cond_signal(&sla.cond);
01446 ast_mutex_unlock(&sla.lock);
01447 }
01448
01449 static void sla_queue_event_nolock(enum sla_event_type type)
01450 {
01451 sla_queue_event_full(type, NULL, NULL, 0);
01452 }
01453
01454 static void sla_queue_event(enum sla_event_type type)
01455 {
01456 sla_queue_event_full(type, NULL, NULL, 1);
01457 }
01458
01459
01460 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
01461 struct ast_conference *conf)
01462 {
01463 struct sla_station *station;
01464 struct sla_trunk_ref *trunk_ref = NULL;
01465 char *trunk_name;
01466
01467 trunk_name = ast_strdupa(conf->confno);
01468 strsep(&trunk_name, "_");
01469 if (ast_strlen_zero(trunk_name)) {
01470 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
01471 return;
01472 }
01473
01474 AST_RWLIST_RDLOCK(&sla_stations);
01475 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
01476 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01477 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
01478 break;
01479 }
01480 if (trunk_ref)
01481 break;
01482 }
01483 AST_RWLIST_UNLOCK(&sla_stations);
01484
01485 if (!trunk_ref) {
01486 ast_debug(1, "Trunk not found for event!\n");
01487 return;
01488 }
01489
01490 sla_queue_event_full(type, trunk_ref, station, 1);
01491 }
01492
01493
01494 static int dispose_conf(struct ast_conference *conf)
01495 {
01496 int res = 0;
01497 int confno_int = 0;
01498
01499 AST_LIST_LOCK(&confs);
01500 if (ast_atomic_dec_and_test(&conf->refcount)) {
01501
01502 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
01503 conf_map[confno_int] = 0;
01504 conf_free(conf);
01505 res = 1;
01506 }
01507 AST_LIST_UNLOCK(&confs);
01508
01509 return res;
01510 }
01511
01512 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
01513 {
01514 char *original_moh;
01515
01516 ast_channel_lock(chan);
01517 original_moh = ast_strdupa(chan->musicclass);
01518 ast_string_field_set(chan, musicclass, musicclass);
01519 ast_channel_unlock(chan);
01520
01521 ast_moh_start(chan, original_moh, NULL);
01522
01523 ast_channel_lock(chan);
01524 ast_string_field_set(chan, musicclass, original_moh);
01525 ast_channel_unlock(chan);
01526 }
01527
01528 static const char *get_announce_filename(enum announcetypes type)
01529 {
01530 switch (type) {
01531 case CONF_HASLEFT:
01532 return "conf-hasleft";
01533 break;
01534 case CONF_HASJOIN:
01535 return "conf-hasjoin";
01536 break;
01537 default:
01538 return "";
01539 }
01540 }
01541
01542 static void *announce_thread(void *data)
01543 {
01544 struct announce_listitem *current;
01545 struct ast_conference *conf = data;
01546 int res = 0;
01547 char filename[PATH_MAX] = "";
01548 AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
01549 AST_LIST_HEAD_INIT_NOLOCK(&local_list);
01550
01551 while (!conf->announcethread_stop) {
01552 ast_mutex_lock(&conf->announcelistlock);
01553 if (conf->announcethread_stop) {
01554 ast_mutex_unlock(&conf->announcelistlock);
01555 break;
01556 }
01557 if (AST_LIST_EMPTY(&conf->announcelist))
01558 ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
01559
01560 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
01561 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
01562
01563 ast_mutex_unlock(&conf->announcelistlock);
01564 if (conf->announcethread_stop) {
01565 break;
01566 }
01567
01568 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
01569 ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
01570 if (!ast_fileexists(current->namerecloc, NULL, NULL))
01571 continue;
01572 if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
01573 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
01574 res = ast_waitstream(current->confchan, "");
01575 if (!res) {
01576 ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
01577 if (!ast_streamfile(current->confchan, filename, current->language))
01578 ast_waitstream(current->confchan, "");
01579 }
01580 }
01581 if (current->announcetype == CONF_HASLEFT) {
01582 ast_filedelete(current->namerecloc, NULL);
01583 }
01584 }
01585 }
01586
01587
01588 while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
01589 ast_filedelete(current->namerecloc, NULL);
01590 ao2_ref(current, -1);
01591 }
01592 return NULL;
01593 }
01594
01595 static int can_write(struct ast_channel *chan, int confflags)
01596 {
01597 if (!(confflags & CONFFLAG_NO_AUDIO_UNTIL_UP)) {
01598 return 1;
01599 }
01600
01601 return (chan->_state == AST_STATE_UP);
01602 }
01603
01604 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
01605 {
01606 struct ast_conf_user *user = NULL;
01607 struct ast_conf_user *usr = NULL;
01608 int fd;
01609 struct dahdi_confinfo dahdic, dahdic_empty;
01610 struct ast_frame *f;
01611 struct ast_channel *c;
01612 struct ast_frame fr;
01613 int outfd;
01614 int ms;
01615 int nfds;
01616 int res;
01617 int retrydahdi;
01618 int origfd;
01619 int musiconhold = 0;
01620 int firstpass = 0;
01621 int lastmarked = 0;
01622 int currentmarked = 0;
01623 int ret = -1;
01624 int x;
01625 int menu_active = 0;
01626 int talkreq_manager = 0;
01627 int using_pseudo = 0;
01628 int duration = 20;
01629 int hr, min, sec;
01630 int sent_event = 0;
01631 int checked = 0;
01632 int announcement_played = 0;
01633 struct timeval now;
01634 struct ast_dsp *dsp = NULL;
01635 struct ast_app *app;
01636 const char *agifile;
01637 const char *agifiledefault = "conf-background.agi";
01638 char meetmesecs[30] = "";
01639 char exitcontext[AST_MAX_CONTEXT] = "";
01640 char recordingtmp[AST_MAX_EXTENSION] = "";
01641 char members[10] = "";
01642 int dtmf, opt_waitmarked_timeout = 0;
01643 time_t timeout = 0;
01644 struct dahdi_bufferinfo bi;
01645 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
01646 char *buf = __buf + AST_FRIENDLY_OFFSET;
01647 char *exitkeys = NULL;
01648 unsigned int calldurationlimit = 0;
01649 long timelimit = 0;
01650 long play_warning = 0;
01651 long warning_freq = 0;
01652 const char *warning_sound = NULL;
01653 const char *end_sound = NULL;
01654 char *parse;
01655 long time_left_ms = 0;
01656 struct timeval nexteventts = { 0, };
01657 int to;
01658 int setusercount = 0;
01659
01660 if (!(user = ast_calloc(1, sizeof(*user))))
01661 return ret;
01662
01663
01664 if ((confflags & CONFFLAG_WAITMARKED) &&
01665 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
01666 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
01667 (opt_waitmarked_timeout > 0)) {
01668 timeout = time(NULL) + opt_waitmarked_timeout;
01669 }
01670
01671 if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
01672 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
01673 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
01674 }
01675
01676 if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
01677 char *limit_str, *warning_str, *warnfreq_str;
01678 const char *var;
01679
01680 parse = optargs[OPT_ARG_DURATION_LIMIT];
01681 limit_str = strsep(&parse, ":");
01682 warning_str = strsep(&parse, ":");
01683 warnfreq_str = parse;
01684
01685 timelimit = atol(limit_str);
01686 if (warning_str)
01687 play_warning = atol(warning_str);
01688 if (warnfreq_str)
01689 warning_freq = atol(warnfreq_str);
01690
01691 if (!timelimit) {
01692 timelimit = play_warning = warning_freq = 0;
01693 warning_sound = NULL;
01694 } else if (play_warning > timelimit) {
01695 if (!warning_freq) {
01696 play_warning = 0;
01697 } else {
01698 while (play_warning > timelimit)
01699 play_warning -= warning_freq;
01700 if (play_warning < 1)
01701 play_warning = warning_freq = 0;
01702 }
01703 }
01704
01705 var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE");
01706 warning_sound = var ? var : "timeleft";
01707
01708 var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE");
01709 end_sound = var ? var : NULL;
01710
01711
01712 calldurationlimit = 0;
01713
01714 if (!play_warning && !end_sound && timelimit) {
01715 calldurationlimit = timelimit / 1000;
01716 timelimit = play_warning = warning_freq = 0;
01717 } else {
01718 ast_debug(2, "Limit Data for this call:\n");
01719 ast_debug(2, "- timelimit = %ld\n", timelimit);
01720 ast_debug(2, "- play_warning = %ld\n", play_warning);
01721 ast_debug(2, "- warning_freq = %ld\n", warning_freq);
01722 ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
01723 ast_debug(2, "- end_sound = %s\n", end_sound ? end_sound : "UNDEF");
01724 }
01725 }
01726
01727
01728 if ((confflags & CONFFLAG_KEYEXIT)) {
01729 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
01730 exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
01731 else
01732 exitkeys = ast_strdupa("#");
01733 }
01734
01735 if (confflags & CONFFLAG_RECORDCONF) {
01736 if (!conf->recordingfilename) {
01737 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
01738 if (!conf->recordingfilename) {
01739 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
01740 conf->recordingfilename = ast_strdupa(recordingtmp);
01741 }
01742 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
01743 if (!conf->recordingformat) {
01744 ast_copy_string(recordingtmp, "wav", sizeof(recordingtmp));
01745 conf->recordingformat = ast_strdupa(recordingtmp);
01746 }
01747 ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
01748 conf->confno, conf->recordingfilename, conf->recordingformat);
01749 }
01750 }
01751
01752 ast_mutex_lock(&conf->recordthreadlock);
01753 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
01754 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
01755 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
01756 dahdic.chan = 0;
01757 dahdic.confno = conf->dahdiconf;
01758 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01759 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
01760 ast_log(LOG_WARNING, "Error starting listen channel\n");
01761 ast_hangup(conf->lchan);
01762 conf->lchan = NULL;
01763 } else {
01764 ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
01765 }
01766 }
01767 ast_mutex_unlock(&conf->recordthreadlock);
01768
01769 ast_mutex_lock(&conf->announcethreadlock);
01770 if ((conf->announcethread == AST_PTHREADT_NULL) && !(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
01771 ast_mutex_init(&conf->announcelistlock);
01772 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
01773 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
01774 }
01775 ast_mutex_unlock(&conf->announcethreadlock);
01776
01777 time(&user->jointime);
01778
01779 user->timelimit = timelimit;
01780 user->play_warning = play_warning;
01781 user->warning_freq = warning_freq;
01782 user->warning_sound = warning_sound;
01783 user->end_sound = end_sound;
01784
01785 if (calldurationlimit > 0) {
01786 time(&user->kicktime);
01787 user->kicktime = user->kicktime + calldurationlimit;
01788 }
01789
01790 if (ast_tvzero(user->start_time))
01791 user->start_time = ast_tvnow();
01792 time_left_ms = user->timelimit;
01793
01794 if (user->timelimit) {
01795 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
01796 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
01797 }
01798
01799 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
01800
01801 if (!ast_streamfile(chan, "conf-locked", chan->language))
01802 ast_waitstream(chan, "");
01803 goto outrun;
01804 }
01805
01806 ast_mutex_lock(&conf->playlock);
01807
01808 if (AST_LIST_EMPTY(&conf->userlist))
01809 user->user_no = 1;
01810 else
01811 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
01812
01813 if (rt_schedule && conf->maxusers)
01814 if (conf->users >= conf->maxusers) {
01815
01816 if (!ast_streamfile(chan, "conf-full", chan->language))
01817 ast_waitstream(chan, "");
01818 ast_mutex_unlock(&conf->playlock);
01819 user->user_no = 0;
01820 goto outrun;
01821 }
01822
01823 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
01824
01825 user->chan = chan;
01826 user->userflags = confflags;
01827 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
01828 user->talking = -1;
01829
01830 ast_mutex_unlock(&conf->playlock);
01831
01832 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
01833 char destdir[PATH_MAX];
01834
01835 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
01836
01837 if (ast_mkdir(destdir, 0777) != 0) {
01838 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
01839 goto outrun;
01840 }
01841
01842 snprintf(user->namerecloc, sizeof(user->namerecloc),
01843 "%s/meetme-username-%s-%d", destdir,
01844 conf->confno, user->user_no);
01845 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
01846 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
01847 else
01848 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
01849 if (res == -1)
01850 goto outrun;
01851 }
01852
01853 ast_mutex_lock(&conf->playlock);
01854
01855 if (confflags & CONFFLAG_MARKEDUSER)
01856 conf->markedusers++;
01857 conf->users++;
01858 if (rt_log_members) {
01859
01860 snprintf(members, sizeof(members), "%d", conf->users);
01861 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
01862 }
01863 setusercount = 1;
01864
01865
01866 if (conf->users == 1)
01867 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
01868
01869 ast_mutex_unlock(&conf->playlock);
01870
01871
01872 pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
01873
01874 if (confflags & CONFFLAG_EXIT_CONTEXT) {
01875 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
01876 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
01877 else if (!ast_strlen_zero(chan->macrocontext))
01878 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
01879 else
01880 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
01881 }
01882
01883 if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
01884 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
01885 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
01886 ast_waitstream(chan, "");
01887 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
01888 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
01889 ast_waitstream(chan, "");
01890 }
01891
01892 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
01893 int keepplaying = 1;
01894
01895 if (conf->users == 2) {
01896 if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
01897 res = ast_waitstream(chan, AST_DIGIT_ANY);
01898 ast_stopstream(chan);
01899 if (res > 0)
01900 keepplaying = 0;
01901 else if (res == -1)
01902 goto outrun;
01903 }
01904 } else {
01905 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
01906 res = ast_waitstream(chan, AST_DIGIT_ANY);
01907 ast_stopstream(chan);
01908 if (res > 0)
01909 keepplaying = 0;
01910 else if (res == -1)
01911 goto outrun;
01912 }
01913 if (keepplaying) {
01914 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
01915 if (res > 0)
01916 keepplaying = 0;
01917 else if (res == -1)
01918 goto outrun;
01919 }
01920 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
01921 res = ast_waitstream(chan, AST_DIGIT_ANY);
01922 ast_stopstream(chan);
01923 if (res > 0)
01924 keepplaying = 0;
01925 else if (res == -1)
01926 goto outrun;
01927 }
01928 }
01929 }
01930
01931 if (!(confflags & CONFFLAG_NO_AUDIO_UNTIL_UP)) {
01932
01933 ast_indicate(chan, -1);
01934 }
01935
01936 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01937 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
01938 goto outrun;
01939 }
01940
01941 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
01942 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
01943 goto outrun;
01944 }
01945
01946 retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
01947 user->dahdichannel = !retrydahdi;
01948
01949 dahdiretry:
01950 origfd = chan->fds[0];
01951 if (retrydahdi) {
01952
01953 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
01954 if (fd < 0) {
01955 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
01956 goto outrun;
01957 }
01958 using_pseudo = 1;
01959
01960 memset(&bi, 0, sizeof(bi));
01961 bi.bufsize = CONF_SIZE / 2;
01962 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
01963 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
01964 bi.numbufs = audio_buffers;
01965 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
01966 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
01967 close(fd);
01968 goto outrun;
01969 }
01970 x = 1;
01971 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
01972 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
01973 close(fd);
01974 goto outrun;
01975 }
01976 nfds = 1;
01977 } else {
01978
01979 fd = chan->fds[0];
01980 nfds = 0;
01981 }
01982 memset(&dahdic, 0, sizeof(dahdic));
01983 memset(&dahdic_empty, 0, sizeof(dahdic_empty));
01984
01985 dahdic.chan = 0;
01986 if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
01987 ast_log(LOG_WARNING, "Error getting conference\n");
01988 close(fd);
01989 goto outrun;
01990 }
01991 if (dahdic.confmode) {
01992
01993 if (!retrydahdi) {
01994 ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
01995 retrydahdi = 1;
01996 goto dahdiretry;
01997 }
01998 }
01999 memset(&dahdic, 0, sizeof(dahdic));
02000
02001 dahdic.chan = 0;
02002 dahdic.confno = conf->dahdiconf;
02003
02004 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
02005 struct announce_listitem *item;
02006 if (!(item = ao2_alloc(sizeof(*item), NULL)))
02007 return -1;
02008 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
02009 ast_copy_string(item->language, chan->language, sizeof(item->language));
02010 item->confchan = conf->chan;
02011 item->confusers = conf->users;
02012 item->announcetype = CONF_HASJOIN;
02013 ast_mutex_lock(&conf->announcelistlock);
02014 ao2_ref(item, +1);
02015 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
02016 ast_cond_signal(&conf->announcelist_addition);
02017 ast_mutex_unlock(&conf->announcelistlock);
02018
02019 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
02020 ;
02021 }
02022 ao2_ref(item, -1);
02023 }
02024
02025 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
02026 dahdic.confmode = DAHDI_CONF_CONF;
02027 else if (confflags & CONFFLAG_MONITOR)
02028 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
02029 else if (confflags & CONFFLAG_TALKER)
02030 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
02031 else
02032 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
02033
02034 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02035 ast_log(LOG_WARNING, "Error setting conference\n");
02036 close(fd);
02037 goto outrun;
02038 }
02039 ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
02040
02041 if (!sent_event) {
02042 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
02043 "Channel: %s\r\n"
02044 "Uniqueid: %s\r\n"
02045 "Meetme: %s\r\n"
02046 "Usernum: %d\r\n"
02047 "CallerIDnum: %s\r\n"
02048 "CallerIDname: %s\r\n",
02049 chan->name, chan->uniqueid, conf->confno,
02050 user->user_no,
02051 S_OR(user->chan->cid.cid_num, "<unknown>"),
02052 S_OR(user->chan->cid.cid_name, "<unknown>")
02053 );
02054 sent_event = 1;
02055 }
02056
02057 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
02058 firstpass = 1;
02059 if (!(confflags & CONFFLAG_QUIET))
02060 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
02061 conf_play(chan, conf, ENTER);
02062 }
02063
02064 conf_flush(fd, chan);
02065
02066 if (confflags & CONFFLAG_AGI) {
02067
02068
02069
02070 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
02071 if (!agifile)
02072 agifile = agifiledefault;
02073
02074 if (user->dahdichannel) {
02075
02076 x = 1;
02077 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02078 }
02079
02080 app = pbx_findapp("agi");
02081 if (app) {
02082 char *s = ast_strdupa(agifile);
02083 ret = pbx_exec(chan, app, s);
02084 } else {
02085 ast_log(LOG_WARNING, "Could not find application (agi)\n");
02086 ret = -2;
02087 }
02088 if (user->dahdichannel) {
02089
02090 x = 0;
02091 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02092 }
02093 } else {
02094 if (user->dahdichannel && (confflags & CONFFLAG_STARMENU)) {
02095
02096 x = 1;
02097 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02098 }
02099 if (confflags & (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER) && !(dsp = ast_dsp_new())) {
02100 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
02101 res = -1;
02102 }
02103 for (;;) {
02104 int menu_was_active = 0;
02105
02106 outfd = -1;
02107 ms = -1;
02108 now = ast_tvnow();
02109
02110 if (rt_schedule) {
02111 if (now.tv_sec % 60 == 0) {
02112 if (!checked) {
02113 if (now.tv_sec > conf->endtime) {
02114 ast_verbose("Quitting time...\n");
02115 goto outrun;
02116 }
02117
02118 if (!announcement_played && conf->endalert) {
02119 if (now.tv_sec + conf->endalert > conf->endtime) {
02120 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
02121 ast_waitstream(chan, "");
02122 ast_say_digits(chan, (now.tv_sec + conf->endalert - conf->endtime) / 60, "", chan->language);
02123 if (!ast_streamfile(chan, "minutes", chan->language))
02124 ast_waitstream(chan, "");
02125 announcement_played = 1;
02126 }
02127 }
02128 checked = 1;
02129
02130 }
02131 } else {
02132 checked = 0;
02133 }
02134 }
02135
02136 if (user->kicktime && (user->kicktime <= now.tv_sec))
02137 break;
02138
02139 to = -1;
02140 if (user->timelimit) {
02141 int minutes = 0, seconds = 0, remain = 0;
02142
02143 to = ast_tvdiff_ms(nexteventts, now);
02144 if (to < 0)
02145 to = 0;
02146 time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
02147 if (time_left_ms < to)
02148 to = time_left_ms;
02149
02150 if (time_left_ms <= 0) {
02151 if (user->end_sound) {
02152 res = ast_streamfile(chan, user->end_sound, chan->language);
02153 res = ast_waitstream(chan, "");
02154 }
02155 break;
02156 }
02157
02158 if (!to) {
02159 if (time_left_ms >= 5000) {
02160
02161 remain = (time_left_ms + 500) / 1000;
02162 if (remain / 60 >= 1) {
02163 minutes = remain / 60;
02164 seconds = remain % 60;
02165 } else {
02166 seconds = remain;
02167 }
02168
02169
02170 if (user->warning_sound && user->play_warning) {
02171 if (!strcmp(user->warning_sound, "timeleft")) {
02172
02173 res = ast_streamfile(chan, "vm-youhave", chan->language);
02174 res = ast_waitstream(chan, "");
02175 if (minutes) {
02176 res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
02177 res = ast_streamfile(chan, "queue-minutes", chan->language);
02178 res = ast_waitstream(chan, "");
02179 }
02180 if (seconds) {
02181 res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
02182 res = ast_streamfile(chan, "queue-seconds", chan->language);
02183 res = ast_waitstream(chan, "");
02184 }
02185 } else {
02186 res = ast_streamfile(chan, user->warning_sound, chan->language);
02187 res = ast_waitstream(chan, "");
02188 }
02189 }
02190 }
02191 if (user->warning_freq)
02192 nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
02193 else
02194 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
02195 }
02196 }
02197
02198 now = ast_tvnow();
02199 if (timeout && now.tv_sec >= timeout)
02200 break;
02201
02202
02203
02204
02205 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
02206 set_talk_volume(user, user->listen.desired);
02207
02208 menu_was_active = menu_active;
02209
02210 currentmarked = conf->markedusers;
02211 if (!(confflags & CONFFLAG_QUIET) &&
02212 (confflags & CONFFLAG_MARKEDUSER) &&
02213 (confflags & CONFFLAG_WAITMARKED) &&
02214 lastmarked == 0) {
02215 if (currentmarked == 1 && conf->users > 1) {
02216 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
02217 if (conf->users - 1 == 1) {
02218 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
02219 ast_waitstream(chan, "");
02220 } else {
02221 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
02222 ast_waitstream(chan, "");
02223 }
02224 }
02225 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
02226 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
02227 ast_waitstream(chan, "");
02228 }
02229
02230
02231 user->userflags = confflags;
02232
02233 if (confflags & CONFFLAG_WAITMARKED) {
02234 if (currentmarked == 0) {
02235 if (lastmarked != 0) {
02236 if (!(confflags & CONFFLAG_QUIET))
02237 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
02238 ast_waitstream(chan, "");
02239 if (confflags & CONFFLAG_MARKEDEXIT) {
02240 if (confflags & CONFFLAG_KICK_CONTINUE)
02241 ret = 0;
02242 break;
02243 } else {
02244 dahdic.confmode = DAHDI_CONF_CONF;
02245 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02246 ast_log(LOG_WARNING, "Error setting conference\n");
02247 close(fd);
02248 goto outrun;
02249 }
02250 }
02251 }
02252 if (!musiconhold && (confflags & CONFFLAG_MOH)) {
02253 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02254 musiconhold = 1;
02255 }
02256 } else if (currentmarked >= 1 && lastmarked == 0) {
02257
02258 timeout = 0;
02259 if (confflags & CONFFLAG_MONITOR)
02260 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
02261 else if (confflags & CONFFLAG_TALKER)
02262 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
02263 else
02264 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
02265 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02266 ast_log(LOG_WARNING, "Error setting conference\n");
02267 close(fd);
02268 goto outrun;
02269 }
02270 if (musiconhold && (confflags & CONFFLAG_MOH)) {
02271 ast_moh_stop(chan);
02272 musiconhold = 0;
02273 }
02274 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
02275 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
02276 ast_waitstream(chan, "");
02277 conf_play(chan, conf, ENTER);
02278 }
02279 }
02280 }
02281
02282
02283 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
02284 if (conf->users == 1) {
02285 if (!musiconhold) {
02286 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02287 musiconhold = 1;
02288 }
02289 } else {
02290 if (musiconhold) {
02291 ast_moh_stop(chan);
02292 musiconhold = 0;
02293 }
02294 }
02295 }
02296
02297
02298 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
02299 if (confflags & CONFFLAG_KICK_CONTINUE)
02300 ret = 0;
02301 else
02302 ret = -1;
02303 break;
02304 }
02305
02306
02307
02308
02309 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
02310 dahdic.confmode ^= DAHDI_CONF_TALKER;
02311 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02312 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
02313 ret = -1;
02314 break;
02315 }
02316
02317 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
02318 "Channel: %s\r\n"
02319 "Uniqueid: %s\r\n"
02320 "Meetme: %s\r\n"
02321 "Usernum: %i\r\n"
02322 "Status: on\r\n",
02323 chan->name, chan->uniqueid, conf->confno, user->user_no);
02324 }
02325
02326
02327 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
02328 dahdic.confmode |= DAHDI_CONF_TALKER;
02329 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02330 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
02331 ret = -1;
02332 break;
02333 }
02334
02335 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
02336 "Channel: %s\r\n"
02337 "Uniqueid: %s\r\n"
02338 "Meetme: %s\r\n"
02339 "Usernum: %i\r\n"
02340 "Status: off\r\n",
02341 chan->name, chan->uniqueid, conf->confno, user->user_no);
02342 }
02343
02344 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
02345 (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
02346 talkreq_manager = 1;
02347
02348 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
02349 "Channel: %s\r\n"
02350 "Uniqueid: %s\r\n"
02351 "Meetme: %s\r\n"
02352 "Usernum: %i\r\n"
02353 "Status: on\r\n",
02354 chan->name, chan->uniqueid, conf->confno, user->user_no);
02355 }
02356
02357
02358 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
02359 !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
02360 talkreq_manager = 0;
02361 manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest",
02362 "Channel: %s\r\n"
02363 "Uniqueid: %s\r\n"
02364 "Meetme: %s\r\n"
02365 "Usernum: %i\r\n"
02366 "Status: off\r\n",
02367 chan->name, chan->uniqueid, conf->confno, user->user_no);
02368 }
02369
02370
02371 if (user->adminflags & ADMINFLAG_KICKME) {
02372
02373 if (!(confflags & CONFFLAG_QUIET) &&
02374 !ast_streamfile(chan, "conf-kicked", chan->language)) {
02375 ast_waitstream(chan, "");
02376 }
02377 ret = 0;
02378 break;
02379 }
02380
02381
02382 if (ast_check_hangup(chan))
02383 break;
02384
02385 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
02386
02387 if (c) {
02388 char dtmfstr[2] = "";
02389
02390 if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
02391 if (using_pseudo) {
02392
02393 close(fd);
02394 using_pseudo = 0;
02395 }
02396 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
02397 retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
02398 user->dahdichannel = !retrydahdi;
02399 goto dahdiretry;
02400 }
02401 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
02402 f = ast_read_noaudio(c);
02403 else
02404 f = ast_read(c);
02405 if (!f)
02406 break;
02407 if (f->frametype == AST_FRAME_DTMF) {
02408 dtmfstr[0] = f->subclass;
02409 dtmfstr[1] = '\0';
02410 }
02411
02412 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
02413 if (user->talk.actual)
02414 ast_frame_adjust_volume(f, user->talk.actual);
02415
02416 if (confflags & (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER)) {
02417 int totalsilence;
02418
02419 if (user->talking == -1)
02420 user->talking = 0;
02421
02422 res = ast_dsp_silence(dsp, f, &totalsilence);
02423 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
02424 user->talking = 1;
02425 if (confflags & CONFFLAG_MONITORTALKER)
02426 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
02427 "Channel: %s\r\n"
02428 "Uniqueid: %s\r\n"
02429 "Meetme: %s\r\n"
02430 "Usernum: %d\r\n"
02431 "Status: on\r\n",
02432 chan->name, chan->uniqueid, conf->confno, user->user_no);
02433 }
02434 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
02435 user->talking = 0;
02436 if (confflags & CONFFLAG_MONITORTALKER)
02437 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
02438 "Channel: %s\r\n"
02439 "Uniqueid: %s\r\n"
02440 "Meetme: %s\r\n"
02441 "Usernum: %d\r\n"
02442 "Status: off\r\n",
02443 chan->name, chan->uniqueid, conf->confno, user->user_no);
02444 }
02445 }
02446 if (using_pseudo) {
02447
02448
02449
02450
02451
02452
02453
02454
02455
02456
02457
02458
02459 if (user->talking && !(confflags & CONFFLAG_OPTIMIZETALKER)) {
02460 careful_write(fd, f->data, f->datalen, 0);
02461 }
02462 }
02463 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
02464 if (confflags & CONFFLAG_PASS_DTMF)
02465 conf_queue_dtmf(conf, user, f);
02466 if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
02467 ast_log(LOG_WARNING, "Error setting conference\n");
02468 close(fd);
02469 ast_frfree(f);
02470 goto outrun;
02471 }
02472
02473
02474
02475
02476 if (!menu_active && user->talk.desired && !user->talk.actual)
02477 set_talk_volume(user, 0);
02478
02479 if (musiconhold) {
02480 ast_moh_stop(chan);
02481 }
02482 if ((confflags & CONFFLAG_ADMIN)) {
02483
02484 if (!menu_active) {
02485 menu_active = 1;
02486
02487 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
02488 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
02489 ast_stopstream(chan);
02490 } else
02491 dtmf = 0;
02492 } else
02493 dtmf = f->subclass;
02494 if (dtmf) {
02495 switch(dtmf) {
02496 case '1':
02497 menu_active = 0;
02498
02499
02500 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
02501 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02502 else
02503 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02504
02505 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02506 if (!ast_streamfile(chan, "conf-muted", chan->language))
02507 ast_waitstream(chan, "");
02508 } else {
02509 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
02510 ast_waitstream(chan, "");
02511 }
02512 break;
02513 case '2':
02514 menu_active = 0;
02515 if (conf->locked) {
02516 conf->locked = 0;
02517 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
02518 ast_waitstream(chan, "");
02519 } else {
02520 conf->locked = 1;
02521 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
02522 ast_waitstream(chan, "");
02523 }
02524 break;
02525 case '3':
02526 menu_active = 0;
02527 usr = AST_LIST_LAST(&conf->userlist);
02528 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
02529 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
02530 ast_waitstream(chan, "");
02531 } else
02532 usr->adminflags |= ADMINFLAG_KICKME;
02533 ast_stopstream(chan);
02534 break;
02535 case '4':
02536 tweak_listen_volume(user, VOL_DOWN);
02537 break;
02538 case '6':
02539 tweak_listen_volume(user, VOL_UP);
02540 break;
02541 case '7':
02542 tweak_talk_volume(user, VOL_DOWN);
02543 break;
02544 case '8':
02545 menu_active = 0;
02546 break;
02547 case '9':
02548 tweak_talk_volume(user, VOL_UP);
02549 break;
02550 default:
02551 menu_active = 0;
02552
02553 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
02554 ast_waitstream(chan, "");
02555 break;
02556 }
02557 }
02558 } else {
02559
02560 if (!menu_active) {
02561 menu_active = 1;
02562 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
02563 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
02564 ast_stopstream(chan);
02565 } else
02566 dtmf = 0;
02567 } else
02568 dtmf = f->subclass;
02569 if (dtmf) {
02570 switch(dtmf) {
02571 case '1':
02572 menu_active = 0;
02573
02574
02575 user->adminflags ^= ADMINFLAG_SELFMUTED;
02576
02577
02578 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02579 if (!ast_streamfile(chan, "conf-muted", chan->language))
02580 ast_waitstream(chan, "");
02581 } else {
02582 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
02583 ast_waitstream(chan, "");
02584 }
02585 break;
02586 case '2':
02587 menu_active = 0;
02588 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
02589 user->adminflags |= ADMINFLAG_T_REQUEST;
02590
02591 if (user->adminflags & ADMINFLAG_T_REQUEST)
02592 if (!ast_streamfile(chan, "beep", chan->language))
02593 ast_waitstream(chan, "");
02594 break;
02595 case '4':
02596 tweak_listen_volume(user, VOL_DOWN);
02597 break;
02598 case '6':
02599 tweak_listen_volume(user, VOL_UP);
02600 break;
02601 case '7':
02602 tweak_talk_volume(user, VOL_DOWN);
02603 break;
02604 case '8':
02605 menu_active = 0;
02606 break;
02607 case '9':
02608 tweak_talk_volume(user, VOL_UP);
02609 break;
02610 default:
02611 menu_active = 0;
02612 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
02613 ast_waitstream(chan, "");
02614 break;
02615 }
02616 }
02617 }
02618 if (musiconhold)
02619 conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02620
02621 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02622 ast_log(LOG_WARNING, "Error setting conference\n");
02623 close(fd);
02624 ast_frfree(f);
02625 goto outrun;
02626 }
02627
02628 conf_flush(fd, chan);
02629
02630 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
02631 if (confflags & CONFFLAG_PASS_DTMF)
02632 conf_queue_dtmf(conf, user, f);
02633
02634 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
02635 ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
02636 ret = 0;
02637 ast_frfree(f);
02638 break;
02639 } else {
02640 ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
02641 }
02642 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
02643 pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
02644
02645 if (confflags & CONFFLAG_PASS_DTMF)
02646 conf_queue_dtmf(conf, user, f);
02647 ret = 0;
02648 ast_frfree(f);
02649 break;
02650 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
02651 && confflags & CONFFLAG_PASS_DTMF) {
02652 conf_queue_dtmf(conf, user, f);
02653 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
02654 switch (f->subclass) {
02655 case AST_CONTROL_HOLD:
02656 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
02657 break;
02658 default:
02659 break;
02660 }
02661 } else if (f->frametype == AST_FRAME_NULL) {
02662
02663 } else {
02664 ast_debug(1,
02665 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
02666 chan->name, f->frametype, f->subclass);
02667 }
02668 ast_frfree(f);
02669 } else if (outfd > -1) {
02670 res = read(outfd, buf, CONF_SIZE);
02671 if (res > 0) {
02672 memset(&fr, 0, sizeof(fr));
02673 fr.frametype = AST_FRAME_VOICE;
02674 fr.subclass = AST_FORMAT_SLINEAR;
02675 fr.datalen = res;
02676 fr.samples = res / 2;
02677 fr.data = buf;
02678 fr.offset = AST_FRIENDLY_OFFSET;
02679 if (!user->listen.actual &&
02680 ((confflags & CONFFLAG_MONITOR) ||
02681 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
02682 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
02683 )) {
02684 int index;
02685 for (index = 0; index < AST_FRAME_BITS; index++)
02686 if (chan->rawwriteformat & (1 << index))
02687 break;
02688 if (index >= AST_FRAME_BITS)
02689 goto bailoutandtrynormal;
02690 ast_mutex_lock(&conf->listenlock);
02691 if (!conf->transframe[index]) {
02692 if (conf->origframe) {
02693 if (!conf->transpath[index])
02694 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
02695 if (conf->transpath[index]) {
02696 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
02697 if (!conf->transframe[index])
02698 conf->transframe[index] = &ast_null_frame;
02699 }
02700 }
02701 }
02702 if (conf->transframe[index]) {
02703 if ((conf->transframe[index]->frametype != AST_FRAME_NULL) &&
02704 can_write(chan, confflags)) {
02705 struct ast_frame *cur;
02706
02707
02708
02709
02710 for (cur = conf->transframe[index]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
02711 if (ast_write(chan, cur)) {
02712 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
02713 break;
02714 }
02715 }
02716 }
02717 } else {
02718 ast_mutex_unlock(&conf->listenlock);
02719 goto bailoutandtrynormal;
02720 }
02721 ast_mutex_unlock(&conf->listenlock);
02722 } else {
02723 bailoutandtrynormal:
02724 if (user->listen.actual)
02725 ast_frame_adjust_volume(&fr, user->listen.actual);
02726 if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
02727 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
02728 }
02729 }
02730 } else
02731 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
02732 }
02733 lastmarked = currentmarked;
02734 }
02735 }
02736
02737 if (musiconhold)
02738 ast_moh_stop(chan);
02739
02740 if (using_pseudo)
02741 close(fd);
02742 else {
02743
02744 dahdic.chan = 0;
02745 dahdic.confno = 0;
02746 dahdic.confmode = 0;
02747 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02748 ast_log(LOG_WARNING, "Error setting conference\n");
02749 }
02750 }
02751
02752 reset_volumes(user);
02753
02754 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
02755 conf_play(chan, conf, LEAVE);
02756
02757 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
02758 struct announce_listitem *item;
02759 if (!(item = ao2_alloc(sizeof(*item), NULL)))
02760 return -1;
02761 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
02762 ast_copy_string(item->language, chan->language, sizeof(item->language));
02763 item->confchan = conf->chan;
02764 item->confusers = conf->users;
02765 item->announcetype = CONF_HASLEFT;
02766 ast_mutex_lock(&conf->announcelistlock);
02767 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
02768 ast_cond_signal(&conf->announcelist_addition);
02769 ast_mutex_unlock(&conf->announcelistlock);
02770 } else if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
02771
02772 ast_filedelete(user->namerecloc, NULL);
02773 }
02774
02775 outrun:
02776 AST_LIST_LOCK(&confs);
02777
02778 if (dsp)
02779 ast_dsp_free(dsp);
02780
02781 if (user->user_no) {
02782 now = ast_tvnow();
02783 hr = (now.tv_sec - user->jointime) / 3600;
02784 min = ((now.tv_sec - user->jointime) % 3600) / 60;
02785 sec = (now.tv_sec - user->jointime) % 60;
02786
02787 if (sent_event) {
02788 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
02789 "Channel: %s\r\n"
02790 "Uniqueid: %s\r\n"
02791 "Meetme: %s\r\n"
02792 "Usernum: %d\r\n"
02793 "CallerIDNum: %s\r\n"
02794 "CallerIDName: %s\r\n"
02795 "Duration: %ld\r\n",
02796 chan->name, chan->uniqueid, conf->confno,
02797 user->user_no,
02798 S_OR(user->chan->cid.cid_num, "<unknown>"),
02799 S_OR(user->chan->cid.cid_name, "<unknown>"),
02800 (long)(now.tv_sec - user->jointime));
02801 }
02802
02803 if (setusercount) {
02804 conf->users--;
02805 if (rt_log_members) {
02806
02807 snprintf(members, sizeof(members), "%d", conf->users);
02808 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
02809 }
02810 if (confflags & CONFFLAG_MARKEDUSER)
02811 conf->markedusers--;
02812 }
02813
02814 AST_LIST_REMOVE(&conf->userlist, user, list);
02815
02816
02817 if (!conf->users)
02818 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
02819
02820
02821 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
02822 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
02823 }
02824 ast_free(user);
02825 AST_LIST_UNLOCK(&confs);
02826
02827 return ret;
02828 }
02829
02830 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
02831 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags,
02832 char *optargs[], int *too_early)
02833 {
02834 struct ast_variable *var;
02835 struct ast_conference *cnf;
02836
02837 *too_early = 0;
02838
02839
02840 AST_LIST_LOCK(&confs);
02841 AST_LIST_TRAVERSE(&confs, cnf, list) {
02842 if (!strcmp(confno, cnf->confno))
02843 break;
02844 }
02845 if (cnf) {
02846 cnf->refcount += refcount;
02847 }
02848 AST_LIST_UNLOCK(&confs);
02849
02850 if (!cnf) {
02851 char *pin = NULL, *pinadmin = NULL;
02852 int maxusers = 0;
02853 struct timeval now;
02854 char currenttime[19] = "";
02855 char eatime[19] = "";
02856 char useropts[32] = "";
02857 char adminopts[32] = "";
02858 struct ast_tm tm, etm;
02859 struct timeval endtime = { .tv_sec = 0 };
02860
02861 if (rt_schedule) {
02862 now = ast_tvnow();
02863
02864 ast_localtime(&now, &tm, NULL);
02865 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02866
02867 ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
02868
02869 var = ast_load_realtime("meetme", "confno",
02870 confno, "starttime <= ", currenttime, "endtime >= ",
02871 currenttime, NULL);
02872
02873 if (!var && fuzzystart) {
02874 now = ast_tvnow();
02875 now.tv_sec += fuzzystart;
02876
02877 ast_localtime(&now, &tm, NULL);
02878 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02879 var = ast_load_realtime("meetme", "confno",
02880 confno, "starttime <= ", currenttime, "endtime >= ",
02881 currenttime, NULL);
02882 }
02883
02884 if (!var && earlyalert) {
02885 now = ast_tvnow();
02886 now.tv_sec += earlyalert;
02887 ast_localtime(&now, &etm, NULL);
02888 ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
02889 var = ast_load_realtime("meetme", "confno",
02890 confno, "starttime <= ", eatime, "endtime >= ",
02891 currenttime, NULL);
02892 if (var)
02893 *too_early = 1;
02894 }
02895
02896 } else
02897 var = ast_load_realtime("meetme", "confno", confno, NULL);
02898
02899 if (!var)
02900 return NULL;
02901
02902 if (rt_schedule && *too_early) {
02903
02904 if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
02905 ast_waitstream(chan, "");
02906 ast_variables_destroy(var);
02907 return NULL;
02908 }
02909
02910 while (var) {
02911 if (!strcasecmp(var->name, "pin")) {
02912 pin = ast_strdupa(var->value);
02913 } else if (!strcasecmp(var->name, "adminpin")) {
02914 pinadmin = ast_strdupa(var->value);
02915 } else if (!strcasecmp(var->name, "opts")) {
02916 ast_copy_string(useropts, var->value, sizeof(useropts));
02917 } else if (!strcasecmp(var->name, "maxusers")) {
02918 maxusers = atoi(var->value);
02919 } else if (!strcasecmp(var->name, "adminopts")) {
02920 ast_copy_string(adminopts, var->value, sizeof(adminopts));
02921 } else if (!strcasecmp(var->name, "endtime")) {
02922 union {
02923 struct ast_tm atm;
02924 struct tm tm;
02925 } t = { { 0, }, };
02926 strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
02927
02928
02929
02930
02931 t.tm.tm_isdst = -1;
02932 endtime = ast_mktime(&t.atm, NULL);
02933 }
02934
02935 var = var->next;
02936 }
02937
02938 ast_variables_destroy(var);
02939
02940 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
02941
02942 if (cnf) {
02943 cnf->maxusers = maxusers;
02944 cnf->endalert = endalert;
02945 cnf->endtime = endtime.tv_sec;
02946 }
02947 }
02948
02949 if (cnf) {
02950 if (confflags && !cnf->chan &&
02951 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
02952 ast_test_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
02953 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
02954 ast_clear_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
02955 }
02956
02957 if (confflags && !cnf->chan &&
02958 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
02959 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
02960 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
02961 }
02962 }
02963
02964 return cnf;
02965 }
02966
02967
02968 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
02969 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
02970 {
02971 struct ast_config *cfg;
02972 struct ast_variable *var;
02973 struct ast_flags config_flags = { 0 };
02974 struct ast_conference *cnf;
02975 char *parse;
02976 AST_DECLARE_APP_ARGS(args,
02977 AST_APP_ARG(confno);
02978 AST_APP_ARG(pin);
02979 AST_APP_ARG(pinadmin);
02980 );
02981
02982
02983 ast_debug(1, "The requested confno is '%s'?\n", confno);
02984 AST_LIST_LOCK(&confs);
02985 AST_LIST_TRAVERSE(&confs, cnf, list) {
02986 ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
02987 if (!strcmp(confno, cnf->confno))
02988 break;
02989 }
02990 if (cnf) {
02991 cnf->refcount += refcount;
02992 }
02993 AST_LIST_UNLOCK(&confs);
02994
02995 if (!cnf) {
02996 if (dynamic) {
02997
02998 ast_debug(1, "Building dynamic conference '%s'\n", confno);
02999 if (dynamic_pin) {
03000 if (dynamic_pin[0] == 'q') {
03001
03002 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
03003 return NULL;
03004 }
03005 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
03006 } else {
03007 cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
03008 }
03009 } else {
03010
03011 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
03012 if (!cfg) {
03013 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
03014 return NULL;
03015 }
03016 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
03017 if (strcasecmp(var->name, "conf"))
03018 continue;
03019
03020 if (!(parse = ast_strdupa(var->value)))
03021 return NULL;
03022
03023 AST_STANDARD_APP_ARGS(args, parse);
03024 ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
03025 if (!strcasecmp(args.confno, confno)) {
03026
03027 cnf = build_conf(args.confno,
03028 S_OR(args.pin, ""),
03029 S_OR(args.pinadmin, ""),
03030 make, dynamic, refcount, chan);
03031 break;
03032 }
03033 }
03034 if (!var) {
03035 ast_debug(1, "%s isn't a valid conference\n", confno);
03036 }
03037 ast_config_destroy(cfg);
03038 }
03039 } else if (dynamic_pin) {
03040
03041
03042
03043 if (dynamic_pin[0] == 'q')
03044 dynamic_pin[0] = '\0';
03045 }
03046
03047 if (cnf) {
03048 if (confflags && !cnf->chan &&
03049 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
03050 ast_test_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
03051 ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
03052 ast_clear_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
03053 }
03054
03055 if (confflags && !cnf->chan &&
03056 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
03057 ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
03058 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
03059 }
03060 }
03061
03062 return cnf;
03063 }
03064
03065
03066 static int count_exec(struct ast_channel *chan, void *data)
03067 {
03068 int res = 0;
03069 struct ast_conference *conf;
03070 int count;
03071 char *localdata;
03072 char val[80] = "0";
03073 AST_DECLARE_APP_ARGS(args,
03074 AST_APP_ARG(confno);
03075 AST_APP_ARG(varname);
03076 );
03077
03078 if (ast_strlen_zero(data)) {
03079 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
03080 return -1;
03081 }
03082
03083 if (!(localdata = ast_strdupa(data)))
03084 return -1;
03085
03086 AST_STANDARD_APP_ARGS(args, localdata);
03087
03088 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
03089
03090 if (conf) {
03091 count = conf->users;
03092 dispose_conf(conf);
03093 conf = NULL;
03094 } else
03095 count = 0;
03096
03097 if (!ast_strlen_zero(args.varname)) {
03098
03099 snprintf(val, sizeof(val), "%d", count);
03100 pbx_builtin_setvar_helper(chan, args.varname, val);
03101 } else {
03102 if (chan->_state != AST_STATE_UP)
03103 ast_answer(chan);
03104 res = ast_say_number(chan, count, "", chan->language, (char *) NULL);
03105 }
03106
03107 return res;
03108 }
03109
03110
03111 static int conf_exec(struct ast_channel *chan, void *data)
03112 {
03113 int res = -1;
03114 char confno[MAX_CONFNUM] = "";
03115 int allowretry = 0;
03116 int retrycnt = 0;
03117 struct ast_conference *cnf = NULL;
03118 struct ast_flags confflags = {0}, config_flags = { 0 };
03119 int dynamic = 0;
03120 int empty = 0, empty_no_pin = 0;
03121 int always_prompt = 0;
03122 char *notdata, *info, the_pin[MAX_PIN] = "";
03123 AST_DECLARE_APP_ARGS(args,
03124 AST_APP_ARG(confno);
03125 AST_APP_ARG(options);
03126 AST_APP_ARG(pin);
03127 );
03128 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
03129
03130 if (ast_strlen_zero(data)) {
03131 allowretry = 1;
03132 notdata = "";
03133 } else {
03134 notdata = data;
03135 }
03136
03137 if (chan->_state != AST_STATE_UP)
03138 ast_answer(chan);
03139
03140 info = ast_strdupa(notdata);
03141
03142 AST_STANDARD_APP_ARGS(args, info);
03143
03144 if (args.confno) {
03145 ast_copy_string(confno, args.confno, sizeof(confno));
03146 if (ast_strlen_zero(confno)) {
03147 allowretry = 1;
03148 }
03149 }
03150
03151 if (args.pin)
03152 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
03153
03154 if (args.options) {
03155 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
03156 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
03157 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
03158 strcpy(the_pin, "q");
03159
03160 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
03161 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
03162 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
03163 }
03164
03165 do {
03166 if (retrycnt > 3)
03167 allowretry = 0;
03168 if (empty) {
03169 int i;
03170 struct ast_config *cfg;
03171 struct ast_variable *var;
03172 int confno_int;
03173
03174
03175 if ((empty_no_pin) || (!dynamic)) {
03176 cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
03177 if (cfg) {
03178 var = ast_variable_browse(cfg, "rooms");
03179 while (var) {
03180 if (!strcasecmp(var->name, "conf")) {
03181 char *stringp = ast_strdupa(var->value);
03182 if (stringp) {
03183 char *confno_tmp = strsep(&stringp, "|,");
03184 int found = 0;
03185 if (!dynamic) {
03186
03187 AST_LIST_LOCK(&confs);
03188 AST_LIST_TRAVERSE(&confs, cnf, list) {
03189 if (!strcmp(confno_tmp, cnf->confno)) {
03190
03191 found = 1;
03192 break;
03193 }
03194 }
03195 AST_LIST_UNLOCK(&confs);
03196 if (!found) {
03197
03198 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
03199
03200
03201
03202
03203 ast_copy_string(confno, confno_tmp, sizeof(confno));
03204 break;
03205
03206 }
03207 }
03208 }
03209 }
03210 }
03211 var = var->next;
03212 }
03213 ast_config_destroy(cfg);
03214 }
03215 }
03216
03217
03218 if (ast_strlen_zero(confno) && dynamic) {
03219 AST_LIST_LOCK(&confs);
03220 for (i = 0; i < ARRAY_LEN(conf_map); i++) {
03221 if (!conf_map[i]) {
03222 snprintf(confno, sizeof(confno), "%d", i);
03223 conf_map[i] = 1;
03224 break;
03225 }
03226 }
03227 AST_LIST_UNLOCK(&confs);
03228 }
03229
03230
03231 if (ast_strlen_zero(confno)) {
03232 res = ast_streamfile(chan, "conf-noempty", chan->language);
03233 if (!res)
03234 ast_waitstream(chan, "");
03235 } else {
03236 if (sscanf(confno, "%d", &confno_int) == 1) {
03237 if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
03238 res = ast_streamfile(chan, "conf-enteringno", chan->language);
03239 if (!res) {
03240 ast_waitstream(chan, "");
03241 res = ast_say_digits(chan, confno_int, "", chan->language);
03242 }
03243 }
03244 } else {
03245 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
03246 }
03247 }
03248 }
03249
03250 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
03251
03252 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
03253 if (res < 0) {
03254
03255 confno[0] = '\0';
03256 allowretry = 0;
03257 break;
03258 }
03259 }
03260 if (!ast_strlen_zero(confno)) {
03261
03262 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
03263 sizeof(the_pin), 1, &confflags);
03264 if (!cnf) {
03265 int too_early = 0;
03266 cnf = find_conf_realtime(chan, confno, 1, dynamic,
03267 the_pin, sizeof(the_pin), 1, &confflags, optargs, &too_early);
03268 if (rt_schedule && too_early)
03269 allowretry = 0;
03270 }
03271
03272 if (!cnf) {
03273 if (allowretry) {
03274 confno[0] = '\0';
03275 res = ast_streamfile(chan, "conf-invalid", chan->language);
03276 if (!res)
03277 ast_waitstream(chan, "");
03278 res = -1;
03279 }
03280 } else {
03281 if ((!ast_strlen_zero(cnf->pin) &&
03282 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
03283 (!ast_strlen_zero(cnf->pinadmin) &&
03284 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
03285 char pin[MAX_PIN] = "";
03286 int j;
03287
03288
03289 for (j = 0; j < 3; j++) {
03290 if (*the_pin && (always_prompt == 0)) {
03291 ast_copy_string(pin, the_pin, sizeof(pin));
03292 res = 0;
03293 } else {
03294
03295 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
03296 }
03297 if (res >= 0) {
03298 if (!strcasecmp(pin, cnf->pin) ||
03299 (!ast_strlen_zero(cnf->pinadmin) &&
03300 !strcasecmp(pin, cnf->pinadmin))) {
03301
03302 allowretry = 0;
03303 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
03304 ast_set_flag(&confflags, CONFFLAG_ADMIN);
03305
03306 res = conf_run(chan, cnf, confflags.flags, optargs);
03307 break;
03308 } else {
03309
03310 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
03311 res = ast_waitstream(chan, AST_DIGIT_ANY);
03312 ast_stopstream(chan);
03313 }
03314 else {
03315 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
03316 break;
03317 }
03318 if (res < 0)
03319 break;
03320 pin[0] = res;
03321 pin[1] = '\0';
03322 res = -1;
03323 if (allowretry)
03324 confno[0] = '\0';
03325 }
03326 } else {
03327
03328 res = -1;
03329 allowretry = 0;
03330
03331 break;
03332 }
03333
03334
03335 if (*the_pin && (always_prompt == 0)) {
03336 break;
03337 }
03338 }
03339 } else {
03340
03341 allowretry = 0;
03342
03343
03344 res = conf_run(chan, cnf, confflags.flags, optargs);
03345 }
03346 dispose_conf(cnf);
03347 cnf = NULL;
03348 }
03349 }
03350 } while (allowretry);
03351
03352 if (cnf)
03353 dispose_conf(cnf);
03354
03355 return res;
03356 }
03357
03358 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
03359 {
03360 struct ast_conf_user *user = NULL;
03361 int cid;
03362
03363 sscanf(callerident, "%i", &cid);
03364 if (conf && callerident) {
03365 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
03366 if (cid == user->user_no)
03367 return user;
03368 }
03369 }
03370 return NULL;
03371 }
03372
03373
03374
03375 static int admin_exec(struct ast_channel *chan, void *data) {
03376 char *params;
03377 struct ast_conference *cnf;
03378 struct ast_conf_user *user = NULL;
03379 AST_DECLARE_APP_ARGS(args,
03380 AST_APP_ARG(confno);
03381 AST_APP_ARG(command);
03382 AST_APP_ARG(user);
03383 );
03384
03385 if (ast_strlen_zero(data)) {
03386 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
03387 return -1;
03388 }
03389
03390 params = ast_strdupa(data);
03391 AST_STANDARD_APP_ARGS(args, params);
03392
03393 if (!args.command) {
03394 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
03395 return -1;
03396 }
03397
03398 AST_LIST_LOCK(&confs);
03399 AST_LIST_TRAVERSE(&confs, cnf, list) {
03400 if (!strcmp(cnf->confno, args.confno))
03401 break;
03402 }
03403
03404 if (!cnf) {
03405 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
03406 AST_LIST_UNLOCK(&confs);
03407 return 0;
03408 }
03409
03410 ast_atomic_fetchadd_int(&cnf->refcount, 1);
03411
03412 if (args.user)
03413 user = find_user(cnf, args.user);
03414
03415 switch (*args.command) {
03416 case 76:
03417 cnf->locked = 1;
03418 break;
03419 case 108:
03420 cnf->locked = 0;
03421 break;
03422 case 75:
03423 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03424 user->adminflags |= ADMINFLAG_KICKME;
03425 break;
03426 case 101:
03427 user = AST_LIST_LAST(&cnf->userlist);
03428 if (!(user->userflags & CONFFLAG_ADMIN))
03429 user->adminflags |= ADMINFLAG_KICKME;
03430 else
03431 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
03432 break;
03433 case 77:
03434 if (user) {
03435 user->adminflags |= ADMINFLAG_MUTED;
03436 } else
03437 ast_log(LOG_NOTICE, "Specified User not found!\n");
03438 break;
03439 case 78:
03440 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
03441 if (!(user->userflags & CONFFLAG_ADMIN))
03442 user->adminflags |= ADMINFLAG_MUTED;
03443 }
03444 break;
03445 case 109:
03446 if (user) {
03447 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
03448 } else
03449 ast_log(LOG_NOTICE, "Specified User not found!\n");
03450 break;
03451 case 110:
03452 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03453 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
03454 break;
03455 case 107:
03456 if (user)
03457 user->adminflags |= ADMINFLAG_KICKME;
03458 else
03459 ast_log(LOG_NOTICE, "Specified User not found!\n");
03460 break;
03461 case 118:
03462 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03463 tweak_listen_volume(user, VOL_DOWN);
03464 break;
03465 case 86:
03466 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03467 tweak_listen_volume(user, VOL_UP);
03468 break;
03469 case 115:
03470 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03471 tweak_talk_volume(user, VOL_DOWN);
03472 break;
03473 case 83:
03474 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03475 tweak_talk_volume(user, VOL_UP);
03476 break;
03477 case 82:
03478 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03479 reset_volumes(user);
03480 break;
03481 case 114:
03482 if (user)
03483 reset_volumes(user);
03484 else
03485 ast_log(LOG_NOTICE, "Specified User not found!\n");
03486 break;
03487 case 85:
03488 if (user)
03489 tweak_listen_volume(user, VOL_UP);
03490 else
03491 ast_log(LOG_NOTICE, "Specified User not found!\n");
03492 break;
03493 case 117:
03494 if (user)
03495 tweak_listen_volume(user, VOL_DOWN);
03496 else
03497 ast_log(LOG_NOTICE, "Specified User not found!\n");
03498 break;
03499 case 84:
03500 if (user)
03501 tweak_talk_volume(user, VOL_UP);
03502 else
03503 ast_log(LOG_NOTICE, "Specified User not found!\n");
03504 break;
03505 case 116:
03506 if (user)
03507 tweak_talk_volume(user, VOL_DOWN);
03508 else
03509 ast_log(LOG_NOTICE, "Specified User not found!\n");
03510 break;
03511 }
03512
03513 AST_LIST_UNLOCK(&confs);
03514
03515 dispose_conf(cnf);
03516
03517 return 0;
03518 }
03519
03520
03521
03522 static int channel_admin_exec(struct ast_channel *chan, void *data) {
03523 char *params;
03524 struct ast_conference *conf = NULL;
03525 struct ast_conf_user *user = NULL;
03526 AST_DECLARE_APP_ARGS(args,
03527 AST_APP_ARG(channel);
03528 AST_APP_ARG(command);
03529 );
03530
03531 if (ast_strlen_zero(data)) {
03532 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
03533 return -1;
03534 }
03535
03536 params = ast_strdupa(data);
03537 AST_STANDARD_APP_ARGS(args, params);
03538
03539 if (!args.channel) {
03540 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
03541 return -1;
03542 }
03543
03544 if (!args.command) {
03545 ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
03546 return -1;
03547 }
03548
03549 AST_LIST_LOCK(&confs);
03550 AST_LIST_TRAVERSE(&confs, conf, list) {
03551 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
03552 if (!strcmp(user->chan->name, args.channel))
03553 break;
03554 }
03555 }
03556
03557 if (!user) {
03558 ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
03559 AST_LIST_UNLOCK(&confs);
03560 return 0;
03561 }
03562
03563
03564 switch (*args.command) {
03565 case 77:
03566 user->adminflags |= ADMINFLAG_MUTED;
03567 break;
03568 case 109:
03569 user->adminflags &= ~ADMINFLAG_MUTED;
03570 break;
03571 case 107:
03572 user->adminflags |= ADMINFLAG_KICKME;
03573 break;
03574 default:
03575 ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
03576 break;
03577 }
03578
03579 AST_LIST_UNLOCK(&confs);
03580
03581 return 0;
03582 }
03583
03584 static int meetmemute(struct mansession *s, const struct message *m, int mute)
03585 {
03586 struct ast_conference *conf;
03587 struct ast_conf_user *user;
03588 const char *confid = astman_get_header(m, "Meetme");
03589 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
03590 int userno;
03591
03592 if (ast_strlen_zero(confid)) {
03593 astman_send_error(s, m, "Meetme conference not specified");
03594 return 0;
03595 }
03596
03597 if (ast_strlen_zero(userid)) {
03598 astman_send_error(s, m, "Meetme user number not specified");
03599 return 0;
03600 }
03601
03602 userno = strtoul(userid, &userid, 10);
03603
03604 if (*userid) {
03605 astman_send_error(s, m, "Invalid user number");
03606 return 0;
03607 }
03608
03609
03610 AST_LIST_LOCK(&confs);
03611 AST_LIST_TRAVERSE(&confs, conf, list) {
03612 if (!strcmp(confid, conf->confno))
03613 break;
03614 }
03615
03616 if (!conf) {
03617 AST_LIST_UNLOCK(&confs);
03618 astman_send_error(s, m, "Meetme conference does not exist");
03619 return 0;
03620 }
03621
03622 AST_LIST_TRAVERSE(&conf->userlist, user, list)
03623 if (user->user_no == userno)
03624 break;
03625
03626 if (!user) {
03627 AST_LIST_UNLOCK(&confs);
03628 astman_send_error(s, m, "User number not found");
03629 return 0;
03630 }
03631
03632 if (mute)
03633 user->adminflags |= ADMINFLAG_MUTED;
03634 else
03635 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
03636
03637 AST_LIST_UNLOCK(&confs);
03638
03639 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);
03640
03641 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
03642 return 0;
03643 }
03644
03645 static int action_meetmemute(struct mansession *s, const struct message *m)
03646 {
03647 return meetmemute(s, m, 1);
03648 }
03649
03650 static int action_meetmeunmute(struct mansession *s, const struct message *m)
03651 {
03652 return meetmemute(s, m, 0);
03653 }
03654
03655 static char mandescr_meetmelist[] =
03656 "Description: Lists all users in a particular MeetMe conference.\n"
03657 "MeetmeList will follow as separate events, followed by a final event called\n"
03658 "MeetmeListComplete.\n"
03659 "Variables:\n"
03660 " *ActionId: <id>\n"
03661 " *Conference: <confno>\n";
03662
03663 static int action_meetmelist(struct mansession *s, const struct message *m)
03664 {
03665 const char *actionid = astman_get_header(m, "ActionID");
03666 const char *conference = astman_get_header(m, "Conference");
03667 char idText[80] = "";
03668 struct ast_conference *cnf;
03669 struct ast_conf_user *user;
03670 int total = 0;
03671
03672 if (!ast_strlen_zero(actionid))
03673 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
03674
03675 if (AST_LIST_EMPTY(&confs)) {
03676 astman_send_error(s, m, "No active conferences.");
03677 return 0;
03678 }
03679
03680 astman_send_listack(s, m, "Meetme user list will follow", "start");
03681
03682
03683 AST_LIST_LOCK(&confs);
03684 AST_LIST_TRAVERSE(&confs, cnf, list) {
03685
03686 if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
03687 continue;
03688
03689
03690 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
03691 total++;
03692 astman_append(s,
03693 "Event: MeetmeList\r\n"
03694 "%s"
03695 "Conference: %s\r\n"
03696 "UserNumber: %d\r\n"
03697 "CallerIDNum: %s\r\n"
03698 "CallerIDName: %s\r\n"
03699 "Channel: %s\r\n"
03700 "Admin: %s\r\n"
03701 "Role: %s\r\n"
03702 "MarkedUser: %s\r\n"
03703 "Muted: %s\r\n"
03704 "Talking: %s\r\n"
03705 "\r\n",
03706 idText,
03707 cnf->confno,
03708 user->user_no,
03709 S_OR(user->chan->cid.cid_num, "<unknown>"),
03710 S_OR(user->chan->cid.cid_name, "<no name>"),
03711 user->chan->name,
03712 user->userflags & CONFFLAG_ADMIN ? "Yes" : "No",
03713 user->userflags & CONFFLAG_MONITOR ? "Listen only" : user->userflags & CONFFLAG_TALKER ? "Talk only" : "Talk and listen",
03714 user->userflags & CONFFLAG_MARKEDUSER ? "Yes" : "No",
03715 user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
03716 user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
03717 }
03718 }
03719 AST_LIST_UNLOCK(&confs);
03720
03721 astman_append(s,
03722 "Event: MeetmeListComplete\r\n"
03723 "EventList: Complete\r\n"
03724 "ListItems: %d\r\n"
03725 "%s"
03726 "\r\n", total, idText);
03727 return 0;
03728 }
03729
03730 static void *recordthread(void *args)
03731 {
03732 struct ast_conference *cnf = args;
03733 struct ast_frame *f = NULL;
03734 int flags;
03735 struct ast_filestream *s = NULL;
03736 int res = 0;
03737 int x;
03738 const char *oldrecordingfilename = NULL;
03739
03740 if (!cnf || !cnf->lchan) {
03741 pthread_exit(0);
03742 }
03743
03744 ast_stopstream(cnf->lchan);
03745 flags = O_CREAT | O_TRUNC | O_WRONLY;
03746
03747
03748 cnf->recording = MEETME_RECORD_ACTIVE;
03749 while (ast_waitfor(cnf->lchan, -1) > -1) {
03750 if (cnf->recording == MEETME_RECORD_TERMINATE) {
03751 AST_LIST_LOCK(&confs);
03752 AST_LIST_UNLOCK(&confs);
03753 break;
03754 }
03755 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
03756 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
03757 oldrecordingfilename = cnf->recordingfilename;
03758 }
03759
03760 f = ast_read(cnf->lchan);
03761 if (!f) {
03762 res = -1;
03763 break;
03764 }
03765 if (f->frametype == AST_FRAME_VOICE) {
03766 ast_mutex_lock(&cnf->listenlock);
03767 for (x = 0; x < AST_FRAME_BITS; x++) {
03768
03769 if (cnf->transframe[x]) {
03770 ast_frfree(cnf->transframe[x]);
03771 cnf->transframe[x] = NULL;
03772 }
03773 }
03774 if (cnf->origframe)
03775 ast_frfree(cnf->origframe);
03776 cnf->origframe = ast_frdup(f);
03777 ast_mutex_unlock(&cnf->listenlock);
03778 if (s)
03779 res = ast_writestream(s, f);
03780 if (res) {
03781 ast_frfree(f);
03782 break;
03783 }
03784 }
03785 ast_frfree(f);
03786 }
03787 cnf->recording = MEETME_RECORD_OFF;
03788 if (s)
03789 ast_closestream(s);
03790
03791 pthread_exit(0);
03792 }
03793
03794
03795 static enum ast_device_state meetmestate(const char *data)
03796 {
03797 struct ast_conference *conf;
03798
03799
03800 AST_LIST_LOCK(&confs);
03801 AST_LIST_TRAVERSE(&confs, conf, list) {
03802 if (!strcmp(data, conf->confno))
03803 break;
03804 }
03805 AST_LIST_UNLOCK(&confs);
03806 if (!conf)
03807 return AST_DEVICE_INVALID;
03808
03809
03810
03811 if (!conf->users)
03812 return AST_DEVICE_NOT_INUSE;
03813
03814 return AST_DEVICE_INUSE;
03815 }
03816
03817 static void load_config_meetme(void)
03818 {
03819 struct ast_config *cfg;
03820 struct ast_flags config_flags = { 0 };
03821 const char *val;
03822
03823 if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags)))
03824 return;
03825
03826 audio_buffers = DEFAULT_AUDIO_BUFFERS;
03827
03828
03829 rt_schedule = 0;
03830 fuzzystart = 0;
03831 earlyalert = 0;
03832 endalert = 0;
03833
03834
03835 rt_log_members = 1;
03836
03837 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
03838 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
03839 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
03840 audio_buffers = DEFAULT_AUDIO_BUFFERS;
03841 } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
03842 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
03843 DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
03844 audio_buffers = DEFAULT_AUDIO_BUFFERS;
03845 }
03846 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
03847 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
03848 }
03849
03850 if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
03851 rt_schedule = ast_true(val);
03852 if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
03853 rt_log_members = ast_true(val);
03854 if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
03855 if ((sscanf(val, "%d", &fuzzystart) != 1)) {
03856 ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
03857 fuzzystart = 0;
03858 }
03859 }
03860 if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
03861 if ((sscanf(val, "%d", &earlyalert) != 1)) {
03862 ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
03863 earlyalert = 0;
03864 }
03865 }
03866 if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
03867 if ((sscanf(val, "%d", &endalert) != 1)) {
03868 ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
03869 endalert = 0;
03870 }
03871 }
03872
03873 ast_config_destroy(cfg);
03874 }
03875
03876
03877
03878
03879 static struct sla_trunk *sla_find_trunk(const char *name)
03880 {
03881 struct sla_trunk *trunk = NULL;
03882
03883 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
03884 if (!strcasecmp(trunk->name, name))
03885 break;
03886 }
03887
03888 return trunk;
03889 }
03890
03891
03892
03893
03894 static struct sla_station *sla_find_station(const char *name)
03895 {
03896 struct sla_station *station = NULL;
03897
03898 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
03899 if (!strcasecmp(station->name, name))
03900 break;
03901 }
03902
03903 return station;
03904 }
03905
03906 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
03907 const struct sla_station *station)
03908 {
03909 struct sla_station_ref *station_ref;
03910 struct sla_trunk_ref *trunk_ref;
03911
03912
03913 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
03914 AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
03915 if (trunk_ref->trunk != trunk || station_ref->station == station)
03916 continue;
03917 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
03918 station_ref->station->hold_access == SLA_HOLD_PRIVATE)
03919 return 1;
03920 return 0;
03921 }
03922 }
03923
03924 return 0;
03925 }
03926
03927
03928
03929
03930
03931
03932
03933
03934 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
03935 const char *name)
03936 {
03937 struct sla_trunk_ref *trunk_ref = NULL;
03938
03939 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
03940 if (strcasecmp(trunk_ref->trunk->name, name))
03941 continue;
03942
03943 if ( (trunk_ref->trunk->barge_disabled
03944 && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
03945 (trunk_ref->trunk->hold_stations
03946 && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
03947 && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
03948 sla_check_station_hold_access(trunk_ref->trunk, station) )
03949 {
03950 trunk_ref = NULL;
03951 }
03952
03953 break;
03954 }
03955
03956 return trunk_ref;
03957 }
03958
03959 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
03960 {
03961 struct sla_station_ref *station_ref;
03962
03963 if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
03964 return NULL;
03965
03966 station_ref->station = station;
03967
03968 return station_ref;
03969 }
03970
03971 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
03972 {
03973 struct sla_ringing_station *ringing_station;
03974
03975 if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
03976 return NULL;
03977
03978 ringing_station->station = station;
03979 ringing_station->ring_begin = ast_tvnow();
03980
03981 return ringing_station;
03982 }
03983
03984 static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
03985 {
03986 switch (state) {
03987 case SLA_TRUNK_STATE_IDLE:
03988 return AST_DEVICE_NOT_INUSE;
03989 case SLA_TRUNK_STATE_RINGING:
03990 return AST_DEVICE_RINGING;
03991 case SLA_TRUNK_STATE_UP:
03992 return AST_DEVICE_INUSE;
03993 case SLA_TRUNK_STATE_ONHOLD:
03994 case SLA_TRUNK_STATE_ONHOLD_BYME:
03995 return AST_DEVICE_ONHOLD;
03996 }
03997
03998 return AST_DEVICE_UNKNOWN;
03999 }
04000
04001 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
04002 enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
04003 {
04004 struct sla_station *station;
04005 struct sla_trunk_ref *trunk_ref;
04006
04007 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
04008 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04009 if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
04010 || trunk_ref == exclude)
04011 continue;
04012 trunk_ref->state = state;
04013 ast_devstate_changed(sla_state_to_devstate(state),
04014 "SLA:%s_%s", station->name, trunk->name);
04015 break;
04016 }
04017 }
04018 }
04019
04020 struct run_station_args {
04021 struct sla_station *station;
04022 struct sla_trunk_ref *trunk_ref;
04023 ast_mutex_t *cond_lock;
04024 ast_cond_t *cond;
04025 };
04026
04027 static void answer_trunk_chan(struct ast_channel *chan)
04028 {
04029 ast_answer(chan);
04030 ast_indicate(chan, -1);
04031 }
04032
04033 static void *run_station(void *data)
04034 {
04035 struct sla_station *station;
04036 struct sla_trunk_ref *trunk_ref;
04037 char conf_name[MAX_CONFNUM];
04038 struct ast_flags conf_flags = { 0 };
04039 struct ast_conference *conf;
04040
04041 {
04042 struct run_station_args *args = data;
04043 station = args->station;
04044 trunk_ref = args->trunk_ref;
04045 ast_mutex_lock(args->cond_lock);
04046 ast_cond_signal(args->cond);
04047 ast_mutex_unlock(args->cond_lock);
04048
04049 }
04050
04051 ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
04052 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
04053 ast_set_flag(&conf_flags,
04054 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
04055 answer_trunk_chan(trunk_ref->chan);
04056 conf = build_conf(conf_name, "", "", 0, 0, 1, trunk_ref->chan);
04057 if (conf) {
04058 conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
04059 dispose_conf(conf);
04060 conf = NULL;
04061 }
04062 trunk_ref->chan = NULL;
04063 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
04064 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
04065 strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
04066 admin_exec(NULL, conf_name);
04067 trunk_ref->trunk->hold_stations = 0;
04068 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04069 }
04070
04071 ast_dial_join(station->dial);
04072 ast_dial_destroy(station->dial);
04073 station->dial = NULL;
04074
04075 return NULL;
04076 }
04077
04078 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
04079 {
04080 char buf[80];
04081 struct sla_station_ref *station_ref;
04082
04083 snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
04084 admin_exec(NULL, buf);
04085 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04086
04087 while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
04088 ast_free(station_ref);
04089
04090 ast_free(ringing_trunk);
04091 }
04092
04093 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
04094 enum sla_station_hangup hangup)
04095 {
04096 struct sla_ringing_trunk *ringing_trunk;
04097 struct sla_trunk_ref *trunk_ref;
04098 struct sla_station_ref *station_ref;
04099
04100 ast_dial_join(ringing_station->station->dial);
04101 ast_dial_destroy(ringing_station->station->dial);
04102 ringing_station->station->dial = NULL;
04103
04104 if (hangup == SLA_STATION_HANGUP_NORMAL)
04105 goto done;
04106
04107
04108
04109
04110
04111
04112 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
04113 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
04114 if (ringing_trunk->trunk == trunk_ref->trunk)
04115 break;
04116 }
04117 if (!trunk_ref)
04118 continue;
04119 if (!(station_ref = sla_create_station_ref(ringing_station->station)))
04120 continue;
04121 AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
04122 }
04123
04124 done:
04125 ast_free(ringing_station);
04126 }
04127
04128 static void sla_dial_state_callback(struct ast_dial *dial)
04129 {
04130 sla_queue_event(SLA_EVENT_DIAL_STATE);
04131 }
04132
04133
04134
04135
04136 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
04137 const struct sla_station *station)
04138 {
04139 struct sla_station_ref *timed_out_station;
04140
04141 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
04142 if (station == timed_out_station->station)
04143 return 1;
04144 }
04145
04146 return 0;
04147 }
04148
04149
04150
04151
04152
04153
04154
04155
04156
04157 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
04158 struct sla_trunk_ref **trunk_ref, int remove)
04159 {
04160 struct sla_trunk_ref *s_trunk_ref;
04161 struct sla_ringing_trunk *ringing_trunk = NULL;
04162
04163 AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
04164 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
04165
04166 if (s_trunk_ref->trunk != ringing_trunk->trunk)
04167 continue;
04168
04169
04170
04171 if (sla_check_timed_out_station(ringing_trunk, station))
04172 continue;
04173
04174 if (remove)
04175 AST_LIST_REMOVE_CURRENT(entry);
04176
04177 if (trunk_ref)
04178 *trunk_ref = s_trunk_ref;
04179
04180 break;
04181 }
04182 AST_LIST_TRAVERSE_SAFE_END;
04183
04184 if (ringing_trunk)
04185 break;
04186 }
04187
04188 return ringing_trunk;
04189 }
04190
04191 static void sla_handle_dial_state_event(void)
04192 {
04193 struct sla_ringing_station *ringing_station;
04194
04195 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
04196 struct sla_trunk_ref *s_trunk_ref = NULL;
04197 struct sla_ringing_trunk *ringing_trunk = NULL;
04198 struct run_station_args args;
04199 enum ast_dial_result dial_res;
04200 pthread_t dont_care;
04201 ast_mutex_t cond_lock;
04202 ast_cond_t cond;
04203
04204 switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
04205 case AST_DIAL_RESULT_HANGUP:
04206 case AST_DIAL_RESULT_INVALID:
04207 case AST_DIAL_RESULT_FAILED:
04208 case AST_DIAL_RESULT_TIMEOUT:
04209 case AST_DIAL_RESULT_UNANSWERED:
04210 AST_LIST_REMOVE_CURRENT(entry);
04211 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
04212 break;
04213 case AST_DIAL_RESULT_ANSWERED:
04214 AST_LIST_REMOVE_CURRENT(entry);
04215
04216 ast_mutex_lock(&sla.lock);
04217 ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
04218 ast_mutex_unlock(&sla.lock);
04219 if (!ringing_trunk) {
04220 ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
04221 break;
04222 }
04223
04224 s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
04225
04226 answer_trunk_chan(ringing_trunk->trunk->chan);
04227 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
04228
04229
04230
04231 args.trunk_ref = s_trunk_ref;
04232 args.station = ringing_station->station;
04233 args.cond = &cond;
04234 args.cond_lock = &cond_lock;
04235 ast_free(ringing_trunk);
04236 ast_free(ringing_station);
04237 ast_mutex_init(&cond_lock);
04238 ast_cond_init(&cond, NULL);
04239 ast_mutex_lock(&cond_lock);
04240 ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
04241 ast_cond_wait(&cond, &cond_lock);
04242 ast_mutex_unlock(&cond_lock);
04243 ast_mutex_destroy(&cond_lock);
04244 ast_cond_destroy(&cond);
04245 break;
04246 case AST_DIAL_RESULT_TRYING:
04247 case AST_DIAL_RESULT_RINGING:
04248 case AST_DIAL_RESULT_PROGRESS:
04249 case AST_DIAL_RESULT_PROCEEDING:
04250 break;
04251 }
04252 if (dial_res == AST_DIAL_RESULT_ANSWERED) {
04253
04254 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
04255 sla_queue_event(SLA_EVENT_DIAL_STATE);
04256 break;
04257 }
04258 }
04259 AST_LIST_TRAVERSE_SAFE_END;
04260 }
04261
04262
04263
04264
04265 static int sla_check_ringing_station(const struct sla_station *station)
04266 {
04267 struct sla_ringing_station *ringing_station;
04268
04269 AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
04270 if (station == ringing_station->station)
04271 return 1;
04272 }
04273
04274 return 0;
04275 }
04276
04277
04278
04279
04280 static int sla_check_failed_station(const struct sla_station *station)
04281 {
04282 struct sla_failed_station *failed_station;
04283 int res = 0;
04284
04285 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
04286 if (station != failed_station->station)
04287 continue;
04288 if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
04289 AST_LIST_REMOVE_CURRENT(entry);
04290 ast_free(failed_station);
04291 break;
04292 }
04293 res = 1;
04294 }
04295 AST_LIST_TRAVERSE_SAFE_END
04296
04297 return res;
04298 }
04299
04300
04301
04302
04303 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
04304 {
04305 char *tech, *tech_data;
04306 struct ast_dial *dial;
04307 struct sla_ringing_station *ringing_station;
04308 const char *cid_name = NULL, *cid_num = NULL;
04309 enum ast_dial_result res;
04310
04311 if (!(dial = ast_dial_create()))
04312 return -1;
04313
04314 ast_dial_set_state_callback(dial, sla_dial_state_callback);
04315 tech_data = ast_strdupa(station->device);
04316 tech = strsep(&tech_data, "/");
04317
04318 if (ast_dial_append(dial, tech, tech_data) == -1) {
04319 ast_dial_destroy(dial);
04320 return -1;
04321 }
04322
04323 if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_name)) {
04324 cid_name = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_name);
04325 ast_free(ringing_trunk->trunk->chan->cid.cid_name);
04326 ringing_trunk->trunk->chan->cid.cid_name = NULL;
04327 }
04328 if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_num)) {
04329 cid_num = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_num);
04330 ast_free(ringing_trunk->trunk->chan->cid.cid_num);
04331 ringing_trunk->trunk->chan->cid.cid_num = NULL;
04332 }
04333
04334 res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
04335
04336 if (cid_name)
04337 ringing_trunk->trunk->chan->cid.cid_name = ast_strdup(cid_name);
04338 if (cid_num)
04339 ringing_trunk->trunk->chan->cid.cid_num = ast_strdup(cid_num);
04340
04341 if (res != AST_DIAL_RESULT_TRYING) {
04342 struct sla_failed_station *failed_station;
04343 ast_dial_destroy(dial);
04344 if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
04345 return -1;
04346 failed_station->station = station;
04347 failed_station->last_try = ast_tvnow();
04348 AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
04349 return -1;
04350 }
04351 if (!(ringing_station = sla_create_ringing_station(station))) {
04352 ast_dial_join(dial);
04353 ast_dial_destroy(dial);
04354 return -1;
04355 }
04356
04357 station->dial = dial;
04358
04359 AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
04360
04361 return 0;
04362 }
04363
04364
04365
04366 static int sla_check_inuse_station(const struct sla_station *station)
04367 {
04368 struct sla_trunk_ref *trunk_ref;
04369
04370 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04371 if (trunk_ref->chan)
04372 return 1;
04373 }
04374
04375 return 0;
04376 }
04377
04378 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
04379 const struct sla_trunk *trunk)
04380 {
04381 struct sla_trunk_ref *trunk_ref = NULL;
04382
04383 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04384 if (trunk_ref->trunk == trunk)
04385 break;
04386 }
04387
04388 return trunk_ref;
04389 }
04390
04391
04392
04393
04394
04395
04396 static int sla_check_station_delay(struct sla_station *station,
04397 struct sla_ringing_trunk *ringing_trunk)
04398 {
04399 struct sla_trunk_ref *trunk_ref;
04400 unsigned int delay = UINT_MAX;
04401 int time_left, time_elapsed;
04402
04403 if (!ringing_trunk)
04404 ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
04405 else
04406 trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
04407
04408 if (!ringing_trunk || !trunk_ref)
04409 return delay;
04410
04411
04412
04413
04414 delay = trunk_ref->ring_delay;
04415 if (!delay)
04416 delay = station->ring_delay;
04417 if (!delay)
04418 return INT_MAX;
04419
04420 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
04421 time_left = (delay * 1000) - time_elapsed;
04422
04423 return time_left;
04424 }
04425
04426
04427
04428
04429 static void sla_ring_stations(void)
04430 {
04431 struct sla_station_ref *station_ref;
04432 struct sla_ringing_trunk *ringing_trunk;
04433
04434
04435
04436 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
04437 AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
04438 int time_left;
04439
04440
04441 if (sla_check_ringing_station(station_ref->station))
04442 continue;
04443
04444
04445 if (sla_check_inuse_station(station_ref->station))
04446 continue;
04447
04448
04449
04450 if (sla_check_failed_station(station_ref->station))
04451 continue;
04452
04453
04454
04455 if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
04456 continue;
04457
04458
04459 time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
04460 if (time_left != INT_MAX && time_left > 0)
04461 continue;
04462
04463
04464 sla_ring_station(ringing_trunk, station_ref->station);
04465 }
04466 }
04467
04468 }
04469
04470 static void sla_hangup_stations(void)
04471 {
04472 struct sla_trunk_ref *trunk_ref;
04473 struct sla_ringing_station *ringing_station;
04474
04475 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
04476 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
04477 struct sla_ringing_trunk *ringing_trunk;
04478 ast_mutex_lock(&sla.lock);
04479 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
04480 if (trunk_ref->trunk == ringing_trunk->trunk)
04481 break;
04482 }
04483 ast_mutex_unlock(&sla.lock);
04484 if (ringing_trunk)
04485 break;
04486 }
04487 if (!trunk_ref) {
04488 AST_LIST_REMOVE_CURRENT(entry);
04489 ast_dial_join(ringing_station->station->dial);
04490 ast_dial_destroy(ringing_station->station->dial);
04491 ringing_station->station->dial = NULL;
04492 ast_free(ringing_station);
04493 }
04494 }
04495 AST_LIST_TRAVERSE_SAFE_END
04496 }
04497
04498 static void sla_handle_ringing_trunk_event(void)
04499 {
04500 ast_mutex_lock(&sla.lock);
04501 sla_ring_stations();
04502 ast_mutex_unlock(&sla.lock);
04503
04504
04505 sla_hangup_stations();
04506 }
04507
04508 static void sla_handle_hold_event(struct sla_event *event)
04509 {
04510 ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
04511 event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
04512 ast_devstate_changed(AST_DEVICE_ONHOLD, "SLA:%s_%s",
04513 event->station->name, event->trunk_ref->trunk->name);
04514 sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
04515 INACTIVE_TRUNK_REFS, event->trunk_ref);
04516
04517 if (event->trunk_ref->trunk->active_stations == 1) {
04518
04519
04520 event->trunk_ref->trunk->on_hold = 1;
04521 ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
04522 }
04523
04524 ast_softhangup(event->trunk_ref->chan, AST_CAUSE_NORMAL);
04525 event->trunk_ref->chan = NULL;
04526 }
04527
04528
04529
04530
04531
04532 static int sla_calc_trunk_timeouts(unsigned int *timeout)
04533 {
04534 struct sla_ringing_trunk *ringing_trunk;
04535 int res = 0;
04536
04537 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
04538 int time_left, time_elapsed;
04539 if (!ringing_trunk->trunk->ring_timeout)
04540 continue;
04541 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
04542 time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
04543 if (time_left <= 0) {
04544 pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
04545 AST_LIST_REMOVE_CURRENT(entry);
04546 sla_stop_ringing_trunk(ringing_trunk);
04547 res = 1;
04548 continue;
04549 }
04550 if (time_left < *timeout)
04551 *timeout = time_left;
04552 }
04553 AST_LIST_TRAVERSE_SAFE_END;
04554
04555 return res;
04556 }
04557
04558
04559
04560
04561
04562 static int sla_calc_station_timeouts(unsigned int *timeout)
04563 {
04564 struct sla_ringing_trunk *ringing_trunk;
04565 struct sla_ringing_station *ringing_station;
04566 int res = 0;
04567
04568 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
04569 unsigned int ring_timeout = 0;
04570 int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
04571 struct sla_trunk_ref *trunk_ref;
04572
04573
04574
04575
04576 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
04577 struct sla_station_ref *station_ref;
04578 int trunk_time_elapsed, trunk_time_left;
04579
04580 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
04581 if (ringing_trunk->trunk == trunk_ref->trunk)
04582 break;
04583 }
04584 if (!ringing_trunk)
04585 continue;
04586
04587
04588
04589 if (!trunk_ref->ring_timeout)
04590 break;
04591
04592
04593
04594
04595 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
04596 if (station_ref->station == ringing_station->station)
04597 break;
04598 }
04599 if (station_ref)
04600 continue;
04601
04602 trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
04603 trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
04604 if (trunk_time_left > final_trunk_time_left)
04605 final_trunk_time_left = trunk_time_left;
04606 }
04607
04608
04609 if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
04610 continue;
04611
04612
04613 if (ringing_station->station->ring_timeout) {
04614 ring_timeout = ringing_station->station->ring_timeout;
04615 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
04616 time_left = (ring_timeout * 1000) - time_elapsed;
04617 }
04618
04619
04620
04621 if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
04622 time_left = final_trunk_time_left;
04623
04624
04625 if (time_left <= 0) {
04626 AST_LIST_REMOVE_CURRENT(entry);
04627 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
04628 res = 1;
04629 continue;
04630 }
04631
04632
04633
04634 if (time_left < *timeout)
04635 *timeout = time_left;
04636 }
04637 AST_LIST_TRAVERSE_SAFE_END;
04638
04639 return res;
04640 }
04641
04642
04643
04644
04645 static int sla_calc_station_delays(unsigned int *timeout)
04646 {
04647 struct sla_station *station;
04648 int res = 0;
04649
04650 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
04651 struct sla_ringing_trunk *ringing_trunk;
04652 int time_left;
04653
04654
04655 if (sla_check_ringing_station(station))
04656 continue;
04657
04658
04659 if (sla_check_inuse_station(station))
04660 continue;
04661
04662
04663 if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
04664 continue;
04665
04666 if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
04667 continue;
04668
04669
04670
04671
04672 if (time_left <= 0) {
04673 res = 1;
04674 continue;
04675 }
04676
04677 if (time_left < *timeout)
04678 *timeout = time_left;
04679 }
04680
04681 return res;
04682 }
04683
04684
04685
04686 static int sla_process_timers(struct timespec *ts)
04687 {
04688 unsigned int timeout = UINT_MAX;
04689 struct timeval tv;
04690 unsigned int change_made = 0;
04691
04692
04693 if (sla_calc_trunk_timeouts(&timeout))
04694 change_made = 1;
04695
04696
04697 if (sla_calc_station_timeouts(&timeout))
04698 change_made = 1;
04699
04700
04701 if (sla_calc_station_delays(&timeout))
04702 change_made = 1;
04703
04704
04705 if (change_made)
04706 sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
04707
04708
04709 if (timeout == UINT_MAX)
04710 return 0;
04711
04712 if (ts) {
04713 tv = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
04714 ts->tv_sec = tv.tv_sec;
04715 ts->tv_nsec = tv.tv_usec * 1000;
04716 }
04717
04718 return 1;
04719 }
04720
04721 static int sla_load_config(int reload);
04722
04723
04724 static void sla_check_reload(void)
04725 {
04726 struct sla_station *station;
04727 struct sla_trunk *trunk;
04728
04729 ast_mutex_lock(&sla.lock);
04730
04731 if (!AST_LIST_EMPTY(&sla.event_q) || !AST_LIST_EMPTY(&sla.ringing_trunks)
04732 || !AST_LIST_EMPTY(&sla.ringing_stations)) {
04733 ast_mutex_unlock(&sla.lock);
04734 return;
04735 }
04736
04737 AST_RWLIST_RDLOCK(&sla_stations);
04738 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
04739 if (station->ref_count)
04740 break;
04741 }
04742 AST_RWLIST_UNLOCK(&sla_stations);
04743 if (station) {
04744 ast_mutex_unlock(&sla.lock);
04745 return;
04746 }
04747
04748 AST_RWLIST_RDLOCK(&sla_trunks);
04749 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
04750 if (trunk->ref_count)
04751 break;
04752 }
04753 AST_RWLIST_UNLOCK(&sla_trunks);
04754 if (trunk) {
04755 ast_mutex_unlock(&sla.lock);
04756 return;
04757 }
04758
04759
04760 sla_load_config(1);
04761 sla.reload = 0;
04762
04763 ast_mutex_unlock(&sla.lock);
04764 }
04765
04766 static void *sla_thread(void *data)
04767 {
04768 struct sla_failed_station *failed_station;
04769 struct sla_ringing_station *ringing_station;
04770
04771 ast_mutex_lock(&sla.lock);
04772
04773 while (!sla.stop) {
04774 struct sla_event *event;
04775 struct timespec ts = { 0, };
04776 unsigned int have_timeout = 0;
04777
04778 if (AST_LIST_EMPTY(&sla.event_q)) {
04779 if ((have_timeout = sla_process_timers(&ts)))
04780 ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
04781 else
04782 ast_cond_wait(&sla.cond, &sla.lock);
04783 if (sla.stop)
04784 break;
04785 }
04786
04787 if (have_timeout)
04788 sla_process_timers(NULL);
04789
04790 while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
04791 ast_mutex_unlock(&sla.lock);
04792 switch (event->type) {
04793 case SLA_EVENT_HOLD:
04794 sla_handle_hold_event(event);
04795 break;
04796 case SLA_EVENT_DIAL_STATE:
04797 sla_handle_dial_state_event();
04798 break;
04799 case SLA_EVENT_RINGING_TRUNK:
04800 sla_handle_ringing_trunk_event();
04801 break;
04802 case SLA_EVENT_RELOAD:
04803 sla.reload = 1;
04804 case SLA_EVENT_CHECK_RELOAD:
04805 break;
04806 }
04807 ast_free(event);
04808 ast_mutex_lock(&sla.lock);
04809 }
04810
04811 if (sla.reload)
04812 sla_check_reload();
04813 }
04814
04815 ast_mutex_unlock(&sla.lock);
04816
04817 while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
04818 ast_free(ringing_station);
04819
04820 while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
04821 ast_free(failed_station);
04822
04823 return NULL;
04824 }
04825
04826 struct dial_trunk_args {
04827 struct sla_trunk_ref *trunk_ref;
04828 struct sla_station *station;
04829 ast_mutex_t *cond_lock;
04830 ast_cond_t *cond;
04831 };
04832
04833 static void *dial_trunk(void *data)
04834 {
04835 struct dial_trunk_args *args = data;
04836 struct ast_dial *dial;
04837 char *tech, *tech_data;
04838 enum ast_dial_result dial_res;
04839 char conf_name[MAX_CONFNUM];
04840 struct ast_conference *conf;
04841 struct ast_flags conf_flags = { 0 };
04842 struct sla_trunk_ref *trunk_ref = args->trunk_ref;
04843 const char *cid_name = NULL, *cid_num = NULL;
04844
04845 if (!(dial = ast_dial_create())) {
04846 ast_mutex_lock(args->cond_lock);
04847 ast_cond_signal(args->cond);
04848 ast_mutex_unlock(args->cond_lock);
04849 return NULL;
04850 }
04851
04852 tech_data = ast_strdupa(trunk_ref->trunk->device);
04853 tech = strsep(&tech_data, "/");
04854 if (ast_dial_append(dial, tech, tech_data) == -1) {
04855 ast_mutex_lock(args->cond_lock);
04856 ast_cond_signal(args->cond);
04857 ast_mutex_unlock(args->cond_lock);
04858 ast_dial_destroy(dial);
04859 return NULL;
04860 }
04861
04862 if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_name)) {
04863 cid_name = ast_strdupa(trunk_ref->chan->cid.cid_name);
04864 ast_free(trunk_ref->chan->cid.cid_name);
04865 trunk_ref->chan->cid.cid_name = NULL;
04866 }
04867 if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_num)) {
04868 cid_num = ast_strdupa(trunk_ref->chan->cid.cid_num);
04869 ast_free(trunk_ref->chan->cid.cid_num);
04870 trunk_ref->chan->cid.cid_num = NULL;
04871 }
04872
04873 dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
04874
04875 if (cid_name)
04876 trunk_ref->chan->cid.cid_name = ast_strdup(cid_name);
04877 if (cid_num)
04878 trunk_ref->chan->cid.cid_num = ast_strdup(cid_num);
04879
04880 if (dial_res != AST_DIAL_RESULT_TRYING) {
04881 ast_mutex_lock(args->cond_lock);
04882 ast_cond_signal(args->cond);
04883 ast_mutex_unlock(args->cond_lock);
04884 ast_dial_destroy(dial);
04885 return NULL;
04886 }
04887
04888 for (;;) {
04889 unsigned int done = 0;
04890 switch ((dial_res = ast_dial_state(dial))) {
04891 case AST_DIAL_RESULT_ANSWERED:
04892 trunk_ref->trunk->chan = ast_dial_answered(dial);
04893 case AST_DIAL_RESULT_HANGUP:
04894 case AST_DIAL_RESULT_INVALID:
04895 case AST_DIAL_RESULT_FAILED:
04896 case AST_DIAL_RESULT_TIMEOUT:
04897 case AST_DIAL_RESULT_UNANSWERED:
04898 done = 1;
04899 case AST_DIAL_RESULT_TRYING:
04900 case AST_DIAL_RESULT_RINGING:
04901 case AST_DIAL_RESULT_PROGRESS:
04902 case AST_DIAL_RESULT_PROCEEDING:
04903 break;
04904 }
04905 if (done)
04906 break;
04907 }
04908
04909 if (!trunk_ref->trunk->chan) {
04910 ast_mutex_lock(args->cond_lock);
04911 ast_cond_signal(args->cond);
04912 ast_mutex_unlock(args->cond_lock);
04913 ast_dial_join(dial);
04914 ast_dial_destroy(dial);
04915 return NULL;
04916 }
04917
04918 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
04919 ast_set_flag(&conf_flags,
04920 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
04921 CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
04922 conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan);
04923
04924 ast_mutex_lock(args->cond_lock);
04925 ast_cond_signal(args->cond);
04926 ast_mutex_unlock(args->cond_lock);
04927
04928 if (conf) {
04929 conf_run(trunk_ref->trunk->chan, conf, conf_flags.flags, NULL);
04930 dispose_conf(conf);
04931 conf = NULL;
04932 }
04933
04934
04935 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04936
04937 trunk_ref->trunk->chan = NULL;
04938 trunk_ref->trunk->on_hold = 0;
04939
04940 ast_dial_join(dial);
04941 ast_dial_destroy(dial);
04942
04943 return NULL;
04944 }
04945
04946
04947
04948 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
04949 {
04950 struct sla_trunk_ref *trunk_ref = NULL;
04951
04952 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04953 if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
04954 break;
04955 }
04956
04957 return trunk_ref;
04958 }
04959
04960 static int sla_station_exec(struct ast_channel *chan, void *data)
04961 {
04962 char *station_name, *trunk_name;
04963 struct sla_station *station;
04964 struct sla_trunk_ref *trunk_ref = NULL;
04965 char conf_name[MAX_CONFNUM];
04966 struct ast_flags conf_flags = { 0 };
04967 struct ast_conference *conf;
04968
04969 if (ast_strlen_zero(data)) {
04970 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
04971 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
04972 return 0;
04973 }
04974
04975 trunk_name = ast_strdupa(data);
04976 station_name = strsep(&trunk_name, "_");
04977
04978 if (ast_strlen_zero(station_name)) {
04979 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
04980 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
04981 return 0;
04982 }
04983
04984 AST_RWLIST_RDLOCK(&sla_stations);
04985 station = sla_find_station(station_name);
04986 if (station)
04987 ast_atomic_fetchadd_int((int *) &station->ref_count, 1);
04988 AST_RWLIST_UNLOCK(&sla_stations);
04989
04990 if (!station) {
04991 ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
04992 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
04993 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
04994 return 0;
04995 }
04996
04997 AST_RWLIST_RDLOCK(&sla_trunks);
04998 if (!ast_strlen_zero(trunk_name)) {
04999 trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
05000 } else
05001 trunk_ref = sla_choose_idle_trunk(station);
05002 AST_RWLIST_UNLOCK(&sla_trunks);
05003
05004 if (!trunk_ref) {
05005 if (ast_strlen_zero(trunk_name))
05006 ast_log(LOG_NOTICE, "No trunks available for call.\n");
05007 else {
05008 ast_log(LOG_NOTICE, "Can't join existing call on trunk "
05009 "'%s' due to access controls.\n", trunk_name);
05010 }
05011 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
05012 ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
05013 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
05014 return 0;
05015 }
05016
05017 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
05018 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
05019 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
05020 else {
05021 trunk_ref->state = SLA_TRUNK_STATE_UP;
05022 ast_devstate_changed(AST_DEVICE_INUSE,
05023 "SLA:%s_%s", station->name, trunk_ref->trunk->name);
05024 }
05025 } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
05026 struct sla_ringing_trunk *ringing_trunk;
05027
05028 ast_mutex_lock(&sla.lock);
05029 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05030 if (ringing_trunk->trunk == trunk_ref->trunk) {
05031 AST_LIST_REMOVE_CURRENT(entry);
05032 break;
05033 }
05034 }
05035 AST_LIST_TRAVERSE_SAFE_END
05036 ast_mutex_unlock(&sla.lock);
05037
05038 if (ringing_trunk) {
05039 answer_trunk_chan(ringing_trunk->trunk->chan);
05040 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
05041
05042 free(ringing_trunk);
05043
05044
05045 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
05046 sla_queue_event(SLA_EVENT_DIAL_STATE);
05047 }
05048 }
05049
05050 trunk_ref->chan = chan;
05051
05052 if (!trunk_ref->trunk->chan) {
05053 ast_mutex_t cond_lock;
05054 ast_cond_t cond;
05055 pthread_t dont_care;
05056 struct dial_trunk_args args = {
05057 .trunk_ref = trunk_ref,
05058 .station = station,
05059 .cond_lock = &cond_lock,
05060 .cond = &cond,
05061 };
05062 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
05063
05064
05065
05066 ast_autoservice_start(chan);
05067 ast_mutex_init(&cond_lock);
05068 ast_cond_init(&cond, NULL);
05069 ast_mutex_lock(&cond_lock);
05070 ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
05071 ast_cond_wait(&cond, &cond_lock);
05072 ast_mutex_unlock(&cond_lock);
05073 ast_mutex_destroy(&cond_lock);
05074 ast_cond_destroy(&cond);
05075 ast_autoservice_stop(chan);
05076 if (!trunk_ref->trunk->chan) {
05077 ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
05078 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
05079 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05080 trunk_ref->chan = NULL;
05081 ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
05082 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
05083 return 0;
05084 }
05085 }
05086
05087 if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
05088 trunk_ref->trunk->on_hold) {
05089 trunk_ref->trunk->on_hold = 0;
05090 ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
05091 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
05092 }
05093
05094 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
05095 ast_set_flag(&conf_flags,
05096 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
05097 ast_answer(chan);
05098 conf = build_conf(conf_name, "", "", 0, 0, 1, chan);
05099 if (conf) {
05100 conf_run(chan, conf, conf_flags.flags, NULL);
05101 dispose_conf(conf);
05102 conf = NULL;
05103 }
05104 trunk_ref->chan = NULL;
05105 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
05106 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
05107 strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
05108 admin_exec(NULL, conf_name);
05109 trunk_ref->trunk->hold_stations = 0;
05110 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05111 }
05112
05113 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
05114
05115 ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
05116 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
05117
05118 return 0;
05119 }
05120
05121 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
05122 {
05123 struct sla_trunk_ref *trunk_ref;
05124
05125 if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
05126 return NULL;
05127
05128 trunk_ref->trunk = trunk;
05129
05130 return trunk_ref;
05131 }
05132
05133 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
05134 {
05135 struct sla_ringing_trunk *ringing_trunk;
05136
05137 if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
05138 return NULL;
05139
05140 ringing_trunk->trunk = trunk;
05141 ringing_trunk->ring_begin = ast_tvnow();
05142
05143 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
05144
05145 ast_mutex_lock(&sla.lock);
05146 AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
05147 ast_mutex_unlock(&sla.lock);
05148
05149 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
05150
05151 return ringing_trunk;
05152 }
05153
05154 enum {
05155 SLA_TRUNK_OPT_MOH = (1 << 0),
05156 };
05157
05158 enum {
05159 SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
05160 SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
05161 };
05162
05163 AST_APP_OPTIONS(sla_trunk_opts, BEGIN_OPTIONS
05164 AST_APP_OPTION_ARG('M', SLA_TRUNK_OPT_MOH, SLA_TRUNK_OPT_ARG_MOH_CLASS),
05165 END_OPTIONS );
05166
05167 static int sla_trunk_exec(struct ast_channel *chan, void *data)
05168 {
05169 char conf_name[MAX_CONFNUM];
05170 struct ast_conference *conf;
05171 struct ast_flags conf_flags = { 0 };
05172 struct sla_trunk *trunk;
05173 struct sla_ringing_trunk *ringing_trunk;
05174 AST_DECLARE_APP_ARGS(args,
05175 AST_APP_ARG(trunk_name);
05176 AST_APP_ARG(options);
05177 );
05178 char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
05179 char *conf_opt_args[OPT_ARG_ARRAY_SIZE] = { NULL, };
05180 struct ast_flags opt_flags = { 0 };
05181 char *parse;
05182
05183 if (ast_strlen_zero(data)) {
05184 ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
05185 return -1;
05186 }
05187
05188 parse = ast_strdupa(data);
05189 AST_STANDARD_APP_ARGS(args, parse);
05190 if (args.argc == 2) {
05191 if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
05192 ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
05193 return -1;
05194 }
05195 }
05196
05197 AST_RWLIST_RDLOCK(&sla_trunks);
05198 trunk = sla_find_trunk(args.trunk_name);
05199 if (trunk)
05200 ast_atomic_fetchadd_int((int *) &trunk->ref_count, 1);
05201 AST_RWLIST_UNLOCK(&sla_trunks);
05202
05203 if (!trunk) {
05204 ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
05205 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
05206 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
05207 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
05208 return 0;
05209 }
05210
05211 if (trunk->chan) {
05212 ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
05213 args.trunk_name);
05214 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
05215 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
05216 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
05217 return 0;
05218 }
05219
05220 trunk->chan = chan;
05221
05222 if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
05223 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
05224 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
05225 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
05226 return 0;
05227 }
05228
05229 snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
05230 conf = build_conf(conf_name, "", "", 1, 1, 1, chan);
05231 if (!conf) {
05232 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
05233 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
05234 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
05235 return 0;
05236 }
05237 ast_set_flag(&conf_flags,
05238 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);
05239
05240 if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
05241 ast_indicate(chan, -1);
05242 ast_set_flag(&conf_flags, CONFFLAG_MOH);
05243 conf_opt_args[OPT_ARG_MOH_CLASS] = opts[SLA_TRUNK_OPT_ARG_MOH_CLASS];
05244 } else
05245 ast_indicate(chan, AST_CONTROL_RINGING);
05246
05247 conf_run(chan, conf, conf_flags.flags, opts);
05248 dispose_conf(conf);
05249 conf = NULL;
05250 trunk->chan = NULL;
05251 trunk->on_hold = 0;
05252
05253 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05254
05255 if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
05256 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
05257
05258
05259 ast_mutex_lock(&sla.lock);
05260 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
05261 if (ringing_trunk->trunk == trunk) {
05262 AST_LIST_REMOVE_CURRENT(entry);
05263 break;
05264 }
05265 }
05266 AST_LIST_TRAVERSE_SAFE_END;
05267 ast_mutex_unlock(&sla.lock);
05268 if (ringing_trunk) {
05269 ast_free(ringing_trunk);
05270 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
05271
05272
05273 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
05274 }
05275
05276 ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
05277 sla_queue_event(SLA_EVENT_CHECK_RELOAD);
05278
05279 return 0;
05280 }
05281
05282 static enum ast_device_state sla_state(const char *data)
05283 {
05284 char *buf, *station_name, *trunk_name;
05285 struct sla_station *station;
05286 struct sla_trunk_ref *trunk_ref;
05287 enum ast_device_state res = AST_DEVICE_INVALID;
05288
05289 trunk_name = buf = ast_strdupa(data);
05290 station_name = strsep(&trunk_name, "_");
05291
05292 AST_RWLIST_RDLOCK(&sla_stations);
05293 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
05294 if (strcasecmp(station_name, station->name))
05295 continue;
05296 AST_RWLIST_RDLOCK(&sla_trunks);
05297 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05298 if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
05299 break;
05300 }
05301 if (!trunk_ref) {
05302 AST_RWLIST_UNLOCK(&sla_trunks);
05303 break;
05304 }
05305 res = sla_state_to_devstate(trunk_ref->state);
05306 AST_RWLIST_UNLOCK(&sla_trunks);
05307 }
05308 AST_RWLIST_UNLOCK(&sla_stations);
05309
05310 if (res == AST_DEVICE_INVALID) {
05311 ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
05312 trunk_name, station_name);
05313 }
05314
05315 return res;
05316 }
05317
05318 static void destroy_trunk(struct sla_trunk *trunk)
05319 {
05320 struct sla_station_ref *station_ref;
05321
05322 if (!ast_strlen_zero(trunk->autocontext))
05323 ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
05324
05325 while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
05326 ast_free(station_ref);
05327
05328 ast_string_field_free_memory(trunk);
05329 ast_free(trunk);
05330 }
05331
05332 static void destroy_station(struct sla_station *station)
05333 {
05334 struct sla_trunk_ref *trunk_ref;
05335
05336 if (!ast_strlen_zero(station->autocontext)) {
05337 AST_RWLIST_RDLOCK(&sla_trunks);
05338 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05339 char exten[AST_MAX_EXTENSION];
05340 char hint[AST_MAX_APP];
05341 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
05342 snprintf(hint, sizeof(hint), "SLA:%s", exten);
05343 ast_context_remove_extension(station->autocontext, exten,
05344 1, sla_registrar);
05345 ast_context_remove_extension(station->autocontext, hint,
05346 PRIORITY_HINT, sla_registrar);
05347 }
05348 AST_RWLIST_UNLOCK(&sla_trunks);
05349 }
05350
05351 while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
05352 ast_free(trunk_ref);
05353
05354 ast_string_field_free_memory(station);
05355 ast_free(station);
05356 }
05357
05358 static void sla_destroy(void)
05359 {
05360 struct sla_trunk *trunk;
05361 struct sla_station *station;
05362
05363 AST_RWLIST_WRLOCK(&sla_trunks);
05364 while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
05365 destroy_trunk(trunk);
05366 AST_RWLIST_UNLOCK(&sla_trunks);
05367
05368 AST_RWLIST_WRLOCK(&sla_stations);
05369 while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
05370 destroy_station(station);
05371 AST_RWLIST_UNLOCK(&sla_stations);
05372
05373 if (sla.thread != AST_PTHREADT_NULL) {
05374 ast_mutex_lock(&sla.lock);
05375 sla.stop = 1;
05376 ast_cond_signal(&sla.cond);
05377 ast_mutex_unlock(&sla.lock);
05378 pthread_join(sla.thread, NULL);
05379 }
05380
05381
05382 ast_context_destroy(NULL, sla_registrar);
05383
05384 ast_mutex_destroy(&sla.lock);
05385 ast_cond_destroy(&sla.cond);
05386 }
05387
05388 static int sla_check_device(const char *device)
05389 {
05390 char *tech, *tech_data;
05391
05392 tech_data = ast_strdupa(device);
05393 tech = strsep(&tech_data, "/");
05394
05395 if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
05396 return -1;
05397
05398 return 0;
05399 }
05400
05401 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
05402 {
05403 struct sla_trunk *trunk;
05404 struct ast_variable *var;
05405 const char *dev;
05406
05407 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
05408 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
05409 return -1;
05410 }
05411
05412 if (sla_check_device(dev)) {
05413 ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
05414 cat, dev);
05415 return -1;
05416 }
05417
05418 if (!(trunk = ast_calloc(1, sizeof(*trunk))))
05419 return -1;
05420 if (ast_string_field_init(trunk, 32)) {
05421 ast_free(trunk);
05422 return -1;
05423 }
05424
05425 ast_string_field_set(trunk, name, cat);
05426 ast_string_field_set(trunk, device, dev);
05427
05428 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05429 if (!strcasecmp(var->name, "autocontext"))
05430 ast_string_field_set(trunk, autocontext, var->value);
05431 else if (!strcasecmp(var->name, "ringtimeout")) {
05432 if (sscanf(var->value, "%u", &trunk->ring_timeout) != 1) {
05433 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
05434 var->value, trunk->name);
05435 trunk->ring_timeout = 0;
05436 }
05437 } else if (!strcasecmp(var->name, "barge"))
05438 trunk->barge_disabled = ast_false(var->value);
05439 else if (!strcasecmp(var->name, "hold")) {
05440 if (!strcasecmp(var->value, "private"))
05441 trunk->hold_access = SLA_HOLD_PRIVATE;
05442 else if (!strcasecmp(var->value, "open"))
05443 trunk->hold_access = SLA_HOLD_OPEN;
05444 else {
05445 ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
05446 var->value, trunk->name);
05447 }
05448 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
05449 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
05450 var->name, var->lineno, SLA_CONFIG_FILE);
05451 }
05452 }
05453
05454 if (!ast_strlen_zero(trunk->autocontext)) {
05455 struct ast_context *context;
05456 context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
05457 if (!context) {
05458 ast_log(LOG_ERROR, "Failed to automatically find or create "
05459 "context '%s' for SLA!\n", trunk->autocontext);
05460 destroy_trunk(trunk);
05461 return -1;
05462 }
05463 if (ast_add_extension2(context, 0 , "s", 1,
05464 NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
05465 ast_log(LOG_ERROR, "Failed to automatically create extension "
05466 "for trunk '%s'!\n", trunk->name);
05467 destroy_trunk(trunk);
05468 return -1;
05469 }
05470 }
05471
05472 AST_RWLIST_WRLOCK(&sla_trunks);
05473 AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
05474 AST_RWLIST_UNLOCK(&sla_trunks);
05475
05476 return 0;
05477 }
05478
05479 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
05480 {
05481 struct sla_trunk *trunk;
05482 struct sla_trunk_ref *trunk_ref;
05483 struct sla_station_ref *station_ref;
05484 char *trunk_name, *options, *cur;
05485
05486 options = ast_strdupa(var->value);
05487 trunk_name = strsep(&options, ",");
05488
05489 AST_RWLIST_RDLOCK(&sla_trunks);
05490 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
05491 if (!strcasecmp(trunk->name, trunk_name))
05492 break;
05493 }
05494
05495 AST_RWLIST_UNLOCK(&sla_trunks);
05496 if (!trunk) {
05497 ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
05498 return;
05499 }
05500 if (!(trunk_ref = create_trunk_ref(trunk)))
05501 return;
05502 trunk_ref->state = SLA_TRUNK_STATE_IDLE;
05503
05504 while ((cur = strsep(&options, ","))) {
05505 char *name, *value = cur;
05506 name = strsep(&value, "=");
05507 if (!strcasecmp(name, "ringtimeout")) {
05508 if (sscanf(value, "%u", &trunk_ref->ring_timeout) != 1) {
05509 ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
05510 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
05511 trunk_ref->ring_timeout = 0;
05512 }
05513 } else if (!strcasecmp(name, "ringdelay")) {
05514 if (sscanf(value, "%u", &trunk_ref->ring_delay) != 1) {
05515 ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
05516 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
05517 trunk_ref->ring_delay = 0;
05518 }
05519 } else {
05520 ast_log(LOG_WARNING, "Invalid option '%s' for "
05521 "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
05522 }
05523 }
05524
05525 if (!(station_ref = sla_create_station_ref(station))) {
05526 ast_free(trunk_ref);
05527 return;
05528 }
05529 ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
05530 AST_RWLIST_WRLOCK(&sla_trunks);
05531 AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
05532 AST_RWLIST_UNLOCK(&sla_trunks);
05533 AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
05534 }
05535
05536 static int sla_build_station(struct ast_config *cfg, const char *cat)
05537 {
05538 struct sla_station *station;
05539 struct ast_variable *var;
05540 const char *dev;
05541
05542 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
05543 ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
05544 return -1;
05545 }
05546
05547 if (!(station = ast_calloc(1, sizeof(*station))))
05548 return -1;
05549 if (ast_string_field_init(station, 32)) {
05550 ast_free(station);
05551 return -1;
05552 }
05553
05554 ast_string_field_set(station, name, cat);
05555 ast_string_field_set(station, device, dev);
05556
05557 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05558 if (!strcasecmp(var->name, "trunk"))
05559 sla_add_trunk_to_station(station, var);
05560 else if (!strcasecmp(var->name, "autocontext"))
05561 ast_string_field_set(station, autocontext, var->value);
05562 else if (!strcasecmp(var->name, "ringtimeout")) {
05563 if (sscanf(var->value, "%u", &station->ring_timeout) != 1) {
05564 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
05565 var->value, station->name);
05566 station->ring_timeout = 0;
05567 }
05568 } else if (!strcasecmp(var->name, "ringdelay")) {
05569 if (sscanf(var->value, "%u", &station->ring_delay) != 1) {
05570 ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
05571 var->value, station->name);
05572 station->ring_delay = 0;
05573 }
05574 } else if (!strcasecmp(var->name, "hold")) {
05575 if (!strcasecmp(var->value, "private"))
05576 station->hold_access = SLA_HOLD_PRIVATE;
05577 else if (!strcasecmp(var->value, "open"))
05578 station->hold_access = SLA_HOLD_OPEN;
05579 else {
05580 ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
05581 var->value, station->name);
05582 }
05583
05584 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
05585 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
05586 var->name, var->lineno, SLA_CONFIG_FILE);
05587 }
05588 }
05589
05590 if (!ast_strlen_zero(station->autocontext)) {
05591 struct ast_context *context;
05592 struct sla_trunk_ref *trunk_ref;
05593 context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
05594 if (!context) {
05595 ast_log(LOG_ERROR, "Failed to automatically find or create "
05596 "context '%s' for SLA!\n", station->autocontext);
05597 destroy_station(station);
05598 return -1;
05599 }
05600
05601
05602 if (ast_add_extension2(context, 0 , station->name, 1,
05603 NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
05604 ast_log(LOG_ERROR, "Failed to automatically create extension "
05605 "for trunk '%s'!\n", station->name);
05606 destroy_station(station);
05607 return -1;
05608 }
05609 AST_RWLIST_RDLOCK(&sla_trunks);
05610 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05611 char exten[AST_MAX_EXTENSION];
05612 char hint[AST_MAX_APP];
05613 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
05614 snprintf(hint, sizeof(hint), "SLA:%s", exten);
05615
05616
05617 if (ast_add_extension2(context, 0 , exten, 1,
05618 NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
05619 ast_log(LOG_ERROR, "Failed to automatically create extension "
05620 "for trunk '%s'!\n", station->name);
05621 destroy_station(station);
05622 return -1;
05623 }
05624
05625
05626 if (ast_add_extension2(context, 0 , exten, PRIORITY_HINT,
05627 NULL, NULL, hint, NULL, NULL, sla_registrar)) {
05628 ast_log(LOG_ERROR, "Failed to automatically create hint "
05629 "for trunk '%s'!\n", station->name);
05630 destroy_station(station);
05631 return -1;
05632 }
05633 }
05634 AST_RWLIST_UNLOCK(&sla_trunks);
05635 }
05636
05637 AST_RWLIST_WRLOCK(&sla_stations);
05638 AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
05639 AST_RWLIST_UNLOCK(&sla_stations);
05640
05641 return 0;
05642 }
05643
05644 static int sla_load_config(int reload)
05645 {
05646 struct ast_config *cfg;
05647 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05648 const char *cat = NULL;
05649 int res = 0;
05650 const char *val;
05651
05652 if (!reload) {
05653 ast_mutex_init(&sla.lock);
05654 ast_cond_init(&sla.cond, NULL);
05655 }
05656
05657 if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags)))
05658 return 0;
05659 else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
05660 return 0;
05661
05662 if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
05663 sla.attempt_callerid = ast_true(val);
05664
05665 while ((cat = ast_category_browse(cfg, cat)) && !res) {
05666 const char *type;
05667 if (!strcasecmp(cat, "general"))
05668 continue;
05669 if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
05670 ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
05671 SLA_CONFIG_FILE);
05672 continue;
05673 }
05674 if (!strcasecmp(type, "trunk"))
05675 res = sla_build_trunk(cfg, cat);
05676 else if (!strcasecmp(type, "station"))
05677 res = sla_build_station(cfg, cat);
05678 else {
05679 ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
05680 SLA_CONFIG_FILE, type);
05681 }
05682 }
05683
05684 ast_config_destroy(cfg);
05685
05686 if (!reload && (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_stations)))
05687 ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
05688
05689 return res;
05690 }
05691
05692 static int load_config(int reload)
05693 {
05694 load_config_meetme();
05695
05696 if (reload) {
05697 sla_queue_event(SLA_EVENT_RELOAD);
05698 ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
05699 "and will be completed when the system is idle.\n");
05700 return 0;
05701 }
05702
05703 return sla_load_config(0);
05704 }
05705
05706 static int unload_module(void)
05707 {
05708 int res = 0;
05709
05710 ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
05711 res = ast_manager_unregister("MeetmeMute");
05712 res |= ast_manager_unregister("MeetmeUnmute");
05713 res |= ast_manager_unregister("MeetmeList");
05714 res |= ast_unregister_application(app4);
05715 res |= ast_unregister_application(app3);
05716 res |= ast_unregister_application(app2);
05717 res |= ast_unregister_application(app);
05718 res |= ast_unregister_application(slastation_app);
05719 res |= ast_unregister_application(slatrunk_app);
05720
05721 ast_devstate_prov_del("Meetme");
05722 ast_devstate_prov_del("SLA");
05723
05724 sla_destroy();
05725
05726 return res;
05727 }
05728
05729 static int load_module(void)
05730 {
05731 int res = 0;
05732
05733 res |= load_config(0);
05734
05735 ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
05736 res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL,
05737 action_meetmemute, "Mute a Meetme user");
05738 res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL,
05739 action_meetmeunmute, "Unmute a Meetme user");
05740 res |= ast_manager_register2("MeetmeList", EVENT_FLAG_REPORTING,
05741 action_meetmelist, "List participants in a conference", mandescr_meetmelist);
05742 res |= ast_register_application(app4, channel_admin_exec, synopsis4, descrip4);
05743 res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
05744 res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
05745 res |= ast_register_application(app, conf_exec, synopsis, descrip);
05746 res |= ast_register_application(slastation_app, sla_station_exec,
05747 slastation_synopsis, slastation_desc);
05748 res |= ast_register_application(slatrunk_app, sla_trunk_exec,
05749 slatrunk_synopsis, slatrunk_desc);
05750
05751 res |= ast_devstate_prov_add("Meetme", meetmestate);
05752 res |= ast_devstate_prov_add("SLA", sla_state);
05753
05754 return res;
05755 }
05756
05757 static int reload(void)
05758 {
05759 return load_config(1);
05760 }
05761
05762 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MeetMe conference bridge",
05763 .load = load_module,
05764 .unload = unload_module,
05765 .reload = reload,
05766 );
05767