2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Meet me conference bridge
23 * \author Mark Spencer <markster@digium.com>
25 * \ingroup applications
29 <depend>zaptel</depend>
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
42 #include "asterisk/zapata.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/file.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/pbx.h"
49 #include "asterisk/module.h"
50 #include "asterisk/config.h"
51 #include "asterisk/app.h"
52 #include "asterisk/dsp.h"
53 #include "asterisk/musiconhold.h"
54 #include "asterisk/manager.h"
55 #include "asterisk/options.h"
56 #include "asterisk/cli.h"
57 #include "asterisk/say.h"
58 #include "asterisk/utils.h"
59 #include "asterisk/translate.h"
60 #include "asterisk/ulaw.h"
61 #include "asterisk/astobj.h"
62 #include "asterisk/devicestate.h"
63 #include "asterisk/dial.h"
68 #define CONFIG_FILE_NAME "meetme.conf"
69 #define SLA_CONFIG_FILE "sla.conf"
71 /*! each buffer is 20ms, so this is 640ms total */
72 #define DEFAULT_AUDIO_BUFFERS 32
75 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
76 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
77 ADMINFLAG_KICKME = (1 << 3) /*!< User has been kicked */
80 #define MEETME_DELAYDETECTTALK 300
81 #define MEETME_DELAYDETECTENDTALK 1000
83 #define AST_FRAME_BITS 32
95 enum recording_state {
97 MEETME_RECORD_STARTED,
99 MEETME_RECORD_TERMINATE
102 #define CONF_SIZE 320
105 /*! user has admin access on the conference */
106 CONFFLAG_ADMIN = (1 << 0),
107 /*! If set the user can only receive audio from the conference */
108 CONFFLAG_MONITOR = (1 << 1),
109 /*! If set asterisk will exit conference when '#' is pressed */
110 CONFFLAG_POUNDEXIT = (1 << 2),
111 /*! If set asterisk will provide a menu to the user when '*' is pressed */
112 CONFFLAG_STARMENU = (1 << 3),
113 /*! If set the use can only send audio to the conference */
114 CONFFLAG_TALKER = (1 << 4),
115 /*! If set there will be no enter or leave sounds */
116 CONFFLAG_QUIET = (1 << 5),
117 /*! If set, when user joins the conference, they will be told the number
118 * of users that are already in */
119 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
120 /*! Set to run AGI Script in Background */
121 CONFFLAG_AGI = (1 << 7),
122 /*! Set to have music on hold when user is alone in conference */
123 CONFFLAG_MOH = (1 << 8),
124 /*! If set the MeetMe will return if all marked with this flag left */
125 CONFFLAG_MARKEDEXIT = (1 << 9),
126 /*! If set, the MeetMe will wait until a marked user enters */
127 CONFFLAG_WAITMARKED = (1 << 10),
128 /*! If set, the MeetMe will exit to the specified context */
129 CONFFLAG_EXIT_CONTEXT = (1 << 11),
130 /*! If set, the user will be marked */
131 CONFFLAG_MARKEDUSER = (1 << 12),
132 /*! If set, user will be ask record name on entry of conference */
133 CONFFLAG_INTROUSER = (1 << 13),
134 /*! If set, the MeetMe will be recorded */
135 CONFFLAG_RECORDCONF = (1<< 14),
136 /*! If set, the user will be monitored if the user is talking or not */
137 CONFFLAG_MONITORTALKER = (1 << 15),
138 CONFFLAG_DYNAMIC = (1 << 16),
139 CONFFLAG_DYNAMICPIN = (1 << 17),
140 CONFFLAG_EMPTY = (1 << 18),
141 CONFFLAG_EMPTYNOPIN = (1 << 19),
142 CONFFLAG_ALWAYSPROMPT = (1 << 20),
143 /*! If set, treats talking users as muted users */
144 CONFFLAG_OPTIMIZETALKER = (1 << 21),
145 /*! If set, won't speak the extra prompt when the first person
146 * enters the conference */
147 CONFFLAG_NOONLYPERSON = (1 << 22),
148 /*! If set, user will be asked to record name on entry of conference
150 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
151 /*! If set, the user will be initially self-muted */
152 CONFFLAG_STARTMUTED = (1 << 24),
153 /*! Pass DTMF through the conference */
154 CONFFLAG_PASS_DTMF = (1 << 25),
155 CONFFLAG_SLA_STATION = (1 << 26),
156 CONFFLAG_SLA_TRUNK = (1 << 27),
157 /*! If set, the user should continue in the dialplan if kicked out */
158 CONFFLAG_KICK_CONTINUE = (1 << 28)
162 OPT_ARG_WAITMARKED = 0,
163 OPT_ARG_ARRAY_SIZE = 1,
166 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
167 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
168 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
169 AST_APP_OPTION('b', CONFFLAG_AGI ),
170 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
171 AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
172 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
173 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
174 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
175 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
176 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
177 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
178 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
179 AST_APP_OPTION('M', CONFFLAG_MOH ),
180 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
181 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
182 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
183 AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
184 AST_APP_OPTION('q', CONFFLAG_QUIET ),
185 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
186 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
187 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
188 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
189 AST_APP_OPTION('t', CONFFLAG_TALKER ),
190 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
191 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
192 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
193 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
196 static const char *app = "MeetMe";
197 static const char *app2 = "MeetMeCount";
198 static const char *app3 = "MeetMeAdmin";
199 static const char *slastation_app = "SLAStation";
200 static const char *slatrunk_app = "SLATrunk";
202 static const char *synopsis = "MeetMe conference bridge";
203 static const char *synopsis2 = "MeetMe participant count";
204 static const char *synopsis3 = "MeetMe conference Administration";
205 static const char *slastation_synopsis = "Shared Line Appearance Station";
206 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
208 static const char *descrip =
209 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
210 "conference. If the conference number is omitted, the user will be prompted\n"
211 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
212 "is specified, by pressing '#'.\n"
213 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
214 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
215 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
216 "The option string may contain zero or more of the following characters:\n"
217 " 'a' -- set admin mode\n"
218 " 'A' -- set marked mode\n"
219 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
220 " Default: conf-background.agi (Note: This does not work with\n"
221 " non-Zap channels in the same conference)\n"
222 " 'c' -- announce user(s) count on joining a conference\n"
223 " 'C' -- continue in dialplan when kicked out of conference\n"
224 " 'd' -- dynamically add conference\n"
225 " 'D' -- dynamically add conference, prompting for a PIN\n"
226 " 'e' -- select an empty conference\n"
227 " 'E' -- select an empty pinless conference\n"
228 " 'F' -- Pass DTMF through the conference. DTMF used to activate any\n"
229 " conference features will not be passed through.\n"
230 " 'i' -- announce user join/leave with review\n"
231 " 'I' -- announce user join/leave without review\n"
232 " 'l' -- set listen only mode (Listen only, no talking)\n"
233 " 'm' -- set initially muted\n"
234 " 'M' -- enable music on hold when the conference has a single caller\n"
235 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
236 " being muted, meaning (a) No encode is done on transmission and\n"
237 " (b) Received audio that is not registered as talking is omitted\n"
238 " causing no buildup in background noise\n"
239 " 'p' -- allow user to exit the conference by pressing '#'\n"
240 " 'P' -- always prompt for the pin even if it is specified\n"
241 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
242 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
243 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
244 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
246 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
247 " 't' -- set talk only mode. (Talk only, no listening)\n"
248 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
250 " -- wait until the marked user enters the conference\n"
251 " 'x' -- close the conference when last marked user exits\n"
252 " 'X' -- allow user to exit the conference by entering a valid single\n"
253 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
254 " if that variable is not defined.\n"
255 " '1' -- do not play message when first person enters\n";
257 static const char *descrip2 =
258 " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
259 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
260 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
261 "the channel, unless priority n+1 exists, in which case priority progress will\n"
263 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
265 static const char *descrip3 =
266 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
267 " 'e' -- Eject last user that joined\n"
268 " 'k' -- Kick one user out of conference\n"
269 " 'K' -- Kick all users out of conference\n"
270 " 'l' -- Unlock conference\n"
271 " 'L' -- Lock conference\n"
272 " 'm' -- Unmute one user\n"
273 " 'M' -- Mute one user\n"
274 " 'n' -- Unmute all users in the conference\n"
275 " 'N' -- Mute all non-admin users in the conference\n"
276 " 'r' -- Reset one user's volume settings\n"
277 " 'R' -- Reset all users volume settings\n"
278 " 's' -- Lower entire conference speaking volume\n"
279 " 'S' -- Raise entire conference speaking volume\n"
280 " 't' -- Lower one user's talk volume\n"
281 " 'T' -- Raise one user's talk volume\n"
282 " 'u' -- Lower one user's listen volume\n"
283 " 'U' -- Raise one user's listen volume\n"
284 " 'v' -- Lower entire conference listening volume\n"
285 " 'V' -- Raise entire conference listening volume\n"
288 static const char *slastation_desc =
291 static const char *slatrunk_desc =
294 #define MAX_CONFNUM 80
297 /*! \brief The MeetMe Conference object */
298 struct ast_conference {
299 ast_mutex_t playlock; /*!< Conference specific lock (players) */
300 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
301 char confno[MAX_CONFNUM]; /*!< Conference */
302 struct ast_channel *chan; /*!< Announcements channel */
303 struct ast_channel *lchan; /*!< Listen/Record channel */
304 int fd; /*!< Announcements fd */
305 int zapconf; /*!< Zaptel Conf # */
306 int users; /*!< Number of active users */
307 int markedusers; /*!< Number of marked users */
308 time_t start; /*!< Start time (s) */
309 int refcount; /*!< reference count of usage */
310 enum recording_state recording:2; /*!< recording status */
311 unsigned int isdynamic:1; /*!< Created on the fly? */
312 unsigned int locked:1; /*!< Is the conference locked? */
313 pthread_t recordthread; /*!< thread for recording */
314 pthread_attr_t attr; /*!< thread attribute */
315 const char *recordingfilename; /*!< Filename to record the Conference into */
316 const char *recordingformat; /*!< Format to record the Conference in */
317 char pin[MAX_PIN]; /*!< If protected by a PIN */
318 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
319 struct ast_frame *transframe[32];
320 struct ast_frame *origframe;
321 struct ast_trans_pvt *transpath[32];
322 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
323 AST_LIST_ENTRY(ast_conference) list;
326 static AST_LIST_HEAD_STATIC(confs, ast_conference);
329 int desired; /*!< Desired volume adjustment */
330 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
333 struct ast_conf_user {
334 int user_no; /*!< User Number */
335 int userflags; /*!< Flags as set in the conference */
336 int adminflags; /*!< Flags set by the Admin */
337 struct ast_channel *chan; /*!< Connected channel */
338 int talking; /*!< Is user talking */
339 int zapchannel; /*!< Is a Zaptel channel */
340 char usrvalue[50]; /*!< Custom User Value */
341 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
342 time_t jointime; /*!< Time the user joined the conference */
344 struct volume listen;
345 AST_LIST_HEAD_NOLOCK(, ast_frame) frame_q;
346 AST_LIST_ENTRY(ast_conf_user) list;
349 enum sla_trunk_state {
350 SLA_TRUNK_STATE_IDLE,
351 SLA_TRUNK_STATE_RINGING,
353 SLA_TRUNK_STATE_ONHOLD,
356 struct sla_trunk_ref;
359 AST_RWLIST_ENTRY(sla_station) entry;
360 AST_DECLARE_STRING_FIELDS(
361 AST_STRING_FIELD(name);
362 AST_STRING_FIELD(device);
363 AST_STRING_FIELD(autocontext);
365 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
366 struct ast_dial *dial;
369 struct sla_station_ref {
370 AST_LIST_ENTRY(sla_station_ref) entry;
371 struct sla_station *station;
375 AST_RWLIST_ENTRY(sla_trunk) entry;
376 AST_DECLARE_STRING_FIELDS(
377 AST_STRING_FIELD(name);
378 AST_STRING_FIELD(device);
379 AST_STRING_FIELD(autocontext);
381 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
382 /*! Number of stations that use this trunk */
383 unsigned int num_stations;
384 /*! Number of stations currently on a call with this trunk */
385 unsigned int active_stations;
386 /*! Number of stations that have this trunk on hold. */
387 unsigned int hold_stations;
388 struct ast_channel *chan;
389 pthread_t station_thread;
392 struct sla_trunk_ref {
393 AST_LIST_ENTRY(sla_trunk_ref) entry;
394 struct sla_trunk *trunk;
395 enum sla_trunk_state state;
396 struct ast_channel *chan;
399 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
400 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
402 static const char sla_registrar[] = "SLA";
404 enum sla_event_type {
410 enum sla_event_type type;
411 struct sla_station *station;
412 struct sla_trunk_ref *trunk_ref;
413 AST_LIST_ENTRY(sla_event) entry;
417 * \brief A structure for data used by the sla thread
420 /*! The SLA thread ID */
424 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) ringing_trunks;
425 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
428 .thread = AST_PTHREADT_NULL,
431 /*! The number of audio buffers to be allocated on pseudo channels
432 * when in a conference */
433 static int audio_buffers;
435 /*! Map 'volume' levels from -5 through +5 into
436 * decibel (dB) settings for channel drivers
437 * Note: these are not a straight linear-to-dB
438 * conversion... the numbers have been modified
439 * to give the user a better level of adjustability
441 static const char const gain_map[] = {
456 static int admin_exec(struct ast_channel *chan, void *data);
457 static void *recordthread(void *args);
459 static char *istalking(int x)
464 return "(unmonitored)";
466 return "(not talking)";
469 static int careful_write(int fd, unsigned char *data, int len, int block)
476 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
477 res = ioctl(fd, ZT_IOMUX, &x);
481 res = write(fd, data, len);
483 if (errno != EAGAIN) {
484 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
496 static int set_talk_volume(struct ast_conf_user *user, int volume)
500 /* attempt to make the adjustment in the channel driver;
501 if successful, don't adjust in the frame reading routine
503 gain_adjust = gain_map[volume + 5];
505 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
508 static int set_listen_volume(struct ast_conf_user *user, int volume)
512 /* attempt to make the adjustment in the channel driver;
513 if successful, don't adjust in the frame reading routine
515 gain_adjust = gain_map[volume + 5];
517 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
520 static void tweak_volume(struct volume *vol, enum volume_action action)
524 switch (vol->desired) {
539 switch (vol->desired) {
555 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
557 tweak_volume(&user->talk, action);
558 /* attempt to make the adjustment in the channel driver;
559 if successful, don't adjust in the frame reading routine
561 if (!set_talk_volume(user, user->talk.desired))
562 user->talk.actual = 0;
564 user->talk.actual = user->talk.desired;
567 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
569 tweak_volume(&user->listen, action);
570 /* attempt to make the adjustment in the channel driver;
571 if successful, don't adjust in the frame reading routine
573 if (!set_listen_volume(user, user->listen.desired))
574 user->listen.actual = 0;
576 user->listen.actual = user->listen.desired;
579 static void reset_volumes(struct ast_conf_user *user)
581 signed char zero_volume = 0;
583 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
584 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
587 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
593 if (!chan->_softhangup)
594 res = ast_autoservice_start(chan);
596 AST_LIST_LOCK(&confs);
612 careful_write(conf->fd, data, len, 1);
615 AST_LIST_UNLOCK(&confs);
618 ast_autoservice_stop(chan);
622 * \brief Find or create a conference
624 * \param confno The conference name/number
625 * \param pin The regular user pin
626 * \param pinadmin The admin pin
627 * \param make Make the conf if it doesn't exist
628 * \param dynamic Mark the newly created conference as dynamic
629 * \param refcount How many references to mark on the conference
631 * \return A pointer to the conference struct, or NULL if it wasn't found and
632 * make or dynamic were not set.
634 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
636 struct ast_conference *cnf;
637 struct zt_confinfo ztc = { 0, };
639 AST_LIST_LOCK(&confs);
641 AST_LIST_TRAVERSE(&confs, cnf, list) {
642 if (!strcmp(confno, cnf->confno))
646 if (cnf || (!make && !dynamic))
650 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
653 ast_mutex_init(&cnf->playlock);
654 ast_mutex_init(&cnf->listenlock);
655 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
656 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
657 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
658 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
660 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
661 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
662 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
664 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
665 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
667 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
674 /* Setup a new zap conference */
676 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
677 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
678 ast_log(LOG_WARNING, "Error setting conference\n");
680 ast_hangup(cnf->chan);
687 cnf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
689 ast_set_read_format(cnf->lchan, AST_FORMAT_SLINEAR);
690 ast_set_write_format(cnf->lchan, AST_FORMAT_SLINEAR);
692 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
693 if (ioctl(cnf->lchan->fds[0], ZT_SETCONF, &ztc)) {
694 ast_log(LOG_WARNING, "Error setting conference\n");
695 ast_hangup(cnf->lchan);
699 /* Fill the conference struct */
700 cnf->start = time(NULL);
701 cnf->zapconf = ztc.confno;
702 cnf->isdynamic = dynamic ? 1 : 0;
703 if (option_verbose > 2)
704 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
705 AST_LIST_INSERT_HEAD(&confs, cnf, list);
709 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
711 AST_LIST_UNLOCK(&confs);
716 static int meetme_cmd(int fd, int argc, char **argv)
718 /* Process the command */
719 struct ast_conference *cnf;
720 struct ast_conf_user *user;
722 int i = 0, total = 0;
724 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
725 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
726 char cmdline[1024] = "";
729 ast_cli(fd, "Invalid Arguments.\n");
730 /* Check for length so no buffer will overflow... */
731 for (i = 0; i < argc; i++) {
732 if (strlen(argv[i]) > 100)
733 ast_cli(fd, "Invalid Arguments.\n");
736 /* 'MeetMe': List all the conferences */
738 if (AST_LIST_EMPTY(&confs)) {
739 ast_cli(fd, "No active MeetMe conferences.\n");
740 return RESULT_SUCCESS;
742 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
743 AST_LIST_TRAVERSE(&confs, cnf, list) {
744 if (cnf->markedusers == 0)
745 strcpy(cmdline, "N/A ");
747 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
748 hr = (now - cnf->start) / 3600;
749 min = ((now - cnf->start) % 3600) / 60;
750 sec = (now - cnf->start) % 60;
752 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
756 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
757 return RESULT_SUCCESS;
760 return RESULT_SHOWUSAGE;
761 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
762 if (strstr(argv[1], "lock")) {
763 if (strcmp(argv[1], "lock") == 0) {
765 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
768 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
770 } else if (strstr(argv[1], "mute")) {
772 return RESULT_SHOWUSAGE;
773 if (strcmp(argv[1], "mute") == 0) {
775 if (strcmp(argv[3], "all") == 0) {
776 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
778 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
779 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
783 if (strcmp(argv[3], "all") == 0) {
784 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
786 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
787 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
790 } else if (strcmp(argv[1], "kick") == 0) {
792 return RESULT_SHOWUSAGE;
793 if (strcmp(argv[3], "all") == 0) {
795 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
797 /* Kick a single user */
798 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
799 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
801 } else if(strcmp(argv[1], "list") == 0) {
802 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
803 /* List all the users in a conference */
804 if (AST_LIST_EMPTY(&confs)) {
806 ast_cli(fd, "No active conferences.\n");
807 return RESULT_SUCCESS;
809 /* Find the right conference */
810 AST_LIST_TRAVERSE(&confs, cnf, list) {
811 if (strcmp(cnf->confno, argv[2]) == 0)
816 ast_cli(fd, "No such conference: %s.\n",argv[2]);
817 return RESULT_SUCCESS;
819 /* Show all the users */
820 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
822 hr = (now - user->jointime) / 3600;
823 min = ((now - user->jointime) % 3600) / 60;
824 sec = (now - user->jointime) % 60;
826 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
828 S_OR(user->chan->cid.cid_num, "<unknown>"),
829 S_OR(user->chan->cid.cid_name, "<no name>"),
831 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
832 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
833 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
834 istalking(user->talking), hr, min, sec);
836 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
838 S_OR(user->chan->cid.cid_num, ""),
839 S_OR(user->chan->cid.cid_name, ""),
841 user->userflags & CONFFLAG_ADMIN ? "1" : "",
842 user->userflags & CONFFLAG_MONITOR ? "1" : "",
843 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
844 user->talking, hr, min, sec);
848 ast_cli(fd,"%d users in that conference.\n",cnf->users);
850 return RESULT_SUCCESS;
852 return RESULT_SHOWUSAGE;
855 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
857 admin_exec(NULL, cmdline);
862 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
864 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
866 int len = strlen(word);
868 struct ast_conference *cnf = NULL;
869 struct ast_conf_user *usr = NULL;
872 char *myline, *ret = NULL;
874 if (pos == 1) { /* Command */
875 return ast_cli_complete(word, cmds, state);
876 } else if (pos == 2) { /* Conference Number */
877 AST_LIST_LOCK(&confs);
878 AST_LIST_TRAVERSE(&confs, cnf, list) {
879 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
884 ret = ast_strdup(ret); /* dup before releasing the lock */
885 AST_LIST_UNLOCK(&confs);
887 } else if (pos == 3) {
888 /* User Number || Conf Command option*/
889 if (strstr(line, "mute") || strstr(line, "kick")) {
890 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
891 return strdup("all");
893 AST_LIST_LOCK(&confs);
895 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
896 myline = ast_strdupa(line);
897 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
898 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
902 AST_LIST_TRAVERSE(&confs, cnf, list) {
903 if (!strcmp(confno, cnf->confno))
908 /* Search for the user */
909 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
910 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
911 if (!strncasecmp(word, usrno, len) && ++which > state)
915 AST_LIST_UNLOCK(&confs);
916 return usr ? strdup(usrno) : NULL;
917 } else if ( strstr(line, "list") && ( 0 == state ) )
918 return strdup("concise");
924 static const char meetme_usage[] =
925 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
926 " Executes a command for the conference or on a conferee\n";
928 static int sla_show_trunks(int fd, int argc, char **argv)
930 const struct sla_trunk *trunk;
932 ast_cli(fd, "--- Configured SLA Trunks -----------------------------------\n"
933 "-------------------------------------------------------------\n\n");
934 AST_RWLIST_RDLOCK(&sla_trunks);
935 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
936 struct sla_station_ref *station_ref;
937 ast_cli(fd, "--- Trunk Name: %s\n"
938 "--- ==> Device: %s\n"
939 "--- ==> AutoContext: %s\n"
940 "--- ==> Stations ...\n",
941 trunk->name, trunk->device,
942 S_OR(trunk->autocontext, "(none)"));
943 AST_RWLIST_RDLOCK(&sla_stations);
944 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
945 ast_cli(fd, "--- =====> Station name: %s\n", station_ref->station->name);
946 AST_RWLIST_UNLOCK(&sla_stations);
949 AST_RWLIST_UNLOCK(&sla_trunks);
950 ast_cli(fd, "-------------------------------------------------------------\n");
952 return RESULT_SUCCESS;
955 static const char *trunkstate2str(enum sla_trunk_state state)
957 #define S(e) case e: return # e;
959 S(SLA_TRUNK_STATE_IDLE)
960 S(SLA_TRUNK_STATE_RINGING)
961 S(SLA_TRUNK_STATE_UP)
962 S(SLA_TRUNK_STATE_ONHOLD)
964 return "Uknown State";
968 static const char sla_show_trunks_usage[] =
969 "Usage: sla show trunks\n"
970 " This will list all trunks defined in sla.conf\n";
972 static int sla_show_stations(int fd, int argc, char **argv)
974 const struct sla_station *station;
976 ast_cli(fd, "--- Configured SLA Stations ---------------------------------\n"
977 "-------------------------------------------------------------\n\n");
978 AST_RWLIST_RDLOCK(&sla_stations);
979 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
980 struct sla_trunk_ref *trunk_ref;
981 ast_cli(fd, "--- Station Name: %s\n"
982 "--- ==> Device: %s\n"
983 "--- ==> AutoContext: %s\n"
984 "--- ==> Trunks ...\n",
985 station->name, station->device,
986 S_OR(station->autocontext, "(none)"));
987 AST_RWLIST_RDLOCK(&sla_trunks);
988 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry)
989 ast_cli(fd, "--- =====> Trunk Name: %s State: %s\n",
990 trunk_ref->trunk->name, trunkstate2str(trunk_ref->state));
991 AST_RWLIST_UNLOCK(&sla_trunks);
994 AST_RWLIST_UNLOCK(&sla_stations);
995 ast_cli(fd, "-------------------------------------------------------------\n");
997 return RESULT_SUCCESS;
1000 static const char sla_show_stations_usage[] =
1001 "Usage: sla show stations\n"
1002 " This will list all stations defined in sla.conf\n";
1004 static struct ast_cli_entry cli_meetme[] = {
1005 { { "meetme", NULL, NULL },
1006 meetme_cmd, "Execute a command on a conference or conferee",
1007 meetme_usage, complete_meetmecmd },
1009 { { "sla", "show", "trunks", NULL },
1010 sla_show_trunks, "Show SLA Trunks",
1011 sla_show_trunks_usage, NULL },
1013 { { "sla", "show", "stations", NULL },
1014 sla_show_stations, "Show SLA Stations",
1015 sla_show_stations_usage, NULL },
1018 static void conf_flush(int fd, struct ast_channel *chan)
1022 /* read any frames that may be waiting on the channel
1026 struct ast_frame *f;
1028 /* when no frames are available, this will wait
1029 for 1 millisecond maximum
1031 while (ast_waitfor(chan, 1)) {
1035 else /* channel was hung up or something else happened */
1040 /* flush any data sitting in the pseudo channel */
1042 if (ioctl(fd, ZT_FLUSH, &x))
1043 ast_log(LOG_WARNING, "Error flushing channel\n");
1047 /* Remove the conference from the list and free it.
1048 We assume that this was called while holding conflock. */
1049 static int conf_free(struct ast_conference *conf)
1053 AST_LIST_REMOVE(&confs, conf, list);
1054 manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1056 if (conf->recording == MEETME_RECORD_ACTIVE) {
1057 conf->recording = MEETME_RECORD_TERMINATE;
1058 AST_LIST_UNLOCK(&confs);
1061 AST_LIST_LOCK(&confs);
1062 if (conf->recording == MEETME_RECORD_OFF)
1064 AST_LIST_UNLOCK(&confs);
1068 for (x=0;x<AST_FRAME_BITS;x++) {
1069 if (conf->transframe[x])
1070 ast_frfree(conf->transframe[x]);
1071 if (conf->transpath[x])
1072 ast_translator_free_path(conf->transpath[x]);
1074 if (conf->origframe)
1075 ast_frfree(conf->origframe);
1077 ast_hangup(conf->lchan);
1079 ast_hangup(conf->chan);
1088 static void conf_queue_dtmf(const struct ast_conference *conf,
1089 const struct ast_conf_user *sender, const struct ast_frame *_f)
1091 struct ast_frame *f;
1092 struct ast_conf_user *user;
1094 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1097 if (!(f = ast_frdup(_f)))
1099 AST_LIST_INSERT_TAIL(&user->frame_q, f, frame_list);
1103 static void sla_queue_event(enum sla_event_type type, const struct ast_channel *chan,
1104 struct ast_conference *conf)
1106 struct sla_event *event;
1107 struct sla_station *station;
1108 struct sla_trunk_ref *trunk_ref = NULL;
1111 trunk_name = ast_strdupa(conf->confno);
1112 strsep(&trunk_name, "_");
1113 if (ast_strlen_zero(trunk_name)) {
1114 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1118 AST_RWLIST_RDLOCK(&sla_stations);
1119 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1120 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1121 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1127 AST_RWLIST_UNLOCK(&sla_stations);
1130 ast_log(LOG_DEBUG, "Trunk not found for event!\n");
1134 if (!(event = ast_calloc(1, sizeof(*event))))
1138 event->trunk_ref = trunk_ref;
1139 event->station = station;
1141 ast_mutex_lock(&sla.lock);
1142 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1143 ast_cond_signal(&sla.cond);
1144 ast_mutex_unlock(&sla.lock);
1147 /* Decrement reference counts, as incremented by find_conf() */
1148 static int dispose_conf(struct ast_conference *conf)
1152 AST_LIST_LOCK(&confs);
1153 if (ast_atomic_dec_and_test(&conf->refcount)) {
1157 AST_LIST_UNLOCK(&confs);
1163 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1165 struct ast_conf_user *user = NULL;
1166 struct ast_conf_user *usr = NULL;
1168 struct zt_confinfo ztc, ztc_empty;
1169 struct ast_frame *f;
1170 struct ast_channel *c;
1171 struct ast_frame fr;
1179 int musiconhold = 0;
1182 int currentmarked = 0;
1185 int menu_active = 0;
1186 int using_pseudo = 0;
1191 struct ast_dsp *dsp=NULL;
1192 struct ast_app *app;
1193 const char *agifile;
1194 const char *agifiledefault = "conf-background.agi";
1195 char meetmesecs[30] = "";
1196 char exitcontext[AST_MAX_CONTEXT] = "";
1197 char recordingtmp[AST_MAX_EXTENSION] = "";
1198 char members[10] = "";
1199 int dtmf, opt_waitmarked_timeout = 0;
1202 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1203 char *buf = __buf + AST_FRIENDLY_OFFSET;
1205 if (!(user = ast_calloc(1, sizeof(*user))))
1208 /* Possible timeout waiting for marked user */
1209 if ((confflags & CONFFLAG_WAITMARKED) &&
1210 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1211 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1212 (opt_waitmarked_timeout > 0)) {
1213 timeout = time(NULL) + opt_waitmarked_timeout;
1216 if (confflags & CONFFLAG_RECORDCONF) {
1217 if (!conf->recordingfilename) {
1218 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1219 if (!conf->recordingfilename) {
1220 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1221 conf->recordingfilename = ast_strdupa(recordingtmp);
1223 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1224 if (!conf->recordingformat) {
1225 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1226 conf->recordingformat = ast_strdupa(recordingtmp);
1228 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1229 conf->confno, conf->recordingfilename, conf->recordingformat);
1233 if ((conf->recording == MEETME_RECORD_OFF) && ((confflags & CONFFLAG_RECORDCONF) || (conf->lchan))) {
1234 pthread_attr_init(&conf->attr);
1235 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
1236 ast_pthread_create_background(&conf->recordthread, &conf->attr, recordthread, conf);
1237 pthread_attr_destroy(&conf->attr);
1240 time(&user->jointime);
1242 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1243 /* Sorry, but this conference is locked! */
1244 if (!ast_streamfile(chan, "conf-locked", chan->language))
1245 ast_waitstream(chan, "");
1249 if (confflags & CONFFLAG_MARKEDUSER)
1250 conf->markedusers++;
1252 ast_mutex_lock(&conf->playlock);
1254 if (AST_LIST_EMPTY(&conf->userlist))
1257 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1259 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1262 user->userflags = confflags;
1263 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1267 snprintf(members, sizeof(members), "%d", conf->users);
1268 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1270 /* This device changed state now - if this is the first user */
1271 if (conf->users == 1)
1272 ast_device_state_changed("meetme:%s", conf->confno);
1274 ast_mutex_unlock(&conf->playlock);
1276 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1277 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1278 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1279 else if (!ast_strlen_zero(chan->macrocontext))
1280 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1282 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1285 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1286 snprintf(user->namerecloc, sizeof(user->namerecloc),
1287 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1288 conf->confno, user->user_no);
1289 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1290 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1292 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1297 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1298 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1299 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1300 ast_waitstream(chan, "");
1301 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1302 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1303 ast_waitstream(chan, "");
1306 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1307 int keepplaying = 1;
1309 if (conf->users == 2) {
1310 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1311 res = ast_waitstream(chan, AST_DIGIT_ANY);
1312 ast_stopstream(chan);
1319 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1320 res = ast_waitstream(chan, AST_DIGIT_ANY);
1321 ast_stopstream(chan);
1328 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1334 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1335 res = ast_waitstream(chan, AST_DIGIT_ANY);
1336 ast_stopstream(chan);
1345 ast_indicate(chan, -1);
1347 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1348 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1352 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1353 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1357 retryzap = strcasecmp(chan->tech->type, "Zap");
1358 user->zapchannel = !retryzap;
1361 origfd = chan->fds[0];
1363 fd = open("/dev/zap/pseudo", O_RDWR);
1365 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1369 /* Make non-blocking */
1370 flags = fcntl(fd, F_GETFL);
1372 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1376 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1377 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1381 /* Setup buffering information */
1382 memset(&bi, 0, sizeof(bi));
1383 bi.bufsize = CONF_SIZE/2;
1384 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1385 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1386 bi.numbufs = audio_buffers;
1387 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1388 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1393 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1394 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1400 /* XXX Make sure we're not running on a pseudo channel XXX */
1404 memset(&ztc, 0, sizeof(ztc));
1405 memset(&ztc_empty, 0, sizeof(ztc_empty));
1406 /* Check to see if we're in a conference... */
1408 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1409 ast_log(LOG_WARNING, "Error getting conference\n");
1414 /* Whoa, already in a conference... Retry... */
1417 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1422 memset(&ztc, 0, sizeof(ztc));
1423 /* Add us to the conference */
1425 ztc.confno = conf->zapconf;
1427 ast_mutex_lock(&conf->playlock);
1429 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1430 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1431 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1432 ast_waitstream(conf->chan, "");
1433 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1434 ast_waitstream(conf->chan, "");
1438 if (confflags & CONFFLAG_MONITOR)
1439 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1440 else if (confflags & CONFFLAG_TALKER)
1441 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1443 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1445 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1446 ast_log(LOG_WARNING, "Error setting conference\n");
1448 ast_mutex_unlock(&conf->playlock);
1452 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1455 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1460 chan->name, chan->uniqueid, conf->confno, user->user_no);
1464 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1466 if (!(confflags & CONFFLAG_QUIET))
1467 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1468 conf_play(chan, conf, ENTER);
1471 ast_mutex_unlock(&conf->playlock);
1473 conf_flush(fd, chan);
1475 if (confflags & CONFFLAG_AGI) {
1476 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1477 or use default filename of conf-background.agi */
1479 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1481 agifile = agifiledefault;
1483 if (user->zapchannel) {
1484 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1486 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1488 /* Find a pointer to the agi app and execute the script */
1489 app = pbx_findapp("agi");
1491 char *s = ast_strdupa(agifile);
1492 ret = pbx_exec(chan, app, s);
1494 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1497 if (user->zapchannel) {
1498 /* Remove CONFMUTE mode on Zap channel */
1500 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1503 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1504 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1506 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1508 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1509 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1513 int menu_was_active = 0;
1518 if (timeout && time(NULL) >= timeout)
1521 /* if we have just exited from the menu, and the user had a channel-driver
1522 volume adjustment, restore it
1524 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1525 set_talk_volume(user, user->listen.desired);
1527 menu_was_active = menu_active;
1529 currentmarked = conf->markedusers;
1530 if (!(confflags & CONFFLAG_QUIET) &&
1531 (confflags & CONFFLAG_MARKEDUSER) &&
1532 (confflags & CONFFLAG_WAITMARKED) &&
1534 if (currentmarked == 1 && conf->users > 1) {
1535 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1536 if (conf->users - 1 == 1) {
1537 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1538 ast_waitstream(chan, "");
1540 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1541 ast_waitstream(chan, "");
1544 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1545 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1546 ast_waitstream(chan, "");
1549 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1552 /* Update the struct with the actual confflags */
1553 user->userflags = confflags;
1555 if (confflags & CONFFLAG_WAITMARKED) {
1556 if(currentmarked == 0) {
1557 if (lastmarked != 0) {
1558 if (!(confflags & CONFFLAG_QUIET))
1559 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1560 ast_waitstream(chan, "");
1561 if (confflags & CONFFLAG_MARKEDEXIT) {
1562 if (confflags & CONFFLAG_KICK_CONTINUE)
1566 ztc.confmode = ZT_CONF_CONF;
1567 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1568 ast_log(LOG_WARNING, "Error setting conference\n");
1574 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1575 ast_moh_start(chan, NULL, NULL);
1578 ztc.confmode = ZT_CONF_CONF;
1579 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1580 ast_log(LOG_WARNING, "Error setting conference\n");
1585 } else if(currentmarked >= 1 && lastmarked == 0) {
1586 /* Marked user entered, so cancel timeout */
1588 if (confflags & CONFFLAG_MONITOR)
1589 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1590 else if (confflags & CONFFLAG_TALKER)
1591 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1593 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1594 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1595 ast_log(LOG_WARNING, "Error setting conference\n");
1599 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1603 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1604 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1605 ast_waitstream(chan, "");
1606 conf_play(chan, conf, ENTER);
1611 /* trying to add moh for single person conf */
1612 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1613 if (conf->users == 1) {
1614 if (musiconhold == 0) {
1615 ast_moh_start(chan, NULL, NULL);
1626 /* Leave if the last marked user left */
1627 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1628 if (confflags & CONFFLAG_KICK_CONTINUE)
1635 /* Check if my modes have changed */
1637 /* If I should be muted but am still talker, mute me */
1638 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1639 ztc.confmode ^= ZT_CONF_TALKER;
1640 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1641 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1646 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1652 chan->name, chan->uniqueid, conf->confno, user->user_no);
1655 /* If I should be un-muted but am not talker, un-mute me */
1656 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1657 ztc.confmode |= ZT_CONF_TALKER;
1658 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1659 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1664 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1670 chan->name, chan->uniqueid, conf->confno, user->user_no);
1673 /* If I have been kicked, exit the conference */
1674 if (user->adminflags & ADMINFLAG_KICKME) {
1675 //You have been kicked.
1676 if (!(confflags & CONFFLAG_QUIET) &&
1677 !ast_streamfile(chan, "conf-kicked", chan->language)) {
1678 ast_waitstream(chan, "");
1685 if (c->fds[0] != origfd) {
1687 /* Kill old pseudo */
1692 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1693 retryzap = strcasecmp(c->tech->type, "Zap");
1694 user->zapchannel = !retryzap;
1697 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1698 f = ast_read_noaudio(c);
1703 if (!AST_LIST_EMPTY(&user->frame_q)) {
1704 struct ast_frame *f;
1705 f = AST_LIST_REMOVE_HEAD(&user->frame_q, frame_list);
1706 if (ast_write(chan, f) < 0) {
1707 ast_log(LOG_WARNING, "Error writing frame to channel!\n");
1711 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1712 if (user->talk.actual)
1713 ast_frame_adjust_volume(f, user->talk.actual);
1715 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1718 if (user->talking == -1)
1721 res = ast_dsp_silence(dsp, f, &totalsilence);
1722 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1724 if (confflags & CONFFLAG_MONITORTALKER)
1725 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1731 chan->name, chan->uniqueid, conf->confno, user->user_no);
1733 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1735 if (confflags & CONFFLAG_MONITORTALKER)
1736 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1742 chan->name, chan->uniqueid, conf->confno, user->user_no);
1746 /* Absolutely do _not_ use careful_write here...
1747 it is important that we read data from the channel
1748 as fast as it arrives, and feed it into the conference.
1749 The buffering in the pseudo channel will take care of any
1750 timing differences, unless they are so drastic as to lose
1751 audio frames (in which case carefully writing would only
1752 have delayed the audio even further).
1754 /* As it turns out, we do want to use careful write. We just
1755 don't want to block, but we do want to at least *try*
1756 to write out all the samples.
1758 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1759 careful_write(fd, f->data, f->datalen, 0);
1761 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1764 tmp[0] = f->subclass;
1766 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1768 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
1772 } else if (option_debug > 1)
1773 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1774 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1778 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1779 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1780 ast_log(LOG_WARNING, "Error setting conference\n");
1786 /* if we are entering the menu, and the user has a channel-driver
1787 volume adjustment, clear it
1789 if (!menu_active && user->talk.desired && !user->talk.actual)
1790 set_talk_volume(user, 0);
1795 if ((confflags & CONFFLAG_ADMIN)) {
1799 /* Record this sound! */
1800 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
1801 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1802 ast_stopstream(chan);
1809 case '1': /* Un/Mute */
1812 /* for admin, change both admin and use flags */
1813 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
1814 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1816 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
1818 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1819 if (!ast_streamfile(chan, "conf-muted", chan->language))
1820 ast_waitstream(chan, "");
1822 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1823 ast_waitstream(chan, "");
1826 case '2': /* Un/Lock the Conference */
1830 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1831 ast_waitstream(chan, "");
1834 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1835 ast_waitstream(chan, "");
1838 case '3': /* Eject last user */
1840 usr = AST_LIST_LAST(&conf->userlist);
1841 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1842 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1843 ast_waitstream(chan, "");
1845 usr->adminflags |= ADMINFLAG_KICKME;
1846 ast_stopstream(chan);
1849 tweak_listen_volume(user, VOL_DOWN);
1852 tweak_listen_volume(user, VOL_UP);
1855 tweak_talk_volume(user, VOL_DOWN);
1861 tweak_talk_volume(user, VOL_UP);
1865 /* Play an error message! */
1866 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1867 ast_waitstream(chan, "");
1875 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
1876 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1877 ast_stopstream(chan);
1884 case '1': /* Un/Mute */
1887 /* user can only toggle the self-muted state */
1888 user->adminflags ^= ADMINFLAG_SELFMUTED;
1890 /* they can't override the admin mute state */
1891 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
1892 if (!ast_streamfile(chan, "conf-muted", chan->language))
1893 ast_waitstream(chan, "");
1895 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1896 ast_waitstream(chan, "");
1900 tweak_listen_volume(user, VOL_DOWN);
1903 tweak_listen_volume(user, VOL_UP);
1906 tweak_talk_volume(user, VOL_DOWN);
1912 tweak_talk_volume(user, VOL_UP);
1916 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1917 ast_waitstream(chan, "");
1923 ast_moh_start(chan, NULL, NULL);
1925 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1926 ast_log(LOG_WARNING, "Error setting conference\n");
1932 conf_flush(fd, chan);
1933 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
1934 && confflags & CONFFLAG_PASS_DTMF) {
1935 conf_queue_dtmf(conf, user, f);
1936 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
1937 switch (f->subclass) {
1938 case AST_CONTROL_HOLD:
1939 ast_log(LOG_DEBUG, "Got a HOLD frame!\n");
1940 sla_queue_event(SLA_EVENT_HOLD, chan, conf);
1942 case AST_CONTROL_UNHOLD:
1943 ast_log(LOG_DEBUG, "Got a UNHOLD frame!\n");
1944 sla_queue_event(SLA_EVENT_UNHOLD, chan, conf);
1948 } else if (option_debug) {
1950 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
1951 chan->name, f->frametype, f->subclass);
1954 } else if (outfd > -1) {
1955 res = read(outfd, buf, CONF_SIZE);
1957 memset(&fr, 0, sizeof(fr));
1958 fr.frametype = AST_FRAME_VOICE;
1959 fr.subclass = AST_FORMAT_SLINEAR;
1963 fr.offset = AST_FRIENDLY_OFFSET;
1964 if (!user->listen.actual &&
1965 ((confflags & CONFFLAG_MONITOR) ||
1966 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
1967 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
1970 for (index=0;index<AST_FRAME_BITS;index++)
1971 if (chan->rawwriteformat & (1 << index))
1973 if (index >= AST_FRAME_BITS)
1974 goto bailoutandtrynormal;
1975 ast_mutex_lock(&conf->listenlock);
1976 if (!conf->transframe[index]) {
1977 if (conf->origframe) {
1978 if (!conf->transpath[index])
1979 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
1980 if (conf->transpath[index]) {
1981 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
1982 if (!conf->transframe[index])
1983 conf->transframe[index] = &ast_null_frame;
1987 if (conf->transframe[index]) {
1988 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
1989 if (ast_write(chan, conf->transframe[index]))
1990 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
1993 ast_mutex_unlock(&conf->listenlock);
1994 goto bailoutandtrynormal;
1996 ast_mutex_unlock(&conf->listenlock);
1998 bailoutandtrynormal:
1999 if (user->listen.actual)
2000 ast_frame_adjust_volume(&fr, user->listen.actual);
2001 if (ast_write(chan, &fr) < 0) {
2002 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2006 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2008 lastmarked = currentmarked;
2018 /* Take out of conference */
2022 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2023 ast_log(LOG_WARNING, "Error setting conference\n");
2027 reset_volumes(user);
2029 AST_LIST_LOCK(&confs);
2030 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2031 conf_play(chan, conf, LEAVE);
2033 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2034 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2035 if ((conf->chan) && (conf->users > 1)) {
2036 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2037 ast_waitstream(conf->chan, "");
2038 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2039 ast_waitstream(conf->chan, "");
2041 ast_filedelete(user->namerecloc, NULL);
2044 AST_LIST_UNLOCK(&confs);
2047 AST_LIST_LOCK(&confs);
2052 if (user->user_no) { /* Only cleanup users who really joined! */
2054 hr = (now - user->jointime) / 3600;
2055 min = ((now - user->jointime) % 3600) / 60;
2056 sec = (now - user->jointime) % 60;
2059 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2064 "CallerIDNum: %s\r\n"
2065 "CallerIDName: %s\r\n"
2066 "Duration: %ld\r\n",
2067 chan->name, chan->uniqueid, conf->confno,
2069 S_OR(user->chan->cid.cid_num, "<unknown>"),
2070 S_OR(user->chan->cid.cid_name, "<unknown>"),
2071 (long)(now - user->jointime));
2076 snprintf(members, sizeof(members), "%d", conf->users);
2077 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2078 if (confflags & CONFFLAG_MARKEDUSER)
2079 conf->markedusers--;
2080 /* Remove ourselves from the list */
2081 AST_LIST_REMOVE(&conf->userlist, user, list);
2083 /* Change any states */
2085 ast_device_state_changed("meetme:%s", conf->confno);
2087 /* Return the number of seconds the user was in the conf */
2088 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2089 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2092 AST_LIST_UNLOCK(&confs);
2097 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2098 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2100 struct ast_variable *var;
2101 struct ast_conference *cnf;
2103 /* Check first in the conference list */
2104 AST_LIST_LOCK(&confs);
2105 AST_LIST_TRAVERSE(&confs, cnf, list) {
2106 if (!strcmp(confno, cnf->confno))
2110 cnf->refcount += refcount;
2112 AST_LIST_UNLOCK(&confs);
2115 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2117 var = ast_load_realtime("meetme", "confno", confno, NULL);
2123 if (!strcasecmp(var->name, "pin")) {
2124 pin = ast_strdupa(var->value);
2125 } else if (!strcasecmp(var->name, "adminpin")) {
2126 pinadmin = ast_strdupa(var->value);
2130 ast_variables_destroy(var);
2132 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
2136 if (confflags && !cnf->chan &&
2137 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2138 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2139 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2140 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2143 if (confflags && !cnf->chan &&
2144 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2145 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2146 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2154 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2155 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2157 struct ast_config *cfg;
2158 struct ast_variable *var;
2159 struct ast_conference *cnf;
2161 AST_DECLARE_APP_ARGS(args,
2162 AST_APP_ARG(confno);
2164 AST_APP_ARG(pinadmin);
2167 /* Check first in the conference list */
2168 AST_LIST_LOCK(&confs);
2169 AST_LIST_TRAVERSE(&confs, cnf, list) {
2170 if (!strcmp(confno, cnf->confno))
2174 cnf->refcount += refcount;
2176 AST_LIST_UNLOCK(&confs);
2180 /* No need to parse meetme.conf */
2182 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
2184 if (dynamic_pin[0] == 'q') {
2185 /* Query the user to enter a PIN */
2186 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2189 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
2191 cnf = build_conf(confno, "", "", make, dynamic, refcount);
2194 /* Check the config */
2195 cfg = ast_config_load(CONFIG_FILE_NAME);
2197 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2200 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2201 if (strcasecmp(var->name, "conf"))
2204 if (!(parse = ast_strdupa(var->value)))
2207 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
2208 if (!strcasecmp(args.confno, confno)) {
2209 /* Bingo it's a valid conference */
2210 cnf = build_conf(args.confno,
2212 S_OR(args.pinadmin, ""),
2213 make, dynamic, refcount);
2218 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
2220 ast_config_destroy(cfg);
2222 } else if (dynamic_pin) {
2223 /* Correct for the user selecting 'D' instead of 'd' to have
2224 someone join into a conference that has already been created
2226 if (dynamic_pin[0] == 'q')
2227 dynamic_pin[0] = '\0';
2231 if (confflags && !cnf->chan &&
2232 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2233 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2234 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2235 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2238 if (confflags && !cnf->chan &&
2239 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2240 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2241 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2248 /*! \brief The MeetmeCount application */
2249 static int count_exec(struct ast_channel *chan, void *data)
2251 struct ast_module_user *u;
2253 struct ast_conference *conf;
2257 AST_DECLARE_APP_ARGS(args,
2258 AST_APP_ARG(confno);
2259 AST_APP_ARG(varname);
2262 if (ast_strlen_zero(data)) {
2263 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2267 u = ast_module_user_add(chan);
2269 if (!(localdata = ast_strdupa(data))) {
2270 ast_module_user_remove(u);
2274 AST_STANDARD_APP_ARGS(args, localdata);
2276 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2279 count = conf->users;
2285 if (!ast_strlen_zero(args.varname)){
2286 /* have var so load it and exit */
2287 snprintf(val, sizeof(val), "%d",count);
2288 pbx_builtin_setvar_helper(chan, args.varname, val);
2290 if (chan->_state != AST_STATE_UP)
2292 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2294 ast_module_user_remove(u);
2299 /*! \brief The meetme() application */
2300 static int conf_exec(struct ast_channel *chan, void *data)
2303 struct ast_module_user *u;
2304 char confno[MAX_CONFNUM] = "";
2307 struct ast_conference *cnf = NULL;
2308 struct ast_flags confflags = {0};
2310 int empty = 0, empty_no_pin = 0;
2311 int always_prompt = 0;
2312 char *notdata, *info, the_pin[MAX_PIN] = "";
2313 AST_DECLARE_APP_ARGS(args,
2314 AST_APP_ARG(confno);
2315 AST_APP_ARG(options);
2318 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2320 u = ast_module_user_add(chan);
2322 if (ast_strlen_zero(data)) {
2329 if (chan->_state != AST_STATE_UP)
2332 info = ast_strdupa(notdata);
2334 AST_STANDARD_APP_ARGS(args, info);
2337 ast_copy_string(confno, args.confno, sizeof(confno));
2338 if (ast_strlen_zero(confno)) {
2344 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2347 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2348 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2349 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2350 strcpy(the_pin, "q");
2352 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2353 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2354 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2361 int i, map[1024] = { 0, };
2362 struct ast_config *cfg;
2363 struct ast_variable *var;
2366 AST_LIST_LOCK(&confs);
2367 AST_LIST_TRAVERSE(&confs, cnf, list) {
2368 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
2369 /* Disqualify in use conference */
2370 if (confno_int >= 0 && confno_int < 1024)
2374 AST_LIST_UNLOCK(&confs);
2376 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2377 if ((empty_no_pin) || (!dynamic)) {
2378 cfg = ast_config_load(CONFIG_FILE_NAME);
2380 var = ast_variable_browse(cfg, "rooms");
2382 if (!strcasecmp(var->name, "conf")) {
2383 char *stringp = ast_strdupa(var->value);
2385 char *confno_tmp = strsep(&stringp, "|,");
2387 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
2388 if ((confno_int >= 0) && (confno_int < 1024)) {
2389 if (stringp && empty_no_pin) {
2395 /* For static: run through the list and see if this conference is empty */
2396 AST_LIST_LOCK(&confs);
2397 AST_LIST_TRAVERSE(&confs, cnf, list) {
2398 if (!strcmp(confno_tmp, cnf->confno)) {
2399 /* The conference exists, therefore it's not empty */
2404 AST_LIST_UNLOCK(&confs);
2406 /* At this point, we have a confno_tmp (static conference) that is empty */
2407 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2408 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2409 * Case 2: empty_no_pin and pin is blank (but not NULL)
2410 * Case 3: not empty_no_pin
2412 ast_copy_string(confno, confno_tmp, sizeof(confno));
2414 /* XXX the map is not complete (but we do have a confno) */
2422 ast_config_destroy(cfg);
2426 /* Select first conference number not in use */
2427 if (ast_strlen_zero(confno) && dynamic) {
2428 for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
2430 snprintf(confno, sizeof(confno), "%d", i);
2437 if (ast_strlen_zero(confno)) {
2438 res = ast_streamfile(chan, "conf-noempty", chan->language);
2440 ast_waitstream(chan, "");
2442 if (sscanf(confno, "%d", &confno_int) == 1) {
2443 if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
2444 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2446 ast_waitstream(chan, "");
2447 res = ast_say_digits(chan, confno_int, "", chan->language);
2451 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2456 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2457 /* Prompt user for conference number */
2458 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2460 /* Don't try to validate when we catch an error */
2466 if (!ast_strlen_zero(confno)) {
2467 /* Check the validity of the conference */
2468 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
2469 sizeof(the_pin), 1, &confflags);
2471 cnf = find_conf_realtime(chan, confno, 1, dynamic,
2472 the_pin, sizeof(the_pin), 1, &confflags);
2476 res = ast_streamfile(chan, "conf-invalid", chan->language);
2478 ast_waitstream(chan, "");
2483 if ((!ast_strlen_zero(cnf->pin) &&
2484 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2485 (!ast_strlen_zero(cnf->pinadmin) &&
2486 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2487 char pin[MAX_PIN] = "";
2490 /* Allow the pin to be retried up to 3 times */
2491 for (j = 0; j < 3; j++) {
2492 if (*the_pin && (always_prompt == 0)) {
2493 ast_copy_string(pin, the_pin, sizeof(pin));
2496 /* Prompt user for pin if pin is required */
2497 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2500 if (!strcasecmp(pin, cnf->pin) ||
2501 (!ast_strlen_zero(cnf->pinadmin) &&
2502 !strcasecmp(pin, cnf->pinadmin))) {
2505 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2506 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2507 /* Run the conference */
2508 res = conf_run(chan, cnf, confflags.flags, optargs);
2512 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
2513 res = ast_waitstream(chan, AST_DIGIT_ANY);
2514 ast_stopstream(chan);
2517 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2529 /* failed when getting the pin */
2532 /* see if we need to get rid of the conference */
2536 /* Don't retry pin with a static pin */
2537 if (*the_pin && (always_prompt==0)) {
2542 /* No pin required */
2545 /* Run the conference */
2546 res = conf_run(chan, cnf, confflags.flags, optargs);
2552 } while (allowretry);
2557 ast_module_user_remove(u);
2562 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2564 struct ast_conf_user *user = NULL;
2567 sscanf(callerident, "%i", &cid);
2568 if (conf && callerident) {
2569 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2570 if (cid == user->user_no)
2577 /*! \brief The MeetMeadmin application */
2578 /* MeetMeAdmin(confno, command, caller) */
2579 static int admin_exec(struct ast_channel *chan, void *data) {
2581 struct ast_conference *cnf;
2582 struct ast_conf_user *user = NULL;
2583 struct ast_module_user *u;
2584 AST_DECLARE_APP_ARGS(args,
2585 AST_APP_ARG(confno);
2586 AST_APP_ARG(command);
2590 if (ast_strlen_zero(data)) {
2591 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
2595 u = ast_module_user_add(chan);
2597 AST_LIST_LOCK(&confs);
2599 params = ast_strdupa(data);
2600 AST_STANDARD_APP_ARGS(args, params);
2602 if (!args.command) {
2603 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2604 AST_LIST_UNLOCK(&confs);
2605 ast_module_user_remove(u);
2608 AST_LIST_TRAVERSE(&confs, cnf, list) {
2609 if (!strcmp(cnf->confno, args.confno))
2614 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
2615 AST_LIST_UNLOCK(&confs);
2616 ast_module_user_remove(u);
2620 ast_atomic_fetchadd_int(&cnf->refcount, 1);
2623 user = find_user(cnf, args.user);
2625 switch (*args.command) {
2626 case 76: /* L: Lock */
2629 case 108: /* l: Unlock */
2632 case 75: /* K: kick all users */
2633 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2634 user->adminflags |= ADMINFLAG_KICKME;
2636 case 101: /* e: Eject last user*/
2637 user = AST_LIST_LAST(&cnf->userlist);
2638 if (!(user->userflags & CONFFLAG_ADMIN))
2639 user->adminflags |= ADMINFLAG_KICKME;
2641 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2643 case 77: /* M: Mute */
2645 user->adminflags |= ADMINFLAG_MUTED;
2647 ast_log(LOG_NOTICE, "Specified User not found!\n");
2649 case 78: /* N: Mute all (non-admin) users */
2650 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2651 if (!(user->userflags & CONFFLAG_ADMIN))
2652 user->adminflags |= ADMINFLAG_MUTED;
2655 case 109: /* m: Unmute */
2657 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2659 ast_log(LOG_NOTICE, "Specified User not found!\n");
2661 case 110: /* n: Unmute all users */
2662 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2663 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2665 case 107: /* k: Kick user */
2667 user->adminflags |= ADMINFLAG_KICKME;
2669 ast_log(LOG_NOTICE, "Specified User not found!\n");
2671 case 118: /* v: Lower all users listen volume */
2672 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2673 tweak_listen_volume(user, VOL_DOWN);
2675 case 86: /* V: Raise all users listen volume */
2676 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2677 tweak_listen_volume(user, VOL_UP);
2679 case 115: /* s: Lower all users speaking volume */
2680 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2681 tweak_talk_volume(user, VOL_DOWN);
2683 case 83: /* S: Raise all users speaking volume */
2684 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2685 tweak_talk_volume(user, VOL_UP);
2687 case 82: /* R: Reset all volume levels */
2688 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2689 reset_volumes(user);
2691 case 114: /* r: Reset user's volume level */
2693 reset_volumes(user);
2695 ast_log(LOG_NOTICE, "Specified User not found!\n");
2697 case 85: /* U: Raise user's listen volume */
2699 tweak_listen_volume(user, VOL_UP);
2701 ast_log(LOG_NOTICE, "Specified User not found!\n");
2703 case 117: /* u: Lower user's listen volume */
2705 tweak_listen_volume(user, VOL_DOWN);
2707 ast_log(LOG_NOTICE, "Specified User not found!\n");
2709 case 84: /* T: Raise user's talk volume */
2711 tweak_talk_volume(user, VOL_UP);
2713 ast_log(LOG_NOTICE, "Specified User not found!\n");
2715 case 116: /* t: Lower user's talk volume */
2717 tweak_talk_volume(user, VOL_DOWN);
2719 ast_log(LOG_NOTICE, "Specified User not found!\n");
2723 AST_LIST_UNLOCK(&confs);
2727 ast_module_user_remove(u);
2732 static int meetmemute(struct mansession *s, const struct message *m, int mute)
2734 struct ast_conference *conf;
2735 struct ast_conf_user *user;
2736 const char *confid = astman_get_header(m, "Meetme");
2737 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
2740 if (ast_strlen_zero(confid)) {
2741 astman_send_error(s, m, "Meetme conference not specified");
2745 if (ast_strlen_zero(userid)) {
2746 astman_send_error(s, m, "Meetme user number not specified");
2750 userno = strtoul(userid, &userid, 10);
2753 astman_send_error(s, m, "Invalid user number");
2757 /* Look in the conference list */
2758 AST_LIST_LOCK(&confs);
2759 AST_LIST_TRAVERSE(&confs, conf, list) {
2760 if (!strcmp(confid, conf->confno))
2765 AST_LIST_UNLOCK(&confs);
2766 astman_send_error(s, m, "Meetme conference does not exist");
2770 AST_LIST_TRAVERSE(&conf->userlist, user, list)
2771 if (user->user_no == userno)
2775 AST_LIST_UNLOCK(&confs);
2776 astman_send_error(s, m, "User number not found");
2781 user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
2783 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED); /* request user unmuting */
2785 AST_LIST_UNLOCK(&confs);
2787 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);
2789 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2793 static int action_meetmemute(struct mansession *s, const struct message *m)
2795 return meetmemute(s, m, 1);
2798 static int action_meetmeunmute(struct mansession *s, const struct message *m)
2800 return meetmemute(s, m, 0);
2803 static void *recordthread(void *args)
2805 struct ast_conference *cnf = args;
2806 struct ast_frame *f=NULL;
2808 struct ast_filestream *s=NULL;
2811 const char *oldrecordingfilename = NULL;
2813 if (!cnf || !cnf->lchan) {
2817 ast_stopstream(cnf->lchan);
2818 flags = O_CREAT|O_TRUNC|O_WRONLY;
2821 cnf->recording = MEETME_RECORD_ACTIVE;
2822 while (ast_waitfor(cnf->lchan, -1) > -1) {
2823 if (cnf->recording == MEETME_RECORD_TERMINATE) {
2824 AST_LIST_LOCK(&confs);
2825 AST_LIST_UNLOCK(&confs);
2828 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
2829 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
2830 oldrecordingfilename = cnf->recordingfilename;
2833 f = ast_read(cnf->lchan);
2838 if (f->frametype == AST_FRAME_VOICE) {
2839 ast_mutex_lock(&cnf->listenlock);
2840 for (x=0;x<AST_FRAME_BITS;x++) {
2841 /* Free any translations that have occured */
2842 if (cnf->transframe[x]) {
2843 ast_frfree(cnf->transframe[x]);
2844 cnf->transframe[x] = NULL;
2848 ast_frfree(cnf->origframe);
2850 ast_mutex_unlock(&cnf->listenlock);
2852 res = ast_writestream(s, f);
2860 cnf->recording = MEETME_RECORD_OFF;
2867 /*! \brief Callback for devicestate providers */
2868 static enum ast_device_state meetmestate(const char *data)
2870 struct ast_conference *conf;
2872 /* Find conference */
2873 AST_LIST_LOCK(&confs);
2874 AST_LIST_TRAVERSE(&confs, conf, list) {
2875 if (!strcmp(data, conf->confno))
2878 AST_LIST_UNLOCK(&confs);
2880 return AST_DEVICE_INVALID;
2885 return AST_DEVICE_NOT_INUSE;
2887 return AST_DEVICE_INUSE;
2890 static void load_config_meetme(void)
2892 struct ast_config *cfg;
2895 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2897 if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
2900 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
2901 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
2902 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
2903 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2904 } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
2905 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
2906 ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
2907 audio_buffers = DEFAULT_AUDIO_BUFFERS;
2909 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
2910 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
2913 ast_config_destroy(cfg);
2916 /*! \brief Find an SLA trunk by name
2917 * \note This must be called with the sla_trunks container locked
2919 static struct sla_trunk *find_trunk(const char *name)
2921 struct sla_trunk *trunk = NULL;
2923 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
2924 if (!strcasecmp(trunk->name, name))
2931 /*! \brief Find an SLA station by name
2932 * \note This must be called with the sla_stations container locked
2934 static struct sla_station *find_station(const char *name)
2936 struct sla_station *station = NULL;
2938 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
2939 if (!strcasecmp(station->name, name))
2946 static struct sla_trunk_ref *find_trunk_ref(const struct sla_station *station,
2949 struct sla_trunk_ref *trunk_ref = NULL;
2951 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
2952 if (!strcasecmp(trunk_ref->trunk->name, name))
2959 static struct sla_station_ref *create_station_ref(struct sla_station *station)
2961 struct sla_station_ref *station_ref;
2963 if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
2966 station_ref->station = station;
2971 static void change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state, int inactive_only)
2973 struct sla_station *station;
2974 struct sla_trunk_ref *trunk_ref;
2976 ast_log(LOG_DEBUG, "Setting all refs of trunk %s to state %s\n", trunk->name, trunkstate2str(state));
2978 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
2979 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
2980 if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0))
2982 trunk_ref->state = state;
2983 ast_device_state_changed("SLA:%s_%s", station->name, trunk->name);
2989 struct run_station_args {
2990 struct sla_station *station;
2991 struct sla_trunk_ref *trunk_ref;
2992 ast_mutex_t *cond_lock;
2996 static void *run_station(void *data)
2998 struct sla_station *station;
2999 struct sla_trunk_ref *trunk_ref;
3000 char conf_name[MAX_CONFNUM];
3001 struct ast_flags conf_flags = { 0 };
3002 struct ast_conference *conf;
3005 struct run_station_args *args = data;
3006 station = args->station;
3007 trunk_ref = args->trunk_ref;
3008 ast_mutex_lock(args->cond_lock);
3009 ast_cond_signal(args->cond);
3010 ast_mutex_unlock(args->cond_lock);
3011 /* args is no longer valid here. */
3014 ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
3015 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
3016 ast_set_flag(&conf_flags,
3017 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
3018 ast_answer(trunk_ref->chan);
3019 conf = build_conf(conf_name, "", "", 0, 0, 1);
3021 conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
3025 trunk_ref->chan = NULL;
3026 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations)) {
3027 strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
3028 admin_exec(NULL, conf_name);
3029 change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, 0);
3032 ast_dial_join(station->dial);
3033 ast_dial_destroy(station->dial);
3034 station->dial = NULL;
3039 static void *sla_thread(void *data)
3041 AST_LIST_HEAD_NOLOCK_STATIC(ringing_stations, sla_station_ref);
3042 struct sla_failed_station {
3043 struct sla_station *station;
3044 struct timeval last_try;
3045 AST_LIST_ENTRY(sla_failed_station) entry;
3047 AST_LIST_HEAD_NOLOCK_STATIC(failed_stations, sla_failed_station);
3048 struct sla_station_ref *station_ref;
3049 struct sla_failed_station *failed_station;
3051 for (; !sla.stop;) {
3052 struct sla_trunk_ref *trunk_ref = NULL;
3053 struct sla_event *event;
3054 enum ast_dial_result dial_res = AST_DIAL_RESULT_TRYING;
3056 ast_mutex_lock(&sla.lock);
3057 if (AST_LIST_EMPTY(&sla.ringing_trunks) && AST_LIST_EMPTY(&sla.event_q)) {
3058 ast_cond_wait(&sla.cond, &sla.lock);
3061 ast_log(LOG_DEBUG, "Ooh, I was woken up!\n");
3064 while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
3065 switch (event->type) {
3066 case SLA_EVENT_HOLD:
3067 ast_log(LOG_DEBUG, "HOLD, station: %s trunk: %s\n",
3068 event->station->name, event->trunk_ref->trunk->name);
3069 ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
3070 event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD;
3071 ast_device_state_changed("SLA:%s_%s",
3072 event->station->name, event->trunk_ref->trunk->name);
3073 change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 1);
3075 case SLA_EVENT_UNHOLD:
3076 ast_log(LOG_DEBUG, "UNHOLD, station: %s trunk: %s\n",
3077 event->station->name, event->trunk_ref->trunk->name);
3078 if (ast_atomic_dec_and_test((int *) &event->trunk_ref->trunk->hold_stations) == 1)
3079 change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_UP, 0);
3081 event->trunk_ref->state = SLA_TRUNK_STATE_UP;
3082 ast_device_state_changed("SLA:%s_%s",
3083 event->station->name, event->trunk_ref->trunk->name);
3090 /* At this point, we know there are ringing trunks. So, make sure that every
3091 * station that uses at least one of the ringing trunks, is ringing. */
3092 AST_LIST_TRAVERSE(&sla.ringing_trunks, trunk_ref, entry) {
3093 AST_LIST_TRAVERSE(&trunk_ref->trunk->stations, station_ref, entry) {
3094 char *tech, *tech_data;
3095 struct ast_dial *dial;
3096 struct sla_station_ref *ringing_ref;
3097 struct sla_failed_station *failed_station;
3098 /* Did we fail to dial this station earlier? If so, has it been
3099 * a minute since we tried? */
3100 AST_LIST_TRAVERSE_SAFE_BEGIN(&failed_stations, failed_station, entry) {
3101 if (station_ref->station != failed_station->station)
3103 if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
3104 AST_LIST_REMOVE_CURRENT(&failed_stations, entry);
3105 free(failed_station);
3106 failed_station = NULL;