reduce the likelihood that HTTP Manager session ids will consist of primarily '1...
[asterisk-bristuff.git] / apps / app_meetme.c
blobad547e509941ba39992a8a41961306a7cd6263ca
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2007, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * SLA Implementation by:
9 * Russell Bryant <russell@digium.com>
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
22 /*! \file
24 * \brief Meet me conference bridge and Shared Line Appearances
26 * \author Mark Spencer <markster@digium.com>
27 * \author (SLA) Russell Bryant <russell@digium.com>
29 * \ingroup applications
32 /*** MODULEINFO
33 <depend>zaptel</depend>
34 ***/
36 #include "asterisk.h"
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <errno.h>
45 #include <sys/ioctl.h>
46 #include <zaptel/zaptel.h>
48 #include "asterisk/lock.h"
49 #include "asterisk/file.h"
50 #include "asterisk/logger.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/module.h"
54 #include "asterisk/config.h"
55 #include "asterisk/app.h"
56 #include "asterisk/dsp.h"
57 #include "asterisk/musiconhold.h"
58 #include "asterisk/manager.h"
59 #include "asterisk/options.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/say.h"
62 #include "asterisk/utils.h"
63 #include "asterisk/translate.h"
64 #include "asterisk/ulaw.h"
65 #include "asterisk/astobj.h"
66 #include "asterisk/devicestate.h"
67 #include "asterisk/dial.h"
68 #include "asterisk/causes.h"
70 #include "enter.h"
71 #include "leave.h"
73 #define CONFIG_FILE_NAME "meetme.conf"
74 #define SLA_CONFIG_FILE "sla.conf"
76 /*! each buffer is 20ms, so this is 640ms total */
77 #define DEFAULT_AUDIO_BUFFERS 32
79 enum {
80 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
81 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
82 ADMINFLAG_KICKME = (1 << 3) /*!< User has been kicked */
85 #define MEETME_DELAYDETECTTALK 300
86 #define MEETME_DELAYDETECTENDTALK 1000
88 #define AST_FRAME_BITS 32
90 enum volume_action {
91 VOL_UP,
92 VOL_DOWN
95 enum entrance_sound {
96 ENTER,
97 LEAVE
100 enum recording_state {
101 MEETME_RECORD_OFF,
102 MEETME_RECORD_STARTED,
103 MEETME_RECORD_ACTIVE,
104 MEETME_RECORD_TERMINATE
107 #define CONF_SIZE 320
109 enum {
110 /*! user has admin access on the conference */
111 CONFFLAG_ADMIN = (1 << 0),
112 /*! If set the user can only receive audio from the conference */
113 CONFFLAG_MONITOR = (1 << 1),
114 /*! If set asterisk will exit conference when '#' is pressed */
115 CONFFLAG_POUNDEXIT = (1 << 2),
116 /*! If set asterisk will provide a menu to the user when '*' is pressed */
117 CONFFLAG_STARMENU = (1 << 3),
118 /*! If set the use can only send audio to the conference */
119 CONFFLAG_TALKER = (1 << 4),
120 /*! If set there will be no enter or leave sounds */
121 CONFFLAG_QUIET = (1 << 5),
122 /*! If set, when user joins the conference, they will be told the number
123 * of users that are already in */
124 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
125 /*! Set to run AGI Script in Background */
126 CONFFLAG_AGI = (1 << 7),
127 /*! Set to have music on hold when user is alone in conference */
128 CONFFLAG_MOH = (1 << 8),
129 /*! If set the MeetMe will return if all marked with this flag left */
130 CONFFLAG_MARKEDEXIT = (1 << 9),
131 /*! If set, the MeetMe will wait until a marked user enters */
132 CONFFLAG_WAITMARKED = (1 << 10),
133 /*! If set, the MeetMe will exit to the specified context */
134 CONFFLAG_EXIT_CONTEXT = (1 << 11),
135 /*! If set, the user will be marked */
136 CONFFLAG_MARKEDUSER = (1 << 12),
137 /*! If set, user will be ask record name on entry of conference */
138 CONFFLAG_INTROUSER = (1 << 13),
139 /*! If set, the MeetMe will be recorded */
140 CONFFLAG_RECORDCONF = (1<< 14),
141 /*! If set, the user will be monitored if the user is talking or not */
142 CONFFLAG_MONITORTALKER = (1 << 15),
143 CONFFLAG_DYNAMIC = (1 << 16),
144 CONFFLAG_DYNAMICPIN = (1 << 17),
145 CONFFLAG_EMPTY = (1 << 18),
146 CONFFLAG_EMPTYNOPIN = (1 << 19),
147 CONFFLAG_ALWAYSPROMPT = (1 << 20),
148 /*! If set, treats talking users as muted users */
149 CONFFLAG_OPTIMIZETALKER = (1 << 21),
150 /*! If set, won't speak the extra prompt when the first person
151 * enters the conference */
152 CONFFLAG_NOONLYPERSON = (1 << 22),
153 /*! If set, user will be asked to record name on entry of conference
154 * without review */
155 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
156 /*! If set, the user will be initially self-muted */
157 CONFFLAG_STARTMUTED = (1 << 24),
158 /*! Pass DTMF through the conference */
159 CONFFLAG_PASS_DTMF = (1 << 25),
160 /*! This is a SLA station. (Only for use by the SLA applications.) */
161 CONFFLAG_SLA_STATION = (1 << 26),
162 /*! This is a SLA trunk. (Only for use by the SLA applications.) */
163 CONFFLAG_SLA_TRUNK = (1 << 27),
166 enum {
167 OPT_ARG_WAITMARKED = 0,
168 OPT_ARG_ARRAY_SIZE = 1,
171 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
172 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
173 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
174 AST_APP_OPTION('b', CONFFLAG_AGI ),
175 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
176 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
177 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
178 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
179 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
180 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
181 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
182 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
183 AST_APP_OPTION('M', CONFFLAG_MOH ),
184 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
185 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
186 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
187 AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
188 AST_APP_OPTION('q', CONFFLAG_QUIET ),
189 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
190 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
191 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
192 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
193 AST_APP_OPTION('t', CONFFLAG_TALKER ),
194 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
195 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
196 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
197 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
198 END_OPTIONS );
200 static const char *app = "MeetMe";
201 static const char *app2 = "MeetMeCount";
202 static const char *app3 = "MeetMeAdmin";
203 static const char *slastation_app = "SLAStation";
204 static const char *slatrunk_app = "SLATrunk";
206 static const char *synopsis = "MeetMe conference bridge";
207 static const char *synopsis2 = "MeetMe participant count";
208 static const char *synopsis3 = "MeetMe conference Administration";
209 static const char *slastation_synopsis = "Shared Line Appearance Station";
210 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
212 static const char *descrip =
213 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
214 "conference. If the conference number is omitted, the user will be prompted\n"
215 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
216 "is specified, by pressing '#'.\n"
217 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
218 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
219 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
220 "The option string may contain zero or more of the following characters:\n"
221 " 'a' -- set admin mode\n"
222 " 'A' -- set marked mode\n"
223 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
224 " Default: conf-background.agi (Note: This does not work with\n"
225 " non-Zap channels in the same conference)\n"
226 " 'c' -- announce user(s) count on joining a conference\n"
227 " 'd' -- dynamically add conference\n"
228 " 'D' -- dynamically add conference, prompting for a PIN\n"
229 " 'e' -- select an empty conference\n"
230 " 'E' -- select an empty pinless conference\n"
231 " 'F' -- Pass DTMF through the conference.\n"
232 " 'i' -- announce user join/leave with review\n"
233 " 'I' -- announce user join/leave without review\n"
234 " 'l' -- set listen only mode (Listen only, no talking)\n"
235 " 'm' -- set initially muted\n"
236 " 'M' -- enable music on hold when the conference has a single caller\n"
237 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
238 " being muted, meaning (a) No encode is done on transmission and\n"
239 " (b) Received audio that is not registered as talking is omitted\n"
240 " causing no buildup in background noise. Note that this option\n"
241 " will be removed in 1.6 and enabled by default.\n"
242 " 'p' -- allow user to exit the conference by pressing '#'\n"
243 " 'P' -- always prompt for the pin even if it is specified\n"
244 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
245 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
246 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
247 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
248 " wav.\n"
249 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
250 " 't' -- set talk only mode. (Talk only, no listening)\n"
251 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
252 " 'w[(<secs>)]'\n"
253 " -- wait until the marked user enters the conference\n"
254 " 'x' -- close the conference when last marked user exits\n"
255 " 'X' -- allow user to exit the conference by entering a valid single\n"
256 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
257 " if that variable is not defined.\n"
258 " '1' -- do not play message when first person enters\n";
260 static const char *descrip2 =
261 " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
262 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
263 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
264 "the channel, unless priority n+1 exists, in which case priority progress will\n"
265 "continue.\n"
266 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
268 static const char *descrip3 =
269 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
270 " 'e' -- Eject last user that joined\n"
271 " 'k' -- Kick one user out of conference\n"
272 " 'K' -- Kick all users out of conference\n"
273 " 'l' -- Unlock conference\n"
274 " 'L' -- Lock conference\n"
275 " 'm' -- Unmute one user\n"
276 " 'M' -- Mute one user\n"
277 " 'n' -- Unmute all users in the conference\n"
278 " 'N' -- Mute all non-admin users in the conference\n"
279 " 'r' -- Reset one user's volume settings\n"
280 " 'R' -- Reset all users volume settings\n"
281 " 's' -- Lower entire conference speaking volume\n"
282 " 'S' -- Raise entire conference speaking volume\n"
283 " 't' -- Lower one user's talk volume\n"
284 " 'T' -- Raise one user's talk volume\n"
285 " 'u' -- Lower one user's listen volume\n"
286 " 'U' -- Raise one user's listen volume\n"
287 " 'v' -- Lower entire conference listening volume\n"
288 " 'V' -- Raise entire conference listening volume\n"
291 static const char *slastation_desc =
292 " SLAStation(station):\n"
293 "This application should be executed by an SLA station. The argument depends\n"
294 "on how the call was initiated. If the phone was just taken off hook, then\n"
295 "the argument \"station\" should be just the station name. If the call was\n"
296 "initiated by pressing a line key, then the station name should be preceded\n"
297 "by an underscore and the trunk name associated with that line button.\n"
298 "For example: \"station1_line1\"."
299 " On exit, this application will set the variable SLASTATION_STATUS to\n"
300 "one of the following values:\n"
301 " FAILURE | CONGESTION | SUCCESS\n"
304 static const char *slatrunk_desc =
305 " SLATrunk(trunk):\n"
306 "This application should be executed by an SLA trunk on an inbound call.\n"
307 "The channel calling this application should correspond to the SLA trunk\n"
308 "with the name \"trunk\" that is being passed as an argument.\n"
309 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
310 "one of the following values:\n"
311 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
314 #define MAX_CONFNUM 80
315 #define MAX_PIN 80
317 /*! \brief The MeetMe Conference object */
318 struct ast_conference {
319 ast_mutex_t playlock; /*!< Conference specific lock (players) */
320 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
321 char confno[MAX_CONFNUM]; /*!< Conference */
322 struct ast_channel *chan; /*!< Announcements channel */
323 struct ast_channel *lchan; /*!< Listen/Record channel */
324 int fd; /*!< Announcements fd */
325 int zapconf; /*!< Zaptel Conf # */
326 int users; /*!< Number of active users */
327 int markedusers; /*!< Number of marked users */
328 time_t start; /*!< Start time (s) */
329 int refcount; /*!< reference count of usage */
330 enum recording_state recording:2; /*!< recording status */
331 unsigned int isdynamic:1; /*!< Created on the fly? */
332 unsigned int locked:1; /*!< Is the conference locked? */
333 pthread_t recordthread; /*!< thread for recording */
334 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
335 pthread_attr_t attr; /*!< thread attribute */
336 const char *recordingfilename; /*!< Filename to record the Conference into */
337 const char *recordingformat; /*!< Format to record the Conference in */
338 char pin[MAX_PIN]; /*!< If protected by a PIN */
339 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
340 struct ast_frame *transframe[32];
341 struct ast_frame *origframe;
342 struct ast_trans_pvt *transpath[32];
343 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
344 AST_LIST_ENTRY(ast_conference) list;
347 static AST_LIST_HEAD_STATIC(confs, ast_conference);
349 static unsigned int conf_map[1024] = {0, };
351 struct volume {
352 int desired; /*!< Desired volume adjustment */
353 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
356 struct ast_conf_user {
357 int user_no; /*!< User Number */
358 int userflags; /*!< Flags as set in the conference */
359 int adminflags; /*!< Flags set by the Admin */
360 struct ast_channel *chan; /*!< Connected channel */
361 int talking; /*!< Is user talking */
362 int zapchannel; /*!< Is a Zaptel channel */
363 char usrvalue[50]; /*!< Custom User Value */
364 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
365 time_t jointime; /*!< Time the user joined the conference */
366 struct volume talk;
367 struct volume listen;
368 AST_LIST_ENTRY(ast_conf_user) list;
371 enum sla_which_trunk_refs {
372 ALL_TRUNK_REFS,
373 INACTIVE_TRUNK_REFS,
376 enum sla_trunk_state {
377 SLA_TRUNK_STATE_IDLE,
378 SLA_TRUNK_STATE_RINGING,
379 SLA_TRUNK_STATE_UP,
380 SLA_TRUNK_STATE_ONHOLD,
381 SLA_TRUNK_STATE_ONHOLD_BYME,
384 enum sla_hold_access {
385 /*! This means that any station can put it on hold, and any station
386 * can retrieve the call from hold. */
387 SLA_HOLD_OPEN,
388 /*! This means that only the station that put the call on hold may
389 * retrieve it from hold. */
390 SLA_HOLD_PRIVATE,
393 struct sla_trunk_ref;
395 struct sla_station {
396 AST_RWLIST_ENTRY(sla_station) entry;
397 AST_DECLARE_STRING_FIELDS(
398 AST_STRING_FIELD(name);
399 AST_STRING_FIELD(device);
400 AST_STRING_FIELD(autocontext);
402 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
403 struct ast_dial *dial;
404 /*! Ring timeout for this station, for any trunk. If a ring timeout
405 * is set for a specific trunk on this station, that will take
406 * priority over this value. */
407 unsigned int ring_timeout;
408 /*! Ring delay for this station, for any trunk. If a ring delay
409 * is set for a specific trunk on this station, that will take
410 * priority over this value. */
411 unsigned int ring_delay;
412 /*! This option uses the values in the sla_hold_access enum and sets the
413 * access control type for hold on this station. */
414 unsigned int hold_access:1;
417 struct sla_station_ref {
418 AST_LIST_ENTRY(sla_station_ref) entry;
419 struct sla_station *station;
422 struct sla_trunk {
423 AST_RWLIST_ENTRY(sla_trunk) entry;
424 AST_DECLARE_STRING_FIELDS(
425 AST_STRING_FIELD(name);
426 AST_STRING_FIELD(device);
427 AST_STRING_FIELD(autocontext);
429 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
430 /*! Number of stations that use this trunk */
431 unsigned int num_stations;
432 /*! Number of stations currently on a call with this trunk */
433 unsigned int active_stations;
434 /*! Number of stations that have this trunk on hold. */
435 unsigned int hold_stations;
436 struct ast_channel *chan;
437 unsigned int ring_timeout;
438 /*! If set to 1, no station will be able to join an active call with
439 * this trunk. */
440 unsigned int barge_disabled:1;
441 /*! This option uses the values in the sla_hold_access enum and sets the
442 * access control type for hold on this trunk. */
443 unsigned int hold_access:1;
444 /*! Whether this trunk is currently on hold, meaning that once a station
445 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
446 unsigned int on_hold:1;
449 struct sla_trunk_ref {
450 AST_LIST_ENTRY(sla_trunk_ref) entry;
451 struct sla_trunk *trunk;
452 enum sla_trunk_state state;
453 struct ast_channel *chan;
454 /*! Ring timeout to use when this trunk is ringing on this specific
455 * station. This takes higher priority than a ring timeout set at
456 * the station level. */
457 unsigned int ring_timeout;
458 /*! Ring delay to use when this trunk is ringing on this specific
459 * station. This takes higher priority than a ring delay set at
460 * the station level. */
461 unsigned int ring_delay;
464 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
465 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
467 static const char sla_registrar[] = "SLA";
469 /*! \brief Event types that can be queued up for the SLA thread */
470 enum sla_event_type {
471 /*! A station has put the call on hold */
472 SLA_EVENT_HOLD,
473 /*! The state of a dial has changed */
474 SLA_EVENT_DIAL_STATE,
475 /*! The state of a ringing trunk has changed */
476 SLA_EVENT_RINGING_TRUNK,
479 struct sla_event {
480 enum sla_event_type type;
481 struct sla_station *station;
482 struct sla_trunk_ref *trunk_ref;
483 AST_LIST_ENTRY(sla_event) entry;
486 /*! \brief A station that failed to be dialed
487 * \note Only used by the SLA thread. */
488 struct sla_failed_station {
489 struct sla_station *station;
490 struct timeval last_try;
491 AST_LIST_ENTRY(sla_failed_station) entry;
494 /*! \brief A trunk that is ringing */
495 struct sla_ringing_trunk {
496 struct sla_trunk *trunk;
497 /*! The time that this trunk started ringing */
498 struct timeval ring_begin;
499 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
500 AST_LIST_ENTRY(sla_ringing_trunk) entry;
503 enum sla_station_hangup {
504 SLA_STATION_HANGUP_NORMAL,
505 SLA_STATION_HANGUP_TIMEOUT,
508 /*! \brief A station that is ringing */
509 struct sla_ringing_station {
510 struct sla_station *station;
511 /*! The time that this station started ringing */
512 struct timeval ring_begin;
513 AST_LIST_ENTRY(sla_ringing_station) entry;
517 * \brief A structure for data used by the sla thread
519 static struct {
520 /*! The SLA thread ID */
521 pthread_t thread;
522 ast_cond_t cond;
523 ast_mutex_t lock;
524 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
525 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
526 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
527 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
528 unsigned int stop:1;
529 /*! Attempt to handle CallerID, even though it is known not to work
530 * properly in some situations. */
531 unsigned int attempt_callerid:1;
532 } sla = {
533 .thread = AST_PTHREADT_NULL,
536 /*! The number of audio buffers to be allocated on pseudo channels
537 * when in a conference */
538 static int audio_buffers;
540 /*! Map 'volume' levels from -5 through +5 into
541 * decibel (dB) settings for channel drivers
542 * Note: these are not a straight linear-to-dB
543 * conversion... the numbers have been modified
544 * to give the user a better level of adjustability
546 static char const gain_map[] = {
547 -15,
548 -13,
549 -10,
561 static int admin_exec(struct ast_channel *chan, void *data);
562 static void *recordthread(void *args);
564 static char *istalking(int x)
566 if (x > 0)
567 return "(talking)";
568 else if (x < 0)
569 return "(unmonitored)";
570 else
571 return "(not talking)";
574 static int careful_write(int fd, unsigned char *data, int len, int block)
576 int res;
577 int x;
579 while (len) {
580 if (block) {
581 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
582 res = ioctl(fd, ZT_IOMUX, &x);
583 } else
584 res = 0;
585 if (res >= 0)
586 res = write(fd, data, len);
587 if (res < 1) {
588 if (errno != EAGAIN) {
589 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
590 return -1;
591 } else
592 return 0;
594 len -= res;
595 data += res;
598 return 0;
601 static int set_talk_volume(struct ast_conf_user *user, int volume)
603 char gain_adjust;
605 /* attempt to make the adjustment in the channel driver;
606 if successful, don't adjust in the frame reading routine
608 gain_adjust = gain_map[volume + 5];
610 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
613 static int set_listen_volume(struct ast_conf_user *user, int volume)
615 char gain_adjust;
617 /* attempt to make the adjustment in the channel driver;
618 if successful, don't adjust in the frame reading routine
620 gain_adjust = gain_map[volume + 5];
622 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
625 static void tweak_volume(struct volume *vol, enum volume_action action)
627 switch (action) {
628 case VOL_UP:
629 switch (vol->desired) {
630 case 5:
631 break;
632 case 0:
633 vol->desired = 2;
634 break;
635 case -2:
636 vol->desired = 0;
637 break;
638 default:
639 vol->desired++;
640 break;
642 break;
643 case VOL_DOWN:
644 switch (vol->desired) {
645 case -5:
646 break;
647 case 2:
648 vol->desired = 0;
649 break;
650 case 0:
651 vol->desired = -2;
652 break;
653 default:
654 vol->desired--;
655 break;
660 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
662 tweak_volume(&user->talk, action);
663 /* attempt to make the adjustment in the channel driver;
664 if successful, don't adjust in the frame reading routine
666 if (!set_talk_volume(user, user->talk.desired))
667 user->talk.actual = 0;
668 else
669 user->talk.actual = user->talk.desired;
672 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
674 tweak_volume(&user->listen, action);
675 /* attempt to make the adjustment in the channel driver;
676 if successful, don't adjust in the frame reading routine
678 if (!set_listen_volume(user, user->listen.desired))
679 user->listen.actual = 0;
680 else
681 user->listen.actual = user->listen.desired;
684 static void reset_volumes(struct ast_conf_user *user)
686 signed char zero_volume = 0;
688 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
689 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
692 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
694 unsigned char *data;
695 int len;
696 int res = -1;
698 if (!chan->_softhangup)
699 res = ast_autoservice_start(chan);
701 AST_LIST_LOCK(&confs);
703 switch(sound) {
704 case ENTER:
705 data = enter;
706 len = sizeof(enter);
707 break;
708 case LEAVE:
709 data = leave;
710 len = sizeof(leave);
711 break;
712 default:
713 data = NULL;
714 len = 0;
716 if (data) {
717 careful_write(conf->fd, data, len, 1);
720 AST_LIST_UNLOCK(&confs);
722 if (!res)
723 ast_autoservice_stop(chan);
727 * \brief Find or create a conference
729 * \param confno The conference name/number
730 * \param pin The regular user pin
731 * \param pinadmin The admin pin
732 * \param make Make the conf if it doesn't exist
733 * \param dynamic Mark the newly created conference as dynamic
734 * \param refcount How many references to mark on the conference
736 * \return A pointer to the conference struct, or NULL if it wasn't found and
737 * make or dynamic were not set.
739 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
741 struct ast_conference *cnf;
742 struct zt_confinfo ztc = { 0, };
743 int confno_int = 0;
745 AST_LIST_LOCK(&confs);
747 AST_LIST_TRAVERSE(&confs, cnf, list) {
748 if (!strcmp(confno, cnf->confno))
749 break;
752 if (cnf || (!make && !dynamic))
753 goto cnfout;
755 /* Make a new one */
756 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
757 goto cnfout;
759 ast_mutex_init(&cnf->playlock);
760 ast_mutex_init(&cnf->listenlock);
761 cnf->recordthread = AST_PTHREADT_NULL;
762 ast_mutex_init(&cnf->recordthreadlock);
763 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
764 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
765 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
767 /* Setup a new zap conference */
768 ztc.confno = -1;
769 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
770 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
771 if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
772 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
773 if (cnf->fd >= 0)
774 close(cnf->fd);
775 free(cnf);
776 cnf = NULL;
777 goto cnfout;
780 cnf->zapconf = ztc.confno;
782 /* Setup a new channel for playback of audio files */
783 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
784 if (cnf->chan) {
785 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
786 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
787 ztc.chan = 0;
788 ztc.confno = cnf->zapconf;
789 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
790 if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
791 ast_log(LOG_WARNING, "Error setting conference\n");
792 if (cnf->chan)
793 ast_hangup(cnf->chan);
794 else
795 close(cnf->fd);
796 free(cnf);
797 cnf = NULL;
798 goto cnfout;
802 /* Fill the conference struct */
803 cnf->start = time(NULL);
804 cnf->isdynamic = dynamic ? 1 : 0;
805 if (option_verbose > 2)
806 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
807 AST_LIST_INSERT_HEAD(&confs, cnf, list);
809 /* Reserve conference number in map */
810 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
811 conf_map[confno_int] = 1;
813 cnfout:
814 if (cnf)
815 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
817 AST_LIST_UNLOCK(&confs);
819 return cnf;
822 static int meetme_cmd(int fd, int argc, char **argv)
824 /* Process the command */
825 struct ast_conference *cnf;
826 struct ast_conf_user *user;
827 int hr, min, sec;
828 int i = 0, total = 0;
829 time_t now;
830 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
831 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
832 char cmdline[1024] = "";
834 if (argc > 8)
835 ast_cli(fd, "Invalid Arguments.\n");
836 /* Check for length so no buffer will overflow... */
837 for (i = 0; i < argc; i++) {
838 if (strlen(argv[i]) > 100)
839 ast_cli(fd, "Invalid Arguments.\n");
841 if (argc == 1) {
842 /* 'MeetMe': List all the conferences */
843 now = time(NULL);
844 AST_LIST_LOCK(&confs);
845 if (AST_LIST_EMPTY(&confs)) {
846 ast_cli(fd, "No active MeetMe conferences.\n");
847 AST_LIST_UNLOCK(&confs);
848 return RESULT_SUCCESS;
850 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
851 AST_LIST_TRAVERSE(&confs, cnf, list) {
852 if (cnf->markedusers == 0)
853 strcpy(cmdline, "N/A ");
854 else
855 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
856 hr = (now - cnf->start) / 3600;
857 min = ((now - cnf->start) % 3600) / 60;
858 sec = (now - cnf->start) % 60;
860 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
862 total += cnf->users;
864 AST_LIST_UNLOCK(&confs);
865 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
866 return RESULT_SUCCESS;
868 if (argc < 3)
869 return RESULT_SHOWUSAGE;
870 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
871 if (strstr(argv[1], "lock")) {
872 if (strcmp(argv[1], "lock") == 0) {
873 /* Lock */
874 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
875 } else {
876 /* Unlock */
877 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
879 } else if (strstr(argv[1], "mute")) {
880 if (argc < 4)
881 return RESULT_SHOWUSAGE;
882 if (strcmp(argv[1], "mute") == 0) {
883 /* Mute */
884 if (strcmp(argv[3], "all") == 0) {
885 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
886 } else {
887 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
888 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
890 } else {
891 /* Unmute */
892 if (strcmp(argv[3], "all") == 0) {
893 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
894 } else {
895 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
896 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
899 } else if (strcmp(argv[1], "kick") == 0) {
900 if (argc < 4)
901 return RESULT_SHOWUSAGE;
902 if (strcmp(argv[3], "all") == 0) {
903 /* Kick all */
904 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
905 } else {
906 /* Kick a single user */
907 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
908 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
910 } else if(strcmp(argv[1], "list") == 0) {
911 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
912 /* List all the users in a conference */
913 if (AST_LIST_EMPTY(&confs)) {
914 if ( !concise )
915 ast_cli(fd, "No active conferences.\n");
916 return RESULT_SUCCESS;
918 /* Find the right conference */
919 AST_LIST_LOCK(&confs);
920 AST_LIST_TRAVERSE(&confs, cnf, list) {
921 if (strcmp(cnf->confno, argv[2]) == 0)
922 break;
924 if (!cnf) {
925 if ( !concise )
926 ast_cli(fd, "No such conference: %s.\n",argv[2]);
927 AST_LIST_UNLOCK(&confs);
928 return RESULT_SUCCESS;
930 /* Show all the users */
931 time(&now);
932 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
933 hr = (now - user->jointime) / 3600;
934 min = ((now - user->jointime) % 3600) / 60;
935 sec = (now - user->jointime) % 60;
936 if ( !concise )
937 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
938 user->user_no,
939 S_OR(user->chan->cid.cid_num, "<unknown>"),
940 S_OR(user->chan->cid.cid_name, "<no name>"),
941 user->chan->name,
942 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
943 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
944 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
945 istalking(user->talking), hr, min, sec);
946 else
947 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
948 user->user_no,
949 S_OR(user->chan->cid.cid_num, ""),
950 S_OR(user->chan->cid.cid_name, ""),
951 user->chan->name,
952 user->userflags & CONFFLAG_ADMIN ? "1" : "",
953 user->userflags & CONFFLAG_MONITOR ? "1" : "",
954 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
955 user->talking, hr, min, sec);
958 if ( !concise )
959 ast_cli(fd,"%d users in that conference.\n",cnf->users);
960 AST_LIST_UNLOCK(&confs);
961 return RESULT_SUCCESS;
962 } else
963 return RESULT_SHOWUSAGE;
964 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
965 admin_exec(NULL, cmdline);
967 return 0;
970 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
972 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
974 int len = strlen(word);
975 int which = 0;
976 struct ast_conference *cnf = NULL;
977 struct ast_conf_user *usr = NULL;
978 char *confno = NULL;
979 char usrno[50] = "";
980 char *myline, *ret = NULL;
982 if (pos == 1) { /* Command */
983 return ast_cli_complete(word, cmds, state);
984 } else if (pos == 2) { /* Conference Number */
985 AST_LIST_LOCK(&confs);
986 AST_LIST_TRAVERSE(&confs, cnf, list) {
987 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
988 ret = cnf->confno;
989 break;
992 ret = ast_strdup(ret); /* dup before releasing the lock */
993 AST_LIST_UNLOCK(&confs);
994 return ret;
995 } else if (pos == 3) {
996 /* User Number || Conf Command option*/
997 if (strstr(line, "mute") || strstr(line, "kick")) {
998 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
999 return strdup("all");
1000 which++;
1001 AST_LIST_LOCK(&confs);
1003 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1004 myline = ast_strdupa(line);
1005 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1006 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1010 AST_LIST_TRAVERSE(&confs, cnf, list) {
1011 if (!strcmp(confno, cnf->confno))
1012 break;
1015 if (cnf) {
1016 /* Search for the user */
1017 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
1018 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1019 if (!strncasecmp(word, usrno, len) && ++which > state)
1020 break;
1023 AST_LIST_UNLOCK(&confs);
1024 return usr ? strdup(usrno) : NULL;
1025 } else if ( strstr(line, "list") && ( 0 == state ) )
1026 return strdup("concise");
1029 return NULL;
1032 static char meetme_usage[] =
1033 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
1034 " Executes a command for the conference or on a conferee\n";
1036 static const char *sla_hold_str(unsigned int hold_access)
1038 const char *hold = "Unknown";
1040 switch (hold_access) {
1041 case SLA_HOLD_OPEN:
1042 hold = "Open";
1043 break;
1044 case SLA_HOLD_PRIVATE:
1045 hold = "Private";
1046 default:
1047 break;
1050 return hold;
1053 static int sla_show_trunks(int fd, int argc, char **argv)
1055 const struct sla_trunk *trunk;
1057 ast_cli(fd, "\n"
1058 "=============================================================\n"
1059 "=== Configured SLA Trunks ===================================\n"
1060 "=============================================================\n"
1061 "===\n");
1062 AST_RWLIST_RDLOCK(&sla_trunks);
1063 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1064 struct sla_station_ref *station_ref;
1065 char ring_timeout[16] = "(none)";
1066 if (trunk->ring_timeout)
1067 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1068 ast_cli(fd, "=== ---------------------------------------------------------\n"
1069 "=== Trunk Name: %s\n"
1070 "=== ==> Device: %s\n"
1071 "=== ==> AutoContext: %s\n"
1072 "=== ==> RingTimeout: %s\n"
1073 "=== ==> BargeAllowed: %s\n"
1074 "=== ==> HoldAccess: %s\n"
1075 "=== ==> Stations ...\n",
1076 trunk->name, trunk->device,
1077 S_OR(trunk->autocontext, "(none)"),
1078 ring_timeout,
1079 trunk->barge_disabled ? "No" : "Yes",
1080 sla_hold_str(trunk->hold_access));
1081 AST_RWLIST_RDLOCK(&sla_stations);
1082 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1083 ast_cli(fd, "=== ==> Station name: %s\n", station_ref->station->name);
1084 AST_RWLIST_UNLOCK(&sla_stations);
1085 ast_cli(fd, "=== ---------------------------------------------------------\n"
1086 "===\n");
1088 AST_RWLIST_UNLOCK(&sla_trunks);
1089 ast_cli(fd, "=============================================================\n"
1090 "\n");
1092 return RESULT_SUCCESS;
1095 static const char *trunkstate2str(enum sla_trunk_state state)
1097 #define S(e) case e: return # e;
1098 switch (state) {
1099 S(SLA_TRUNK_STATE_IDLE)
1100 S(SLA_TRUNK_STATE_RINGING)
1101 S(SLA_TRUNK_STATE_UP)
1102 S(SLA_TRUNK_STATE_ONHOLD)
1103 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1105 return "Uknown State";
1106 #undef S
1109 static const char sla_show_trunks_usage[] =
1110 "Usage: sla show trunks\n"
1111 " This will list all trunks defined in sla.conf\n";
1113 static int sla_show_stations(int fd, int argc, char **argv)
1115 const struct sla_station *station;
1117 ast_cli(fd, "\n"
1118 "=============================================================\n"
1119 "=== Configured SLA Stations =================================\n"
1120 "=============================================================\n"
1121 "===\n");
1122 AST_RWLIST_RDLOCK(&sla_stations);
1123 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1124 struct sla_trunk_ref *trunk_ref;
1125 char ring_timeout[16] = "(none)";
1126 char ring_delay[16] = "(none)";
1127 if (station->ring_timeout) {
1128 snprintf(ring_timeout, sizeof(ring_timeout),
1129 "%u", station->ring_timeout);
1131 if (station->ring_delay) {
1132 snprintf(ring_delay, sizeof(ring_delay),
1133 "%u", station->ring_delay);
1135 ast_cli(fd, "=== ---------------------------------------------------------\n"
1136 "=== Station Name: %s\n"
1137 "=== ==> Device: %s\n"
1138 "=== ==> AutoContext: %s\n"
1139 "=== ==> RingTimeout: %s\n"
1140 "=== ==> RingDelay: %s\n"
1141 "=== ==> HoldAccess: %s\n"
1142 "=== ==> Trunks ...\n",
1143 station->name, station->device,
1144 S_OR(station->autocontext, "(none)"),
1145 ring_timeout, ring_delay,
1146 sla_hold_str(station->hold_access));
1147 AST_RWLIST_RDLOCK(&sla_trunks);
1148 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1149 if (trunk_ref->ring_timeout) {
1150 snprintf(ring_timeout, sizeof(ring_timeout),
1151 "%u", trunk_ref->ring_timeout);
1152 } else
1153 strcpy(ring_timeout, "(none)");
1154 if (trunk_ref->ring_delay) {
1155 snprintf(ring_delay, sizeof(ring_delay),
1156 "%u", trunk_ref->ring_delay);
1157 } else
1158 strcpy(ring_delay, "(none)");
1159 ast_cli(fd, "=== ==> Trunk Name: %s\n"
1160 "=== ==> State: %s\n"
1161 "=== ==> RingTimeout: %s\n"
1162 "=== ==> RingDelay: %s\n",
1163 trunk_ref->trunk->name,
1164 trunkstate2str(trunk_ref->state),
1165 ring_timeout, ring_delay);
1167 AST_RWLIST_UNLOCK(&sla_trunks);
1168 ast_cli(fd, "=== ---------------------------------------------------------\n"
1169 "===\n");
1171 AST_RWLIST_UNLOCK(&sla_stations);
1172 ast_cli(fd, "============================================================\n"
1173 "\n");
1175 return RESULT_SUCCESS;
1178 static const char sla_show_stations_usage[] =
1179 "Usage: sla show stations\n"
1180 " This will list all stations defined in sla.conf\n";
1182 static struct ast_cli_entry cli_meetme[] = {
1183 { { "meetme", NULL, NULL },
1184 meetme_cmd, "Execute a command on a conference or conferee",
1185 meetme_usage, complete_meetmecmd },
1187 { { "sla", "show", "trunks", NULL },
1188 sla_show_trunks, "Show SLA Trunks",
1189 sla_show_trunks_usage, NULL },
1191 { { "sla", "show", "stations", NULL },
1192 sla_show_stations, "Show SLA Stations",
1193 sla_show_stations_usage, NULL },
1196 static void conf_flush(int fd, struct ast_channel *chan)
1198 int x;
1200 /* read any frames that may be waiting on the channel
1201 and throw them away
1203 if (chan) {
1204 struct ast_frame *f;
1206 /* when no frames are available, this will wait
1207 for 1 millisecond maximum
1209 while (ast_waitfor(chan, 1)) {
1210 f = ast_read(chan);
1211 if (f)
1212 ast_frfree(f);
1213 else /* channel was hung up or something else happened */
1214 break;
1218 /* flush any data sitting in the pseudo channel */
1219 x = ZT_FLUSH_ALL;
1220 if (ioctl(fd, ZT_FLUSH, &x))
1221 ast_log(LOG_WARNING, "Error flushing channel\n");
1225 /* Remove the conference from the list and free it.
1226 We assume that this was called while holding conflock. */
1227 static int conf_free(struct ast_conference *conf)
1229 int x;
1231 AST_LIST_REMOVE(&confs, conf, list);
1233 if (conf->recording == MEETME_RECORD_ACTIVE) {
1234 conf->recording = MEETME_RECORD_TERMINATE;
1235 AST_LIST_UNLOCK(&confs);
1236 while (1) {
1237 usleep(1);
1238 AST_LIST_LOCK(&confs);
1239 if (conf->recording == MEETME_RECORD_OFF)
1240 break;
1241 AST_LIST_UNLOCK(&confs);
1245 for (x=0;x<AST_FRAME_BITS;x++) {
1246 if (conf->transframe[x])
1247 ast_frfree(conf->transframe[x]);
1248 if (conf->transpath[x])
1249 ast_translator_free_path(conf->transpath[x]);
1251 if (conf->origframe)
1252 ast_frfree(conf->origframe);
1253 if (conf->lchan)
1254 ast_hangup(conf->lchan);
1255 if (conf->chan)
1256 ast_hangup(conf->chan);
1257 if (conf->fd >= 0)
1258 close(conf->fd);
1260 ast_mutex_destroy(&conf->playlock);
1261 ast_mutex_destroy(&conf->listenlock);
1262 ast_mutex_destroy(&conf->recordthreadlock);
1263 free(conf);
1265 return 0;
1268 static void conf_queue_dtmf(const struct ast_conference *conf,
1269 const struct ast_conf_user *sender, struct ast_frame *f)
1271 struct ast_conf_user *user;
1273 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1274 if (user == sender)
1275 continue;
1276 if (ast_write(user->chan, f) < 0)
1277 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1281 static void sla_queue_event_full(enum sla_event_type type,
1282 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1284 struct sla_event *event;
1286 if (!(event = ast_calloc(1, sizeof(*event))))
1287 return;
1289 event->type = type;
1290 event->trunk_ref = trunk_ref;
1291 event->station = station;
1293 if (!lock) {
1294 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1295 return;
1298 ast_mutex_lock(&sla.lock);
1299 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1300 ast_cond_signal(&sla.cond);
1301 ast_mutex_unlock(&sla.lock);
1304 static void sla_queue_event_nolock(enum sla_event_type type)
1306 sla_queue_event_full(type, NULL, NULL, 0);
1309 static void sla_queue_event(enum sla_event_type type)
1311 sla_queue_event_full(type, NULL, NULL, 1);
1314 /*! \brief Queue a SLA event from the conference */
1315 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1316 struct ast_conference *conf)
1318 struct sla_station *station;
1319 struct sla_trunk_ref *trunk_ref = NULL;
1320 char *trunk_name;
1322 trunk_name = ast_strdupa(conf->confno);
1323 strsep(&trunk_name, "_");
1324 if (ast_strlen_zero(trunk_name)) {
1325 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1326 return;
1329 AST_RWLIST_RDLOCK(&sla_stations);
1330 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1331 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1332 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1333 break;
1335 if (trunk_ref)
1336 break;
1338 AST_RWLIST_UNLOCK(&sla_stations);
1340 if (!trunk_ref) {
1341 ast_log(LOG_DEBUG, "Trunk not found for event!\n");
1342 return;
1345 sla_queue_event_full(type, trunk_ref, station, 1);
1348 /* Decrement reference counts, as incremented by find_conf() */
1349 static int dispose_conf(struct ast_conference *conf)
1351 int res = 0;
1352 int confno_int = 0;
1354 AST_LIST_LOCK(&confs);
1355 if (ast_atomic_dec_and_test(&conf->refcount)) {
1356 /* Take the conference room number out of an inuse state */
1357 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1358 conf_map[confno_int] = 0;
1359 conf_free(conf);
1360 res = 1;
1362 AST_LIST_UNLOCK(&confs);
1364 return res;
1368 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1370 struct ast_conf_user *user = NULL;
1371 struct ast_conf_user *usr = NULL;
1372 int fd;
1373 struct zt_confinfo ztc, ztc_empty;
1374 struct ast_frame *f;
1375 struct ast_channel *c;
1376 struct ast_frame fr;
1377 int outfd;
1378 int ms;
1379 int nfds;
1380 int res;
1381 int flags;
1382 int retryzap;
1383 int origfd;
1384 int musiconhold = 0;
1385 int firstpass = 0;
1386 int lastmarked = 0;
1387 int currentmarked = 0;
1388 int ret = -1;
1389 int x;
1390 int menu_active = 0;
1391 int using_pseudo = 0;
1392 int duration=20;
1393 int hr, min, sec;
1394 int sent_event = 0;
1395 time_t now;
1396 struct ast_dsp *dsp=NULL;
1397 struct ast_app *app;
1398 const char *agifile;
1399 const char *agifiledefault = "conf-background.agi";
1400 char meetmesecs[30] = "";
1401 char exitcontext[AST_MAX_CONTEXT] = "";
1402 char recordingtmp[AST_MAX_EXTENSION] = "";
1403 char members[10] = "";
1404 int dtmf, opt_waitmarked_timeout = 0;
1405 time_t timeout = 0;
1406 ZT_BUFFERINFO bi;
1407 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1408 char *buf = __buf + AST_FRIENDLY_OFFSET;
1409 int setusercount = 0;
1411 if (!(user = ast_calloc(1, sizeof(*user))))
1412 return ret;
1414 /* Possible timeout waiting for marked user */
1415 if ((confflags & CONFFLAG_WAITMARKED) &&
1416 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1417 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1418 (opt_waitmarked_timeout > 0)) {
1419 timeout = time(NULL) + opt_waitmarked_timeout;
1422 if (confflags & CONFFLAG_RECORDCONF) {
1423 if (!conf->recordingfilename) {
1424 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1425 if (!conf->recordingfilename) {
1426 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1427 conf->recordingfilename = ast_strdupa(recordingtmp);
1429 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1430 if (!conf->recordingformat) {
1431 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1432 conf->recordingformat = ast_strdupa(recordingtmp);
1434 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1435 conf->confno, conf->recordingfilename, conf->recordingformat);
1439 ast_mutex_lock(&conf->recordthreadlock);
1440 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1441 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1442 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1443 ztc.chan = 0;
1444 ztc.confno = conf->zapconf;
1445 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1446 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1447 ast_log(LOG_WARNING, "Error starting listen channel\n");
1448 ast_hangup(conf->lchan);
1449 conf->lchan = NULL;
1450 } else {
1451 pthread_attr_init(&conf->attr);
1452 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
1453 ast_pthread_create_background(&conf->recordthread, &conf->attr, recordthread, conf);
1454 pthread_attr_destroy(&conf->attr);
1457 ast_mutex_unlock(&conf->recordthreadlock);
1459 time(&user->jointime);
1461 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1462 /* Sorry, but this confernce is locked! */
1463 if (!ast_streamfile(chan, "conf-locked", chan->language))
1464 ast_waitstream(chan, "");
1465 goto outrun;
1468 ast_mutex_lock(&conf->playlock);
1470 if (AST_LIST_EMPTY(&conf->userlist))
1471 user->user_no = 1;
1472 else
1473 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1475 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1477 user->chan = chan;
1478 user->userflags = confflags;
1479 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1480 user->talking = -1;
1482 ast_mutex_unlock(&conf->playlock);
1484 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1485 snprintf(user->namerecloc, sizeof(user->namerecloc),
1486 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1487 conf->confno, user->user_no);
1488 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1489 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1490 else
1491 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1492 if (res == -1)
1493 goto outrun;
1496 ast_mutex_lock(&conf->playlock);
1498 if (confflags & CONFFLAG_MARKEDUSER)
1499 conf->markedusers++;
1500 conf->users++;
1501 /* Update table */
1502 snprintf(members, sizeof(members), "%d", conf->users);
1503 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1504 setusercount = 1;
1506 /* This device changed state now - if this is the first user */
1507 if (conf->users == 1)
1508 ast_device_state_changed("meetme:%s", conf->confno);
1510 ast_mutex_unlock(&conf->playlock);
1512 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1513 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1514 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1515 else if (!ast_strlen_zero(chan->macrocontext))
1516 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1517 else
1518 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1521 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1522 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1523 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1524 ast_waitstream(chan, "");
1525 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1526 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1527 ast_waitstream(chan, "");
1530 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1531 int keepplaying = 1;
1533 if (conf->users == 2) {
1534 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1535 res = ast_waitstream(chan, AST_DIGIT_ANY);
1536 ast_stopstream(chan);
1537 if (res > 0)
1538 keepplaying=0;
1539 else if (res == -1)
1540 goto outrun;
1542 } else {
1543 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1544 res = ast_waitstream(chan, AST_DIGIT_ANY);
1545 ast_stopstream(chan);
1546 if (res > 0)
1547 keepplaying=0;
1548 else if (res == -1)
1549 goto outrun;
1551 if (keepplaying) {
1552 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1553 if (res > 0)
1554 keepplaying=0;
1555 else if (res == -1)
1556 goto outrun;
1558 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1559 res = ast_waitstream(chan, AST_DIGIT_ANY);
1560 ast_stopstream(chan);
1561 if (res > 0)
1562 keepplaying=0;
1563 else if (res == -1)
1564 goto outrun;
1569 ast_indicate(chan, -1);
1571 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1572 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1573 goto outrun;
1576 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1577 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1578 goto outrun;
1581 retryzap = (strcasecmp(chan->tech->type, "Zap") || (chan->audiohooks || chan->monitor) ? 1 : 0);
1582 user->zapchannel = !retryzap;
1584 zapretry:
1585 origfd = chan->fds[0];
1586 if (retryzap) {
1587 fd = open("/dev/zap/pseudo", O_RDWR);
1588 if (fd < 0) {
1589 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1590 goto outrun;
1592 using_pseudo = 1;
1593 /* Make non-blocking */
1594 flags = fcntl(fd, F_GETFL);
1595 if (flags < 0) {
1596 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1597 close(fd);
1598 goto outrun;
1600 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1601 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1602 close(fd);
1603 goto outrun;
1605 /* Setup buffering information */
1606 memset(&bi, 0, sizeof(bi));
1607 bi.bufsize = CONF_SIZE/2;
1608 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1609 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1610 bi.numbufs = audio_buffers;
1611 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1612 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1613 close(fd);
1614 goto outrun;
1616 x = 1;
1617 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1618 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1619 close(fd);
1620 goto outrun;
1622 nfds = 1;
1623 } else {
1624 /* XXX Make sure we're not running on a pseudo channel XXX */
1625 fd = chan->fds[0];
1626 nfds = 0;
1628 memset(&ztc, 0, sizeof(ztc));
1629 memset(&ztc_empty, 0, sizeof(ztc_empty));
1630 /* Check to see if we're in a conference... */
1631 ztc.chan = 0;
1632 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1633 ast_log(LOG_WARNING, "Error getting conference\n");
1634 close(fd);
1635 goto outrun;
1637 if (ztc.confmode) {
1638 /* Whoa, already in a conference... Retry... */
1639 if (!retryzap) {
1640 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1641 retryzap = 1;
1642 goto zapretry;
1645 memset(&ztc, 0, sizeof(ztc));
1646 /* Add us to the conference */
1647 ztc.chan = 0;
1648 ztc.confno = conf->zapconf;
1650 ast_mutex_lock(&conf->playlock);
1652 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1653 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1654 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1655 ast_waitstream(conf->chan, "");
1656 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1657 ast_waitstream(conf->chan, "");
1661 if (confflags & CONFFLAG_WAITMARKED)
1662 ztc.confmode = ZT_CONF_CONF;
1663 else if (confflags & CONFFLAG_MONITOR)
1664 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1665 else if (confflags & CONFFLAG_TALKER)
1666 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1667 else
1668 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1670 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1671 ast_log(LOG_WARNING, "Error setting conference\n");
1672 close(fd);
1673 ast_mutex_unlock(&conf->playlock);
1674 goto outrun;
1676 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1678 if (!sent_event) {
1679 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1680 "Channel: %s\r\n"
1681 "Uniqueid: %s\r\n"
1682 "Meetme: %s\r\n"
1683 "Usernum: %d\r\n",
1684 chan->name, chan->uniqueid, conf->confno, user->user_no);
1685 sent_event = 1;
1688 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1689 firstpass = 1;
1690 if (!(confflags & CONFFLAG_QUIET))
1691 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1692 conf_play(chan, conf, ENTER);
1695 ast_mutex_unlock(&conf->playlock);
1697 conf_flush(fd, chan);
1699 if (confflags & CONFFLAG_AGI) {
1700 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1701 or use default filename of conf-background.agi */
1703 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1704 if (!agifile)
1705 agifile = agifiledefault;
1707 if (user->zapchannel) {
1708 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1709 x = 1;
1710 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1712 /* Find a pointer to the agi app and execute the script */
1713 app = pbx_findapp("agi");
1714 if (app) {
1715 char *s = ast_strdupa(agifile);
1716 ret = pbx_exec(chan, app, s);
1717 } else {
1718 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1719 ret = -2;
1721 if (user->zapchannel) {
1722 /* Remove CONFMUTE mode on Zap channel */
1723 x = 0;
1724 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1726 } else {
1727 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1728 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1729 x = 1;
1730 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1732 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1733 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1734 res = -1;
1736 for(;;) {
1737 int menu_was_active = 0;
1739 outfd = -1;
1740 ms = -1;
1742 if (timeout && time(NULL) >= timeout)
1743 break;
1745 /* if we have just exited from the menu, and the user had a channel-driver
1746 volume adjustment, restore it
1748 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1749 set_talk_volume(user, user->listen.desired);
1751 menu_was_active = menu_active;
1753 currentmarked = conf->markedusers;
1754 if (!(confflags & CONFFLAG_QUIET) &&
1755 (confflags & CONFFLAG_MARKEDUSER) &&
1756 (confflags & CONFFLAG_WAITMARKED) &&
1757 lastmarked == 0) {
1758 if (currentmarked == 1 && conf->users > 1) {
1759 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1760 if (conf->users - 1 == 1) {
1761 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1762 ast_waitstream(chan, "");
1763 } else {
1764 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1765 ast_waitstream(chan, "");
1768 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1769 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1770 ast_waitstream(chan, "");
1773 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1776 /* Update the struct with the actual confflags */
1777 user->userflags = confflags;
1779 if (confflags & CONFFLAG_WAITMARKED) {
1780 if(currentmarked == 0) {
1781 if (lastmarked != 0) {
1782 if (!(confflags & CONFFLAG_QUIET))
1783 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1784 ast_waitstream(chan, "");
1785 if(confflags & CONFFLAG_MARKEDEXIT)
1786 break;
1787 else {
1788 ztc.confmode = ZT_CONF_CONF;
1789 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1790 ast_log(LOG_WARNING, "Error setting conference\n");
1791 close(fd);
1792 goto outrun;
1796 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1797 ast_moh_start(chan, NULL, NULL);
1798 musiconhold = 1;
1800 } else if(currentmarked >= 1 && lastmarked == 0) {
1801 /* Marked user entered, so cancel timeout */
1802 timeout = 0;
1803 if (confflags & CONFFLAG_MONITOR)
1804 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1805 else if (confflags & CONFFLAG_TALKER)
1806 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1807 else
1808 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1809 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1810 ast_log(LOG_WARNING, "Error setting conference\n");
1811 close(fd);
1812 goto outrun;
1814 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1815 ast_moh_stop(chan);
1816 musiconhold = 0;
1818 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1819 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1820 ast_waitstream(chan, "");
1821 conf_play(chan, conf, ENTER);
1826 /* trying to add moh for single person conf */
1827 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1828 if (conf->users == 1) {
1829 if (musiconhold == 0) {
1830 ast_moh_start(chan, NULL, NULL);
1831 musiconhold = 1;
1833 } else {
1834 if (musiconhold) {
1835 ast_moh_stop(chan);
1836 musiconhold = 0;
1841 /* Leave if the last marked user left */
1842 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1843 ret = -1;
1844 break;
1847 /* Check if my modes have changed */
1849 /* If I should be muted but am still talker, mute me */
1850 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1851 ztc.confmode ^= ZT_CONF_TALKER;
1852 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1853 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1854 ret = -1;
1855 break;
1858 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1859 "Channel: %s\r\n"
1860 "Uniqueid: %s\r\n"
1861 "Meetme: %s\r\n"
1862 "Usernum: %i\r\n"
1863 "Status: on\r\n",
1864 chan->name, chan->uniqueid, conf->confno, user->user_no);
1867 /* If I should be un-muted but am not talker, un-mute me */
1868 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1869 ztc.confmode |= ZT_CONF_TALKER;
1870 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1871 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1872 ret = -1;
1873 break;
1876 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1877 "Channel: %s\r\n"
1878 "Uniqueid: %s\r\n"
1879 "Meetme: %s\r\n"
1880 "Usernum: %i\r\n"
1881 "Status: off\r\n",
1882 chan->name, chan->uniqueid, conf->confno, user->user_no);
1885 /* If I have been kicked, exit the conference */
1886 if (user->adminflags & ADMINFLAG_KICKME) {
1887 //You have been kicked.
1888 if (!(confflags & CONFFLAG_QUIET) &&
1889 !ast_streamfile(chan, "conf-kicked", chan->language)) {
1890 ast_waitstream(chan, "");
1892 ret = 0;
1893 break;
1896 /* Perform an extra hangup check just in case */
1897 if (ast_check_hangup(chan))
1898 break;
1900 if (c) {
1901 if (c->fds[0] != origfd || (user->zapchannel && (c->audiohooks || c->monitor))) {
1902 if (using_pseudo) {
1903 /* Kill old pseudo */
1904 close(fd);
1905 using_pseudo = 0;
1907 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1908 retryzap = (strcasecmp(c->tech->type, "Zap") || (c->audiohooks || c->monitor) ? 1 : 0);
1909 user->zapchannel = !retryzap;
1910 goto zapretry;
1912 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1913 f = ast_read_noaudio(c);
1914 else
1915 f = ast_read(c);
1916 if (!f)
1917 break;
1918 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1919 if (user->talk.actual)
1920 ast_frame_adjust_volume(f, user->talk.actual);
1922 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1923 int totalsilence;
1925 if (user->talking == -1)
1926 user->talking = 0;
1928 res = ast_dsp_silence(dsp, f, &totalsilence);
1929 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1930 user->talking = 1;
1931 if (confflags & CONFFLAG_MONITORTALKER)
1932 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1933 "Channel: %s\r\n"
1934 "Uniqueid: %s\r\n"
1935 "Meetme: %s\r\n"
1936 "Usernum: %d\r\n"
1937 "Status: on\r\n",
1938 chan->name, chan->uniqueid, conf->confno, user->user_no);
1940 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1941 user->talking = 0;
1942 if (confflags & CONFFLAG_MONITORTALKER)
1943 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1944 "Channel: %s\r\n"
1945 "Uniqueid: %s\r\n"
1946 "Meetme: %s\r\n"
1947 "Usernum: %d\r\n"
1948 "Status: off\r\n",
1949 chan->name, chan->uniqueid, conf->confno, user->user_no);
1952 if (using_pseudo) {
1953 /* Absolutely do _not_ use careful_write here...
1954 it is important that we read data from the channel
1955 as fast as it arrives, and feed it into the conference.
1956 The buffering in the pseudo channel will take care of any
1957 timing differences, unless they are so drastic as to lose
1958 audio frames (in which case carefully writing would only
1959 have delayed the audio even further).
1961 /* As it turns out, we do want to use careful write. We just
1962 don't want to block, but we do want to at least *try*
1963 to write out all the samples.
1965 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1966 careful_write(fd, f->data, f->datalen, 0);
1968 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1969 char tmp[2];
1971 if (confflags & CONFFLAG_PASS_DTMF)
1972 conf_queue_dtmf(conf, user, f);
1974 tmp[0] = f->subclass;
1975 tmp[1] = '\0';
1976 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1977 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
1978 ret = 0;
1979 ast_frfree(f);
1980 break;
1981 } else if (option_debug > 1)
1982 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1983 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1984 if (confflags & CONFFLAG_PASS_DTMF)
1985 conf_queue_dtmf(conf, user, f);
1986 ret = 0;
1987 ast_frfree(f);
1988 break;
1989 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1990 if (confflags & CONFFLAG_PASS_DTMF)
1991 conf_queue_dtmf(conf, user, f);
1992 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1993 ast_log(LOG_WARNING, "Error setting conference\n");
1994 close(fd);
1995 ast_frfree(f);
1996 goto outrun;
1999 /* if we are entering the menu, and the user has a channel-driver
2000 volume adjustment, clear it
2002 if (!menu_active && user->talk.desired && !user->talk.actual)
2003 set_talk_volume(user, 0);
2005 if (musiconhold) {
2006 ast_moh_stop(chan);
2008 if ((confflags & CONFFLAG_ADMIN)) {
2009 /* Admin menu */
2010 if (!menu_active) {
2011 menu_active = 1;
2012 /* Record this sound! */
2013 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2014 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2015 ast_stopstream(chan);
2016 } else
2017 dtmf = 0;
2018 } else
2019 dtmf = f->subclass;
2020 if (dtmf) {
2021 switch(dtmf) {
2022 case '1': /* Un/Mute */
2023 menu_active = 0;
2025 /* for admin, change both admin and use flags */
2026 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2027 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2028 else
2029 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2031 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2032 if (!ast_streamfile(chan, "conf-muted", chan->language))
2033 ast_waitstream(chan, "");
2034 } else {
2035 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2036 ast_waitstream(chan, "");
2038 break;
2039 case '2': /* Un/Lock the Conference */
2040 menu_active = 0;
2041 if (conf->locked) {
2042 conf->locked = 0;
2043 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2044 ast_waitstream(chan, "");
2045 } else {
2046 conf->locked = 1;
2047 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2048 ast_waitstream(chan, "");
2050 break;
2051 case '3': /* Eject last user */
2052 menu_active = 0;
2053 usr = AST_LIST_LAST(&conf->userlist);
2054 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2055 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2056 ast_waitstream(chan, "");
2057 } else
2058 usr->adminflags |= ADMINFLAG_KICKME;
2059 ast_stopstream(chan);
2060 break;
2061 case '4':
2062 tweak_listen_volume(user, VOL_DOWN);
2063 break;
2064 case '6':
2065 tweak_listen_volume(user, VOL_UP);
2066 break;
2067 case '7':
2068 tweak_talk_volume(user, VOL_DOWN);
2069 break;
2070 case '8':
2071 menu_active = 0;
2072 break;
2073 case '9':
2074 tweak_talk_volume(user, VOL_UP);
2075 break;
2076 default:
2077 menu_active = 0;
2078 /* Play an error message! */
2079 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2080 ast_waitstream(chan, "");
2081 break;
2084 } else {
2085 /* User menu */
2086 if (!menu_active) {
2087 menu_active = 1;
2088 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2089 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2090 ast_stopstream(chan);
2091 } else
2092 dtmf = 0;
2093 } else
2094 dtmf = f->subclass;
2095 if (dtmf) {
2096 switch(dtmf) {
2097 case '1': /* Un/Mute */
2098 menu_active = 0;
2100 /* user can only toggle the self-muted state */
2101 user->adminflags ^= ADMINFLAG_SELFMUTED;
2103 /* they can't override the admin mute state */
2104 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2105 if (!ast_streamfile(chan, "conf-muted", chan->language))
2106 ast_waitstream(chan, "");
2107 } else {
2108 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2109 ast_waitstream(chan, "");
2111 break;
2112 case '4':
2113 tweak_listen_volume(user, VOL_DOWN);
2114 break;
2115 case '6':
2116 tweak_listen_volume(user, VOL_UP);
2117 break;
2118 case '7':
2119 tweak_talk_volume(user, VOL_DOWN);
2120 break;
2121 case '8':
2122 menu_active = 0;
2123 break;
2124 case '9':
2125 tweak_talk_volume(user, VOL_UP);
2126 break;
2127 default:
2128 menu_active = 0;
2129 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2130 ast_waitstream(chan, "");
2131 break;
2135 if (musiconhold)
2136 ast_moh_start(chan, NULL, NULL);
2138 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2139 ast_log(LOG_WARNING, "Error setting conference\n");
2140 close(fd);
2141 ast_frfree(f);
2142 goto outrun;
2145 conf_flush(fd, chan);
2146 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2147 && confflags & CONFFLAG_PASS_DTMF) {
2148 conf_queue_dtmf(conf, user, f);
2149 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2150 switch (f->subclass) {
2151 case AST_CONTROL_HOLD:
2152 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2153 break;
2154 default:
2155 break;
2157 } else if (f->frametype == AST_FRAME_NULL) {
2158 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2159 } else if (option_debug) {
2160 ast_log(LOG_DEBUG,
2161 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2162 chan->name, f->frametype, f->subclass);
2164 ast_frfree(f);
2165 } else if (outfd > -1) {
2166 res = read(outfd, buf, CONF_SIZE);
2167 if (res > 0) {
2168 memset(&fr, 0, sizeof(fr));
2169 fr.frametype = AST_FRAME_VOICE;
2170 fr.subclass = AST_FORMAT_SLINEAR;
2171 fr.datalen = res;
2172 fr.samples = res/2;
2173 fr.data = buf;
2174 fr.offset = AST_FRIENDLY_OFFSET;
2175 if (!user->listen.actual &&
2176 ((confflags & CONFFLAG_MONITOR) ||
2177 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2178 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
2179 )) {
2180 int index;
2181 for (index=0;index<AST_FRAME_BITS;index++)
2182 if (chan->rawwriteformat & (1 << index))
2183 break;
2184 if (index >= AST_FRAME_BITS)
2185 goto bailoutandtrynormal;
2186 ast_mutex_lock(&conf->listenlock);
2187 if (!conf->transframe[index]) {
2188 if (conf->origframe) {
2189 if (!conf->transpath[index])
2190 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2191 if (conf->transpath[index]) {
2192 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2193 if (!conf->transframe[index])
2194 conf->transframe[index] = &ast_null_frame;
2198 if (conf->transframe[index]) {
2199 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2200 if (ast_write(chan, conf->transframe[index]))
2201 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2203 } else {
2204 ast_mutex_unlock(&conf->listenlock);
2205 goto bailoutandtrynormal;
2207 ast_mutex_unlock(&conf->listenlock);
2208 } else {
2209 bailoutandtrynormal:
2210 if (user->listen.actual)
2211 ast_frame_adjust_volume(&fr, user->listen.actual);
2212 if (ast_write(chan, &fr) < 0) {
2213 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2216 } else
2217 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2219 lastmarked = currentmarked;
2223 if (musiconhold)
2224 ast_moh_stop(chan);
2226 if (using_pseudo)
2227 close(fd);
2228 else {
2229 /* Take out of conference */
2230 ztc.chan = 0;
2231 ztc.confno = 0;
2232 ztc.confmode = 0;
2233 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2234 ast_log(LOG_WARNING, "Error setting conference\n");
2238 reset_volumes(user);
2240 AST_LIST_LOCK(&confs);
2241 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2242 conf_play(chan, conf, LEAVE);
2244 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2245 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2246 if ((conf->chan) && (conf->users > 1)) {
2247 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2248 ast_waitstream(conf->chan, "");
2249 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2250 ast_waitstream(conf->chan, "");
2252 ast_filedelete(user->namerecloc, NULL);
2255 AST_LIST_UNLOCK(&confs);
2257 outrun:
2258 AST_LIST_LOCK(&confs);
2260 if (dsp)
2261 ast_dsp_free(dsp);
2263 if (user->user_no) { /* Only cleanup users who really joined! */
2264 now = time(NULL);
2265 hr = (now - user->jointime) / 3600;
2266 min = ((now - user->jointime) % 3600) / 60;
2267 sec = (now - user->jointime) % 60;
2269 if (sent_event) {
2270 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2271 "Channel: %s\r\n"
2272 "Uniqueid: %s\r\n"
2273 "Meetme: %s\r\n"
2274 "Usernum: %d\r\n"
2275 "CallerIDnum: %s\r\n"
2276 "CallerIDname: %s\r\n"
2277 "Duration: %ld\r\n",
2278 chan->name, chan->uniqueid, conf->confno,
2279 user->user_no,
2280 S_OR(user->chan->cid.cid_num, "<unknown>"),
2281 S_OR(user->chan->cid.cid_name, "<unknown>"),
2282 (long)(now - user->jointime));
2285 if (setusercount) {
2286 conf->users--;
2287 /* Update table */
2288 snprintf(members, sizeof(members), "%d", conf->users);
2289 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2290 if (confflags & CONFFLAG_MARKEDUSER)
2291 conf->markedusers--;
2293 /* Remove ourselves from the list */
2294 AST_LIST_REMOVE(&conf->userlist, user, list);
2296 /* Change any states */
2297 if (!conf->users)
2298 ast_device_state_changed("meetme:%s", conf->confno);
2300 /* Return the number of seconds the user was in the conf */
2301 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2302 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2304 free(user);
2305 AST_LIST_UNLOCK(&confs);
2307 return ret;
2310 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2311 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2313 struct ast_variable *var;
2314 struct ast_conference *cnf;
2316 /* Check first in the conference list */
2317 AST_LIST_LOCK(&confs);
2318 AST_LIST_TRAVERSE(&confs, cnf, list) {
2319 if (!strcmp(confno, cnf->confno))
2320 break;
2322 if (cnf) {
2323 cnf->refcount += refcount;
2325 AST_LIST_UNLOCK(&confs);
2327 if (!cnf) {
2328 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2330 var = ast_load_realtime("meetme", "confno", confno, NULL);
2332 if (!var)
2333 return NULL;
2335 while (var) {
2336 if (!strcasecmp(var->name, "pin")) {
2337 pin = ast_strdupa(var->value);
2338 } else if (!strcasecmp(var->name, "adminpin")) {
2339 pinadmin = ast_strdupa(var->value);
2341 var = var->next;
2343 ast_variables_destroy(var);
2345 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
2348 if (cnf) {
2349 if (confflags && !cnf->chan &&
2350 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2351 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2352 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2353 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2356 if (confflags && !cnf->chan &&
2357 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2358 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2359 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2363 return cnf;
2367 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2368 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2370 struct ast_config *cfg;
2371 struct ast_variable *var;
2372 struct ast_conference *cnf;
2373 char *parse;
2374 AST_DECLARE_APP_ARGS(args,
2375 AST_APP_ARG(confno);
2376 AST_APP_ARG(pin);
2377 AST_APP_ARG(pinadmin);
2380 /* Check first in the conference list */
2381 AST_LIST_LOCK(&confs);
2382 AST_LIST_TRAVERSE(&confs, cnf, list) {
2383 if (!strcmp(confno, cnf->confno))
2384 break;
2386 if (cnf){
2387 cnf->refcount += refcount;
2389 AST_LIST_UNLOCK(&confs);
2391 if (!cnf) {
2392 if (dynamic) {
2393 /* No need to parse meetme.conf */
2394 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
2395 if (dynamic_pin) {
2396 if (dynamic_pin[0] == 'q') {
2397 /* Query the user to enter a PIN */
2398 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2399 return NULL;
2401 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
2402 } else {
2403 cnf = build_conf(confno, "", "", make, dynamic, refcount);
2405 } else {
2406 /* Check the config */
2407 cfg = ast_config_load(CONFIG_FILE_NAME);
2408 if (!cfg) {
2409 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2410 return NULL;
2412 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2413 if (strcasecmp(var->name, "conf"))
2414 continue;
2416 if (!(parse = ast_strdupa(var->value)))
2417 return NULL;
2419 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
2420 if (!strcasecmp(args.confno, confno)) {
2421 /* Bingo it's a valid conference */
2422 cnf = build_conf(args.confno,
2423 S_OR(args.pin, ""),
2424 S_OR(args.pinadmin, ""),
2425 make, dynamic, refcount);
2426 break;
2429 if (!var) {
2430 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
2432 ast_config_destroy(cfg);
2434 } else if (dynamic_pin) {
2435 /* Correct for the user selecting 'D' instead of 'd' to have
2436 someone join into a conference that has already been created
2437 with a pin. */
2438 if (dynamic_pin[0] == 'q')
2439 dynamic_pin[0] = '\0';
2442 if (cnf) {
2443 if (confflags && !cnf->chan &&
2444 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2445 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2446 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2447 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2450 if (confflags && !cnf->chan &&
2451 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2452 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2453 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2457 return cnf;
2460 /*! \brief The MeetmeCount application */
2461 static int count_exec(struct ast_channel *chan, void *data)
2463 struct ast_module_user *u;
2464 int res = 0;
2465 struct ast_conference *conf;
2466 int count;
2467 char *localdata;
2468 char val[80] = "0";
2469 AST_DECLARE_APP_ARGS(args,
2470 AST_APP_ARG(confno);
2471 AST_APP_ARG(varname);
2474 if (ast_strlen_zero(data)) {
2475 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2476 return -1;
2479 u = ast_module_user_add(chan);
2481 if (!(localdata = ast_strdupa(data))) {
2482 ast_module_user_remove(u);
2483 return -1;
2486 AST_STANDARD_APP_ARGS(args, localdata);
2488 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2490 if (conf) {
2491 count = conf->users;
2492 dispose_conf(conf);
2493 conf = NULL;
2494 } else
2495 count = 0;
2497 if (!ast_strlen_zero(args.varname)){
2498 /* have var so load it and exit */
2499 snprintf(val, sizeof(val), "%d",count);
2500 pbx_builtin_setvar_helper(chan, args.varname, val);
2501 } else {
2502 if (chan->_state != AST_STATE_UP)
2503 ast_answer(chan);
2504 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2506 ast_module_user_remove(u);
2508 return res;
2511 /*! \brief The meetme() application */
2512 static int conf_exec(struct ast_channel *chan, void *data)
2514 int res=-1;
2515 struct ast_module_user *u;
2516 char confno[MAX_CONFNUM] = "";
2517 int allowretry = 0;
2518 int retrycnt = 0;
2519 struct ast_conference *cnf = NULL;
2520 struct ast_flags confflags = {0};
2521 int dynamic = 0;
2522 int empty = 0, empty_no_pin = 0;
2523 int always_prompt = 0;
2524 char *notdata, *info, the_pin[MAX_PIN] = "";
2525 AST_DECLARE_APP_ARGS(args,
2526 AST_APP_ARG(confno);
2527 AST_APP_ARG(options);
2528 AST_APP_ARG(pin);
2530 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2532 u = ast_module_user_add(chan);
2534 if (ast_strlen_zero(data)) {
2535 allowretry = 1;
2536 notdata = "";
2537 } else {
2538 notdata = data;
2541 if (chan->_state != AST_STATE_UP)
2542 ast_answer(chan);
2544 info = ast_strdupa(notdata);
2546 AST_STANDARD_APP_ARGS(args, info);
2548 if (args.confno) {
2549 ast_copy_string(confno, args.confno, sizeof(confno));
2550 if (ast_strlen_zero(confno)) {
2551 allowretry = 1;
2555 if (args.pin)
2556 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2558 if (args.options) {
2559 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2560 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2561 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2562 strcpy(the_pin, "q");
2564 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2565 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2566 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2569 do {
2570 if (retrycnt > 3)
2571 allowretry = 0;
2572 if (empty) {
2573 int i;
2574 struct ast_config *cfg;
2575 struct ast_variable *var;
2576 int confno_int;
2578 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2579 if ((empty_no_pin) || (!dynamic)) {
2580 cfg = ast_config_load(CONFIG_FILE_NAME);
2581 if (cfg) {
2582 var = ast_variable_browse(cfg, "rooms");
2583 while (var) {
2584 if (!strcasecmp(var->name, "conf")) {
2585 char *stringp = ast_strdupa(var->value);
2586 if (stringp) {
2587 char *confno_tmp = strsep(&stringp, "|,");
2588 int found = 0;
2589 if (!dynamic) {
2590 /* For static: run through the list and see if this conference is empty */
2591 AST_LIST_LOCK(&confs);
2592 AST_LIST_TRAVERSE(&confs, cnf, list) {
2593 if (!strcmp(confno_tmp, cnf->confno)) {
2594 /* The conference exists, therefore it's not empty */
2595 found = 1;
2596 break;
2599 AST_LIST_UNLOCK(&confs);
2600 if (!found) {
2601 /* At this point, we have a confno_tmp (static conference) that is empty */
2602 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2603 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2604 * Case 2: empty_no_pin and pin is blank (but not NULL)
2605 * Case 3: not empty_no_pin
2607 ast_copy_string(confno, confno_tmp, sizeof(confno));
2608 break;
2609 /* XXX the map is not complete (but we do have a confno) */
2615 var = var->next;
2617 ast_config_destroy(cfg);
2621 /* Select first conference number not in use */
2622 if (ast_strlen_zero(confno) && dynamic) {
2623 AST_LIST_LOCK(&confs);
2624 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
2625 if (!conf_map[i]) {
2626 snprintf(confno, sizeof(confno), "%d", i);
2627 conf_map[i] = 1;
2628 break;
2631 AST_LIST_UNLOCK(&confs);
2634 /* Not found? */
2635 if (ast_strlen_zero(confno)) {
2636 res = ast_streamfile(chan, "conf-noempty", chan->language);
2637 if (!res)
2638 ast_waitstream(chan, "");
2639 } else {
2640 if (sscanf(confno, "%d", &confno_int) == 1) {
2641 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2642 if (!res) {
2643 ast_waitstream(chan, "");
2644 res = ast_say_digits(chan, confno_int, "", chan->language);
2646 } else {
2647 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2652 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2653 /* Prompt user for conference number */
2654 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2655 if (res < 0) {
2656 /* Don't try to validate when we catch an error */
2657 confno[0] = '\0';
2658 allowretry = 0;
2659 break;
2662 if (!ast_strlen_zero(confno)) {
2663 /* Check the validity of the conference */
2664 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
2665 sizeof(the_pin), 1, &confflags);
2666 if (!cnf) {
2667 cnf = find_conf_realtime(chan, confno, 1, dynamic,
2668 the_pin, sizeof(the_pin), 1, &confflags);
2671 if (!cnf) {
2672 res = ast_streamfile(chan, "conf-invalid", chan->language);
2673 if (!res)
2674 ast_waitstream(chan, "");
2675 res = -1;
2676 if (allowretry)
2677 confno[0] = '\0';
2678 } else {
2679 if ((!ast_strlen_zero(cnf->pin) &&
2680 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2681 (!ast_strlen_zero(cnf->pinadmin) &&
2682 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2683 char pin[MAX_PIN] = "";
2684 int j;
2686 /* Allow the pin to be retried up to 3 times */
2687 for (j = 0; j < 3; j++) {
2688 if (*the_pin && (always_prompt == 0)) {
2689 ast_copy_string(pin, the_pin, sizeof(pin));
2690 res = 0;
2691 } else {
2692 /* Prompt user for pin if pin is required */
2693 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2695 if (res >= 0) {
2696 if (!strcasecmp(pin, cnf->pin) ||
2697 (!ast_strlen_zero(cnf->pinadmin) &&
2698 !strcasecmp(pin, cnf->pinadmin))) {
2699 /* Pin correct */
2700 allowretry = 0;
2701 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2702 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2703 /* Run the conference */
2704 res = conf_run(chan, cnf, confflags.flags, optargs);
2705 break;
2706 } else {
2707 /* Pin invalid */
2708 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
2709 res = ast_waitstream(chan, AST_DIGIT_ANY);
2710 ast_stopstream(chan);
2712 else {
2713 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2714 break;
2716 if (res < 0)
2717 break;
2718 pin[0] = res;
2719 pin[1] = '\0';
2720 res = -1;
2721 if (allowretry)
2722 confno[0] = '\0';
2724 } else {
2725 /* failed when getting the pin */
2726 res = -1;
2727 allowretry = 0;
2728 /* see if we need to get rid of the conference */
2729 break;
2732 /* Don't retry pin with a static pin */
2733 if (*the_pin && (always_prompt==0)) {
2734 break;
2737 } else {
2738 /* No pin required */
2739 allowretry = 0;
2741 /* Run the conference */
2742 res = conf_run(chan, cnf, confflags.flags, optargs);
2744 dispose_conf(cnf);
2745 cnf = NULL;
2748 } while (allowretry);
2750 if (cnf)
2751 dispose_conf(cnf);
2753 ast_module_user_remove(u);
2755 return res;
2758 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2760 struct ast_conf_user *user = NULL;
2761 int cid;
2763 sscanf(callerident, "%i", &cid);
2764 if (conf && callerident) {
2765 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2766 if (cid == user->user_no)
2767 return user;
2770 return NULL;
2773 /*! \brief The MeetMeadmin application */
2774 /* MeetMeAdmin(confno, command, caller) */
2775 static int admin_exec(struct ast_channel *chan, void *data) {
2776 char *params;
2777 struct ast_conference *cnf;
2778 struct ast_conf_user *user = NULL;
2779 struct ast_module_user *u;
2780 AST_DECLARE_APP_ARGS(args,
2781 AST_APP_ARG(confno);
2782 AST_APP_ARG(command);
2783 AST_APP_ARG(user);
2786 if (ast_strlen_zero(data)) {
2787 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
2788 return -1;
2791 u = ast_module_user_add(chan);
2793 AST_LIST_LOCK(&confs);
2795 params = ast_strdupa(data);
2796 AST_STANDARD_APP_ARGS(args, params);
2798 if (!args.command) {
2799 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2800 AST_LIST_UNLOCK(&confs);
2801 ast_module_user_remove(u);
2802 return -1;
2804 AST_LIST_TRAVERSE(&confs, cnf, list) {
2805 if (!strcmp(cnf->confno, args.confno))
2806 break;
2809 if (!cnf) {
2810 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
2811 AST_LIST_UNLOCK(&confs);
2812 ast_module_user_remove(u);
2813 return 0;
2816 ast_atomic_fetchadd_int(&cnf->refcount, 1);
2818 if (args.user)
2819 user = find_user(cnf, args.user);
2821 switch (*args.command) {
2822 case 76: /* L: Lock */
2823 cnf->locked = 1;
2824 break;
2825 case 108: /* l: Unlock */
2826 cnf->locked = 0;
2827 break;
2828 case 75: /* K: kick all users */
2829 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2830 user->adminflags |= ADMINFLAG_KICKME;
2831 break;
2832 case 101: /* e: Eject last user*/
2833 user = AST_LIST_LAST(&cnf->userlist);
2834 if (!(user->userflags & CONFFLAG_ADMIN))
2835 user->adminflags |= ADMINFLAG_KICKME;
2836 else
2837 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2838 break;
2839 case 77: /* M: Mute */
2840 if (user) {
2841 user->adminflags |= ADMINFLAG_MUTED;
2842 } else
2843 ast_log(LOG_NOTICE, "Specified User not found!\n");
2844 break;
2845 case 78: /* N: Mute all (non-admin) users */
2846 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2847 if (!(user->userflags & CONFFLAG_ADMIN))
2848 user->adminflags |= ADMINFLAG_MUTED;
2850 break;
2851 case 109: /* m: Unmute */
2852 if (user) {
2853 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2854 } else
2855 ast_log(LOG_NOTICE, "Specified User not found!\n");
2856 break;
2857 case 110: /* n: Unmute all users */
2858 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2859 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2860 break;
2861 case 107: /* k: Kick user */
2862 if (user)
2863 user->adminflags |= ADMINFLAG_KICKME;
2864 else
2865 ast_log(LOG_NOTICE, "Specified User not found!\n");
2866 break;
2867 case 118: /* v: Lower all users listen volume */
2868 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2869 tweak_listen_volume(user, VOL_DOWN);
2870 break;
2871 case 86: /* V: Raise all users listen volume */
2872 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2873 tweak_listen_volume(user, VOL_UP);
2874 break;
2875 case 115: /* s: Lower all users speaking volume */
2876 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2877 tweak_talk_volume(user, VOL_DOWN);
2878 break;
2879 case 83: /* S: Raise all users speaking volume */
2880 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2881 tweak_talk_volume(user, VOL_UP);
2882 break;
2883 case 82: /* R: Reset all volume levels */
2884 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2885 reset_volumes(user);
2886 break;
2887 case 114: /* r: Reset user's volume level */
2888 if (user)
2889 reset_volumes(user);
2890 else
2891 ast_log(LOG_NOTICE, "Specified User not found!\n");
2892 break;
2893 case 85: /* U: Raise user's listen volume */
2894 if (user)
2895 tweak_listen_volume(user, VOL_UP);
2896 else
2897 ast_log(LOG_NOTICE, "Specified User not found!\n");
2898 break;
2899 case 117: /* u: Lower user's listen volume */
2900 if (user)
2901 tweak_listen_volume(user, VOL_DOWN);
2902 else
2903 ast_log(LOG_NOTICE, "Specified User not found!\n");
2904 break;
2905 case 84: /* T: Raise user's talk volume */
2906 if (user)
2907 tweak_talk_volume(user, VOL_UP);
2908 else
2909 ast_log(LOG_NOTICE, "Specified User not found!\n");
2910 break;
2911 case 116: /* t: Lower user's talk volume */
2912 if (user)
2913 tweak_talk_volume(user, VOL_DOWN);
2914 else
2915 ast_log(LOG_NOTICE, "Specified User not found!\n");
2916 break;
2919 AST_LIST_UNLOCK(&confs);
2921 dispose_conf(cnf);
2923 ast_module_user_remove(u);
2925 return 0;
2928 static int meetmemute(struct mansession *s, const struct message *m, int mute)
2930 struct ast_conference *conf;
2931 struct ast_conf_user *user;
2932 const char *confid = astman_get_header(m, "Meetme");
2933 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
2934 int userno;
2936 if (ast_strlen_zero(confid)) {
2937 astman_send_error(s, m, "Meetme conference not specified");
2938 return 0;
2941 if (ast_strlen_zero(userid)) {
2942 astman_send_error(s, m, "Meetme user number not specified");
2943 return 0;
2946 userno = strtoul(userid, &userid, 10);
2948 if (*userid) {
2949 astman_send_error(s, m, "Invalid user number");
2950 return 0;
2953 /* Look in the conference list */
2954 AST_LIST_LOCK(&confs);
2955 AST_LIST_TRAVERSE(&confs, conf, list) {
2956 if (!strcmp(confid, conf->confno))
2957 break;
2960 if (!conf) {
2961 AST_LIST_UNLOCK(&confs);
2962 astman_send_error(s, m, "Meetme conference does not exist");
2963 return 0;
2966 AST_LIST_TRAVERSE(&conf->userlist, user, list)
2967 if (user->user_no == userno)
2968 break;
2970 if (!user) {
2971 AST_LIST_UNLOCK(&confs);
2972 astman_send_error(s, m, "User number not found");
2973 return 0;
2976 if (mute)
2977 user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
2978 else
2979 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED); /* request user unmuting */
2981 AST_LIST_UNLOCK(&confs);
2983 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);
2985 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2986 return 0;
2989 static int action_meetmemute(struct mansession *s, const struct message *m)
2991 return meetmemute(s, m, 1);
2994 static int action_meetmeunmute(struct mansession *s, const struct message *m)
2996 return meetmemute(s, m, 0);
2999 static void *recordthread(void *args)
3001 struct ast_conference *cnf = args;
3002 struct ast_frame *f=NULL;
3003 int flags;
3004 struct ast_filestream *s=NULL;
3005 int res=0;
3006 int x;
3007 const char *oldrecordingfilename = NULL;
3009 if (!cnf || !cnf->lchan) {
3010 pthread_exit(0);
3013 ast_stopstream(cnf->lchan);
3014 flags = O_CREAT|O_TRUNC|O_WRONLY;
3017 cnf->recording = MEETME_RECORD_ACTIVE;
3018 while (ast_waitfor(cnf->lchan, -1) > -1) {
3019 if (cnf->recording == MEETME_RECORD_TERMINATE) {
3020 AST_LIST_LOCK(&confs);
3021 AST_LIST_UNLOCK(&confs);
3022 break;
3024 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
3025 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
3026 oldrecordingfilename = cnf->recordingfilename;
3029 f = ast_read(cnf->lchan);
3030 if (!f) {
3031 res = -1;
3032 break;
3034 if (f->frametype == AST_FRAME_VOICE) {
3035 ast_mutex_lock(&cnf->listenlock);
3036 for (x=0;x<AST_FRAME_BITS;x++) {
3037 /* Free any translations that have occured */
3038 if (cnf->transframe[x]) {
3039 ast_frfree(cnf->transframe[x]);
3040 cnf->transframe[x] = NULL;
3043 if (cnf->origframe)
3044 ast_frfree(cnf->origframe);
3045 cnf->origframe = ast_frdup(f);
3046 ast_mutex_unlock(&cnf->listenlock);
3047 if (s)
3048 res = ast_writestream(s, f);
3049 if (res) {
3050 ast_frfree(f);
3051 break;
3054 ast_frfree(f);
3056 cnf->recording = MEETME_RECORD_OFF;
3057 if (s)
3058 ast_closestream(s);
3060 pthread_exit(0);
3063 /*! \brief Callback for devicestate providers */
3064 static int meetmestate(const char *data)
3066 struct ast_conference *conf;
3068 /* Find conference */
3069 AST_LIST_LOCK(&confs);
3070 AST_LIST_TRAVERSE(&confs, conf, list) {
3071 if (!strcmp(data, conf->confno))
3072 break;
3074 AST_LIST_UNLOCK(&confs);
3075 if (!conf)
3076 return AST_DEVICE_INVALID;
3079 /* SKREP to fill */
3080 if (!conf->users)
3081 return AST_DEVICE_NOT_INUSE;
3083 return AST_DEVICE_INUSE;
3086 static void load_config_meetme(void)
3088 struct ast_config *cfg;
3089 const char *val;
3091 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3093 if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
3094 return;
3096 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
3097 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
3098 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
3099 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3100 } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
3101 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
3102 ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
3103 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3105 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
3106 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
3109 ast_config_destroy(cfg);
3112 /*! \brief Find an SLA trunk by name
3113 * \note This must be called with the sla_trunks container locked
3115 static struct sla_trunk *sla_find_trunk(const char *name)
3117 struct sla_trunk *trunk = NULL;
3119 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
3120 if (!strcasecmp(trunk->name, name))
3121 break;
3124 return trunk;
3127 /*! \brief Find an SLA station by name
3128 * \note This must be called with the sla_stations container locked
3130 static struct sla_station *sla_find_station(const char *name)
3132 struct sla_station *station = NULL;
3134 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
3135 if (!strcasecmp(station->name, name))
3136 break;
3139 return station;
3142 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
3143 const struct sla_station *station)
3145 struct sla_station_ref *station_ref;
3146 struct sla_trunk_ref *trunk_ref;
3148 /* For each station that has this call on hold, check for private hold. */
3149 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
3150 AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
3151 if (trunk_ref->trunk != trunk || station_ref->station == station)
3152 continue;
3153 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
3154 station_ref->station->hold_access == SLA_HOLD_PRIVATE)
3155 return 1;
3156 return 0;
3160 return 0;
3163 /*! \brief Find a trunk reference on a station by name
3164 * \param station the station
3165 * \param name the trunk's name
3166 * \return a pointer to the station's trunk reference. If the trunk
3167 * is not found, it is not idle and barge is disabled, or if
3168 * it is on hold and private hold is set, then NULL will be returned.
3170 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
3171 const char *name)
3173 struct sla_trunk_ref *trunk_ref = NULL;
3175 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3176 if (strcasecmp(trunk_ref->trunk->name, name))
3177 continue;
3179 if ( (trunk_ref->trunk->barge_disabled
3180 && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
3181 (trunk_ref->trunk->hold_stations
3182 && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
3183 && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
3184 sla_check_station_hold_access(trunk_ref->trunk, station) )
3186 trunk_ref = NULL;
3189 break;
3192 return trunk_ref;
3195 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
3197 struct sla_station_ref *station_ref;
3199 if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
3200 return NULL;
3202 station_ref->station = station;
3204 return station_ref;
3207 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
3209 struct sla_ringing_station *ringing_station;
3211 if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
3212 return NULL;
3214 ringing_station->station = station;
3215 ringing_station->ring_begin = ast_tvnow();
3217 return ringing_station;
3220 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
3221 enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
3223 struct sla_station *station;
3224 struct sla_trunk_ref *trunk_ref;
3226 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
3227 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3228 if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
3229 || trunk_ref == exclude)
3230 continue;
3231 trunk_ref->state = state;
3232 ast_device_state_changed("SLA:%s_%s", station->name, trunk->name);
3233 break;
3238 struct run_station_args {
3239 struct sla_station *station;
3240 struct sla_trunk_ref *trunk_ref;
3241 ast_mutex_t *cond_lock;
3242 ast_cond_t *cond;
3245 static void *run_station(void *data)
3247 struct sla_station *station;
3248 struct sla_trunk_ref *trunk_ref;
3249 char conf_name[MAX_CONFNUM];
3250 struct ast_flags conf_flags = { 0 };
3251 struct ast_conference *conf;
3254 struct run_station_args *args = data;
3255 station = args->station;
3256 trunk_ref = args->trunk_ref;
3257 ast_mutex_lock(args->cond_lock);
3258 ast_cond_signal(args->cond);
3259 ast_mutex_unlock(args->cond_lock);
3260 /* args is no longer valid here. */
3263 ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
3264 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
3265 ast_set_flag(&conf_flags,
3266 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
3267 ast_answer(trunk_ref->chan);
3268 conf = build_conf(conf_name, "", "", 0, 0, 1);
3269 if (conf) {
3270 conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
3271 dispose_conf(conf);
3272 conf = NULL;
3274 trunk_ref->chan = NULL;
3275 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
3276 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
3277 strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
3278 admin_exec(NULL, conf_name);
3279 trunk_ref->trunk->hold_stations = 0;
3280 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
3283 ast_dial_join(station->dial);
3284 ast_dial_destroy(station->dial);
3285 station->dial = NULL;
3287 return NULL;
3290 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
3292 char buf[80];
3293 struct sla_station_ref *station_ref;
3295 snprintf(buf, sizeof(buf), "SLA_%s|K", ringing_trunk->trunk->name);
3296 admin_exec(NULL, buf);
3297 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
3299 while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
3300 free(station_ref);
3302 free(ringing_trunk);
3305 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
3306 enum sla_station_hangup hangup)
3308 struct sla_ringing_trunk *ringing_trunk;
3309 struct sla_trunk_ref *trunk_ref;
3310 struct sla_station_ref *station_ref;
3312 ast_dial_join(ringing_station->station->dial);
3313 ast_dial_destroy(ringing_station->station->dial);
3314 ringing_station->station->dial = NULL;
3316 if (hangup == SLA_STATION_HANGUP_NORMAL)
3317 goto done;
3319 /* If the station is being hung up because of a timeout, then add it to the
3320 * list of timed out stations on each of the ringing trunks. This is so
3321 * that when doing further processing to figure out which stations should be
3322 * ringing, which trunk to answer, determining timeouts, etc., we know which
3323 * ringing trunks we should ignore. */
3324 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3325 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3326 if (ringing_trunk->trunk == trunk_ref->trunk)
3327 break;
3329 if (!trunk_ref)
3330 continue;
3331 if (!(station_ref = sla_create_station_ref(ringing_station->station)))
3332 continue;
3333 AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
3336 done:
3337 free(ringing_station);
3340 static void sla_dial_state_callback(struct ast_dial *dial)
3342 sla_queue_event(SLA_EVENT_DIAL_STATE);
3345 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
3346 * \note Assumes sla.lock is locked
3348 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
3349 const struct sla_station *station)
3351 struct sla_station_ref *timed_out_station;
3353 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
3354 if (station == timed_out_station->station)
3355 return 1;
3358 return 0;
3361 /*! \brief Choose the highest priority ringing trunk for a station
3362 * \param station the station
3363 * \param remove remove the ringing trunk once selected
3364 * \param trunk_ref a place to store the pointer to this stations reference to
3365 * the selected trunk
3366 * \return a pointer to the selected ringing trunk, or NULL if none found
3367 * \note Assumes that sla.lock is locked
3369 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
3370 struct sla_trunk_ref **trunk_ref, int remove)
3372 struct sla_trunk_ref *s_trunk_ref;
3373 struct sla_ringing_trunk *ringing_trunk = NULL;
3375 AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
3376 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
3377 /* Make sure this is the trunk we're looking for */
3378 if (s_trunk_ref->trunk != ringing_trunk->trunk)
3379 continue;
3381 /* This trunk on the station is ringing. But, make sure this station
3382 * didn't already time out while this trunk was ringing. */
3383 if (sla_check_timed_out_station(ringing_trunk, station))
3384 continue;
3386 if (remove)
3387 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
3389 if (trunk_ref)
3390 *trunk_ref = s_trunk_ref;
3392 break;
3394 AST_LIST_TRAVERSE_SAFE_END
3396 if (ringing_trunk)
3397 break;
3400 return ringing_trunk;
3403 static void sla_handle_dial_state_event(void)
3405 struct sla_ringing_station *ringing_station;
3407 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3408 struct sla_trunk_ref *s_trunk_ref = NULL;
3409 struct sla_ringing_trunk *ringing_trunk = NULL;
3410 struct run_station_args args;
3411 enum ast_dial_result dial_res;
3412 pthread_attr_t attr;
3413 pthread_t dont_care;
3414 ast_mutex_t cond_lock;
3415 ast_cond_t cond;
3417 switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
3418 case AST_DIAL_RESULT_HANGUP:
3419 case AST_DIAL_RESULT_INVALID:
3420 case AST_DIAL_RESULT_FAILED:
3421 case AST_DIAL_RESULT_TIMEOUT:
3422 case AST_DIAL_RESULT_UNANSWERED:
3423 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3424 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
3425 break;
3426 case AST_DIAL_RESULT_ANSWERED:
3427 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3428 /* Find the appropriate trunk to answer. */
3429 ast_mutex_lock(&sla.lock);
3430 ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
3431 ast_mutex_unlock(&sla.lock);
3432 if (!ringing_trunk) {
3433 ast_log(LOG_DEBUG, "Found no ringing trunk for station '%s' to answer!\n",
3434 ringing_station->station->name);
3435 break;
3437 /* Track the channel that answered this trunk */
3438 s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
3439 /* Actually answer the trunk */
3440 ast_answer(ringing_trunk->trunk->chan);
3441 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
3442 /* Now, start a thread that will connect this station to the trunk. The rest of
3443 * the code here sets up the thread and ensures that it is able to save the arguments
3444 * before they are no longer valid since they are allocated on the stack. */
3445 args.trunk_ref = s_trunk_ref;
3446 args.station = ringing_station->station;
3447 args.cond = &cond;
3448 args.cond_lock = &cond_lock;
3449 free(ringing_trunk);
3450 free(ringing_station);
3451 ast_mutex_init(&cond_lock);
3452 ast_cond_init(&cond, NULL);
3453 pthread_attr_init(&attr);
3454 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
3455 ast_mutex_lock(&cond_lock);
3456 ast_pthread_create_background(&dont_care, &attr, run_station, &args);
3457 ast_cond_wait(&cond, &cond_lock);
3458 ast_mutex_unlock(&cond_lock);
3459 ast_mutex_destroy(&cond_lock);
3460 ast_cond_destroy(&cond);
3461 pthread_attr_destroy(&attr);
3462 break;
3463 case AST_DIAL_RESULT_TRYING:
3464 case AST_DIAL_RESULT_RINGING:
3465 case AST_DIAL_RESULT_PROGRESS:
3466 case AST_DIAL_RESULT_PROCEEDING:
3467 break;
3469 if (dial_res == AST_DIAL_RESULT_ANSWERED) {
3470 /* Queue up reprocessing ringing trunks, and then ringing stations again */
3471 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
3472 sla_queue_event(SLA_EVENT_DIAL_STATE);
3473 break;
3476 AST_LIST_TRAVERSE_SAFE_END
3479 /*! \brief Check to see if this station is already ringing
3480 * \note Assumes sla.lock is locked
3482 static int sla_check_ringing_station(const struct sla_station *station)
3484 struct sla_ringing_station *ringing_station;
3486 AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
3487 if (station == ringing_station->station)
3488 return 1;
3491 return 0;
3494 /*! \brief Check to see if this station has failed to be dialed in the past minute
3495 * \note assumes sla.lock is locked
3497 static int sla_check_failed_station(const struct sla_station *station)
3499 struct sla_failed_station *failed_station;
3500 int res = 0;
3502 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
3503 if (station != failed_station->station)
3504 continue;
3505 if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
3506 AST_LIST_REMOVE_CURRENT(&sla.failed_stations, entry);
3507 free(failed_station);
3508 break;
3510 res = 1;
3512 AST_LIST_TRAVERSE_SAFE_END
3514 return res;
3517 /*! \brief Ring a station
3518 * \note Assumes sla.lock is locked
3520 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
3522 char *tech, *tech_data;
3523 struct ast_dial *dial;
3524 struct sla_ringing_station *ringing_station;
3525 const char *cid_name = NULL, *cid_num = NULL;
3526 enum ast_dial_result res;
3528 if (!(dial = ast_dial_create()))
3529 return -1;
3531 ast_dial_set_state_callback(dial, sla_dial_state_callback);
3532 tech_data = ast_strdupa(station->device);
3533 tech = strsep(&tech_data, "/");
3535 if (ast_dial_append(dial, tech, tech_data) == -1) {
3536 ast_dial_destroy(dial);
3537 return -1;
3540 if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_name)) {
3541 cid_name = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_name);
3542 free(ringing_trunk->trunk->chan->cid.cid_name);
3543 ringing_trunk->trunk->chan->cid.cid_name = NULL;
3545 if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_num)) {
3546 cid_num = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_num);
3547 free(ringing_trunk->trunk->chan->cid.cid_num);
3548 ringing_trunk->trunk->chan->cid.cid_num = NULL;
3551 res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
3553 if (cid_name)
3554 ringing_trunk->trunk->chan->cid.cid_name = ast_strdup(cid_name);
3555 if (cid_num)
3556 ringing_trunk->trunk->chan->cid.cid_num = ast_strdup(cid_num);
3558 if (res != AST_DIAL_RESULT_TRYING) {
3559 struct sla_failed_station *failed_station;
3560 ast_dial_destroy(dial);
3561 if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
3562 return -1;
3563 failed_station->station = station;
3564 failed_station->last_try = ast_tvnow();
3565 AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
3566 return -1;
3568 if (!(ringing_station = sla_create_ringing_station(station))) {
3569 ast_dial_join(dial);
3570 ast_dial_destroy(dial);
3571 return -1;
3574 station->dial = dial;
3576 AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
3578 return 0;
3581 /*! \brief Check to see if a station is in use
3583 static int sla_check_inuse_station(const struct sla_station *station)
3585 struct sla_trunk_ref *trunk_ref;
3587 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3588 if (trunk_ref->chan)
3589 return 1;
3592 return 0;
3595 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
3596 const struct sla_trunk *trunk)
3598 struct sla_trunk_ref *trunk_ref = NULL;
3600 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3601 if (trunk_ref->trunk == trunk)
3602 break;
3605 return trunk_ref;
3608 /*! \brief Calculate the ring delay for a given ringing trunk on a station
3609 * \param station the station
3610 * \param trunk the trunk. If NULL, the highest priority ringing trunk will be used
3611 * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
3613 static int sla_check_station_delay(struct sla_station *station,
3614 struct sla_ringing_trunk *ringing_trunk)
3616 struct sla_trunk_ref *trunk_ref;
3617 unsigned int delay = UINT_MAX;
3618 int time_left, time_elapsed;
3620 if (!ringing_trunk)
3621 ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
3622 else
3623 trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
3625 if (!ringing_trunk || !trunk_ref)
3626 return delay;
3628 /* If this station has a ring delay specific to the highest priority
3629 * ringing trunk, use that. Otherwise, use the ring delay specified
3630 * globally for the station. */
3631 delay = trunk_ref->ring_delay;
3632 if (!delay)
3633 delay = station->ring_delay;
3634 if (!delay)
3635 return INT_MAX;
3637 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3638 time_left = (delay * 1000) - time_elapsed;
3640 return time_left;
3643 /*! \brief Ring stations based on current set of ringing trunks
3644 * \note Assumes that sla.lock is locked
3646 static void sla_ring_stations(void)
3648 struct sla_station_ref *station_ref;
3649 struct sla_ringing_trunk *ringing_trunk;
3651 /* Make sure that every station that uses at least one of the ringing
3652 * trunks, is ringing. */
3653 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3654 AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
3655 int time_left;
3657 /* Is this station already ringing? */
3658 if (sla_check_ringing_station(station_ref->station))
3659 continue;
3661 /* Is this station already in a call? */
3662 if (sla_check_inuse_station(station_ref->station))
3663 continue;
3665 /* Did we fail to dial this station earlier? If so, has it been
3666 * a minute since we tried? */
3667 if (sla_check_failed_station(station_ref->station))
3668 continue;
3670 /* If this station already timed out while this trunk was ringing,
3671 * do not dial it again for this ringing trunk. */
3672 if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
3673 continue;
3675 /* Check for a ring delay in progress */
3676 time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
3677 if (time_left != INT_MAX && time_left > 0)
3678 continue;
3680 /* It is time to make this station begin to ring. Do it! */
3681 sla_ring_station(ringing_trunk, station_ref->station);
3684 /* Now, all of the stations that should be ringing, are ringing. */
3687 static void sla_hangup_stations(void)
3689 struct sla_trunk_ref *trunk_ref;
3690 struct sla_ringing_station *ringing_station;
3692 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3693 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3694 struct sla_ringing_trunk *ringing_trunk;
3695 ast_mutex_lock(&sla.lock);
3696 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3697 if (trunk_ref->trunk == ringing_trunk->trunk)
3698 break;
3700 ast_mutex_unlock(&sla.lock);
3701 if (ringing_trunk)
3702 break;
3704 if (!trunk_ref) {
3705 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3706 ast_dial_join(ringing_station->station->dial);
3707 ast_dial_destroy(ringing_station->station->dial);
3708 ringing_station->station->dial = NULL;
3709 free(ringing_station);
3712 AST_LIST_TRAVERSE_SAFE_END
3715 static void sla_handle_ringing_trunk_event(void)
3717 ast_mutex_lock(&sla.lock);
3718 sla_ring_stations();
3719 ast_mutex_unlock(&sla.lock);
3721 /* Find stations that shouldn't be ringing anymore. */
3722 sla_hangup_stations();
3725 static void sla_handle_hold_event(struct sla_event *event)
3727 ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
3728 event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
3729 ast_device_state_changed("SLA:%s_%s",
3730 event->station->name, event->trunk_ref->trunk->name);
3731 sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
3732 INACTIVE_TRUNK_REFS, event->trunk_ref);
3734 if (event->trunk_ref->trunk->active_stations == 1) {
3735 /* The station putting it on hold is the only one on the call, so start
3736 * Music on hold to the trunk. */
3737 event->trunk_ref->trunk->on_hold = 1;
3738 ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
3741 ast_softhangup(event->trunk_ref->chan, AST_CAUSE_NORMAL);
3742 event->trunk_ref->chan = NULL;
3745 /*! \brief Process trunk ring timeouts
3746 * \note Called with sla.lock locked
3747 * \return non-zero if a change to the ringing trunks was made
3749 static int sla_calc_trunk_timeouts(unsigned int *timeout)
3751 struct sla_ringing_trunk *ringing_trunk;
3752 int res = 0;
3754 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
3755 int time_left, time_elapsed;
3756 if (!ringing_trunk->trunk->ring_timeout)
3757 continue;
3758 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3759 time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
3760 if (time_left <= 0) {
3761 pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
3762 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
3763 sla_stop_ringing_trunk(ringing_trunk);
3764 res = 1;
3765 continue;
3767 if (time_left < *timeout)
3768 *timeout = time_left;
3770 AST_LIST_TRAVERSE_SAFE_END
3772 return res;
3775 /*! \brief Process station ring timeouts
3776 * \note Called with sla.lock locked
3777 * \return non-zero if a change to the ringing stations was made
3779 static int sla_calc_station_timeouts(unsigned int *timeout)
3781 struct sla_ringing_trunk *ringing_trunk;
3782 struct sla_ringing_station *ringing_station;
3783 int res = 0;
3785 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3786 unsigned int ring_timeout = 0;
3787 int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
3788 struct sla_trunk_ref *trunk_ref;
3790 /* If there are any ring timeouts specified for a specific trunk
3791 * on the station, then use the highest per-trunk ring timeout.
3792 * Otherwise, use the ring timeout set for the entire station. */
3793 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3794 struct sla_station_ref *station_ref;
3795 int trunk_time_elapsed, trunk_time_left;
3797 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3798 if (ringing_trunk->trunk == trunk_ref->trunk)
3799 break;
3801 if (!ringing_trunk)
3802 continue;
3804 /* If there is a trunk that is ringing without a timeout, then the
3805 * only timeout that could matter is a global station ring timeout. */
3806 if (!trunk_ref->ring_timeout)
3807 break;
3809 /* This trunk on this station is ringing and has a timeout.
3810 * However, make sure this trunk isn't still ringing from a
3811 * previous timeout. If so, don't consider it. */
3812 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
3813 if (station_ref->station == ringing_station->station)
3814 break;
3816 if (station_ref)
3817 continue;
3819 trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3820 trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
3821 if (trunk_time_left > final_trunk_time_left)
3822 final_trunk_time_left = trunk_time_left;
3825 /* No timeout was found for ringing trunks, and no timeout for the entire station */
3826 if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
3827 continue;
3829 /* Compute how much time is left for a global station timeout */
3830 if (ringing_station->station->ring_timeout) {
3831 ring_timeout = ringing_station->station->ring_timeout;
3832 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
3833 time_left = (ring_timeout * 1000) - time_elapsed;
3836 /* If the time left based on the per-trunk timeouts is smaller than the
3837 * global station ring timeout, use that. */
3838 if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
3839 time_left = final_trunk_time_left;
3841 /* If there is no time left, the station needs to stop ringing */
3842 if (time_left <= 0) {
3843 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3844 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
3845 res = 1;
3846 continue;
3849 /* There is still some time left for this station to ring, so save that
3850 * timeout if it is the first event scheduled to occur */
3851 if (time_left < *timeout)
3852 *timeout = time_left;
3854 AST_LIST_TRAVERSE_SAFE_END
3856 return res;
3859 /*! \brief Calculate the ring delay for a station
3860 * \note Assumes sla.lock is locked
3862 static int sla_calc_station_delays(unsigned int *timeout)
3864 struct sla_station *station;
3865 int res = 0;
3867 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
3868 struct sla_ringing_trunk *ringing_trunk;
3869 int time_left;
3871 /* Ignore stations already ringing */
3872 if (sla_check_ringing_station(station))
3873 continue;
3875 /* Ignore stations already on a call */
3876 if (sla_check_inuse_station(station))
3877 continue;
3879 /* Ignore stations that don't have one of their trunks ringing */
3880 if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
3881 continue;
3883 if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
3884 continue;
3886 /* If there is no time left, then the station needs to start ringing.
3887 * Return non-zero so that an event will be queued up an event to
3888 * make that happen. */
3889 if (time_left <= 0) {
3890 res = 1;
3891 continue;
3894 if (time_left < *timeout)
3895 *timeout = time_left;
3898 return res;
3901 /*! \brief Calculate the time until the next known event
3902 * \note Called with sla.lock locked */
3903 static int sla_process_timers(struct timespec *ts)
3905 unsigned int timeout = UINT_MAX;
3906 struct timeval tv;
3907 unsigned int change_made = 0;
3909 /* Check for ring timeouts on ringing trunks */
3910 if (sla_calc_trunk_timeouts(&timeout))
3911 change_made = 1;
3913 /* Check for ring timeouts on ringing stations */
3914 if (sla_calc_station_timeouts(&timeout))
3915 change_made = 1;
3917 /* Check for station ring delays */
3918 if (sla_calc_station_delays(&timeout))
3919 change_made = 1;
3921 /* queue reprocessing of ringing trunks */
3922 if (change_made)
3923 sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
3925 /* No timeout */
3926 if (timeout == UINT_MAX)
3927 return 0;
3929 if (ts) {
3930 tv = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
3931 ts->tv_sec = tv.tv_sec;
3932 ts->tv_nsec = tv.tv_usec * 1000;
3935 return 1;
3938 static void *sla_thread(void *data)
3940 struct sla_failed_station *failed_station;
3941 struct sla_ringing_station *ringing_station;
3943 ast_mutex_lock(&sla.lock);
3945 while (!sla.stop) {
3946 struct sla_event *event;
3947 struct timespec ts = { 0, };
3948 unsigned int have_timeout = 0;
3950 if (AST_LIST_EMPTY(&sla.event_q)) {
3951 if ((have_timeout = sla_process_timers(&ts)))
3952 ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
3953 else
3954 ast_cond_wait(&sla.cond, &sla.lock);
3955 if (sla.stop)
3956 break;
3959 if (have_timeout)
3960 sla_process_timers(NULL);
3962 while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
3963 ast_mutex_unlock(&sla.lock);
3964 switch (event->type) {
3965 case SLA_EVENT_HOLD:
3966 sla_handle_hold_event(event);
3967 break;
3968 case SLA_EVENT_DIAL_STATE:
3969 sla_handle_dial_state_event();
3970 break;
3971 case SLA_EVENT_RINGING_TRUNK:
3972 sla_handle_ringing_trunk_event();
3973 break;
3975 free(event);
3976 ast_mutex_lock(&sla.lock);
3980 ast_mutex_unlock(&sla.lock);
3982 while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
3983 free(ringing_station);
3985 while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
3986 free(failed_station);
3988 return NULL;
3991 struct dial_trunk_args {
3992 struct sla_trunk_ref *trunk_ref;
3993 struct sla_station *station;
3994 ast_mutex_t *cond_lock;
3995 ast_cond_t *cond;
3998 static void *dial_trunk(void *data)
4000 struct dial_trunk_args *args = data;
4001 struct ast_dial *dial;
4002 char *tech, *tech_data;
4003 enum ast_dial_result dial_res;
4004 char conf_name[MAX_CONFNUM];
4005 struct ast_conference *conf;
4006 struct ast_flags conf_flags = { 0 };
4007 struct sla_trunk_ref *trunk_ref = args->trunk_ref;
4008 const char *cid_name = NULL, *cid_num = NULL;
4010 if (!(dial = ast_dial_create())) {
4011 ast_mutex_lock(args->cond_lock);
4012 ast_cond_signal(args->cond);
4013 ast_mutex_unlock(args->cond_lock);
4014 return NULL;
4017 tech_data = ast_strdupa(trunk_ref->trunk->device);
4018 tech = strsep(&tech_data, "/");
4019 if (ast_dial_append(dial, tech, tech_data) == -1) {
4020 ast_mutex_lock(args->cond_lock);
4021 ast_cond_signal(args->cond);
4022 ast_mutex_unlock(args->cond_lock);
4023 ast_dial_destroy(dial);
4024 return NULL;
4027 if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_name)) {
4028 cid_name = ast_strdupa(trunk_ref->chan->cid.cid_name);
4029 free(trunk_ref->chan->cid.cid_name);
4030 trunk_ref->chan->cid.cid_name = NULL;
4032 if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_num)) {
4033 cid_num = ast_strdupa(trunk_ref->chan->cid.cid_num);
4034 free(trunk_ref->chan->cid.cid_num);
4035 trunk_ref->chan->cid.cid_num = NULL;
4038 dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
4040 if (cid_name)
4041 trunk_ref->chan->cid.cid_name = ast_strdup(cid_name);
4042 if (cid_num)
4043 trunk_ref->chan->cid.cid_num = ast_strdup(cid_num);
4045 if (dial_res != AST_DIAL_RESULT_TRYING) {
4046 ast_mutex_lock(args->cond_lock);
4047 ast_cond_signal(args->cond);
4048 ast_mutex_unlock(args->cond_lock);
4049 ast_dial_destroy(dial);
4050 return NULL;
4053 for (;;) {
4054 unsigned int done = 0;
4055 switch ((dial_res = ast_dial_state(dial))) {
4056 case AST_DIAL_RESULT_ANSWERED:
4057 trunk_ref->trunk->chan = ast_dial_answered(dial);
4058 case AST_DIAL_RESULT_HANGUP:
4059 case AST_DIAL_RESULT_INVALID:
4060 case AST_DIAL_RESULT_FAILED:
4061 case AST_DIAL_RESULT_TIMEOUT:
4062 case AST_DIAL_RESULT_UNANSWERED:
4063 done = 1;
4064 case AST_DIAL_RESULT_TRYING:
4065 case AST_DIAL_RESULT_RINGING:
4066 case AST_DIAL_RESULT_PROGRESS:
4067 case AST_DIAL_RESULT_PROCEEDING:
4068 break;
4070 if (done)
4071 break;
4074 if (!trunk_ref->trunk->chan) {
4075 ast_mutex_lock(args->cond_lock);
4076 ast_cond_signal(args->cond);
4077 ast_mutex_unlock(args->cond_lock);
4078 ast_dial_join(dial);
4079 ast_dial_destroy(dial);
4080 return NULL;
4083 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
4084 ast_set_flag(&conf_flags,
4085 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
4086 CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
4087 conf = build_conf(conf_name, "", "", 1, 1, 1);
4089 ast_mutex_lock(args->cond_lock);
4090 ast_cond_signal(args->cond);
4091 ast_mutex_unlock(args->cond_lock);
4093 if (conf) {
4094 conf_run(trunk_ref->trunk->chan, conf, conf_flags.flags, NULL);
4095 dispose_conf(conf);
4096 conf = NULL;
4099 /* If the trunk is going away, it is definitely now IDLE. */
4100 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4102 trunk_ref->trunk->chan = NULL;
4103 trunk_ref->trunk->on_hold = 0;
4105 ast_dial_join(dial);
4106 ast_dial_destroy(dial);
4108 return NULL;
4111 /*! \brief For a given station, choose the highest priority idle trunk
4113 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
4115 struct sla_trunk_ref *trunk_ref = NULL;
4117 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4118 if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
4119 break;
4122 return trunk_ref;
4125 static int sla_station_exec(struct ast_channel *chan, void *data)
4127 char *station_name, *trunk_name;
4128 struct sla_station *station;
4129 struct sla_trunk_ref *trunk_ref = NULL;
4130 char conf_name[MAX_CONFNUM];
4131 struct ast_flags conf_flags = { 0 };
4132 struct ast_conference *conf;
4134 if (ast_strlen_zero(data)) {
4135 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
4136 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4137 return 0;
4140 trunk_name = ast_strdupa(data);
4141 station_name = strsep(&trunk_name, "_");
4143 if (ast_strlen_zero(station_name)) {
4144 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
4145 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4146 return 0;
4149 AST_RWLIST_RDLOCK(&sla_stations);
4150 station = sla_find_station(station_name);
4151 AST_RWLIST_UNLOCK(&sla_stations);
4153 if (!station) {
4154 ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
4155 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4156 return 0;
4159 AST_RWLIST_RDLOCK(&sla_trunks);
4160 if (!ast_strlen_zero(trunk_name)) {
4161 trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
4162 } else
4163 trunk_ref = sla_choose_idle_trunk(station);
4164 AST_RWLIST_UNLOCK(&sla_trunks);
4166 if (!trunk_ref) {
4167 if (ast_strlen_zero(trunk_name))
4168 ast_log(LOG_NOTICE, "No trunks available for call.\n");
4169 else {
4170 ast_log(LOG_NOTICE, "Can't join existing call on trunk "
4171 "'%s' due to access controls.\n", trunk_name);
4173 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
4174 return 0;
4177 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
4178 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
4179 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4180 else {
4181 trunk_ref->state = SLA_TRUNK_STATE_UP;
4182 ast_device_state_changed("SLA:%s_%s", station->name, trunk_ref->trunk->name);
4184 } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
4185 struct sla_ringing_trunk *ringing_trunk;
4187 ast_mutex_lock(&sla.lock);
4188 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
4189 if (ringing_trunk->trunk == trunk_ref->trunk) {
4190 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
4191 break;
4194 AST_LIST_TRAVERSE_SAFE_END
4195 ast_mutex_unlock(&sla.lock);
4197 if (ringing_trunk) {
4198 ast_answer(ringing_trunk->trunk->chan);
4199 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4201 free(ringing_trunk);
4203 /* Queue up reprocessing ringing trunks, and then ringing stations again */
4204 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4205 sla_queue_event(SLA_EVENT_DIAL_STATE);
4209 trunk_ref->chan = chan;
4211 if (!trunk_ref->trunk->chan) {
4212 ast_mutex_t cond_lock;
4213 ast_cond_t cond;
4214 pthread_t dont_care;
4215 pthread_attr_t attr;
4216 struct dial_trunk_args args = {
4217 .trunk_ref = trunk_ref,
4218 .station = station,
4219 .cond_lock = &cond_lock,
4220 .cond = &cond,
4222 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4223 /* Create a thread to dial the trunk and dump it into the conference.
4224 * However, we want to wait until the trunk has been dialed and the
4225 * conference is created before continuing on here. */
4226 ast_autoservice_start(chan);
4227 ast_mutex_init(&cond_lock);
4228 ast_cond_init(&cond, NULL);
4229 pthread_attr_init(&attr);
4230 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
4231 ast_mutex_lock(&cond_lock);
4232 ast_pthread_create_background(&dont_care, &attr, dial_trunk, &args);
4233 ast_cond_wait(&cond, &cond_lock);
4234 ast_mutex_unlock(&cond_lock);
4235 ast_mutex_destroy(&cond_lock);
4236 ast_cond_destroy(&cond);
4237 pthread_attr_destroy(&attr);
4238 ast_autoservice_stop(chan);
4239 if (!trunk_ref->trunk->chan) {
4240 ast_log(LOG_DEBUG, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
4241 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
4242 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4243 trunk_ref->chan = NULL;
4244 return 0;
4248 if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
4249 trunk_ref->trunk->on_hold) {
4250 trunk_ref->trunk->on_hold = 0;
4251 ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
4252 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4255 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
4256 ast_set_flag(&conf_flags,
4257 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
4258 ast_answer(chan);
4259 conf = build_conf(conf_name, "", "", 0, 0, 1);
4260 if (conf) {
4261 conf_run(chan, conf, conf_flags.flags, NULL);
4262 dispose_conf(conf);
4263 conf = NULL;
4265 trunk_ref->chan = NULL;
4266 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
4267 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
4268 strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
4269 admin_exec(NULL, conf_name);
4270 trunk_ref->trunk->hold_stations = 0;
4271 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4274 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
4276 return 0;
4279 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
4281 struct sla_trunk_ref *trunk_ref;
4283 if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
4284 return NULL;
4286 trunk_ref->trunk = trunk;
4288 return trunk_ref;
4291 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
4293 struct sla_ringing_trunk *ringing_trunk;
4295 if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
4296 return NULL;
4298 ringing_trunk->trunk = trunk;
4299 ringing_trunk->ring_begin = ast_tvnow();
4301 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
4303 ast_mutex_lock(&sla.lock);
4304 AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
4305 ast_mutex_unlock(&sla.lock);
4307 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4309 return ringing_trunk;
4312 static int sla_trunk_exec(struct ast_channel *chan, void *data)
4314 const char *trunk_name = data;
4315 char conf_name[MAX_CONFNUM];
4316 struct ast_conference *conf;
4317 struct ast_flags conf_flags = { 0 };
4318 struct sla_trunk *trunk;
4319 struct sla_ringing_trunk *ringing_trunk;
4321 AST_RWLIST_RDLOCK(&sla_trunks);
4322 trunk = sla_find_trunk(trunk_name);
4323 AST_RWLIST_UNLOCK(&sla_trunks);
4324 if (!trunk) {
4325 ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", trunk_name);
4326 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4327 return 0;
4329 if (trunk->chan) {
4330 ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
4331 trunk_name);
4332 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4333 return 0;
4335 trunk->chan = chan;
4337 if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
4338 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4339 return 0;
4342 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_name);
4343 conf = build_conf(conf_name, "", "", 1, 1, 1);
4344 if (!conf) {
4345 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4346 return 0;
4348 ast_set_flag(&conf_flags,
4349 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF);
4350 ast_indicate(chan, AST_CONTROL_RINGING);
4351 conf_run(chan, conf, conf_flags.flags, NULL);
4352 dispose_conf(conf);
4353 conf = NULL;
4354 trunk->chan = NULL;
4355 trunk->on_hold = 0;
4357 if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
4358 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
4360 /* Remove the entry from the list of ringing trunks if it is still there. */
4361 ast_mutex_lock(&sla.lock);
4362 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
4363 if (ringing_trunk->trunk == trunk) {
4364 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
4365 break;
4368 AST_LIST_TRAVERSE_SAFE_END
4369 ast_mutex_unlock(&sla.lock);
4370 if (ringing_trunk) {
4371 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4372 free(ringing_trunk);
4373 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
4374 /* Queue reprocessing of ringing trunks to make stations stop ringing
4375 * that shouldn't be ringing after this trunk stopped. */
4376 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4379 return 0;
4382 static int sla_state(const char *data)
4384 char *buf, *station_name, *trunk_name;
4385 struct sla_station *station;
4386 struct sla_trunk_ref *trunk_ref;
4387 int res = AST_DEVICE_INVALID;
4389 trunk_name = buf = ast_strdupa(data);
4390 station_name = strsep(&trunk_name, "_");
4392 AST_RWLIST_RDLOCK(&sla_stations);
4393 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
4394 if (strcasecmp(station_name, station->name))
4395 continue;
4396 AST_RWLIST_RDLOCK(&sla_trunks);
4397 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4398 if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
4399 break;
4401 if (!trunk_ref) {
4402 AST_RWLIST_UNLOCK(&sla_trunks);
4403 break;
4405 switch (trunk_ref->state) {
4406 case SLA_TRUNK_STATE_IDLE:
4407 res = AST_DEVICE_NOT_INUSE;
4408 break;
4409 case SLA_TRUNK_STATE_RINGING:
4410 res = AST_DEVICE_RINGING;
4411 break;
4412 case SLA_TRUNK_STATE_UP:
4413 res = AST_DEVICE_INUSE;
4414 break;
4415 case SLA_TRUNK_STATE_ONHOLD:
4416 case SLA_TRUNK_STATE_ONHOLD_BYME:
4417 res = AST_DEVICE_ONHOLD;
4418 break;
4420 AST_RWLIST_UNLOCK(&sla_trunks);
4422 AST_RWLIST_UNLOCK(&sla_stations);
4424 if (res == AST_DEVICE_INVALID) {
4425 ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
4426 trunk_name, station_name);
4429 return res;
4432 static void destroy_trunk(struct sla_trunk *trunk)
4434 struct sla_station_ref *station_ref;
4436 if (!ast_strlen_zero(trunk->autocontext))
4437 ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
4439 while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
4440 free(station_ref);
4442 ast_string_field_free_memory(trunk);
4443 free(trunk);
4446 static void destroy_station(struct sla_station *station)
4448 struct sla_trunk_ref *trunk_ref;
4450 if (!ast_strlen_zero(station->autocontext)) {
4451 AST_RWLIST_RDLOCK(&sla_trunks);
4452 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4453 char exten[AST_MAX_EXTENSION];
4454 char hint[AST_MAX_APP];
4455 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
4456 snprintf(hint, sizeof(hint), "SLA:%s", exten);
4457 ast_context_remove_extension(station->autocontext, exten,
4458 1, sla_registrar);
4459 ast_context_remove_extension(station->autocontext, hint,
4460 PRIORITY_HINT, sla_registrar);
4462 AST_RWLIST_UNLOCK(&sla_trunks);
4465 while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
4466 free(trunk_ref);
4468 ast_string_field_free_memory(station);
4469 free(station);
4472 static void sla_destroy(void)
4474 struct sla_trunk *trunk;
4475 struct sla_station *station;
4477 AST_RWLIST_WRLOCK(&sla_trunks);
4478 while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
4479 destroy_trunk(trunk);
4480 AST_RWLIST_UNLOCK(&sla_trunks);
4482 AST_RWLIST_WRLOCK(&sla_stations);
4483 while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
4484 destroy_station(station);
4485 AST_RWLIST_UNLOCK(&sla_stations);
4487 if (sla.thread != AST_PTHREADT_NULL) {
4488 ast_mutex_lock(&sla.lock);
4489 sla.stop = 1;
4490 ast_cond_signal(&sla.cond);
4491 ast_mutex_unlock(&sla.lock);
4492 pthread_join(sla.thread, NULL);
4495 /* Drop any created contexts from the dialplan */
4496 ast_context_destroy(NULL, sla_registrar);
4498 ast_mutex_destroy(&sla.lock);
4499 ast_cond_destroy(&sla.cond);
4502 static int sla_check_device(const char *device)
4504 char *tech, *tech_data;
4506 tech_data = ast_strdupa(device);
4507 tech = strsep(&tech_data, "/");
4509 if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
4510 return -1;
4512 return 0;
4515 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
4517 struct sla_trunk *trunk;
4518 struct ast_variable *var;
4519 const char *dev;
4521 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
4522 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
4523 return -1;
4526 if (sla_check_device(dev)) {
4527 ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
4528 cat, dev);
4529 return -1;
4532 if (!(trunk = ast_calloc(1, sizeof(*trunk))))
4533 return -1;
4534 if (ast_string_field_init(trunk, 32)) {
4535 free(trunk);
4536 return -1;
4539 ast_string_field_set(trunk, name, cat);
4540 ast_string_field_set(trunk, device, dev);
4542 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
4543 if (!strcasecmp(var->name, "autocontext"))
4544 ast_string_field_set(trunk, autocontext, var->value);
4545 else if (!strcasecmp(var->name, "ringtimeout")) {
4546 if (sscanf(var->value, "%u", &trunk->ring_timeout) != 1) {
4547 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
4548 var->value, trunk->name);
4549 trunk->ring_timeout = 0;
4551 } else if (!strcasecmp(var->name, "barge"))
4552 trunk->barge_disabled = ast_false(var->value);
4553 else if (!strcasecmp(var->name, "hold")) {
4554 if (!strcasecmp(var->value, "private"))
4555 trunk->hold_access = SLA_HOLD_PRIVATE;
4556 else if (!strcasecmp(var->value, "open"))
4557 trunk->hold_access = SLA_HOLD_OPEN;
4558 else {
4559 ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
4560 var->value, trunk->name);
4562 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
4563 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
4564 var->name, var->lineno, SLA_CONFIG_FILE);
4568 if (!ast_strlen_zero(trunk->autocontext)) {
4569 struct ast_context *context;
4570 context = ast_context_find_or_create(NULL, trunk->autocontext, sla_registrar);
4571 if (!context) {
4572 ast_log(LOG_ERROR, "Failed to automatically find or create "
4573 "context '%s' for SLA!\n", trunk->autocontext);
4574 destroy_trunk(trunk);
4575 return -1;
4577 if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
4578 NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free, sla_registrar)) {
4579 ast_log(LOG_ERROR, "Failed to automatically create extension "
4580 "for trunk '%s'!\n", trunk->name);
4581 destroy_trunk(trunk);
4582 return -1;
4586 AST_RWLIST_WRLOCK(&sla_trunks);
4587 AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
4588 AST_RWLIST_UNLOCK(&sla_trunks);
4590 return 0;
4593 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
4595 struct sla_trunk *trunk;
4596 struct sla_trunk_ref *trunk_ref;
4597 struct sla_station_ref *station_ref;
4598 char *trunk_name, *options, *cur;
4600 options = ast_strdupa(var->value);
4601 trunk_name = strsep(&options, ",");
4603 AST_RWLIST_RDLOCK(&sla_trunks);
4604 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
4605 if (!strcasecmp(trunk->name, trunk_name))
4606 break;
4609 AST_RWLIST_UNLOCK(&sla_trunks);
4610 if (!trunk) {
4611 ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
4612 return;
4614 if (!(trunk_ref = create_trunk_ref(trunk)))
4615 return;
4616 trunk_ref->state = SLA_TRUNK_STATE_IDLE;
4618 while ((cur = strsep(&options, ","))) {
4619 char *name, *value = cur;
4620 name = strsep(&value, "=");
4621 if (!strcasecmp(name, "ringtimeout")) {
4622 if (sscanf(value, "%u", &trunk_ref->ring_timeout) != 1) {
4623 ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
4624 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
4625 trunk_ref->ring_timeout = 0;
4627 } else if (!strcasecmp(name, "ringdelay")) {
4628 if (sscanf(value, "%u", &trunk_ref->ring_delay) != 1) {
4629 ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
4630 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
4631 trunk_ref->ring_delay = 0;
4633 } else {
4634 ast_log(LOG_WARNING, "Invalid option '%s' for "
4635 "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
4639 if (!(station_ref = sla_create_station_ref(station))) {
4640 free(trunk_ref);
4641 return;
4643 ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
4644 AST_RWLIST_WRLOCK(&sla_trunks);
4645 AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
4646 AST_RWLIST_UNLOCK(&sla_trunks);
4647 AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
4650 static int sla_build_station(struct ast_config *cfg, const char *cat)
4652 struct sla_station *station;
4653 struct ast_variable *var;
4654 const char *dev;
4656 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
4657 ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
4658 return -1;
4661 if (!(station = ast_calloc(1, sizeof(*station))))
4662 return -1;
4663 if (ast_string_field_init(station, 32)) {
4664 free(station);
4665 return -1;
4668 ast_string_field_set(station, name, cat);
4669 ast_string_field_set(station, device, dev);
4671 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
4672 if (!strcasecmp(var->name, "trunk"))
4673 sla_add_trunk_to_station(station, var);
4674 else if (!strcasecmp(var->name, "autocontext"))
4675 ast_string_field_set(station, autocontext, var->value);
4676 else if (!strcasecmp(var->name, "ringtimeout")) {
4677 if (sscanf(var->value, "%u", &station->ring_timeout) != 1) {
4678 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
4679 var->value, station->name);
4680 station->ring_timeout = 0;
4682 } else if (!strcasecmp(var->name, "ringdelay")) {
4683 if (sscanf(var->value, "%u", &station->ring_delay) != 1) {
4684 ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
4685 var->value, station->name);
4686 station->ring_delay = 0;
4688 } else if (!strcasecmp(var->name, "hold")) {
4689 if (!strcasecmp(var->value, "private"))
4690 station->hold_access = SLA_HOLD_PRIVATE;
4691 else if (!strcasecmp(var->value, "open"))
4692 station->hold_access = SLA_HOLD_OPEN;
4693 else {
4694 ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
4695 var->value, station->name);
4698 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
4699 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
4700 var->name, var->lineno, SLA_CONFIG_FILE);
4704 if (!ast_strlen_zero(station->autocontext)) {
4705 struct ast_context *context;
4706 struct sla_trunk_ref *trunk_ref;
4707 context = ast_context_find_or_create(NULL, station->autocontext, sla_registrar);
4708 if (!context) {
4709 ast_log(LOG_ERROR, "Failed to automatically find or create "
4710 "context '%s' for SLA!\n", station->autocontext);
4711 destroy_station(station);
4712 return -1;
4714 /* The extension for when the handset goes off-hook.
4715 * exten => station1,1,SLAStation(station1) */
4716 if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
4717 NULL, NULL, slastation_app, ast_strdup(station->name), ast_free, sla_registrar)) {
4718 ast_log(LOG_ERROR, "Failed to automatically create extension "
4719 "for trunk '%s'!\n", station->name);
4720 destroy_station(station);
4721 return -1;
4723 AST_RWLIST_RDLOCK(&sla_trunks);
4724 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4725 char exten[AST_MAX_EXTENSION];
4726 char hint[AST_MAX_APP];
4727 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
4728 snprintf(hint, sizeof(hint), "SLA:%s", exten);
4729 /* Extension for this line button
4730 * exten => station1_line1,1,SLAStation(station1_line1) */
4731 if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
4732 NULL, NULL, slastation_app, ast_strdup(exten), ast_free, sla_registrar)) {
4733 ast_log(LOG_ERROR, "Failed to automatically create extension "
4734 "for trunk '%s'!\n", station->name);
4735 destroy_station(station);
4736 return -1;
4738 /* Hint for this line button
4739 * exten => station1_line1,hint,SLA:station1_line1 */
4740 if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
4741 NULL, NULL, hint, NULL, NULL, sla_registrar)) {
4742 ast_log(LOG_ERROR, "Failed to automatically create hint "
4743 "for trunk '%s'!\n", station->name);
4744 destroy_station(station);
4745 return -1;
4748 AST_RWLIST_UNLOCK(&sla_trunks);
4751 AST_RWLIST_WRLOCK(&sla_stations);
4752 AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
4753 AST_RWLIST_UNLOCK(&sla_stations);
4755 return 0;
4758 static int sla_load_config(void)
4760 struct ast_config *cfg;
4761 const char *cat = NULL;
4762 int res = 0;
4763 const char *val;
4765 ast_mutex_init(&sla.lock);
4766 ast_cond_init(&sla.cond, NULL);
4768 if (!(cfg = ast_config_load(SLA_CONFIG_FILE)))
4769 return 0; /* Treat no config as normal */
4771 if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
4772 sla.attempt_callerid = ast_true(val);
4774 while ((cat = ast_category_browse(cfg, cat)) && !res) {
4775 const char *type;
4776 if (!strcasecmp(cat, "general"))
4777 continue;
4778 if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
4779 ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
4780 SLA_CONFIG_FILE);
4781 continue;
4783 if (!strcasecmp(type, "trunk"))
4784 res = sla_build_trunk(cfg, cat);
4785 else if (!strcasecmp(type, "station"))
4786 res = sla_build_station(cfg, cat);
4787 else {
4788 ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
4789 SLA_CONFIG_FILE, type);
4793 ast_config_destroy(cfg);
4795 ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
4797 return res;
4800 static int load_config(int reload)
4802 int res = 0;
4804 load_config_meetme();
4805 if (!reload)
4806 res = sla_load_config();
4808 return res;
4811 static int unload_module(void)
4813 int res = 0;
4815 ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
4816 res = ast_manager_unregister("MeetmeMute");
4817 res |= ast_manager_unregister("MeetmeUnmute");
4818 res |= ast_unregister_application(app3);
4819 res |= ast_unregister_application(app2);
4820 res |= ast_unregister_application(app);
4821 res |= ast_unregister_application(slastation_app);
4822 res |= ast_unregister_application(slatrunk_app);
4824 ast_devstate_prov_del("Meetme");
4825 ast_devstate_prov_del("SLA");
4827 ast_module_user_hangup_all();
4829 sla_destroy();
4831 return res;
4834 static int load_module(void)
4836 int res = 0;
4838 res |= load_config(0);
4840 ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
4841 res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL,
4842 action_meetmemute, "Mute a Meetme user");
4843 res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL,
4844 action_meetmeunmute, "Unmute a Meetme user");
4845 res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
4846 res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
4847 res |= ast_register_application(app, conf_exec, synopsis, descrip);
4848 res |= ast_register_application(slastation_app, sla_station_exec,
4849 slastation_synopsis, slastation_desc);
4850 res |= ast_register_application(slatrunk_app, sla_trunk_exec,
4851 slatrunk_synopsis, slatrunk_desc);
4853 res |= ast_devstate_prov_add("Meetme", meetmestate);
4854 res |= ast_devstate_prov_add("SLA", sla_state);
4856 return res;
4859 static int reload(void)
4861 return load_config(1);
4864 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MeetMe conference bridge",
4865 .load = load_module,
4866 .unload = unload_module,
4867 .reload = reload,