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