Adds DAHDI support alongside Zaptel. DAHDI usage favored, but all Zap stuff should...
[asterisk-bristuff.git] / apps / app_meetme.c
blob5eabaa2936d3c265fcc5770245efcacef7da4b28
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>dahdi</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 <sys/stat.h>
47 #include <sys/types.h>
49 #include "asterisk/lock.h"
50 #include "asterisk/file.h"
51 #include "asterisk/logger.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/pbx.h"
54 #include "asterisk/module.h"
55 #include "asterisk/config.h"
56 #include "asterisk/app.h"
57 #include "asterisk/dsp.h"
58 #include "asterisk/musiconhold.h"
59 #include "asterisk/manager.h"
60 #include "asterisk/options.h"
61 #include "asterisk/cli.h"
62 #include "asterisk/say.h"
63 #include "asterisk/utils.h"
64 #include "asterisk/translate.h"
65 #include "asterisk/ulaw.h"
66 #include "asterisk/astobj.h"
67 #include "asterisk/devicestate.h"
68 #include "asterisk/dial.h"
69 #include "asterisk/causes.h"
71 #include "asterisk/dahdi_compat.h"
73 #include "enter.h"
74 #include "leave.h"
76 #define CONFIG_FILE_NAME "meetme.conf"
77 #define SLA_CONFIG_FILE "sla.conf"
79 /*! each buffer is 20ms, so this is 640ms total */
80 #define DEFAULT_AUDIO_BUFFERS 32
82 enum {
83 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
84 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
85 ADMINFLAG_KICKME = (1 << 3) /*!< User has been kicked */
88 #define MEETME_DELAYDETECTTALK 300
89 #define MEETME_DELAYDETECTENDTALK 1000
91 #define AST_FRAME_BITS 32
93 enum volume_action {
94 VOL_UP,
95 VOL_DOWN
98 enum entrance_sound {
99 ENTER,
100 LEAVE
103 enum recording_state {
104 MEETME_RECORD_OFF,
105 MEETME_RECORD_STARTED,
106 MEETME_RECORD_ACTIVE,
107 MEETME_RECORD_TERMINATE
110 #define CONF_SIZE 320
112 enum {
113 /*! user has admin access on the conference */
114 CONFFLAG_ADMIN = (1 << 0),
115 /*! If set the user can only receive audio from the conference */
116 CONFFLAG_MONITOR = (1 << 1),
117 /*! If set asterisk will exit conference when '#' is pressed */
118 CONFFLAG_POUNDEXIT = (1 << 2),
119 /*! If set asterisk will provide a menu to the user when '*' is pressed */
120 CONFFLAG_STARMENU = (1 << 3),
121 /*! If set the use can only send audio to the conference */
122 CONFFLAG_TALKER = (1 << 4),
123 /*! If set there will be no enter or leave sounds */
124 CONFFLAG_QUIET = (1 << 5),
125 /*! If set, when user joins the conference, they will be told the number
126 * of users that are already in */
127 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
128 /*! Set to run AGI Script in Background */
129 CONFFLAG_AGI = (1 << 7),
130 /*! Set to have music on hold when user is alone in conference */
131 CONFFLAG_MOH = (1 << 8),
132 /*! If set the MeetMe will return if all marked with this flag left */
133 CONFFLAG_MARKEDEXIT = (1 << 9),
134 /*! If set, the MeetMe will wait until a marked user enters */
135 CONFFLAG_WAITMARKED = (1 << 10),
136 /*! If set, the MeetMe will exit to the specified context */
137 CONFFLAG_EXIT_CONTEXT = (1 << 11),
138 /*! If set, the user will be marked */
139 CONFFLAG_MARKEDUSER = (1 << 12),
140 /*! If set, user will be ask record name on entry of conference */
141 CONFFLAG_INTROUSER = (1 << 13),
142 /*! If set, the MeetMe will be recorded */
143 CONFFLAG_RECORDCONF = (1<< 14),
144 /*! If set, the user will be monitored if the user is talking or not */
145 CONFFLAG_MONITORTALKER = (1 << 15),
146 CONFFLAG_DYNAMIC = (1 << 16),
147 CONFFLAG_DYNAMICPIN = (1 << 17),
148 CONFFLAG_EMPTY = (1 << 18),
149 CONFFLAG_EMPTYNOPIN = (1 << 19),
150 CONFFLAG_ALWAYSPROMPT = (1 << 20),
151 /*! If set, treats talking users as muted users */
152 CONFFLAG_OPTIMIZETALKER = (1 << 21),
153 /*! If set, won't speak the extra prompt when the first person
154 * enters the conference */
155 CONFFLAG_NOONLYPERSON = (1 << 22),
156 /*! If set, user will be asked to record name on entry of conference
157 * without review */
158 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
159 /*! If set, the user will be initially self-muted */
160 CONFFLAG_STARTMUTED = (1 << 24),
161 /*! Pass DTMF through the conference */
162 CONFFLAG_PASS_DTMF = (1 << 25),
163 /*! This is a SLA station. (Only for use by the SLA applications.) */
164 CONFFLAG_SLA_STATION = (1 << 26),
165 /*! This is a SLA trunk. (Only for use by the SLA applications.) */
166 CONFFLAG_SLA_TRUNK = (1 << 27),
169 enum {
170 OPT_ARG_WAITMARKED = 0,
171 OPT_ARG_ARRAY_SIZE = 1,
174 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
175 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
176 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
177 AST_APP_OPTION('b', CONFFLAG_AGI ),
178 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
179 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
180 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
181 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
182 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
183 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
184 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
185 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
186 AST_APP_OPTION('M', CONFFLAG_MOH ),
187 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
188 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
189 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
190 AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
191 AST_APP_OPTION('q', CONFFLAG_QUIET ),
192 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
193 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
194 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
195 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
196 AST_APP_OPTION('t', CONFFLAG_TALKER ),
197 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
198 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
199 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
200 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
201 END_OPTIONS );
203 static const char *app = "MeetMe";
204 static const char *app2 = "MeetMeCount";
205 static const char *app3 = "MeetMeAdmin";
206 static const char *slastation_app = "SLAStation";
207 static const char *slatrunk_app = "SLATrunk";
209 static const char *synopsis = "MeetMe conference bridge";
210 static const char *synopsis2 = "MeetMe participant count";
211 static const char *synopsis3 = "MeetMe conference Administration";
212 static const char *slastation_synopsis = "Shared Line Appearance Station";
213 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
215 static const char *descrip =
216 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
217 "conference. If the conference number is omitted, the user will be prompted\n"
218 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
219 "is specified, by pressing '#'.\n"
220 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
221 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
222 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
223 "The option string may contain zero or more of the following characters:\n"
224 " 'a' -- set admin mode\n"
225 " 'A' -- set marked mode\n"
226 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
227 " Default: conf-background.agi (Note: This does not work with\n"
228 " non-Zap channels in the same conference)\n"
229 " 'c' -- announce user(s) count on joining a conference\n"
230 " 'd' -- dynamically add conference\n"
231 " 'D' -- dynamically add conference, prompting for a PIN\n"
232 " 'e' -- select an empty conference\n"
233 " 'E' -- select an empty pinless conference\n"
234 " 'F' -- Pass DTMF through the conference.\n"
235 " 'i' -- announce user join/leave with review\n"
236 " 'I' -- announce user join/leave without review\n"
237 " 'l' -- set listen only mode (Listen only, no talking)\n"
238 " 'm' -- set initially muted\n"
239 " 'M' -- enable music on hold when the conference has a single caller\n"
240 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
241 " being muted, meaning (a) No encode is done on transmission and\n"
242 " (b) Received audio that is not registered as talking is omitted\n"
243 " causing no buildup in background noise. Note that this option\n"
244 " will be removed in 1.6 and enabled by default.\n"
245 " 'p' -- allow user to exit the conference by pressing '#'\n"
246 " 'P' -- always prompt for the pin even if it is specified\n"
247 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
248 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
249 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
250 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
251 " wav.\n"
252 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
253 " 't' -- set talk only mode. (Talk only, no listening)\n"
254 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
255 " 'w[(<secs>)]'\n"
256 " -- wait until the marked user enters the conference\n"
257 " 'x' -- close the conference when last marked user exits\n"
258 " 'X' -- allow user to exit the conference by entering a valid single\n"
259 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
260 " if that variable is not defined.\n"
261 " '1' -- do not play message when first person enters\n";
263 static const char *descrip2 =
264 " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
265 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
266 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
267 "the channel, unless priority n+1 exists, in which case priority progress will\n"
268 "continue.\n"
269 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
271 static const char *descrip3 =
272 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
273 " 'e' -- Eject last user that joined\n"
274 " 'k' -- Kick one user out of conference\n"
275 " 'K' -- Kick all users out of conference\n"
276 " 'l' -- Unlock conference\n"
277 " 'L' -- Lock conference\n"
278 " 'm' -- Unmute one user\n"
279 " 'M' -- Mute one user\n"
280 " 'n' -- Unmute all users in the conference\n"
281 " 'N' -- Mute all non-admin users in the conference\n"
282 " 'r' -- Reset one user's volume settings\n"
283 " 'R' -- Reset all users volume settings\n"
284 " 's' -- Lower entire conference speaking volume\n"
285 " 'S' -- Raise entire conference speaking volume\n"
286 " 't' -- Lower one user's talk volume\n"
287 " 'T' -- Raise one user's talk volume\n"
288 " 'u' -- Lower one user's listen volume\n"
289 " 'U' -- Raise one user's listen volume\n"
290 " 'v' -- Lower entire conference listening volume\n"
291 " 'V' -- Raise entire conference listening volume\n"
294 static const char *slastation_desc =
295 " SLAStation(station):\n"
296 "This application should be executed by an SLA station. The argument depends\n"
297 "on how the call was initiated. If the phone was just taken off hook, then\n"
298 "the argument \"station\" should be just the station name. If the call was\n"
299 "initiated by pressing a line key, then the station name should be preceded\n"
300 "by an underscore and the trunk name associated with that line button.\n"
301 "For example: \"station1_line1\"."
302 " On exit, this application will set the variable SLASTATION_STATUS to\n"
303 "one of the following values:\n"
304 " FAILURE | CONGESTION | SUCCESS\n"
307 static const char *slatrunk_desc =
308 " SLATrunk(trunk):\n"
309 "This application should be executed by an SLA trunk on an inbound call.\n"
310 "The channel calling this application should correspond to the SLA trunk\n"
311 "with the name \"trunk\" that is being passed as an argument.\n"
312 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
313 "one of the following values:\n"
314 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
317 #define MAX_CONFNUM 80
318 #define MAX_PIN 80
320 /*! \brief The MeetMe Conference object */
321 struct ast_conference {
322 ast_mutex_t playlock; /*!< Conference specific lock (players) */
323 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
324 char confno[MAX_CONFNUM]; /*!< Conference */
325 struct ast_channel *chan; /*!< Announcements channel */
326 struct ast_channel *lchan; /*!< Listen/Record channel */
327 int fd; /*!< Announcements fd */
328 int zapconf; /*!< Zaptel Conf # */
329 int users; /*!< Number of active users */
330 int markedusers; /*!< Number of marked users */
331 time_t start; /*!< Start time (s) */
332 int refcount; /*!< reference count of usage */
333 enum recording_state recording:2; /*!< recording status */
334 unsigned int isdynamic:1; /*!< Created on the fly? */
335 unsigned int locked:1; /*!< Is the conference locked? */
336 pthread_t recordthread; /*!< thread for recording */
337 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
338 pthread_attr_t attr; /*!< thread attribute */
339 const char *recordingfilename; /*!< Filename to record the Conference into */
340 const char *recordingformat; /*!< Format to record the Conference in */
341 char pin[MAX_PIN]; /*!< If protected by a PIN */
342 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
343 struct ast_frame *transframe[32];
344 struct ast_frame *origframe;
345 struct ast_trans_pvt *transpath[32];
346 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
347 AST_LIST_ENTRY(ast_conference) list;
350 static AST_LIST_HEAD_STATIC(confs, ast_conference);
352 static unsigned int conf_map[1024] = {0, };
354 struct volume {
355 int desired; /*!< Desired volume adjustment */
356 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
359 struct ast_conf_user {
360 int user_no; /*!< User Number */
361 int userflags; /*!< Flags as set in the conference */
362 int adminflags; /*!< Flags set by the Admin */
363 struct ast_channel *chan; /*!< Connected channel */
364 int talking; /*!< Is user talking */
365 int zapchannel; /*!< Is a Zaptel channel */
366 char usrvalue[50]; /*!< Custom User Value */
367 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
368 time_t jointime; /*!< Time the user joined the conference */
369 struct volume talk;
370 struct volume listen;
371 AST_LIST_ENTRY(ast_conf_user) list;
374 enum sla_which_trunk_refs {
375 ALL_TRUNK_REFS,
376 INACTIVE_TRUNK_REFS,
379 enum sla_trunk_state {
380 SLA_TRUNK_STATE_IDLE,
381 SLA_TRUNK_STATE_RINGING,
382 SLA_TRUNK_STATE_UP,
383 SLA_TRUNK_STATE_ONHOLD,
384 SLA_TRUNK_STATE_ONHOLD_BYME,
387 enum sla_hold_access {
388 /*! This means that any station can put it on hold, and any station
389 * can retrieve the call from hold. */
390 SLA_HOLD_OPEN,
391 /*! This means that only the station that put the call on hold may
392 * retrieve it from hold. */
393 SLA_HOLD_PRIVATE,
396 struct sla_trunk_ref;
398 struct sla_station {
399 AST_RWLIST_ENTRY(sla_station) entry;
400 AST_DECLARE_STRING_FIELDS(
401 AST_STRING_FIELD(name);
402 AST_STRING_FIELD(device);
403 AST_STRING_FIELD(autocontext);
405 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
406 struct ast_dial *dial;
407 /*! Ring timeout for this station, for any trunk. If a ring timeout
408 * is set for a specific trunk on this station, that will take
409 * priority over this value. */
410 unsigned int ring_timeout;
411 /*! Ring delay for this station, for any trunk. If a ring delay
412 * is set for a specific trunk on this station, that will take
413 * priority over this value. */
414 unsigned int ring_delay;
415 /*! This option uses the values in the sla_hold_access enum and sets the
416 * access control type for hold on this station. */
417 unsigned int hold_access:1;
420 struct sla_station_ref {
421 AST_LIST_ENTRY(sla_station_ref) entry;
422 struct sla_station *station;
425 struct sla_trunk {
426 AST_RWLIST_ENTRY(sla_trunk) entry;
427 AST_DECLARE_STRING_FIELDS(
428 AST_STRING_FIELD(name);
429 AST_STRING_FIELD(device);
430 AST_STRING_FIELD(autocontext);
432 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
433 /*! Number of stations that use this trunk */
434 unsigned int num_stations;
435 /*! Number of stations currently on a call with this trunk */
436 unsigned int active_stations;
437 /*! Number of stations that have this trunk on hold. */
438 unsigned int hold_stations;
439 struct ast_channel *chan;
440 unsigned int ring_timeout;
441 /*! If set to 1, no station will be able to join an active call with
442 * this trunk. */
443 unsigned int barge_disabled:1;
444 /*! This option uses the values in the sla_hold_access enum and sets the
445 * access control type for hold on this trunk. */
446 unsigned int hold_access:1;
447 /*! Whether this trunk is currently on hold, meaning that once a station
448 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
449 unsigned int on_hold:1;
452 struct sla_trunk_ref {
453 AST_LIST_ENTRY(sla_trunk_ref) entry;
454 struct sla_trunk *trunk;
455 enum sla_trunk_state state;
456 struct ast_channel *chan;
457 /*! Ring timeout to use when this trunk is ringing on this specific
458 * station. This takes higher priority than a ring timeout set at
459 * the station level. */
460 unsigned int ring_timeout;
461 /*! Ring delay to use when this trunk is ringing on this specific
462 * station. This takes higher priority than a ring delay set at
463 * the station level. */
464 unsigned int ring_delay;
467 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
468 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
470 static const char sla_registrar[] = "SLA";
472 /*! \brief Event types that can be queued up for the SLA thread */
473 enum sla_event_type {
474 /*! A station has put the call on hold */
475 SLA_EVENT_HOLD,
476 /*! The state of a dial has changed */
477 SLA_EVENT_DIAL_STATE,
478 /*! The state of a ringing trunk has changed */
479 SLA_EVENT_RINGING_TRUNK,
482 struct sla_event {
483 enum sla_event_type type;
484 struct sla_station *station;
485 struct sla_trunk_ref *trunk_ref;
486 AST_LIST_ENTRY(sla_event) entry;
489 /*! \brief A station that failed to be dialed
490 * \note Only used by the SLA thread. */
491 struct sla_failed_station {
492 struct sla_station *station;
493 struct timeval last_try;
494 AST_LIST_ENTRY(sla_failed_station) entry;
497 /*! \brief A trunk that is ringing */
498 struct sla_ringing_trunk {
499 struct sla_trunk *trunk;
500 /*! The time that this trunk started ringing */
501 struct timeval ring_begin;
502 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
503 AST_LIST_ENTRY(sla_ringing_trunk) entry;
506 enum sla_station_hangup {
507 SLA_STATION_HANGUP_NORMAL,
508 SLA_STATION_HANGUP_TIMEOUT,
511 /*! \brief A station that is ringing */
512 struct sla_ringing_station {
513 struct sla_station *station;
514 /*! The time that this station started ringing */
515 struct timeval ring_begin;
516 AST_LIST_ENTRY(sla_ringing_station) entry;
520 * \brief A structure for data used by the sla thread
522 static struct {
523 /*! The SLA thread ID */
524 pthread_t thread;
525 ast_cond_t cond;
526 ast_mutex_t lock;
527 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
528 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
529 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
530 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
531 unsigned int stop:1;
532 /*! Attempt to handle CallerID, even though it is known not to work
533 * properly in some situations. */
534 unsigned int attempt_callerid:1;
535 } sla = {
536 .thread = AST_PTHREADT_NULL,
539 /*! The number of audio buffers to be allocated on pseudo channels
540 * when in a conference */
541 static int audio_buffers;
543 /*! Map 'volume' levels from -5 through +5 into
544 * decibel (dB) settings for channel drivers
545 * Note: these are not a straight linear-to-dB
546 * conversion... the numbers have been modified
547 * to give the user a better level of adjustability
549 static char const gain_map[] = {
550 -15,
551 -13,
552 -10,
564 static int admin_exec(struct ast_channel *chan, void *data);
565 static void *recordthread(void *args);
567 static char *istalking(int x)
569 if (x > 0)
570 return "(talking)";
571 else if (x < 0)
572 return "(unmonitored)";
573 else
574 return "(not talking)";
577 static int careful_write(int fd, unsigned char *data, int len, int block)
579 int res;
580 int x;
582 while (len) {
583 if (block) {
584 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
585 res = ioctl(fd, DAHDI_IOMUX, &x);
586 } else
587 res = 0;
588 if (res >= 0)
589 res = write(fd, data, len);
590 if (res < 1) {
591 if (errno != EAGAIN) {
592 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
593 return -1;
594 } else
595 return 0;
597 len -= res;
598 data += res;
601 return 0;
604 static int set_talk_volume(struct ast_conf_user *user, int volume)
606 char gain_adjust;
608 /* attempt to make the adjustment in the channel driver;
609 if successful, don't adjust in the frame reading routine
611 gain_adjust = gain_map[volume + 5];
613 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
616 static int set_listen_volume(struct ast_conf_user *user, int volume)
618 char gain_adjust;
620 /* attempt to make the adjustment in the channel driver;
621 if successful, don't adjust in the frame reading routine
623 gain_adjust = gain_map[volume + 5];
625 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
628 static void tweak_volume(struct volume *vol, enum volume_action action)
630 switch (action) {
631 case VOL_UP:
632 switch (vol->desired) {
633 case 5:
634 break;
635 case 0:
636 vol->desired = 2;
637 break;
638 case -2:
639 vol->desired = 0;
640 break;
641 default:
642 vol->desired++;
643 break;
645 break;
646 case VOL_DOWN:
647 switch (vol->desired) {
648 case -5:
649 break;
650 case 2:
651 vol->desired = 0;
652 break;
653 case 0:
654 vol->desired = -2;
655 break;
656 default:
657 vol->desired--;
658 break;
663 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
665 tweak_volume(&user->talk, action);
666 /* attempt to make the adjustment in the channel driver;
667 if successful, don't adjust in the frame reading routine
669 if (!set_talk_volume(user, user->talk.desired))
670 user->talk.actual = 0;
671 else
672 user->talk.actual = user->talk.desired;
675 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
677 tweak_volume(&user->listen, action);
678 /* attempt to make the adjustment in the channel driver;
679 if successful, don't adjust in the frame reading routine
681 if (!set_listen_volume(user, user->listen.desired))
682 user->listen.actual = 0;
683 else
684 user->listen.actual = user->listen.desired;
687 static void reset_volumes(struct ast_conf_user *user)
689 signed char zero_volume = 0;
691 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
692 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
695 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
697 unsigned char *data;
698 int len;
699 int res = -1;
701 if (!chan->_softhangup)
702 res = ast_autoservice_start(chan);
704 AST_LIST_LOCK(&confs);
706 switch(sound) {
707 case ENTER:
708 data = enter;
709 len = sizeof(enter);
710 break;
711 case LEAVE:
712 data = leave;
713 len = sizeof(leave);
714 break;
715 default:
716 data = NULL;
717 len = 0;
719 if (data) {
720 careful_write(conf->fd, data, len, 1);
723 AST_LIST_UNLOCK(&confs);
725 if (!res)
726 ast_autoservice_stop(chan);
730 * \brief Find or create a conference
732 * \param confno The conference name/number
733 * \param pin The regular user pin
734 * \param pinadmin The admin pin
735 * \param make Make the conf if it doesn't exist
736 * \param dynamic Mark the newly created conference as dynamic
737 * \param refcount How many references to mark on the conference
739 * \return A pointer to the conference struct, or NULL if it wasn't found and
740 * make or dynamic were not set.
742 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
744 struct ast_conference *cnf;
745 DAHDI_CONFINFO ztc = { 0, };
746 int confno_int = 0;
748 AST_LIST_LOCK(&confs);
750 AST_LIST_TRAVERSE(&confs, cnf, list) {
751 if (!strcmp(confno, cnf->confno))
752 break;
755 if (cnf || (!make && !dynamic))
756 goto cnfout;
758 /* Make a new one */
759 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
760 goto cnfout;
762 ast_mutex_init(&cnf->playlock);
763 ast_mutex_init(&cnf->listenlock);
764 cnf->recordthread = AST_PTHREADT_NULL;
765 ast_mutex_init(&cnf->recordthreadlock);
766 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
767 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
768 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
770 /* Setup a new zap conference */
771 ztc.confno = -1;
772 ztc.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
773 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
774 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &ztc)) {
775 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
776 if (cnf->fd >= 0)
777 close(cnf->fd);
778 free(cnf);
779 cnf = NULL;
780 goto cnfout;
783 cnf->zapconf = ztc.confno;
785 /* Setup a new channel for playback of audio files */
786 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
787 if (cnf->chan) {
788 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
789 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
790 ztc.chan = 0;
791 ztc.confno = cnf->zapconf;
792 ztc.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
793 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &ztc)) {
794 ast_log(LOG_WARNING, "Error setting conference\n");
795 if (cnf->chan)
796 ast_hangup(cnf->chan);
797 else
798 close(cnf->fd);
799 free(cnf);
800 cnf = NULL;
801 goto cnfout;
805 /* Fill the conference struct */
806 cnf->start = time(NULL);
807 cnf->isdynamic = dynamic ? 1 : 0;
808 if (option_verbose > 2)
809 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
810 AST_LIST_INSERT_HEAD(&confs, cnf, list);
812 /* Reserve conference number in map */
813 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
814 conf_map[confno_int] = 1;
816 cnfout:
817 if (cnf)
818 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
820 AST_LIST_UNLOCK(&confs);
822 return cnf;
825 static int meetme_cmd(int fd, int argc, char **argv)
827 /* Process the command */
828 struct ast_conference *cnf;
829 struct ast_conf_user *user;
830 int hr, min, sec;
831 int i = 0, total = 0;
832 time_t now;
833 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
834 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
835 char cmdline[1024] = "";
837 if (argc > 8)
838 ast_cli(fd, "Invalid Arguments.\n");
839 /* Check for length so no buffer will overflow... */
840 for (i = 0; i < argc; i++) {
841 if (strlen(argv[i]) > 100)
842 ast_cli(fd, "Invalid Arguments.\n");
844 if (argc == 1) {
845 /* 'MeetMe': List all the conferences */
846 now = time(NULL);
847 AST_LIST_LOCK(&confs);
848 if (AST_LIST_EMPTY(&confs)) {
849 ast_cli(fd, "No active MeetMe conferences.\n");
850 AST_LIST_UNLOCK(&confs);
851 return RESULT_SUCCESS;
853 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
854 AST_LIST_TRAVERSE(&confs, cnf, list) {
855 if (cnf->markedusers == 0)
856 strcpy(cmdline, "N/A ");
857 else
858 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
859 hr = (now - cnf->start) / 3600;
860 min = ((now - cnf->start) % 3600) / 60;
861 sec = (now - cnf->start) % 60;
863 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
865 total += cnf->users;
867 AST_LIST_UNLOCK(&confs);
868 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
869 return RESULT_SUCCESS;
871 if (argc < 3)
872 return RESULT_SHOWUSAGE;
873 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
874 if (strstr(argv[1], "lock")) {
875 if (strcmp(argv[1], "lock") == 0) {
876 /* Lock */
877 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
878 } else {
879 /* Unlock */
880 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
882 } else if (strstr(argv[1], "mute")) {
883 if (argc < 4)
884 return RESULT_SHOWUSAGE;
885 if (strcmp(argv[1], "mute") == 0) {
886 /* Mute */
887 if (strcmp(argv[3], "all") == 0) {
888 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
889 } else {
890 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
891 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
893 } else {
894 /* Unmute */
895 if (strcmp(argv[3], "all") == 0) {
896 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
897 } else {
898 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
899 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
902 } else if (strcmp(argv[1], "kick") == 0) {
903 if (argc < 4)
904 return RESULT_SHOWUSAGE;
905 if (strcmp(argv[3], "all") == 0) {
906 /* Kick all */
907 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
908 } else {
909 /* Kick a single user */
910 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
911 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
913 } else if(strcmp(argv[1], "list") == 0) {
914 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
915 /* List all the users in a conference */
916 if (AST_LIST_EMPTY(&confs)) {
917 if ( !concise )
918 ast_cli(fd, "No active conferences.\n");
919 return RESULT_SUCCESS;
921 /* Find the right conference */
922 AST_LIST_LOCK(&confs);
923 AST_LIST_TRAVERSE(&confs, cnf, list) {
924 if (strcmp(cnf->confno, argv[2]) == 0)
925 break;
927 if (!cnf) {
928 if ( !concise )
929 ast_cli(fd, "No such conference: %s.\n",argv[2]);
930 AST_LIST_UNLOCK(&confs);
931 return RESULT_SUCCESS;
933 /* Show all the users */
934 time(&now);
935 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
936 hr = (now - user->jointime) / 3600;
937 min = ((now - user->jointime) % 3600) / 60;
938 sec = (now - user->jointime) % 60;
939 if ( !concise )
940 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
941 user->user_no,
942 S_OR(user->chan->cid.cid_num, "<unknown>"),
943 S_OR(user->chan->cid.cid_name, "<no name>"),
944 user->chan->name,
945 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
946 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
947 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
948 istalking(user->talking), hr, min, sec);
949 else
950 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
951 user->user_no,
952 S_OR(user->chan->cid.cid_num, ""),
953 S_OR(user->chan->cid.cid_name, ""),
954 user->chan->name,
955 user->userflags & CONFFLAG_ADMIN ? "1" : "",
956 user->userflags & CONFFLAG_MONITOR ? "1" : "",
957 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
958 user->talking, hr, min, sec);
961 if ( !concise )
962 ast_cli(fd,"%d users in that conference.\n",cnf->users);
963 AST_LIST_UNLOCK(&confs);
964 return RESULT_SUCCESS;
965 } else
966 return RESULT_SHOWUSAGE;
967 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
968 admin_exec(NULL, cmdline);
970 return 0;
973 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
975 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
977 int len = strlen(word);
978 int which = 0;
979 struct ast_conference *cnf = NULL;
980 struct ast_conf_user *usr = NULL;
981 char *confno = NULL;
982 char usrno[50] = "";
983 char *myline, *ret = NULL;
985 if (pos == 1) { /* Command */
986 return ast_cli_complete(word, cmds, state);
987 } else if (pos == 2) { /* Conference Number */
988 AST_LIST_LOCK(&confs);
989 AST_LIST_TRAVERSE(&confs, cnf, list) {
990 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
991 ret = cnf->confno;
992 break;
995 ret = ast_strdup(ret); /* dup before releasing the lock */
996 AST_LIST_UNLOCK(&confs);
997 return ret;
998 } else if (pos == 3) {
999 /* User Number || Conf Command option*/
1000 if (strstr(line, "mute") || strstr(line, "kick")) {
1001 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
1002 return strdup("all");
1003 which++;
1004 AST_LIST_LOCK(&confs);
1006 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1007 myline = ast_strdupa(line);
1008 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1009 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1013 AST_LIST_TRAVERSE(&confs, cnf, list) {
1014 if (!strcmp(confno, cnf->confno))
1015 break;
1018 if (cnf) {
1019 /* Search for the user */
1020 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
1021 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1022 if (!strncasecmp(word, usrno, len) && ++which > state)
1023 break;
1026 AST_LIST_UNLOCK(&confs);
1027 return usr ? strdup(usrno) : NULL;
1028 } else if ( strstr(line, "list") && ( 0 == state ) )
1029 return strdup("concise");
1032 return NULL;
1035 static char meetme_usage[] =
1036 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
1037 " Executes a command for the conference or on a conferee\n";
1039 static const char *sla_hold_str(unsigned int hold_access)
1041 const char *hold = "Unknown";
1043 switch (hold_access) {
1044 case SLA_HOLD_OPEN:
1045 hold = "Open";
1046 break;
1047 case SLA_HOLD_PRIVATE:
1048 hold = "Private";
1049 default:
1050 break;
1053 return hold;
1056 static int sla_show_trunks(int fd, int argc, char **argv)
1058 const struct sla_trunk *trunk;
1060 ast_cli(fd, "\n"
1061 "=============================================================\n"
1062 "=== Configured SLA Trunks ===================================\n"
1063 "=============================================================\n"
1064 "===\n");
1065 AST_RWLIST_RDLOCK(&sla_trunks);
1066 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1067 struct sla_station_ref *station_ref;
1068 char ring_timeout[16] = "(none)";
1069 if (trunk->ring_timeout)
1070 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1071 ast_cli(fd, "=== ---------------------------------------------------------\n"
1072 "=== Trunk Name: %s\n"
1073 "=== ==> Device: %s\n"
1074 "=== ==> AutoContext: %s\n"
1075 "=== ==> RingTimeout: %s\n"
1076 "=== ==> BargeAllowed: %s\n"
1077 "=== ==> HoldAccess: %s\n"
1078 "=== ==> Stations ...\n",
1079 trunk->name, trunk->device,
1080 S_OR(trunk->autocontext, "(none)"),
1081 ring_timeout,
1082 trunk->barge_disabled ? "No" : "Yes",
1083 sla_hold_str(trunk->hold_access));
1084 AST_RWLIST_RDLOCK(&sla_stations);
1085 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1086 ast_cli(fd, "=== ==> Station name: %s\n", station_ref->station->name);
1087 AST_RWLIST_UNLOCK(&sla_stations);
1088 ast_cli(fd, "=== ---------------------------------------------------------\n"
1089 "===\n");
1091 AST_RWLIST_UNLOCK(&sla_trunks);
1092 ast_cli(fd, "=============================================================\n"
1093 "\n");
1095 return RESULT_SUCCESS;
1098 static const char *trunkstate2str(enum sla_trunk_state state)
1100 #define S(e) case e: return # e;
1101 switch (state) {
1102 S(SLA_TRUNK_STATE_IDLE)
1103 S(SLA_TRUNK_STATE_RINGING)
1104 S(SLA_TRUNK_STATE_UP)
1105 S(SLA_TRUNK_STATE_ONHOLD)
1106 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1108 return "Uknown State";
1109 #undef S
1112 static const char sla_show_trunks_usage[] =
1113 "Usage: sla show trunks\n"
1114 " This will list all trunks defined in sla.conf\n";
1116 static int sla_show_stations(int fd, int argc, char **argv)
1118 const struct sla_station *station;
1120 ast_cli(fd, "\n"
1121 "=============================================================\n"
1122 "=== Configured SLA Stations =================================\n"
1123 "=============================================================\n"
1124 "===\n");
1125 AST_RWLIST_RDLOCK(&sla_stations);
1126 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1127 struct sla_trunk_ref *trunk_ref;
1128 char ring_timeout[16] = "(none)";
1129 char ring_delay[16] = "(none)";
1130 if (station->ring_timeout) {
1131 snprintf(ring_timeout, sizeof(ring_timeout),
1132 "%u", station->ring_timeout);
1134 if (station->ring_delay) {
1135 snprintf(ring_delay, sizeof(ring_delay),
1136 "%u", station->ring_delay);
1138 ast_cli(fd, "=== ---------------------------------------------------------\n"
1139 "=== Station Name: %s\n"
1140 "=== ==> Device: %s\n"
1141 "=== ==> AutoContext: %s\n"
1142 "=== ==> RingTimeout: %s\n"
1143 "=== ==> RingDelay: %s\n"
1144 "=== ==> HoldAccess: %s\n"
1145 "=== ==> Trunks ...\n",
1146 station->name, station->device,
1147 S_OR(station->autocontext, "(none)"),
1148 ring_timeout, ring_delay,
1149 sla_hold_str(station->hold_access));
1150 AST_RWLIST_RDLOCK(&sla_trunks);
1151 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1152 if (trunk_ref->ring_timeout) {
1153 snprintf(ring_timeout, sizeof(ring_timeout),
1154 "%u", trunk_ref->ring_timeout);
1155 } else
1156 strcpy(ring_timeout, "(none)");
1157 if (trunk_ref->ring_delay) {
1158 snprintf(ring_delay, sizeof(ring_delay),
1159 "%u", trunk_ref->ring_delay);
1160 } else
1161 strcpy(ring_delay, "(none)");
1162 ast_cli(fd, "=== ==> Trunk Name: %s\n"
1163 "=== ==> State: %s\n"
1164 "=== ==> RingTimeout: %s\n"
1165 "=== ==> RingDelay: %s\n",
1166 trunk_ref->trunk->name,
1167 trunkstate2str(trunk_ref->state),
1168 ring_timeout, ring_delay);
1170 AST_RWLIST_UNLOCK(&sla_trunks);
1171 ast_cli(fd, "=== ---------------------------------------------------------\n"
1172 "===\n");
1174 AST_RWLIST_UNLOCK(&sla_stations);
1175 ast_cli(fd, "============================================================\n"
1176 "\n");
1178 return RESULT_SUCCESS;
1181 static const char sla_show_stations_usage[] =
1182 "Usage: sla show stations\n"
1183 " This will list all stations defined in sla.conf\n";
1185 static struct ast_cli_entry cli_meetme[] = {
1186 { { "meetme", NULL, NULL },
1187 meetme_cmd, "Execute a command on a conference or conferee",
1188 meetme_usage, complete_meetmecmd },
1190 { { "sla", "show", "trunks", NULL },
1191 sla_show_trunks, "Show SLA Trunks",
1192 sla_show_trunks_usage, NULL },
1194 { { "sla", "show", "stations", NULL },
1195 sla_show_stations, "Show SLA Stations",
1196 sla_show_stations_usage, NULL },
1199 static void conf_flush(int fd, struct ast_channel *chan)
1201 int x;
1203 /* read any frames that may be waiting on the channel
1204 and throw them away
1206 if (chan) {
1207 struct ast_frame *f;
1209 /* when no frames are available, this will wait
1210 for 1 millisecond maximum
1212 while (ast_waitfor(chan, 1)) {
1213 f = ast_read(chan);
1214 if (f)
1215 ast_frfree(f);
1216 else /* channel was hung up or something else happened */
1217 break;
1221 /* flush any data sitting in the pseudo channel */
1222 x = DAHDI_FLUSH_ALL;
1223 if (ioctl(fd, DAHDI_FLUSH, &x))
1224 ast_log(LOG_WARNING, "Error flushing channel\n");
1228 /* Remove the conference from the list and free it.
1229 We assume that this was called while holding conflock. */
1230 static int conf_free(struct ast_conference *conf)
1232 int x;
1234 AST_LIST_REMOVE(&confs, conf, list);
1236 if (conf->recording == MEETME_RECORD_ACTIVE) {
1237 conf->recording = MEETME_RECORD_TERMINATE;
1238 AST_LIST_UNLOCK(&confs);
1239 while (1) {
1240 usleep(1);
1241 AST_LIST_LOCK(&confs);
1242 if (conf->recording == MEETME_RECORD_OFF)
1243 break;
1244 AST_LIST_UNLOCK(&confs);
1248 for (x=0;x<AST_FRAME_BITS;x++) {
1249 if (conf->transframe[x])
1250 ast_frfree(conf->transframe[x]);
1251 if (conf->transpath[x])
1252 ast_translator_free_path(conf->transpath[x]);
1254 if (conf->origframe)
1255 ast_frfree(conf->origframe);
1256 if (conf->lchan)
1257 ast_hangup(conf->lchan);
1258 if (conf->chan)
1259 ast_hangup(conf->chan);
1260 if (conf->fd >= 0)
1261 close(conf->fd);
1263 ast_mutex_destroy(&conf->playlock);
1264 ast_mutex_destroy(&conf->listenlock);
1265 ast_mutex_destroy(&conf->recordthreadlock);
1266 free(conf);
1268 return 0;
1271 static void conf_queue_dtmf(const struct ast_conference *conf,
1272 const struct ast_conf_user *sender, struct ast_frame *f)
1274 struct ast_conf_user *user;
1276 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1277 if (user == sender)
1278 continue;
1279 if (ast_write(user->chan, f) < 0)
1280 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1284 static void sla_queue_event_full(enum sla_event_type type,
1285 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1287 struct sla_event *event;
1289 if (!(event = ast_calloc(1, sizeof(*event))))
1290 return;
1292 event->type = type;
1293 event->trunk_ref = trunk_ref;
1294 event->station = station;
1296 if (!lock) {
1297 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1298 return;
1301 ast_mutex_lock(&sla.lock);
1302 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1303 ast_cond_signal(&sla.cond);
1304 ast_mutex_unlock(&sla.lock);
1307 static void sla_queue_event_nolock(enum sla_event_type type)
1309 sla_queue_event_full(type, NULL, NULL, 0);
1312 static void sla_queue_event(enum sla_event_type type)
1314 sla_queue_event_full(type, NULL, NULL, 1);
1317 /*! \brief Queue a SLA event from the conference */
1318 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1319 struct ast_conference *conf)
1321 struct sla_station *station;
1322 struct sla_trunk_ref *trunk_ref = NULL;
1323 char *trunk_name;
1325 trunk_name = ast_strdupa(conf->confno);
1326 strsep(&trunk_name, "_");
1327 if (ast_strlen_zero(trunk_name)) {
1328 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1329 return;
1332 AST_RWLIST_RDLOCK(&sla_stations);
1333 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1334 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1335 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1336 break;
1338 if (trunk_ref)
1339 break;
1341 AST_RWLIST_UNLOCK(&sla_stations);
1343 if (!trunk_ref) {
1344 ast_log(LOG_DEBUG, "Trunk not found for event!\n");
1345 return;
1348 sla_queue_event_full(type, trunk_ref, station, 1);
1351 /* Decrement reference counts, as incremented by find_conf() */
1352 static int dispose_conf(struct ast_conference *conf)
1354 int res = 0;
1355 int confno_int = 0;
1357 AST_LIST_LOCK(&confs);
1358 if (ast_atomic_dec_and_test(&conf->refcount)) {
1359 /* Take the conference room number out of an inuse state */
1360 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1361 conf_map[confno_int] = 0;
1362 conf_free(conf);
1363 res = 1;
1365 AST_LIST_UNLOCK(&confs);
1367 return res;
1371 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1373 struct ast_conf_user *user = NULL;
1374 struct ast_conf_user *usr = NULL;
1375 int fd;
1376 DAHDI_CONFINFO ztc, ztc_empty;
1377 struct ast_frame *f;
1378 struct ast_channel *c;
1379 struct ast_frame fr;
1380 int outfd;
1381 int ms;
1382 int nfds;
1383 int res;
1384 int flags;
1385 int retryzap;
1386 int origfd;
1387 int musiconhold = 0;
1388 int firstpass = 0;
1389 int lastmarked = 0;
1390 int currentmarked = 0;
1391 int ret = -1;
1392 int x;
1393 int menu_active = 0;
1394 int using_pseudo = 0;
1395 int duration=20;
1396 int hr, min, sec;
1397 int sent_event = 0;
1398 time_t now;
1399 struct ast_dsp *dsp=NULL;
1400 struct ast_app *app;
1401 const char *agifile;
1402 const char *agifiledefault = "conf-background.agi";
1403 char meetmesecs[30] = "";
1404 char exitcontext[AST_MAX_CONTEXT] = "";
1405 char recordingtmp[AST_MAX_EXTENSION] = "";
1406 char members[10] = "";
1407 int dtmf, opt_waitmarked_timeout = 0;
1408 time_t timeout = 0;
1409 DAHDI_BUFFERINFO bi;
1410 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1411 char *buf = __buf + AST_FRIENDLY_OFFSET;
1412 int setusercount = 0;
1414 if (!(user = ast_calloc(1, sizeof(*user))))
1415 return ret;
1417 /* Possible timeout waiting for marked user */
1418 if ((confflags & CONFFLAG_WAITMARKED) &&
1419 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1420 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1421 (opt_waitmarked_timeout > 0)) {
1422 timeout = time(NULL) + opt_waitmarked_timeout;
1425 if (confflags & CONFFLAG_RECORDCONF) {
1426 if (!conf->recordingfilename) {
1427 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1428 if (!conf->recordingfilename) {
1429 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1430 conf->recordingfilename = ast_strdupa(recordingtmp);
1432 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1433 if (!conf->recordingformat) {
1434 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1435 conf->recordingformat = ast_strdupa(recordingtmp);
1437 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1438 conf->confno, conf->recordingfilename, conf->recordingformat);
1442 ast_mutex_lock(&conf->recordthreadlock);
1443 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1444 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1445 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1446 ztc.chan = 0;
1447 ztc.confno = conf->zapconf;
1448 ztc.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1449 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &ztc)) {
1450 ast_log(LOG_WARNING, "Error starting listen channel\n");
1451 ast_hangup(conf->lchan);
1452 conf->lchan = NULL;
1453 } else {
1454 pthread_attr_init(&conf->attr);
1455 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
1456 ast_pthread_create_background(&conf->recordthread, &conf->attr, recordthread, conf);
1457 pthread_attr_destroy(&conf->attr);
1460 ast_mutex_unlock(&conf->recordthreadlock);
1462 time(&user->jointime);
1464 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1465 /* Sorry, but this confernce is locked! */
1466 if (!ast_streamfile(chan, "conf-locked", chan->language))
1467 ast_waitstream(chan, "");
1468 goto outrun;
1471 ast_mutex_lock(&conf->playlock);
1473 if (AST_LIST_EMPTY(&conf->userlist))
1474 user->user_no = 1;
1475 else
1476 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1478 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1480 user->chan = chan;
1481 user->userflags = confflags;
1482 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1483 user->talking = -1;
1485 ast_mutex_unlock(&conf->playlock);
1487 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1488 char destdir[PATH_MAX];
1490 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
1492 if (mkdir(destdir, 0777) && errno != EEXIST) {
1493 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1494 goto outrun;
1497 snprintf(user->namerecloc, sizeof(user->namerecloc),
1498 "%s/meetme-username-%s-%d", destdir,
1499 conf->confno, user->user_no);
1500 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1501 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1502 else
1503 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1504 if (res == -1)
1505 goto outrun;
1508 ast_mutex_lock(&conf->playlock);
1510 if (confflags & CONFFLAG_MARKEDUSER)
1511 conf->markedusers++;
1512 conf->users++;
1513 /* Update table */
1514 snprintf(members, sizeof(members), "%d", conf->users);
1515 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1516 setusercount = 1;
1518 /* This device changed state now - if this is the first user */
1519 if (conf->users == 1)
1520 ast_device_state_changed("meetme:%s", conf->confno);
1522 ast_mutex_unlock(&conf->playlock);
1524 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1525 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1526 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1527 else if (!ast_strlen_zero(chan->macrocontext))
1528 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1529 else
1530 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1533 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1534 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1535 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1536 ast_waitstream(chan, "");
1537 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1538 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1539 ast_waitstream(chan, "");
1542 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1543 int keepplaying = 1;
1545 if (conf->users == 2) {
1546 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1547 res = ast_waitstream(chan, AST_DIGIT_ANY);
1548 ast_stopstream(chan);
1549 if (res > 0)
1550 keepplaying=0;
1551 else if (res == -1)
1552 goto outrun;
1554 } else {
1555 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1556 res = ast_waitstream(chan, AST_DIGIT_ANY);
1557 ast_stopstream(chan);
1558 if (res > 0)
1559 keepplaying=0;
1560 else if (res == -1)
1561 goto outrun;
1563 if (keepplaying) {
1564 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1565 if (res > 0)
1566 keepplaying=0;
1567 else if (res == -1)
1568 goto outrun;
1570 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1571 res = ast_waitstream(chan, AST_DIGIT_ANY);
1572 ast_stopstream(chan);
1573 if (res > 0)
1574 keepplaying=0;
1575 else if (res == -1)
1576 goto outrun;
1581 ast_indicate(chan, -1);
1583 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1584 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1585 goto outrun;
1588 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1589 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1590 goto outrun;
1593 retryzap = (strcasecmp(chan->tech->type, "Zap") || (chan->audiohooks || chan->monitor) ? 1 : 0);
1594 user->zapchannel = !retryzap;
1596 zapretry:
1597 origfd = chan->fds[0];
1598 if (retryzap) {
1599 fd = open("/dev/zap/pseudo", O_RDWR);
1600 if (fd < 0) {
1601 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1602 goto outrun;
1604 using_pseudo = 1;
1605 /* Make non-blocking */
1606 flags = fcntl(fd, F_GETFL);
1607 if (flags < 0) {
1608 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1609 close(fd);
1610 goto outrun;
1612 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1613 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1614 close(fd);
1615 goto outrun;
1617 /* Setup buffering information */
1618 memset(&bi, 0, sizeof(bi));
1619 bi.bufsize = CONF_SIZE/2;
1620 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
1621 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
1622 bi.numbufs = audio_buffers;
1623 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
1624 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1625 close(fd);
1626 goto outrun;
1628 x = 1;
1629 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
1630 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1631 close(fd);
1632 goto outrun;
1634 nfds = 1;
1635 } else {
1636 /* XXX Make sure we're not running on a pseudo channel XXX */
1637 fd = chan->fds[0];
1638 nfds = 0;
1640 memset(&ztc, 0, sizeof(ztc));
1641 memset(&ztc_empty, 0, sizeof(ztc_empty));
1642 /* Check to see if we're in a conference... */
1643 ztc.chan = 0;
1644 if (ioctl(fd, DAHDI_GETCONF, &ztc)) {
1645 ast_log(LOG_WARNING, "Error getting conference\n");
1646 close(fd);
1647 goto outrun;
1649 if (ztc.confmode) {
1650 /* Whoa, already in a conference... Retry... */
1651 if (!retryzap) {
1652 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1653 retryzap = 1;
1654 goto zapretry;
1657 memset(&ztc, 0, sizeof(ztc));
1658 /* Add us to the conference */
1659 ztc.chan = 0;
1660 ztc.confno = conf->zapconf;
1662 ast_mutex_lock(&conf->playlock);
1664 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1665 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1666 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1667 ast_waitstream(conf->chan, "");
1668 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1669 ast_waitstream(conf->chan, "");
1673 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
1674 ztc.confmode = DAHDI_CONF_CONF;
1675 else if (confflags & CONFFLAG_MONITOR)
1676 ztc.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
1677 else if (confflags & CONFFLAG_TALKER)
1678 ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
1679 else
1680 ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
1682 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
1683 ast_log(LOG_WARNING, "Error setting conference\n");
1684 close(fd);
1685 ast_mutex_unlock(&conf->playlock);
1686 goto outrun;
1688 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1690 if (!sent_event) {
1691 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1692 "Channel: %s\r\n"
1693 "Uniqueid: %s\r\n"
1694 "Meetme: %s\r\n"
1695 "Usernum: %d\r\n",
1696 chan->name, chan->uniqueid, conf->confno, user->user_no);
1697 sent_event = 1;
1700 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1701 firstpass = 1;
1702 if (!(confflags & CONFFLAG_QUIET))
1703 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1704 conf_play(chan, conf, ENTER);
1707 ast_mutex_unlock(&conf->playlock);
1709 conf_flush(fd, chan);
1711 if (confflags & CONFFLAG_AGI) {
1712 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1713 or use default filename of conf-background.agi */
1715 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1716 if (!agifile)
1717 agifile = agifiledefault;
1719 if (user->zapchannel) {
1720 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1721 x = 1;
1722 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1724 /* Find a pointer to the agi app and execute the script */
1725 app = pbx_findapp("agi");
1726 if (app) {
1727 char *s = ast_strdupa(agifile);
1728 ret = pbx_exec(chan, app, s);
1729 } else {
1730 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1731 ret = -2;
1733 if (user->zapchannel) {
1734 /* Remove CONFMUTE mode on Zap channel */
1735 x = 0;
1736 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1738 } else {
1739 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1740 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1741 x = 1;
1742 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1744 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1745 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1746 res = -1;
1748 for(;;) {
1749 int menu_was_active = 0;
1751 outfd = -1;
1752 ms = -1;
1754 if (timeout && time(NULL) >= timeout)
1755 break;
1757 /* if we have just exited from the menu, and the user had a channel-driver
1758 volume adjustment, restore it
1760 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1761 set_talk_volume(user, user->listen.desired);
1763 menu_was_active = menu_active;
1765 currentmarked = conf->markedusers;
1766 if (!(confflags & CONFFLAG_QUIET) &&
1767 (confflags & CONFFLAG_MARKEDUSER) &&
1768 (confflags & CONFFLAG_WAITMARKED) &&
1769 lastmarked == 0) {
1770 if (currentmarked == 1 && conf->users > 1) {
1771 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1772 if (conf->users - 1 == 1) {
1773 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1774 ast_waitstream(chan, "");
1775 } else {
1776 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1777 ast_waitstream(chan, "");
1780 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1781 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1782 ast_waitstream(chan, "");
1785 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1788 /* Update the struct with the actual confflags */
1789 user->userflags = confflags;
1791 if (confflags & CONFFLAG_WAITMARKED) {
1792 if(currentmarked == 0) {
1793 if (lastmarked != 0) {
1794 if (!(confflags & CONFFLAG_QUIET))
1795 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1796 ast_waitstream(chan, "");
1797 if(confflags & CONFFLAG_MARKEDEXIT)
1798 break;
1799 else {
1800 ztc.confmode = DAHDI_CONF_CONF;
1801 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
1802 ast_log(LOG_WARNING, "Error setting conference\n");
1803 close(fd);
1804 goto outrun;
1808 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1809 ast_moh_start(chan, NULL, NULL);
1810 musiconhold = 1;
1812 } else if(currentmarked >= 1 && lastmarked == 0) {
1813 /* Marked user entered, so cancel timeout */
1814 timeout = 0;
1815 if (confflags & CONFFLAG_MONITOR)
1816 ztc.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
1817 else if (confflags & CONFFLAG_TALKER)
1818 ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
1819 else
1820 ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
1821 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
1822 ast_log(LOG_WARNING, "Error setting conference\n");
1823 close(fd);
1824 goto outrun;
1826 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1827 ast_moh_stop(chan);
1828 musiconhold = 0;
1830 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1831 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1832 ast_waitstream(chan, "");
1833 conf_play(chan, conf, ENTER);
1838 /* trying to add moh for single person conf */
1839 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1840 if (conf->users == 1) {
1841 if (musiconhold == 0) {
1842 ast_moh_start(chan, NULL, NULL);
1843 musiconhold = 1;
1845 } else {
1846 if (musiconhold) {
1847 ast_moh_stop(chan);
1848 musiconhold = 0;
1853 /* Leave if the last marked user left */
1854 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1855 ret = -1;
1856 break;
1859 /* Check if my modes have changed */
1861 /* If I should be muted but am still talker, mute me */
1862 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & DAHDI_CONF_TALKER)) {
1863 ztc.confmode ^= DAHDI_CONF_TALKER;
1864 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
1865 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1866 ret = -1;
1867 break;
1870 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1871 "Channel: %s\r\n"
1872 "Uniqueid: %s\r\n"
1873 "Meetme: %s\r\n"
1874 "Usernum: %i\r\n"
1875 "Status: on\r\n",
1876 chan->name, chan->uniqueid, conf->confno, user->user_no);
1879 /* If I should be un-muted but am not talker, un-mute me */
1880 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & DAHDI_CONF_TALKER)) {
1881 ztc.confmode |= DAHDI_CONF_TALKER;
1882 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
1883 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1884 ret = -1;
1885 break;
1888 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1889 "Channel: %s\r\n"
1890 "Uniqueid: %s\r\n"
1891 "Meetme: %s\r\n"
1892 "Usernum: %i\r\n"
1893 "Status: off\r\n",
1894 chan->name, chan->uniqueid, conf->confno, user->user_no);
1897 /* If I have been kicked, exit the conference */
1898 if (user->adminflags & ADMINFLAG_KICKME) {
1899 //You have been kicked.
1900 if (!(confflags & CONFFLAG_QUIET) &&
1901 !ast_streamfile(chan, "conf-kicked", chan->language)) {
1902 ast_waitstream(chan, "");
1904 ret = 0;
1905 break;
1908 /* Perform an extra hangup check just in case */
1909 if (ast_check_hangup(chan))
1910 break;
1912 if (c) {
1913 char dtmfstr[2] = "";
1915 if (c->fds[0] != origfd || (user->zapchannel && (c->audiohooks || c->monitor))) {
1916 if (using_pseudo) {
1917 /* Kill old pseudo */
1918 close(fd);
1919 using_pseudo = 0;
1921 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1922 retryzap = (strcasecmp(c->tech->type, "Zap") || (c->audiohooks || c->monitor) ? 1 : 0);
1923 user->zapchannel = !retryzap;
1924 goto zapretry;
1926 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1927 f = ast_read_noaudio(c);
1928 else
1929 f = ast_read(c);
1930 if (!f)
1931 break;
1932 if (f->frametype == AST_FRAME_DTMF) {
1933 dtmfstr[0] = f->subclass;
1934 dtmfstr[1] = '\0';
1937 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1938 if (user->talk.actual)
1939 ast_frame_adjust_volume(f, user->talk.actual);
1941 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1942 int totalsilence;
1944 if (user->talking == -1)
1945 user->talking = 0;
1947 res = ast_dsp_silence(dsp, f, &totalsilence);
1948 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1949 user->talking = 1;
1950 if (confflags & CONFFLAG_MONITORTALKER)
1951 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1952 "Channel: %s\r\n"
1953 "Uniqueid: %s\r\n"
1954 "Meetme: %s\r\n"
1955 "Usernum: %d\r\n"
1956 "Status: on\r\n",
1957 chan->name, chan->uniqueid, conf->confno, user->user_no);
1959 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1960 user->talking = 0;
1961 if (confflags & CONFFLAG_MONITORTALKER)
1962 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1963 "Channel: %s\r\n"
1964 "Uniqueid: %s\r\n"
1965 "Meetme: %s\r\n"
1966 "Usernum: %d\r\n"
1967 "Status: off\r\n",
1968 chan->name, chan->uniqueid, conf->confno, user->user_no);
1971 if (using_pseudo) {
1972 /* Absolutely do _not_ use careful_write here...
1973 it is important that we read data from the channel
1974 as fast as it arrives, and feed it into the conference.
1975 The buffering in the pseudo channel will take care of any
1976 timing differences, unless they are so drastic as to lose
1977 audio frames (in which case carefully writing would only
1978 have delayed the audio even further).
1980 /* As it turns out, we do want to use careful write. We just
1981 don't want to block, but we do want to at least *try*
1982 to write out all the samples.
1984 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1985 careful_write(fd, f->data, f->datalen, 0);
1987 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1988 if (confflags & CONFFLAG_PASS_DTMF)
1989 conf_queue_dtmf(conf, user, f);
1990 ret = 0;
1991 ast_frfree(f);
1992 break;
1993 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1994 if (confflags & CONFFLAG_PASS_DTMF)
1995 conf_queue_dtmf(conf, user, f);
1996 if (ioctl(fd, DAHDI_SETCONF, &ztc_empty)) {
1997 ast_log(LOG_WARNING, "Error setting conference\n");
1998 close(fd);
1999 ast_frfree(f);
2000 goto outrun;
2003 /* if we are entering the menu, and the user has a channel-driver
2004 volume adjustment, clear it
2006 if (!menu_active && user->talk.desired && !user->talk.actual)
2007 set_talk_volume(user, 0);
2009 if (musiconhold) {
2010 ast_moh_stop(chan);
2012 if ((confflags & CONFFLAG_ADMIN)) {
2013 /* Admin menu */
2014 if (!menu_active) {
2015 menu_active = 1;
2016 /* Record this sound! */
2017 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2018 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2019 ast_stopstream(chan);
2020 } else
2021 dtmf = 0;
2022 } else
2023 dtmf = f->subclass;
2024 if (dtmf) {
2025 switch(dtmf) {
2026 case '1': /* Un/Mute */
2027 menu_active = 0;
2029 /* for admin, change both admin and use flags */
2030 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2031 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2032 else
2033 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2035 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2036 if (!ast_streamfile(chan, "conf-muted", chan->language))
2037 ast_waitstream(chan, "");
2038 } else {
2039 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2040 ast_waitstream(chan, "");
2042 break;
2043 case '2': /* Un/Lock the Conference */
2044 menu_active = 0;
2045 if (conf->locked) {
2046 conf->locked = 0;
2047 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2048 ast_waitstream(chan, "");
2049 } else {
2050 conf->locked = 1;
2051 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2052 ast_waitstream(chan, "");
2054 break;
2055 case '3': /* Eject last user */
2056 menu_active = 0;
2057 usr = AST_LIST_LAST(&conf->userlist);
2058 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2059 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2060 ast_waitstream(chan, "");
2061 } else
2062 usr->adminflags |= ADMINFLAG_KICKME;
2063 ast_stopstream(chan);
2064 break;
2065 case '4':
2066 tweak_listen_volume(user, VOL_DOWN);
2067 break;
2068 case '6':
2069 tweak_listen_volume(user, VOL_UP);
2070 break;
2071 case '7':
2072 tweak_talk_volume(user, VOL_DOWN);
2073 break;
2074 case '8':
2075 menu_active = 0;
2076 break;
2077 case '9':
2078 tweak_talk_volume(user, VOL_UP);
2079 break;
2080 default:
2081 menu_active = 0;
2082 /* Play an error message! */
2083 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2084 ast_waitstream(chan, "");
2085 break;
2088 } else {
2089 /* User menu */
2090 if (!menu_active) {
2091 menu_active = 1;
2092 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2093 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2094 ast_stopstream(chan);
2095 } else
2096 dtmf = 0;
2097 } else
2098 dtmf = f->subclass;
2099 if (dtmf) {
2100 switch(dtmf) {
2101 case '1': /* Un/Mute */
2102 menu_active = 0;
2104 /* user can only toggle the self-muted state */
2105 user->adminflags ^= ADMINFLAG_SELFMUTED;
2107 /* they can't override the admin mute state */
2108 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2109 if (!ast_streamfile(chan, "conf-muted", chan->language))
2110 ast_waitstream(chan, "");
2111 } else {
2112 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2113 ast_waitstream(chan, "");
2115 break;
2116 case '4':
2117 tweak_listen_volume(user, VOL_DOWN);
2118 break;
2119 case '6':
2120 tweak_listen_volume(user, VOL_UP);
2121 break;
2122 case '7':
2123 tweak_talk_volume(user, VOL_DOWN);
2124 break;
2125 case '8':
2126 menu_active = 0;
2127 break;
2128 case '9':
2129 tweak_talk_volume(user, VOL_UP);
2130 break;
2131 default:
2132 menu_active = 0;
2133 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2134 ast_waitstream(chan, "");
2135 break;
2139 if (musiconhold)
2140 ast_moh_start(chan, NULL, NULL);
2142 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
2143 ast_log(LOG_WARNING, "Error setting conference\n");
2144 close(fd);
2145 ast_frfree(f);
2146 goto outrun;
2149 conf_flush(fd, chan);
2150 /* Since this option could absorb dtmf for the previous, we have to check this one last */
2151 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
2152 if (confflags & CONFFLAG_PASS_DTMF)
2153 conf_queue_dtmf(conf, user, f);
2155 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
2156 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
2157 ret = 0;
2158 ast_frfree(f);
2159 break;
2160 } else if (option_debug > 1)
2161 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension '%s' does not exist in context '%s'\n", dtmfstr, exitcontext);
2162 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2163 && confflags & CONFFLAG_PASS_DTMF) {
2164 conf_queue_dtmf(conf, user, f);
2165 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2166 switch (f->subclass) {
2167 case AST_CONTROL_HOLD:
2168 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2169 break;
2170 default:
2171 break;
2173 } else if (f->frametype == AST_FRAME_NULL) {
2174 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2175 } else if (option_debug) {
2176 ast_log(LOG_DEBUG,
2177 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2178 chan->name, f->frametype, f->subclass);
2180 ast_frfree(f);
2181 } else if (outfd > -1) {
2182 res = read(outfd, buf, CONF_SIZE);
2183 if (res > 0) {
2184 memset(&fr, 0, sizeof(fr));
2185 fr.frametype = AST_FRAME_VOICE;
2186 fr.subclass = AST_FORMAT_SLINEAR;
2187 fr.datalen = res;
2188 fr.samples = res/2;
2189 fr.data = buf;
2190 fr.offset = AST_FRIENDLY_OFFSET;
2191 if (!user->listen.actual &&
2192 ((confflags & CONFFLAG_MONITOR) ||
2193 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2194 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
2195 )) {
2196 int index;
2197 for (index=0;index<AST_FRAME_BITS;index++)
2198 if (chan->rawwriteformat & (1 << index))
2199 break;
2200 if (index >= AST_FRAME_BITS)
2201 goto bailoutandtrynormal;
2202 ast_mutex_lock(&conf->listenlock);
2203 if (!conf->transframe[index]) {
2204 if (conf->origframe) {
2205 if (!conf->transpath[index])
2206 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2207 if (conf->transpath[index]) {
2208 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2209 if (!conf->transframe[index])
2210 conf->transframe[index] = &ast_null_frame;
2214 if (conf->transframe[index]) {
2215 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2216 if (ast_write(chan, conf->transframe[index]))
2217 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2219 } else {
2220 ast_mutex_unlock(&conf->listenlock);
2221 goto bailoutandtrynormal;
2223 ast_mutex_unlock(&conf->listenlock);
2224 } else {
2225 bailoutandtrynormal:
2226 if (user->listen.actual)
2227 ast_frame_adjust_volume(&fr, user->listen.actual);
2228 if (ast_write(chan, &fr) < 0) {
2229 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2232 } else
2233 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2235 lastmarked = currentmarked;
2239 if (musiconhold)
2240 ast_moh_stop(chan);
2242 if (using_pseudo)
2243 close(fd);
2244 else {
2245 /* Take out of conference */
2246 ztc.chan = 0;
2247 ztc.confno = 0;
2248 ztc.confmode = 0;
2249 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
2250 ast_log(LOG_WARNING, "Error setting conference\n");
2254 reset_volumes(user);
2256 AST_LIST_LOCK(&confs);
2257 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2258 conf_play(chan, conf, LEAVE);
2260 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2261 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2262 if ((conf->chan) && (conf->users > 1)) {
2263 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2264 ast_waitstream(conf->chan, "");
2265 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2266 ast_waitstream(conf->chan, "");
2268 ast_filedelete(user->namerecloc, NULL);
2271 AST_LIST_UNLOCK(&confs);
2273 outrun:
2274 AST_LIST_LOCK(&confs);
2276 if (dsp)
2277 ast_dsp_free(dsp);
2279 if (user->user_no) { /* Only cleanup users who really joined! */
2280 now = time(NULL);
2281 hr = (now - user->jointime) / 3600;
2282 min = ((now - user->jointime) % 3600) / 60;
2283 sec = (now - user->jointime) % 60;
2285 if (sent_event) {
2286 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2287 "Channel: %s\r\n"
2288 "Uniqueid: %s\r\n"
2289 "Meetme: %s\r\n"
2290 "Usernum: %d\r\n"
2291 "CallerIDnum: %s\r\n"
2292 "CallerIDname: %s\r\n"
2293 "Duration: %ld\r\n",
2294 chan->name, chan->uniqueid, conf->confno,
2295 user->user_no,
2296 S_OR(user->chan->cid.cid_num, "<unknown>"),
2297 S_OR(user->chan->cid.cid_name, "<unknown>"),
2298 (long)(now - user->jointime));
2301 if (setusercount) {
2302 conf->users--;
2303 /* Update table */
2304 snprintf(members, sizeof(members), "%d", conf->users);
2305 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2306 if (confflags & CONFFLAG_MARKEDUSER)
2307 conf->markedusers--;
2309 /* Remove ourselves from the list */
2310 AST_LIST_REMOVE(&conf->userlist, user, list);
2312 /* Change any states */
2313 if (!conf->users)
2314 ast_device_state_changed("meetme:%s", conf->confno);
2316 /* Return the number of seconds the user was in the conf */
2317 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2318 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2320 free(user);
2321 AST_LIST_UNLOCK(&confs);
2323 return ret;
2326 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2327 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2329 struct ast_variable *var;
2330 struct ast_conference *cnf;
2332 /* Check first in the conference list */
2333 AST_LIST_LOCK(&confs);
2334 AST_LIST_TRAVERSE(&confs, cnf, list) {
2335 if (!strcmp(confno, cnf->confno))
2336 break;
2338 if (cnf) {
2339 cnf->refcount += refcount;
2341 AST_LIST_UNLOCK(&confs);
2343 if (!cnf) {
2344 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2346 var = ast_load_realtime("meetme", "confno", confno, NULL);
2348 if (!var)
2349 return NULL;
2351 while (var) {
2352 if (!strcasecmp(var->name, "pin")) {
2353 pin = ast_strdupa(var->value);
2354 } else if (!strcasecmp(var->name, "adminpin")) {
2355 pinadmin = ast_strdupa(var->value);
2357 var = var->next;
2359 ast_variables_destroy(var);
2361 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
2364 if (cnf) {
2365 if (confflags && !cnf->chan &&
2366 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2367 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2368 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2369 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2372 if (confflags && !cnf->chan &&
2373 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2374 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2375 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2379 return cnf;
2383 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2384 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2386 struct ast_config *cfg;
2387 struct ast_variable *var;
2388 struct ast_conference *cnf;
2389 char *parse;
2390 AST_DECLARE_APP_ARGS(args,
2391 AST_APP_ARG(confno);
2392 AST_APP_ARG(pin);
2393 AST_APP_ARG(pinadmin);
2396 /* Check first in the conference list */
2397 AST_LIST_LOCK(&confs);
2398 AST_LIST_TRAVERSE(&confs, cnf, list) {
2399 if (!strcmp(confno, cnf->confno))
2400 break;
2402 if (cnf){
2403 cnf->refcount += refcount;
2405 AST_LIST_UNLOCK(&confs);
2407 if (!cnf) {
2408 if (dynamic) {
2409 /* No need to parse meetme.conf */
2410 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
2411 if (dynamic_pin) {
2412 if (dynamic_pin[0] == 'q') {
2413 /* Query the user to enter a PIN */
2414 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2415 return NULL;
2417 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
2418 } else {
2419 cnf = build_conf(confno, "", "", make, dynamic, refcount);
2421 } else {
2422 /* Check the config */
2423 cfg = ast_config_load(CONFIG_FILE_NAME);
2424 if (!cfg) {
2425 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2426 return NULL;
2428 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2429 if (strcasecmp(var->name, "conf"))
2430 continue;
2432 if (!(parse = ast_strdupa(var->value)))
2433 return NULL;
2435 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
2436 if (!strcasecmp(args.confno, confno)) {
2437 /* Bingo it's a valid conference */
2438 cnf = build_conf(args.confno,
2439 S_OR(args.pin, ""),
2440 S_OR(args.pinadmin, ""),
2441 make, dynamic, refcount);
2442 break;
2445 if (!var) {
2446 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
2448 ast_config_destroy(cfg);
2450 } else if (dynamic_pin) {
2451 /* Correct for the user selecting 'D' instead of 'd' to have
2452 someone join into a conference that has already been created
2453 with a pin. */
2454 if (dynamic_pin[0] == 'q')
2455 dynamic_pin[0] = '\0';
2458 if (cnf) {
2459 if (confflags && !cnf->chan &&
2460 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2461 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2462 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2463 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2466 if (confflags && !cnf->chan &&
2467 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2468 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2469 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2473 return cnf;
2476 /*! \brief The MeetmeCount application */
2477 static int count_exec(struct ast_channel *chan, void *data)
2479 struct ast_module_user *u;
2480 int res = 0;
2481 struct ast_conference *conf;
2482 int count;
2483 char *localdata;
2484 char val[80] = "0";
2485 AST_DECLARE_APP_ARGS(args,
2486 AST_APP_ARG(confno);
2487 AST_APP_ARG(varname);
2490 if (ast_strlen_zero(data)) {
2491 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2492 return -1;
2495 u = ast_module_user_add(chan);
2497 if (!(localdata = ast_strdupa(data))) {
2498 ast_module_user_remove(u);
2499 return -1;
2502 AST_STANDARD_APP_ARGS(args, localdata);
2504 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2506 if (conf) {
2507 count = conf->users;
2508 dispose_conf(conf);
2509 conf = NULL;
2510 } else
2511 count = 0;
2513 if (!ast_strlen_zero(args.varname)){
2514 /* have var so load it and exit */
2515 snprintf(val, sizeof(val), "%d",count);
2516 pbx_builtin_setvar_helper(chan, args.varname, val);
2517 } else {
2518 if (chan->_state != AST_STATE_UP)
2519 ast_answer(chan);
2520 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2522 ast_module_user_remove(u);
2524 return res;
2527 /*! \brief The meetme() application */
2528 static int conf_exec(struct ast_channel *chan, void *data)
2530 int res=-1;
2531 struct ast_module_user *u;
2532 char confno[MAX_CONFNUM] = "";
2533 int allowretry = 0;
2534 int retrycnt = 0;
2535 struct ast_conference *cnf = NULL;
2536 struct ast_flags confflags = {0};
2537 int dynamic = 0;
2538 int empty = 0, empty_no_pin = 0;
2539 int always_prompt = 0;
2540 char *notdata, *info, the_pin[MAX_PIN] = "";
2541 AST_DECLARE_APP_ARGS(args,
2542 AST_APP_ARG(confno);
2543 AST_APP_ARG(options);
2544 AST_APP_ARG(pin);
2546 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2548 u = ast_module_user_add(chan);
2550 if (ast_strlen_zero(data)) {
2551 allowretry = 1;
2552 notdata = "";
2553 } else {
2554 notdata = data;
2557 if (chan->_state != AST_STATE_UP)
2558 ast_answer(chan);
2560 info = ast_strdupa(notdata);
2562 AST_STANDARD_APP_ARGS(args, info);
2564 if (args.confno) {
2565 ast_copy_string(confno, args.confno, sizeof(confno));
2566 if (ast_strlen_zero(confno)) {
2567 allowretry = 1;
2571 if (args.pin)
2572 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2574 if (args.options) {
2575 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2576 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2577 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2578 strcpy(the_pin, "q");
2580 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2581 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2582 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2585 do {
2586 if (retrycnt > 3)
2587 allowretry = 0;
2588 if (empty) {
2589 int i;
2590 struct ast_config *cfg;
2591 struct ast_variable *var;
2592 int confno_int;
2594 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2595 if ((empty_no_pin) || (!dynamic)) {
2596 cfg = ast_config_load(CONFIG_FILE_NAME);
2597 if (cfg) {
2598 var = ast_variable_browse(cfg, "rooms");
2599 while (var) {
2600 if (!strcasecmp(var->name, "conf")) {
2601 char *stringp = ast_strdupa(var->value);
2602 if (stringp) {
2603 char *confno_tmp = strsep(&stringp, "|,");
2604 int found = 0;
2605 if (!dynamic) {
2606 /* For static: run through the list and see if this conference is empty */
2607 AST_LIST_LOCK(&confs);
2608 AST_LIST_TRAVERSE(&confs, cnf, list) {
2609 if (!strcmp(confno_tmp, cnf->confno)) {
2610 /* The conference exists, therefore it's not empty */
2611 found = 1;
2612 break;
2615 AST_LIST_UNLOCK(&confs);
2616 if (!found) {
2617 /* At this point, we have a confno_tmp (static conference) that is empty */
2618 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2619 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2620 * Case 2: empty_no_pin and pin is blank (but not NULL)
2621 * Case 3: not empty_no_pin
2623 ast_copy_string(confno, confno_tmp, sizeof(confno));
2624 break;
2625 /* XXX the map is not complete (but we do have a confno) */
2631 var = var->next;
2633 ast_config_destroy(cfg);
2637 /* Select first conference number not in use */
2638 if (ast_strlen_zero(confno) && dynamic) {
2639 AST_LIST_LOCK(&confs);
2640 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
2641 if (!conf_map[i]) {
2642 snprintf(confno, sizeof(confno), "%d", i);
2643 conf_map[i] = 1;
2644 break;
2647 AST_LIST_UNLOCK(&confs);
2650 /* Not found? */
2651 if (ast_strlen_zero(confno)) {
2652 res = ast_streamfile(chan, "conf-noempty", chan->language);
2653 if (!res)
2654 ast_waitstream(chan, "");
2655 } else {
2656 if (sscanf(confno, "%d", &confno_int) == 1) {
2657 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2658 if (!res) {
2659 ast_waitstream(chan, "");
2660 res = ast_say_digits(chan, confno_int, "", chan->language);
2662 } else {
2663 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2668 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2669 /* Prompt user for conference number */
2670 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2671 if (res < 0) {
2672 /* Don't try to validate when we catch an error */
2673 confno[0] = '\0';
2674 allowretry = 0;
2675 break;
2678 if (!ast_strlen_zero(confno)) {
2679 /* Check the validity of the conference */
2680 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
2681 sizeof(the_pin), 1, &confflags);
2682 if (!cnf) {
2683 cnf = find_conf_realtime(chan, confno, 1, dynamic,
2684 the_pin, sizeof(the_pin), 1, &confflags);
2687 if (!cnf) {
2688 res = ast_streamfile(chan, "conf-invalid", chan->language);
2689 if (!res)
2690 ast_waitstream(chan, "");
2691 res = -1;
2692 if (allowretry)
2693 confno[0] = '\0';
2694 } else {
2695 if ((!ast_strlen_zero(cnf->pin) &&
2696 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2697 (!ast_strlen_zero(cnf->pinadmin) &&
2698 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2699 char pin[MAX_PIN] = "";
2700 int j;
2702 /* Allow the pin to be retried up to 3 times */
2703 for (j = 0; j < 3; j++) {
2704 if (*the_pin && (always_prompt == 0)) {
2705 ast_copy_string(pin, the_pin, sizeof(pin));
2706 res = 0;
2707 } else {
2708 /* Prompt user for pin if pin is required */
2709 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2711 if (res >= 0) {
2712 if (!strcasecmp(pin, cnf->pin) ||
2713 (!ast_strlen_zero(cnf->pinadmin) &&
2714 !strcasecmp(pin, cnf->pinadmin))) {
2715 /* Pin correct */
2716 allowretry = 0;
2717 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2718 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2719 /* Run the conference */
2720 res = conf_run(chan, cnf, confflags.flags, optargs);
2721 break;
2722 } else {
2723 /* Pin invalid */
2724 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
2725 res = ast_waitstream(chan, AST_DIGIT_ANY);
2726 ast_stopstream(chan);
2728 else {
2729 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2730 break;
2732 if (res < 0)
2733 break;
2734 pin[0] = res;
2735 pin[1] = '\0';
2736 res = -1;
2737 if (allowretry)
2738 confno[0] = '\0';
2740 } else {
2741 /* failed when getting the pin */
2742 res = -1;
2743 allowretry = 0;
2744 /* see if we need to get rid of the conference */
2745 break;
2748 /* Don't retry pin with a static pin */
2749 if (*the_pin && (always_prompt==0)) {
2750 break;
2753 } else {
2754 /* No pin required */
2755 allowretry = 0;
2757 /* Run the conference */
2758 res = conf_run(chan, cnf, confflags.flags, optargs);
2760 dispose_conf(cnf);
2761 cnf = NULL;
2764 } while (allowretry);
2766 if (cnf)
2767 dispose_conf(cnf);
2769 ast_module_user_remove(u);
2771 return res;
2774 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2776 struct ast_conf_user *user = NULL;
2777 int cid;
2779 sscanf(callerident, "%i", &cid);
2780 if (conf && callerident) {
2781 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2782 if (cid == user->user_no)
2783 return user;
2786 return NULL;
2789 /*! \brief The MeetMeadmin application */
2790 /* MeetMeAdmin(confno, command, caller) */
2791 static int admin_exec(struct ast_channel *chan, void *data) {
2792 char *params;
2793 struct ast_conference *cnf;
2794 struct ast_conf_user *user = NULL;
2795 struct ast_module_user *u;
2796 AST_DECLARE_APP_ARGS(args,
2797 AST_APP_ARG(confno);
2798 AST_APP_ARG(command);
2799 AST_APP_ARG(user);
2802 if (ast_strlen_zero(data)) {
2803 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
2804 return -1;
2807 u = ast_module_user_add(chan);
2809 AST_LIST_LOCK(&confs);
2811 params = ast_strdupa(data);
2812 AST_STANDARD_APP_ARGS(args, params);
2814 if (!args.command) {
2815 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2816 AST_LIST_UNLOCK(&confs);
2817 ast_module_user_remove(u);
2818 return -1;
2820 AST_LIST_TRAVERSE(&confs, cnf, list) {
2821 if (!strcmp(cnf->confno, args.confno))
2822 break;
2825 if (!cnf) {
2826 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
2827 AST_LIST_UNLOCK(&confs);
2828 ast_module_user_remove(u);
2829 return 0;
2832 ast_atomic_fetchadd_int(&cnf->refcount, 1);
2834 if (args.user)
2835 user = find_user(cnf, args.user);
2837 switch (*args.command) {
2838 case 76: /* L: Lock */
2839 cnf->locked = 1;
2840 break;
2841 case 108: /* l: Unlock */
2842 cnf->locked = 0;
2843 break;
2844 case 75: /* K: kick all users */
2845 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2846 user->adminflags |= ADMINFLAG_KICKME;
2847 break;
2848 case 101: /* e: Eject last user*/
2849 user = AST_LIST_LAST(&cnf->userlist);
2850 if (!(user->userflags & CONFFLAG_ADMIN))
2851 user->adminflags |= ADMINFLAG_KICKME;
2852 else
2853 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2854 break;
2855 case 77: /* M: Mute */
2856 if (user) {
2857 user->adminflags |= ADMINFLAG_MUTED;
2858 } else
2859 ast_log(LOG_NOTICE, "Specified User not found!\n");
2860 break;
2861 case 78: /* N: Mute all (non-admin) users */
2862 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2863 if (!(user->userflags & CONFFLAG_ADMIN))
2864 user->adminflags |= ADMINFLAG_MUTED;
2866 break;
2867 case 109: /* m: Unmute */
2868 if (user) {
2869 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2870 } else
2871 ast_log(LOG_NOTICE, "Specified User not found!\n");
2872 break;
2873 case 110: /* n: Unmute all users */
2874 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2875 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2876 break;
2877 case 107: /* k: Kick user */
2878 if (user)
2879 user->adminflags |= ADMINFLAG_KICKME;
2880 else
2881 ast_log(LOG_NOTICE, "Specified User not found!\n");
2882 break;
2883 case 118: /* v: Lower all users listen volume */
2884 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2885 tweak_listen_volume(user, VOL_DOWN);
2886 break;
2887 case 86: /* V: Raise all users listen volume */
2888 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2889 tweak_listen_volume(user, VOL_UP);
2890 break;
2891 case 115: /* s: Lower all users speaking volume */
2892 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2893 tweak_talk_volume(user, VOL_DOWN);
2894 break;
2895 case 83: /* S: Raise all users speaking volume */
2896 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2897 tweak_talk_volume(user, VOL_UP);
2898 break;
2899 case 82: /* R: Reset all volume levels */
2900 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2901 reset_volumes(user);
2902 break;
2903 case 114: /* r: Reset user's volume level */
2904 if (user)
2905 reset_volumes(user);
2906 else
2907 ast_log(LOG_NOTICE, "Specified User not found!\n");
2908 break;
2909 case 85: /* U: Raise user's listen volume */
2910 if (user)
2911 tweak_listen_volume(user, VOL_UP);
2912 else
2913 ast_log(LOG_NOTICE, "Specified User not found!\n");
2914 break;
2915 case 117: /* u: Lower user's listen volume */
2916 if (user)
2917 tweak_listen_volume(user, VOL_DOWN);
2918 else
2919 ast_log(LOG_NOTICE, "Specified User not found!\n");
2920 break;
2921 case 84: /* T: Raise user's talk volume */
2922 if (user)
2923 tweak_talk_volume(user, VOL_UP);
2924 else
2925 ast_log(LOG_NOTICE, "Specified User not found!\n");
2926 break;
2927 case 116: /* t: Lower user's talk volume */
2928 if (user)
2929 tweak_talk_volume(user, VOL_DOWN);
2930 else
2931 ast_log(LOG_NOTICE, "Specified User not found!\n");
2932 break;
2935 AST_LIST_UNLOCK(&confs);
2937 dispose_conf(cnf);
2939 ast_module_user_remove(u);
2941 return 0;
2944 static int meetmemute(struct mansession *s, const struct message *m, int mute)
2946 struct ast_conference *conf;
2947 struct ast_conf_user *user;
2948 const char *confid = astman_get_header(m, "Meetme");
2949 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
2950 int userno;
2952 if (ast_strlen_zero(confid)) {
2953 astman_send_error(s, m, "Meetme conference not specified");
2954 return 0;
2957 if (ast_strlen_zero(userid)) {
2958 astman_send_error(s, m, "Meetme user number not specified");
2959 return 0;
2962 userno = strtoul(userid, &userid, 10);
2964 if (*userid) {
2965 astman_send_error(s, m, "Invalid user number");
2966 return 0;
2969 /* Look in the conference list */
2970 AST_LIST_LOCK(&confs);
2971 AST_LIST_TRAVERSE(&confs, conf, list) {
2972 if (!strcmp(confid, conf->confno))
2973 break;
2976 if (!conf) {
2977 AST_LIST_UNLOCK(&confs);
2978 astman_send_error(s, m, "Meetme conference does not exist");
2979 return 0;
2982 AST_LIST_TRAVERSE(&conf->userlist, user, list)
2983 if (user->user_no == userno)
2984 break;
2986 if (!user) {
2987 AST_LIST_UNLOCK(&confs);
2988 astman_send_error(s, m, "User number not found");
2989 return 0;
2992 if (mute)
2993 user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
2994 else
2995 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED); /* request user unmuting */
2997 AST_LIST_UNLOCK(&confs);
2999 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);
3001 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
3002 return 0;
3005 static int action_meetmemute(struct mansession *s, const struct message *m)
3007 return meetmemute(s, m, 1);
3010 static int action_meetmeunmute(struct mansession *s, const struct message *m)
3012 return meetmemute(s, m, 0);
3015 static void *recordthread(void *args)
3017 struct ast_conference *cnf = args;
3018 struct ast_frame *f=NULL;
3019 int flags;
3020 struct ast_filestream *s=NULL;
3021 int res=0;
3022 int x;
3023 const char *oldrecordingfilename = NULL;
3025 if (!cnf || !cnf->lchan) {
3026 pthread_exit(0);
3029 ast_stopstream(cnf->lchan);
3030 flags = O_CREAT|O_TRUNC|O_WRONLY;
3033 cnf->recording = MEETME_RECORD_ACTIVE;
3034 while (ast_waitfor(cnf->lchan, -1) > -1) {
3035 if (cnf->recording == MEETME_RECORD_TERMINATE) {
3036 AST_LIST_LOCK(&confs);
3037 AST_LIST_UNLOCK(&confs);
3038 break;
3040 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
3041 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
3042 oldrecordingfilename = cnf->recordingfilename;
3045 f = ast_read(cnf->lchan);
3046 if (!f) {
3047 res = -1;
3048 break;
3050 if (f->frametype == AST_FRAME_VOICE) {
3051 ast_mutex_lock(&cnf->listenlock);
3052 for (x=0;x<AST_FRAME_BITS;x++) {
3053 /* Free any translations that have occured */
3054 if (cnf->transframe[x]) {
3055 ast_frfree(cnf->transframe[x]);
3056 cnf->transframe[x] = NULL;
3059 if (cnf->origframe)
3060 ast_frfree(cnf->origframe);
3061 cnf->origframe = ast_frdup(f);
3062 ast_mutex_unlock(&cnf->listenlock);
3063 if (s)
3064 res = ast_writestream(s, f);
3065 if (res) {
3066 ast_frfree(f);
3067 break;
3070 ast_frfree(f);
3072 cnf->recording = MEETME_RECORD_OFF;
3073 if (s)
3074 ast_closestream(s);
3076 pthread_exit(0);
3079 /*! \brief Callback for devicestate providers */
3080 static int meetmestate(const char *data)
3082 struct ast_conference *conf;
3084 /* Find conference */
3085 AST_LIST_LOCK(&confs);
3086 AST_LIST_TRAVERSE(&confs, conf, list) {
3087 if (!strcmp(data, conf->confno))
3088 break;
3090 AST_LIST_UNLOCK(&confs);
3091 if (!conf)
3092 return AST_DEVICE_INVALID;
3095 /* SKREP to fill */
3096 if (!conf->users)
3097 return AST_DEVICE_NOT_INUSE;
3099 return AST_DEVICE_INUSE;
3102 static void load_config_meetme(void)
3104 struct ast_config *cfg;
3105 const char *val;
3107 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3109 if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
3110 return;
3112 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
3113 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
3114 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
3115 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3116 } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
3117 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
3118 DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
3119 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3121 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
3122 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
3125 ast_config_destroy(cfg);
3128 /*! \brief Find an SLA trunk by name
3129 * \note This must be called with the sla_trunks container locked
3131 static struct sla_trunk *sla_find_trunk(const char *name)
3133 struct sla_trunk *trunk = NULL;
3135 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
3136 if (!strcasecmp(trunk->name, name))
3137 break;
3140 return trunk;
3143 /*! \brief Find an SLA station by name
3144 * \note This must be called with the sla_stations container locked
3146 static struct sla_station *sla_find_station(const char *name)
3148 struct sla_station *station = NULL;
3150 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
3151 if (!strcasecmp(station->name, name))
3152 break;
3155 return station;
3158 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
3159 const struct sla_station *station)
3161 struct sla_station_ref *station_ref;
3162 struct sla_trunk_ref *trunk_ref;
3164 /* For each station that has this call on hold, check for private hold. */
3165 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
3166 AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
3167 if (trunk_ref->trunk != trunk || station_ref->station == station)
3168 continue;
3169 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
3170 station_ref->station->hold_access == SLA_HOLD_PRIVATE)
3171 return 1;
3172 return 0;
3176 return 0;
3179 /*! \brief Find a trunk reference on a station by name
3180 * \param station the station
3181 * \param name the trunk's name
3182 * \return a pointer to the station's trunk reference. If the trunk
3183 * is not found, it is not idle and barge is disabled, or if
3184 * it is on hold and private hold is set, then NULL will be returned.
3186 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
3187 const char *name)
3189 struct sla_trunk_ref *trunk_ref = NULL;
3191 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3192 if (strcasecmp(trunk_ref->trunk->name, name))
3193 continue;
3195 if ( (trunk_ref->trunk->barge_disabled
3196 && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
3197 (trunk_ref->trunk->hold_stations
3198 && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
3199 && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
3200 sla_check_station_hold_access(trunk_ref->trunk, station) )
3202 trunk_ref = NULL;
3205 break;
3208 return trunk_ref;
3211 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
3213 struct sla_station_ref *station_ref;
3215 if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
3216 return NULL;
3218 station_ref->station = station;
3220 return station_ref;
3223 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
3225 struct sla_ringing_station *ringing_station;
3227 if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
3228 return NULL;
3230 ringing_station->station = station;
3231 ringing_station->ring_begin = ast_tvnow();
3233 return ringing_station;
3236 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
3237 enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
3239 struct sla_station *station;
3240 struct sla_trunk_ref *trunk_ref;
3242 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
3243 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3244 if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
3245 || trunk_ref == exclude)
3246 continue;
3247 trunk_ref->state = state;
3248 ast_device_state_changed("SLA:%s_%s", station->name, trunk->name);
3249 break;
3254 struct run_station_args {
3255 struct sla_station *station;
3256 struct sla_trunk_ref *trunk_ref;
3257 ast_mutex_t *cond_lock;
3258 ast_cond_t *cond;
3261 static void *run_station(void *data)
3263 struct sla_station *station;
3264 struct sla_trunk_ref *trunk_ref;
3265 char conf_name[MAX_CONFNUM];
3266 struct ast_flags conf_flags = { 0 };
3267 struct ast_conference *conf;
3270 struct run_station_args *args = data;
3271 station = args->station;
3272 trunk_ref = args->trunk_ref;
3273 ast_mutex_lock(args->cond_lock);
3274 ast_cond_signal(args->cond);
3275 ast_mutex_unlock(args->cond_lock);
3276 /* args is no longer valid here. */
3279 ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
3280 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
3281 ast_set_flag(&conf_flags,
3282 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
3283 ast_answer(trunk_ref->chan);
3284 conf = build_conf(conf_name, "", "", 0, 0, 1);
3285 if (conf) {
3286 conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
3287 dispose_conf(conf);
3288 conf = NULL;
3290 trunk_ref->chan = NULL;
3291 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
3292 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
3293 strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
3294 admin_exec(NULL, conf_name);
3295 trunk_ref->trunk->hold_stations = 0;
3296 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
3299 ast_dial_join(station->dial);
3300 ast_dial_destroy(station->dial);
3301 station->dial = NULL;
3303 return NULL;
3306 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
3308 char buf[80];
3309 struct sla_station_ref *station_ref;
3311 snprintf(buf, sizeof(buf), "SLA_%s|K", ringing_trunk->trunk->name);
3312 admin_exec(NULL, buf);
3313 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
3315 while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
3316 free(station_ref);
3318 free(ringing_trunk);
3321 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
3322 enum sla_station_hangup hangup)
3324 struct sla_ringing_trunk *ringing_trunk;
3325 struct sla_trunk_ref *trunk_ref;
3326 struct sla_station_ref *station_ref;
3328 ast_dial_join(ringing_station->station->dial);
3329 ast_dial_destroy(ringing_station->station->dial);
3330 ringing_station->station->dial = NULL;
3332 if (hangup == SLA_STATION_HANGUP_NORMAL)
3333 goto done;
3335 /* If the station is being hung up because of a timeout, then add it to the
3336 * list of timed out stations on each of the ringing trunks. This is so
3337 * that when doing further processing to figure out which stations should be
3338 * ringing, which trunk to answer, determining timeouts, etc., we know which
3339 * ringing trunks we should ignore. */
3340 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3341 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3342 if (ringing_trunk->trunk == trunk_ref->trunk)
3343 break;
3345 if (!trunk_ref)
3346 continue;
3347 if (!(station_ref = sla_create_station_ref(ringing_station->station)))
3348 continue;
3349 AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
3352 done:
3353 free(ringing_station);
3356 static void sla_dial_state_callback(struct ast_dial *dial)
3358 sla_queue_event(SLA_EVENT_DIAL_STATE);
3361 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
3362 * \note Assumes sla.lock is locked
3364 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
3365 const struct sla_station *station)
3367 struct sla_station_ref *timed_out_station;
3369 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
3370 if (station == timed_out_station->station)
3371 return 1;
3374 return 0;
3377 /*! \brief Choose the highest priority ringing trunk for a station
3378 * \param station the station
3379 * \param remove remove the ringing trunk once selected
3380 * \param trunk_ref a place to store the pointer to this stations reference to
3381 * the selected trunk
3382 * \return a pointer to the selected ringing trunk, or NULL if none found
3383 * \note Assumes that sla.lock is locked
3385 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
3386 struct sla_trunk_ref **trunk_ref, int remove)
3388 struct sla_trunk_ref *s_trunk_ref;
3389 struct sla_ringing_trunk *ringing_trunk = NULL;
3391 AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
3392 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
3393 /* Make sure this is the trunk we're looking for */
3394 if (s_trunk_ref->trunk != ringing_trunk->trunk)
3395 continue;
3397 /* This trunk on the station is ringing. But, make sure this station
3398 * didn't already time out while this trunk was ringing. */
3399 if (sla_check_timed_out_station(ringing_trunk, station))
3400 continue;
3402 if (remove)
3403 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
3405 if (trunk_ref)
3406 *trunk_ref = s_trunk_ref;
3408 break;
3410 AST_LIST_TRAVERSE_SAFE_END
3412 if (ringing_trunk)
3413 break;
3416 return ringing_trunk;
3419 static void sla_handle_dial_state_event(void)
3421 struct sla_ringing_station *ringing_station;
3423 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3424 struct sla_trunk_ref *s_trunk_ref = NULL;
3425 struct sla_ringing_trunk *ringing_trunk = NULL;
3426 struct run_station_args args;
3427 enum ast_dial_result dial_res;
3428 pthread_attr_t attr;
3429 pthread_t dont_care;
3430 ast_mutex_t cond_lock;
3431 ast_cond_t cond;
3433 switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
3434 case AST_DIAL_RESULT_HANGUP:
3435 case AST_DIAL_RESULT_INVALID:
3436 case AST_DIAL_RESULT_FAILED:
3437 case AST_DIAL_RESULT_TIMEOUT:
3438 case AST_DIAL_RESULT_UNANSWERED:
3439 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3440 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
3441 break;
3442 case AST_DIAL_RESULT_ANSWERED:
3443 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3444 /* Find the appropriate trunk to answer. */
3445 ast_mutex_lock(&sla.lock);
3446 ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
3447 ast_mutex_unlock(&sla.lock);
3448 if (!ringing_trunk) {
3449 ast_log(LOG_DEBUG, "Found no ringing trunk for station '%s' to answer!\n",
3450 ringing_station->station->name);
3451 break;
3453 /* Track the channel that answered this trunk */
3454 s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
3455 /* Actually answer the trunk */
3456 ast_answer(ringing_trunk->trunk->chan);
3457 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
3458 /* Now, start a thread that will connect this station to the trunk. The rest of
3459 * the code here sets up the thread and ensures that it is able to save the arguments
3460 * before they are no longer valid since they are allocated on the stack. */
3461 args.trunk_ref = s_trunk_ref;
3462 args.station = ringing_station->station;
3463 args.cond = &cond;
3464 args.cond_lock = &cond_lock;
3465 free(ringing_trunk);
3466 free(ringing_station);
3467 ast_mutex_init(&cond_lock);
3468 ast_cond_init(&cond, NULL);
3469 pthread_attr_init(&attr);
3470 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
3471 ast_mutex_lock(&cond_lock);
3472 ast_pthread_create_background(&dont_care, &attr, run_station, &args);
3473 ast_cond_wait(&cond, &cond_lock);
3474 ast_mutex_unlock(&cond_lock);
3475 ast_mutex_destroy(&cond_lock);
3476 ast_cond_destroy(&cond);
3477 pthread_attr_destroy(&attr);
3478 break;
3479 case AST_DIAL_RESULT_TRYING:
3480 case AST_DIAL_RESULT_RINGING:
3481 case AST_DIAL_RESULT_PROGRESS:
3482 case AST_DIAL_RESULT_PROCEEDING:
3483 break;
3485 if (dial_res == AST_DIAL_RESULT_ANSWERED) {
3486 /* Queue up reprocessing ringing trunks, and then ringing stations again */
3487 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
3488 sla_queue_event(SLA_EVENT_DIAL_STATE);
3489 break;
3492 AST_LIST_TRAVERSE_SAFE_END
3495 /*! \brief Check to see if this station is already ringing
3496 * \note Assumes sla.lock is locked
3498 static int sla_check_ringing_station(const struct sla_station *station)
3500 struct sla_ringing_station *ringing_station;
3502 AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
3503 if (station == ringing_station->station)
3504 return 1;
3507 return 0;
3510 /*! \brief Check to see if this station has failed to be dialed in the past minute
3511 * \note assumes sla.lock is locked
3513 static int sla_check_failed_station(const struct sla_station *station)
3515 struct sla_failed_station *failed_station;
3516 int res = 0;
3518 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
3519 if (station != failed_station->station)
3520 continue;
3521 if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
3522 AST_LIST_REMOVE_CURRENT(&sla.failed_stations, entry);
3523 free(failed_station);
3524 break;
3526 res = 1;
3528 AST_LIST_TRAVERSE_SAFE_END
3530 return res;
3533 /*! \brief Ring a station
3534 * \note Assumes sla.lock is locked
3536 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
3538 char *tech, *tech_data;
3539 struct ast_dial *dial;
3540 struct sla_ringing_station *ringing_station;
3541 const char *cid_name = NULL, *cid_num = NULL;
3542 enum ast_dial_result res;
3544 if (!(dial = ast_dial_create()))
3545 return -1;
3547 ast_dial_set_state_callback(dial, sla_dial_state_callback);
3548 tech_data = ast_strdupa(station->device);
3549 tech = strsep(&tech_data, "/");
3551 if (ast_dial_append(dial, tech, tech_data) == -1) {
3552 ast_dial_destroy(dial);
3553 return -1;
3556 if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_name)) {
3557 cid_name = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_name);
3558 free(ringing_trunk->trunk->chan->cid.cid_name);
3559 ringing_trunk->trunk->chan->cid.cid_name = NULL;
3561 if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_num)) {
3562 cid_num = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_num);
3563 free(ringing_trunk->trunk->chan->cid.cid_num);
3564 ringing_trunk->trunk->chan->cid.cid_num = NULL;
3567 res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
3569 if (cid_name)
3570 ringing_trunk->trunk->chan->cid.cid_name = ast_strdup(cid_name);
3571 if (cid_num)
3572 ringing_trunk->trunk->chan->cid.cid_num = ast_strdup(cid_num);
3574 if (res != AST_DIAL_RESULT_TRYING) {
3575 struct sla_failed_station *failed_station;
3576 ast_dial_destroy(dial);
3577 if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
3578 return -1;
3579 failed_station->station = station;
3580 failed_station->last_try = ast_tvnow();
3581 AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
3582 return -1;
3584 if (!(ringing_station = sla_create_ringing_station(station))) {
3585 ast_dial_join(dial);
3586 ast_dial_destroy(dial);
3587 return -1;
3590 station->dial = dial;
3592 AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
3594 return 0;
3597 /*! \brief Check to see if a station is in use
3599 static int sla_check_inuse_station(const struct sla_station *station)
3601 struct sla_trunk_ref *trunk_ref;
3603 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3604 if (trunk_ref->chan)
3605 return 1;
3608 return 0;
3611 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
3612 const struct sla_trunk *trunk)
3614 struct sla_trunk_ref *trunk_ref = NULL;
3616 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3617 if (trunk_ref->trunk == trunk)
3618 break;
3621 return trunk_ref;
3624 /*! \brief Calculate the ring delay for a given ringing trunk on a station
3625 * \param station the station
3626 * \param trunk the trunk. If NULL, the highest priority ringing trunk will be used
3627 * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
3629 static int sla_check_station_delay(struct sla_station *station,
3630 struct sla_ringing_trunk *ringing_trunk)
3632 struct sla_trunk_ref *trunk_ref;
3633 unsigned int delay = UINT_MAX;
3634 int time_left, time_elapsed;
3636 if (!ringing_trunk)
3637 ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
3638 else
3639 trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
3641 if (!ringing_trunk || !trunk_ref)
3642 return delay;
3644 /* If this station has a ring delay specific to the highest priority
3645 * ringing trunk, use that. Otherwise, use the ring delay specified
3646 * globally for the station. */
3647 delay = trunk_ref->ring_delay;
3648 if (!delay)
3649 delay = station->ring_delay;
3650 if (!delay)
3651 return INT_MAX;
3653 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3654 time_left = (delay * 1000) - time_elapsed;
3656 return time_left;
3659 /*! \brief Ring stations based on current set of ringing trunks
3660 * \note Assumes that sla.lock is locked
3662 static void sla_ring_stations(void)
3664 struct sla_station_ref *station_ref;
3665 struct sla_ringing_trunk *ringing_trunk;
3667 /* Make sure that every station that uses at least one of the ringing
3668 * trunks, is ringing. */
3669 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3670 AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
3671 int time_left;
3673 /* Is this station already ringing? */
3674 if (sla_check_ringing_station(station_ref->station))
3675 continue;
3677 /* Is this station already in a call? */
3678 if (sla_check_inuse_station(station_ref->station))
3679 continue;
3681 /* Did we fail to dial this station earlier? If so, has it been
3682 * a minute since we tried? */
3683 if (sla_check_failed_station(station_ref->station))
3684 continue;
3686 /* If this station already timed out while this trunk was ringing,
3687 * do not dial it again for this ringing trunk. */
3688 if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
3689 continue;
3691 /* Check for a ring delay in progress */
3692 time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
3693 if (time_left != INT_MAX && time_left > 0)
3694 continue;
3696 /* It is time to make this station begin to ring. Do it! */
3697 sla_ring_station(ringing_trunk, station_ref->station);
3700 /* Now, all of the stations that should be ringing, are ringing. */
3703 static void sla_hangup_stations(void)
3705 struct sla_trunk_ref *trunk_ref;
3706 struct sla_ringing_station *ringing_station;
3708 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3709 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3710 struct sla_ringing_trunk *ringing_trunk;
3711 ast_mutex_lock(&sla.lock);
3712 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3713 if (trunk_ref->trunk == ringing_trunk->trunk)
3714 break;
3716 ast_mutex_unlock(&sla.lock);
3717 if (ringing_trunk)
3718 break;
3720 if (!trunk_ref) {
3721 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3722 ast_dial_join(ringing_station->station->dial);
3723 ast_dial_destroy(ringing_station->station->dial);
3724 ringing_station->station->dial = NULL;
3725 free(ringing_station);
3728 AST_LIST_TRAVERSE_SAFE_END
3731 static void sla_handle_ringing_trunk_event(void)
3733 ast_mutex_lock(&sla.lock);
3734 sla_ring_stations();
3735 ast_mutex_unlock(&sla.lock);
3737 /* Find stations that shouldn't be ringing anymore. */
3738 sla_hangup_stations();
3741 static void sla_handle_hold_event(struct sla_event *event)
3743 ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
3744 event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
3745 ast_device_state_changed("SLA:%s_%s",
3746 event->station->name, event->trunk_ref->trunk->name);
3747 sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
3748 INACTIVE_TRUNK_REFS, event->trunk_ref);
3750 if (event->trunk_ref->trunk->active_stations == 1) {
3751 /* The station putting it on hold is the only one on the call, so start
3752 * Music on hold to the trunk. */
3753 event->trunk_ref->trunk->on_hold = 1;
3754 ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
3757 ast_softhangup(event->trunk_ref->chan, AST_CAUSE_NORMAL);
3758 event->trunk_ref->chan = NULL;
3761 /*! \brief Process trunk ring timeouts
3762 * \note Called with sla.lock locked
3763 * \return non-zero if a change to the ringing trunks was made
3765 static int sla_calc_trunk_timeouts(unsigned int *timeout)
3767 struct sla_ringing_trunk *ringing_trunk;
3768 int res = 0;
3770 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
3771 int time_left, time_elapsed;
3772 if (!ringing_trunk->trunk->ring_timeout)
3773 continue;
3774 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3775 time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
3776 if (time_left <= 0) {
3777 pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
3778 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
3779 sla_stop_ringing_trunk(ringing_trunk);
3780 res = 1;
3781 continue;
3783 if (time_left < *timeout)
3784 *timeout = time_left;
3786 AST_LIST_TRAVERSE_SAFE_END
3788 return res;
3791 /*! \brief Process station ring timeouts
3792 * \note Called with sla.lock locked
3793 * \return non-zero if a change to the ringing stations was made
3795 static int sla_calc_station_timeouts(unsigned int *timeout)
3797 struct sla_ringing_trunk *ringing_trunk;
3798 struct sla_ringing_station *ringing_station;
3799 int res = 0;
3801 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3802 unsigned int ring_timeout = 0;
3803 int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
3804 struct sla_trunk_ref *trunk_ref;
3806 /* If there are any ring timeouts specified for a specific trunk
3807 * on the station, then use the highest per-trunk ring timeout.
3808 * Otherwise, use the ring timeout set for the entire station. */
3809 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3810 struct sla_station_ref *station_ref;
3811 int trunk_time_elapsed, trunk_time_left;
3813 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3814 if (ringing_trunk->trunk == trunk_ref->trunk)
3815 break;
3817 if (!ringing_trunk)
3818 continue;
3820 /* If there is a trunk that is ringing without a timeout, then the
3821 * only timeout that could matter is a global station ring timeout. */
3822 if (!trunk_ref->ring_timeout)
3823 break;
3825 /* This trunk on this station is ringing and has a timeout.
3826 * However, make sure this trunk isn't still ringing from a
3827 * previous timeout. If so, don't consider it. */
3828 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
3829 if (station_ref->station == ringing_station->station)
3830 break;
3832 if (station_ref)
3833 continue;
3835 trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3836 trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
3837 if (trunk_time_left > final_trunk_time_left)
3838 final_trunk_time_left = trunk_time_left;
3841 /* No timeout was found for ringing trunks, and no timeout for the entire station */
3842 if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
3843 continue;
3845 /* Compute how much time is left for a global station timeout */
3846 if (ringing_station->station->ring_timeout) {
3847 ring_timeout = ringing_station->station->ring_timeout;
3848 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
3849 time_left = (ring_timeout * 1000) - time_elapsed;
3852 /* If the time left based on the per-trunk timeouts is smaller than the
3853 * global station ring timeout, use that. */
3854 if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
3855 time_left = final_trunk_time_left;
3857 /* If there is no time left, the station needs to stop ringing */
3858 if (time_left <= 0) {
3859 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3860 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
3861 res = 1;
3862 continue;
3865 /* There is still some time left for this station to ring, so save that
3866 * timeout if it is the first event scheduled to occur */
3867 if (time_left < *timeout)
3868 *timeout = time_left;
3870 AST_LIST_TRAVERSE_SAFE_END
3872 return res;
3875 /*! \brief Calculate the ring delay for a station
3876 * \note Assumes sla.lock is locked
3878 static int sla_calc_station_delays(unsigned int *timeout)
3880 struct sla_station *station;
3881 int res = 0;
3883 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
3884 struct sla_ringing_trunk *ringing_trunk;
3885 int time_left;
3887 /* Ignore stations already ringing */
3888 if (sla_check_ringing_station(station))
3889 continue;
3891 /* Ignore stations already on a call */
3892 if (sla_check_inuse_station(station))
3893 continue;
3895 /* Ignore stations that don't have one of their trunks ringing */
3896 if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
3897 continue;
3899 if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
3900 continue;
3902 /* If there is no time left, then the station needs to start ringing.
3903 * Return non-zero so that an event will be queued up an event to
3904 * make that happen. */
3905 if (time_left <= 0) {
3906 res = 1;
3907 continue;
3910 if (time_left < *timeout)
3911 *timeout = time_left;
3914 return res;
3917 /*! \brief Calculate the time until the next known event
3918 * \note Called with sla.lock locked */
3919 static int sla_process_timers(struct timespec *ts)
3921 unsigned int timeout = UINT_MAX;
3922 struct timeval tv;
3923 unsigned int change_made = 0;
3925 /* Check for ring timeouts on ringing trunks */
3926 if (sla_calc_trunk_timeouts(&timeout))
3927 change_made = 1;
3929 /* Check for ring timeouts on ringing stations */
3930 if (sla_calc_station_timeouts(&timeout))
3931 change_made = 1;
3933 /* Check for station ring delays */
3934 if (sla_calc_station_delays(&timeout))
3935 change_made = 1;
3937 /* queue reprocessing of ringing trunks */
3938 if (change_made)
3939 sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
3941 /* No timeout */
3942 if (timeout == UINT_MAX)
3943 return 0;
3945 if (ts) {
3946 tv = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
3947 ts->tv_sec = tv.tv_sec;
3948 ts->tv_nsec = tv.tv_usec * 1000;
3951 return 1;
3954 static void *sla_thread(void *data)
3956 struct sla_failed_station *failed_station;
3957 struct sla_ringing_station *ringing_station;
3959 ast_mutex_lock(&sla.lock);
3961 while (!sla.stop) {
3962 struct sla_event *event;
3963 struct timespec ts = { 0, };
3964 unsigned int have_timeout = 0;
3966 if (AST_LIST_EMPTY(&sla.event_q)) {
3967 if ((have_timeout = sla_process_timers(&ts)))
3968 ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
3969 else
3970 ast_cond_wait(&sla.cond, &sla.lock);
3971 if (sla.stop)
3972 break;
3975 if (have_timeout)
3976 sla_process_timers(NULL);
3978 while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
3979 ast_mutex_unlock(&sla.lock);
3980 switch (event->type) {
3981 case SLA_EVENT_HOLD:
3982 sla_handle_hold_event(event);
3983 break;
3984 case SLA_EVENT_DIAL_STATE:
3985 sla_handle_dial_state_event();
3986 break;
3987 case SLA_EVENT_RINGING_TRUNK:
3988 sla_handle_ringing_trunk_event();
3989 break;
3991 free(event);
3992 ast_mutex_lock(&sla.lock);
3996 ast_mutex_unlock(&sla.lock);
3998 while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
3999 free(ringing_station);
4001 while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
4002 free(failed_station);
4004 return NULL;
4007 struct dial_trunk_args {
4008 struct sla_trunk_ref *trunk_ref;
4009 struct sla_station *station;
4010 ast_mutex_t *cond_lock;
4011 ast_cond_t *cond;
4014 static void *dial_trunk(void *data)
4016 struct dial_trunk_args *args = data;
4017 struct ast_dial *dial;
4018 char *tech, *tech_data;
4019 enum ast_dial_result dial_res;
4020 char conf_name[MAX_CONFNUM];
4021 struct ast_conference *conf;
4022 struct ast_flags conf_flags = { 0 };
4023 struct sla_trunk_ref *trunk_ref = args->trunk_ref;
4024 const char *cid_name = NULL, *cid_num = NULL;
4026 if (!(dial = ast_dial_create())) {
4027 ast_mutex_lock(args->cond_lock);
4028 ast_cond_signal(args->cond);
4029 ast_mutex_unlock(args->cond_lock);
4030 return NULL;
4033 tech_data = ast_strdupa(trunk_ref->trunk->device);
4034 tech = strsep(&tech_data, "/");
4035 if (ast_dial_append(dial, tech, tech_data) == -1) {
4036 ast_mutex_lock(args->cond_lock);
4037 ast_cond_signal(args->cond);
4038 ast_mutex_unlock(args->cond_lock);
4039 ast_dial_destroy(dial);
4040 return NULL;
4043 if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_name)) {
4044 cid_name = ast_strdupa(trunk_ref->chan->cid.cid_name);
4045 free(trunk_ref->chan->cid.cid_name);
4046 trunk_ref->chan->cid.cid_name = NULL;
4048 if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_num)) {
4049 cid_num = ast_strdupa(trunk_ref->chan->cid.cid_num);
4050 free(trunk_ref->chan->cid.cid_num);
4051 trunk_ref->chan->cid.cid_num = NULL;
4054 dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
4056 if (cid_name)
4057 trunk_ref->chan->cid.cid_name = ast_strdup(cid_name);
4058 if (cid_num)
4059 trunk_ref->chan->cid.cid_num = ast_strdup(cid_num);
4061 if (dial_res != AST_DIAL_RESULT_TRYING) {
4062 ast_mutex_lock(args->cond_lock);
4063 ast_cond_signal(args->cond);
4064 ast_mutex_unlock(args->cond_lock);
4065 ast_dial_destroy(dial);
4066 return NULL;
4069 for (;;) {
4070 unsigned int done = 0;
4071 switch ((dial_res = ast_dial_state(dial))) {
4072 case AST_DIAL_RESULT_ANSWERED:
4073 trunk_ref->trunk->chan = ast_dial_answered(dial);
4074 case AST_DIAL_RESULT_HANGUP:
4075 case AST_DIAL_RESULT_INVALID:
4076 case AST_DIAL_RESULT_FAILED:
4077 case AST_DIAL_RESULT_TIMEOUT:
4078 case AST_DIAL_RESULT_UNANSWERED:
4079 done = 1;
4080 case AST_DIAL_RESULT_TRYING:
4081 case AST_DIAL_RESULT_RINGING:
4082 case AST_DIAL_RESULT_PROGRESS:
4083 case AST_DIAL_RESULT_PROCEEDING:
4084 break;
4086 if (done)
4087 break;
4090 if (!trunk_ref->trunk->chan) {
4091 ast_mutex_lock(args->cond_lock);
4092 ast_cond_signal(args->cond);
4093 ast_mutex_unlock(args->cond_lock);
4094 ast_dial_join(dial);
4095 ast_dial_destroy(dial);
4096 return NULL;
4099 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
4100 ast_set_flag(&conf_flags,
4101 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
4102 CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
4103 conf = build_conf(conf_name, "", "", 1, 1, 1);
4105 ast_mutex_lock(args->cond_lock);
4106 ast_cond_signal(args->cond);
4107 ast_mutex_unlock(args->cond_lock);
4109 if (conf) {
4110 conf_run(trunk_ref->trunk->chan, conf, conf_flags.flags, NULL);
4111 dispose_conf(conf);
4112 conf = NULL;
4115 /* If the trunk is going away, it is definitely now IDLE. */
4116 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4118 trunk_ref->trunk->chan = NULL;
4119 trunk_ref->trunk->on_hold = 0;
4121 ast_dial_join(dial);
4122 ast_dial_destroy(dial);
4124 return NULL;
4127 /*! \brief For a given station, choose the highest priority idle trunk
4129 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
4131 struct sla_trunk_ref *trunk_ref = NULL;
4133 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4134 if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
4135 break;
4138 return trunk_ref;
4141 static int sla_station_exec(struct ast_channel *chan, void *data)
4143 char *station_name, *trunk_name;
4144 struct sla_station *station;
4145 struct sla_trunk_ref *trunk_ref = NULL;
4146 char conf_name[MAX_CONFNUM];
4147 struct ast_flags conf_flags = { 0 };
4148 struct ast_conference *conf;
4150 if (ast_strlen_zero(data)) {
4151 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
4152 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4153 return 0;
4156 trunk_name = ast_strdupa(data);
4157 station_name = strsep(&trunk_name, "_");
4159 if (ast_strlen_zero(station_name)) {
4160 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
4161 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4162 return 0;
4165 AST_RWLIST_RDLOCK(&sla_stations);
4166 station = sla_find_station(station_name);
4167 AST_RWLIST_UNLOCK(&sla_stations);
4169 if (!station) {
4170 ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
4171 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4172 return 0;
4175 AST_RWLIST_RDLOCK(&sla_trunks);
4176 if (!ast_strlen_zero(trunk_name)) {
4177 trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
4178 } else
4179 trunk_ref = sla_choose_idle_trunk(station);
4180 AST_RWLIST_UNLOCK(&sla_trunks);
4182 if (!trunk_ref) {
4183 if (ast_strlen_zero(trunk_name))
4184 ast_log(LOG_NOTICE, "No trunks available for call.\n");
4185 else {
4186 ast_log(LOG_NOTICE, "Can't join existing call on trunk "
4187 "'%s' due to access controls.\n", trunk_name);
4189 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
4190 return 0;
4193 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
4194 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
4195 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4196 else {
4197 trunk_ref->state = SLA_TRUNK_STATE_UP;
4198 ast_device_state_changed("SLA:%s_%s", station->name, trunk_ref->trunk->name);
4200 } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
4201 struct sla_ringing_trunk *ringing_trunk;
4203 ast_mutex_lock(&sla.lock);
4204 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
4205 if (ringing_trunk->trunk == trunk_ref->trunk) {
4206 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
4207 break;
4210 AST_LIST_TRAVERSE_SAFE_END
4211 ast_mutex_unlock(&sla.lock);
4213 if (ringing_trunk) {
4214 ast_answer(ringing_trunk->trunk->chan);
4215 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4217 free(ringing_trunk);
4219 /* Queue up reprocessing ringing trunks, and then ringing stations again */
4220 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4221 sla_queue_event(SLA_EVENT_DIAL_STATE);
4225 trunk_ref->chan = chan;
4227 if (!trunk_ref->trunk->chan) {
4228 ast_mutex_t cond_lock;
4229 ast_cond_t cond;
4230 pthread_t dont_care;
4231 pthread_attr_t attr;
4232 struct dial_trunk_args args = {
4233 .trunk_ref = trunk_ref,
4234 .station = station,
4235 .cond_lock = &cond_lock,
4236 .cond = &cond,
4238 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4239 /* Create a thread to dial the trunk and dump it into the conference.
4240 * However, we want to wait until the trunk has been dialed and the
4241 * conference is created before continuing on here. */
4242 ast_autoservice_start(chan);
4243 ast_mutex_init(&cond_lock);
4244 ast_cond_init(&cond, NULL);
4245 pthread_attr_init(&attr);
4246 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
4247 ast_mutex_lock(&cond_lock);
4248 ast_pthread_create_background(&dont_care, &attr, dial_trunk, &args);
4249 ast_cond_wait(&cond, &cond_lock);
4250 ast_mutex_unlock(&cond_lock);
4251 ast_mutex_destroy(&cond_lock);
4252 ast_cond_destroy(&cond);
4253 pthread_attr_destroy(&attr);
4254 ast_autoservice_stop(chan);
4255 if (!trunk_ref->trunk->chan) {
4256 ast_log(LOG_DEBUG, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
4257 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
4258 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4259 trunk_ref->chan = NULL;
4260 return 0;
4264 if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
4265 trunk_ref->trunk->on_hold) {
4266 trunk_ref->trunk->on_hold = 0;
4267 ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
4268 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4271 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
4272 ast_set_flag(&conf_flags,
4273 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
4274 ast_answer(chan);
4275 conf = build_conf(conf_name, "", "", 0, 0, 1);
4276 if (conf) {
4277 conf_run(chan, conf, conf_flags.flags, NULL);
4278 dispose_conf(conf);
4279 conf = NULL;
4281 trunk_ref->chan = NULL;
4282 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
4283 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
4284 strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
4285 admin_exec(NULL, conf_name);
4286 trunk_ref->trunk->hold_stations = 0;
4287 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4290 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
4292 return 0;
4295 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
4297 struct sla_trunk_ref *trunk_ref;
4299 if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
4300 return NULL;
4302 trunk_ref->trunk = trunk;
4304 return trunk_ref;
4307 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
4309 struct sla_ringing_trunk *ringing_trunk;
4311 if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
4312 return NULL;
4314 ringing_trunk->trunk = trunk;
4315 ringing_trunk->ring_begin = ast_tvnow();
4317 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
4319 ast_mutex_lock(&sla.lock);
4320 AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
4321 ast_mutex_unlock(&sla.lock);
4323 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4325 return ringing_trunk;
4328 static int sla_trunk_exec(struct ast_channel *chan, void *data)
4330 const char *trunk_name = data;
4331 char conf_name[MAX_CONFNUM];
4332 struct ast_conference *conf;
4333 struct ast_flags conf_flags = { 0 };
4334 struct sla_trunk *trunk;
4335 struct sla_ringing_trunk *ringing_trunk;
4337 AST_RWLIST_RDLOCK(&sla_trunks);
4338 trunk = sla_find_trunk(trunk_name);
4339 AST_RWLIST_UNLOCK(&sla_trunks);
4340 if (!trunk) {
4341 ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", trunk_name);
4342 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4343 return 0;
4345 if (trunk->chan) {
4346 ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
4347 trunk_name);
4348 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4349 return 0;
4351 trunk->chan = chan;
4353 if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
4354 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4355 return 0;
4358 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_name);
4359 conf = build_conf(conf_name, "", "", 1, 1, 1);
4360 if (!conf) {
4361 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4362 return 0;
4364 ast_set_flag(&conf_flags,
4365 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF);
4366 ast_indicate(chan, AST_CONTROL_RINGING);
4367 conf_run(chan, conf, conf_flags.flags, NULL);
4368 dispose_conf(conf);
4369 conf = NULL;
4370 trunk->chan = NULL;
4371 trunk->on_hold = 0;
4373 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4375 if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
4376 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
4378 /* Remove the entry from the list of ringing trunks if it is still there. */
4379 ast_mutex_lock(&sla.lock);
4380 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
4381 if (ringing_trunk->trunk == trunk) {
4382 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
4383 break;
4386 AST_LIST_TRAVERSE_SAFE_END
4387 ast_mutex_unlock(&sla.lock);
4388 if (ringing_trunk) {
4389 free(ringing_trunk);
4390 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
4391 /* Queue reprocessing of ringing trunks to make stations stop ringing
4392 * that shouldn't be ringing after this trunk stopped. */
4393 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4396 return 0;
4399 static int sla_state(const char *data)
4401 char *buf, *station_name, *trunk_name;
4402 struct sla_station *station;
4403 struct sla_trunk_ref *trunk_ref;
4404 int res = AST_DEVICE_INVALID;
4406 trunk_name = buf = ast_strdupa(data);
4407 station_name = strsep(&trunk_name, "_");
4409 AST_RWLIST_RDLOCK(&sla_stations);
4410 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
4411 if (strcasecmp(station_name, station->name))
4412 continue;
4413 AST_RWLIST_RDLOCK(&sla_trunks);
4414 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4415 if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
4416 break;
4418 if (!trunk_ref) {
4419 AST_RWLIST_UNLOCK(&sla_trunks);
4420 break;
4422 switch (trunk_ref->state) {
4423 case SLA_TRUNK_STATE_IDLE:
4424 res = AST_DEVICE_NOT_INUSE;
4425 break;
4426 case SLA_TRUNK_STATE_RINGING:
4427 res = AST_DEVICE_RINGING;
4428 break;
4429 case SLA_TRUNK_STATE_UP:
4430 res = AST_DEVICE_INUSE;
4431 break;
4432 case SLA_TRUNK_STATE_ONHOLD:
4433 case SLA_TRUNK_STATE_ONHOLD_BYME:
4434 res = AST_DEVICE_ONHOLD;
4435 break;
4437 AST_RWLIST_UNLOCK(&sla_trunks);
4439 AST_RWLIST_UNLOCK(&sla_stations);
4441 if (res == AST_DEVICE_INVALID) {
4442 ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
4443 trunk_name, station_name);
4446 return res;
4449 static void destroy_trunk(struct sla_trunk *trunk)
4451 struct sla_station_ref *station_ref;
4453 if (!ast_strlen_zero(trunk->autocontext))
4454 ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
4456 while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
4457 free(station_ref);
4459 ast_string_field_free_memory(trunk);
4460 free(trunk);
4463 static void destroy_station(struct sla_station *station)
4465 struct sla_trunk_ref *trunk_ref;
4467 if (!ast_strlen_zero(station->autocontext)) {
4468 AST_RWLIST_RDLOCK(&sla_trunks);
4469 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4470 char exten[AST_MAX_EXTENSION];
4471 char hint[AST_MAX_APP];
4472 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
4473 snprintf(hint, sizeof(hint), "SLA:%s", exten);
4474 ast_context_remove_extension(station->autocontext, exten,
4475 1, sla_registrar);
4476 ast_context_remove_extension(station->autocontext, hint,
4477 PRIORITY_HINT, sla_registrar);
4479 AST_RWLIST_UNLOCK(&sla_trunks);
4482 while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
4483 free(trunk_ref);
4485 ast_string_field_free_memory(station);
4486 free(station);
4489 static void sla_destroy(void)
4491 struct sla_trunk *trunk;
4492 struct sla_station *station;
4494 AST_RWLIST_WRLOCK(&sla_trunks);
4495 while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
4496 destroy_trunk(trunk);
4497 AST_RWLIST_UNLOCK(&sla_trunks);
4499 AST_RWLIST_WRLOCK(&sla_stations);
4500 while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
4501 destroy_station(station);
4502 AST_RWLIST_UNLOCK(&sla_stations);
4504 if (sla.thread != AST_PTHREADT_NULL) {
4505 ast_mutex_lock(&sla.lock);
4506 sla.stop = 1;
4507 ast_cond_signal(&sla.cond);
4508 ast_mutex_unlock(&sla.lock);
4509 pthread_join(sla.thread, NULL);
4512 /* Drop any created contexts from the dialplan */
4513 ast_context_destroy(NULL, sla_registrar);
4515 ast_mutex_destroy(&sla.lock);
4516 ast_cond_destroy(&sla.cond);
4519 static int sla_check_device(const char *device)
4521 char *tech, *tech_data;
4523 tech_data = ast_strdupa(device);
4524 tech = strsep(&tech_data, "/");
4526 if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
4527 return -1;
4529 return 0;
4532 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
4534 struct sla_trunk *trunk;
4535 struct ast_variable *var;
4536 const char *dev;
4538 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
4539 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
4540 return -1;
4543 if (sla_check_device(dev)) {
4544 ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
4545 cat, dev);
4546 return -1;
4549 if (!(trunk = ast_calloc(1, sizeof(*trunk))))
4550 return -1;
4551 if (ast_string_field_init(trunk, 32)) {
4552 free(trunk);
4553 return -1;
4556 ast_string_field_set(trunk, name, cat);
4557 ast_string_field_set(trunk, device, dev);
4559 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
4560 if (!strcasecmp(var->name, "autocontext"))
4561 ast_string_field_set(trunk, autocontext, var->value);
4562 else if (!strcasecmp(var->name, "ringtimeout")) {
4563 if (sscanf(var->value, "%u", &trunk->ring_timeout) != 1) {
4564 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
4565 var->value, trunk->name);
4566 trunk->ring_timeout = 0;
4568 } else if (!strcasecmp(var->name, "barge"))
4569 trunk->barge_disabled = ast_false(var->value);
4570 else if (!strcasecmp(var->name, "hold")) {
4571 if (!strcasecmp(var->value, "private"))
4572 trunk->hold_access = SLA_HOLD_PRIVATE;
4573 else if (!strcasecmp(var->value, "open"))
4574 trunk->hold_access = SLA_HOLD_OPEN;
4575 else {
4576 ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
4577 var->value, trunk->name);
4579 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
4580 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
4581 var->name, var->lineno, SLA_CONFIG_FILE);
4585 if (!ast_strlen_zero(trunk->autocontext)) {
4586 struct ast_context *context;
4587 context = ast_context_find_or_create(NULL, trunk->autocontext, sla_registrar);
4588 if (!context) {
4589 ast_log(LOG_ERROR, "Failed to automatically find or create "
4590 "context '%s' for SLA!\n", trunk->autocontext);
4591 destroy_trunk(trunk);
4592 return -1;
4594 if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
4595 NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free, sla_registrar)) {
4596 ast_log(LOG_ERROR, "Failed to automatically create extension "
4597 "for trunk '%s'!\n", trunk->name);
4598 destroy_trunk(trunk);
4599 return -1;
4603 AST_RWLIST_WRLOCK(&sla_trunks);
4604 AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
4605 AST_RWLIST_UNLOCK(&sla_trunks);
4607 return 0;
4610 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
4612 struct sla_trunk *trunk;
4613 struct sla_trunk_ref *trunk_ref;
4614 struct sla_station_ref *station_ref;
4615 char *trunk_name, *options, *cur;
4617 options = ast_strdupa(var->value);
4618 trunk_name = strsep(&options, ",");
4620 AST_RWLIST_RDLOCK(&sla_trunks);
4621 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
4622 if (!strcasecmp(trunk->name, trunk_name))
4623 break;
4626 AST_RWLIST_UNLOCK(&sla_trunks);
4627 if (!trunk) {
4628 ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
4629 return;
4631 if (!(trunk_ref = create_trunk_ref(trunk)))
4632 return;
4633 trunk_ref->state = SLA_TRUNK_STATE_IDLE;
4635 while ((cur = strsep(&options, ","))) {
4636 char *name, *value = cur;
4637 name = strsep(&value, "=");
4638 if (!strcasecmp(name, "ringtimeout")) {
4639 if (sscanf(value, "%u", &trunk_ref->ring_timeout) != 1) {
4640 ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
4641 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
4642 trunk_ref->ring_timeout = 0;
4644 } else if (!strcasecmp(name, "ringdelay")) {
4645 if (sscanf(value, "%u", &trunk_ref->ring_delay) != 1) {
4646 ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
4647 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
4648 trunk_ref->ring_delay = 0;
4650 } else {
4651 ast_log(LOG_WARNING, "Invalid option '%s' for "
4652 "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
4656 if (!(station_ref = sla_create_station_ref(station))) {
4657 free(trunk_ref);
4658 return;
4660 ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
4661 AST_RWLIST_WRLOCK(&sla_trunks);
4662 AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
4663 AST_RWLIST_UNLOCK(&sla_trunks);
4664 AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
4667 static int sla_build_station(struct ast_config *cfg, const char *cat)
4669 struct sla_station *station;
4670 struct ast_variable *var;
4671 const char *dev;
4673 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
4674 ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
4675 return -1;
4678 if (!(station = ast_calloc(1, sizeof(*station))))
4679 return -1;
4680 if (ast_string_field_init(station, 32)) {
4681 free(station);
4682 return -1;
4685 ast_string_field_set(station, name, cat);
4686 ast_string_field_set(station, device, dev);
4688 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
4689 if (!strcasecmp(var->name, "trunk"))
4690 sla_add_trunk_to_station(station, var);
4691 else if (!strcasecmp(var->name, "autocontext"))
4692 ast_string_field_set(station, autocontext, var->value);
4693 else if (!strcasecmp(var->name, "ringtimeout")) {
4694 if (sscanf(var->value, "%u", &station->ring_timeout) != 1) {
4695 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
4696 var->value, station->name);
4697 station->ring_timeout = 0;
4699 } else if (!strcasecmp(var->name, "ringdelay")) {
4700 if (sscanf(var->value, "%u", &station->ring_delay) != 1) {
4701 ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
4702 var->value, station->name);
4703 station->ring_delay = 0;
4705 } else if (!strcasecmp(var->name, "hold")) {
4706 if (!strcasecmp(var->value, "private"))
4707 station->hold_access = SLA_HOLD_PRIVATE;
4708 else if (!strcasecmp(var->value, "open"))
4709 station->hold_access = SLA_HOLD_OPEN;
4710 else {
4711 ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
4712 var->value, station->name);
4715 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
4716 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
4717 var->name, var->lineno, SLA_CONFIG_FILE);
4721 if (!ast_strlen_zero(station->autocontext)) {
4722 struct ast_context *context;
4723 struct sla_trunk_ref *trunk_ref;
4724 context = ast_context_find_or_create(NULL, station->autocontext, sla_registrar);
4725 if (!context) {
4726 ast_log(LOG_ERROR, "Failed to automatically find or create "
4727 "context '%s' for SLA!\n", station->autocontext);
4728 destroy_station(station);
4729 return -1;
4731 /* The extension for when the handset goes off-hook.
4732 * exten => station1,1,SLAStation(station1) */
4733 if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
4734 NULL, NULL, slastation_app, ast_strdup(station->name), ast_free, sla_registrar)) {
4735 ast_log(LOG_ERROR, "Failed to automatically create extension "
4736 "for trunk '%s'!\n", station->name);
4737 destroy_station(station);
4738 return -1;
4740 AST_RWLIST_RDLOCK(&sla_trunks);
4741 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4742 char exten[AST_MAX_EXTENSION];
4743 char hint[AST_MAX_APP];
4744 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
4745 snprintf(hint, sizeof(hint), "SLA:%s", exten);
4746 /* Extension for this line button
4747 * exten => station1_line1,1,SLAStation(station1_line1) */
4748 if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
4749 NULL, NULL, slastation_app, ast_strdup(exten), ast_free, sla_registrar)) {
4750 ast_log(LOG_ERROR, "Failed to automatically create extension "
4751 "for trunk '%s'!\n", station->name);
4752 destroy_station(station);
4753 return -1;
4755 /* Hint for this line button
4756 * exten => station1_line1,hint,SLA:station1_line1 */
4757 if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
4758 NULL, NULL, hint, NULL, NULL, sla_registrar)) {
4759 ast_log(LOG_ERROR, "Failed to automatically create hint "
4760 "for trunk '%s'!\n", station->name);
4761 destroy_station(station);
4762 return -1;
4765 AST_RWLIST_UNLOCK(&sla_trunks);
4768 AST_RWLIST_WRLOCK(&sla_stations);
4769 AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
4770 AST_RWLIST_UNLOCK(&sla_stations);
4772 return 0;
4775 static int sla_load_config(void)
4777 struct ast_config *cfg;
4778 const char *cat = NULL;
4779 int res = 0;
4780 const char *val;
4782 ast_mutex_init(&sla.lock);
4783 ast_cond_init(&sla.cond, NULL);
4785 if (!(cfg = ast_config_load(SLA_CONFIG_FILE)))
4786 return 0; /* Treat no config as normal */
4788 if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
4789 sla.attempt_callerid = ast_true(val);
4791 while ((cat = ast_category_browse(cfg, cat)) && !res) {
4792 const char *type;
4793 if (!strcasecmp(cat, "general"))
4794 continue;
4795 if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
4796 ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
4797 SLA_CONFIG_FILE);
4798 continue;
4800 if (!strcasecmp(type, "trunk"))
4801 res = sla_build_trunk(cfg, cat);
4802 else if (!strcasecmp(type, "station"))
4803 res = sla_build_station(cfg, cat);
4804 else {
4805 ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
4806 SLA_CONFIG_FILE, type);
4810 ast_config_destroy(cfg);
4812 if (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_stations))
4813 ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
4815 return res;
4818 static int load_config(int reload)
4820 int res = 0;
4822 load_config_meetme();
4823 if (!reload)
4824 res = sla_load_config();
4826 return res;
4829 static int unload_module(void)
4831 int res = 0;
4833 ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
4834 res = ast_manager_unregister("MeetmeMute");
4835 res |= ast_manager_unregister("MeetmeUnmute");
4836 res |= ast_unregister_application(app3);
4837 res |= ast_unregister_application(app2);
4838 res |= ast_unregister_application(app);
4839 res |= ast_unregister_application(slastation_app);
4840 res |= ast_unregister_application(slatrunk_app);
4842 ast_devstate_prov_del("Meetme");
4843 ast_devstate_prov_del("SLA");
4845 ast_module_user_hangup_all();
4847 sla_destroy();
4849 return res;
4852 static int load_module(void)
4854 int res = 0;
4856 res |= load_config(0);
4858 ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
4859 res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL,
4860 action_meetmemute, "Mute a Meetme user");
4861 res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL,
4862 action_meetmeunmute, "Unmute a Meetme user");
4863 res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
4864 res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
4865 res |= ast_register_application(app, conf_exec, synopsis, descrip);
4866 res |= ast_register_application(slastation_app, sla_station_exec,
4867 slastation_synopsis, slastation_desc);
4868 res |= ast_register_application(slatrunk_app, sla_trunk_exec,
4869 slatrunk_synopsis, slatrunk_desc);
4871 res |= ast_devstate_prov_add("Meetme", meetmestate);
4872 res |= ast_devstate_prov_add("SLA", sla_state);
4874 return res;
4877 static int reload(void)
4879 return load_config(1);
4882 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MeetMe conference bridge",
4883 .load = load_module,
4884 .unload = unload_module,
4885 .reload = reload,