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