Let's also include aclocal.m4
[asterisk-bristuff.git] / apps / app_meetme.c
blobdf4148e6526d360106c508977c8858988c3c3f0e
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 DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)\n"
221 " must be present for conferencing to operate properly. In addition, the chan_dahdi\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-DAHDI 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";
270 static const char *descrip3 =
271 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
272 " 'e' -- Eject last user that joined\n"
273 " 'k' -- Kick one user out of conference\n"
274 " 'K' -- Kick all users out of conference\n"
275 " 'l' -- Unlock conference\n"
276 " 'L' -- Lock conference\n"
277 " 'm' -- Unmute one user\n"
278 " 'M' -- Mute one user\n"
279 " 'n' -- Unmute all users in the conference\n"
280 " 'N' -- Mute all non-admin users in the conference\n"
281 " 'r' -- Reset one user's volume settings\n"
282 " 'R' -- Reset all users volume settings\n"
283 " 's' -- Lower entire conference speaking volume\n"
284 " 'S' -- Raise entire conference speaking volume\n"
285 " 't' -- Lower one user's talk volume\n"
286 " 'T' -- Raise one user's talk volume\n"
287 " 'u' -- Lower one user's listen volume\n"
288 " 'U' -- Raise one user's listen volume\n"
289 " 'v' -- Lower entire conference listening volume\n"
290 " 'V' -- Raise entire conference listening volume\n"
293 static const char *slastation_desc =
294 " SLAStation(station):\n"
295 "This application should be executed by an SLA station. The argument depends\n"
296 "on how the call was initiated. If the phone was just taken off hook, then\n"
297 "the argument \"station\" should be just the station name. If the call was\n"
298 "initiated by pressing a line key, then the station name should be preceded\n"
299 "by an underscore and the trunk name associated with that line button.\n"
300 "For example: \"station1_line1\"."
301 " On exit, this application will set the variable SLASTATION_STATUS to\n"
302 "one of the following values:\n"
303 " FAILURE | CONGESTION | SUCCESS\n"
306 static const char *slatrunk_desc =
307 " SLATrunk(trunk):\n"
308 "This application should be executed by an SLA trunk on an inbound call.\n"
309 "The channel calling this application should correspond to the SLA trunk\n"
310 "with the name \"trunk\" that is being passed as an argument.\n"
311 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
312 "one of the following values:\n"
313 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
316 #define MAX_CONFNUM 80
317 #define MAX_PIN 80
319 /*! \brief The MeetMe Conference object */
320 struct ast_conference {
321 ast_mutex_t playlock; /*!< Conference specific lock (players) */
322 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
323 char confno[MAX_CONFNUM]; /*!< Conference */
324 struct ast_channel *chan; /*!< Announcements channel */
325 struct ast_channel *lchan; /*!< Listen/Record channel */
326 int fd; /*!< Announcements fd */
327 int zapconf; /*!< Zaptel Conf # */
328 int users; /*!< Number of active users */
329 int markedusers; /*!< Number of marked users */
330 time_t start; /*!< Start time (s) */
331 int refcount; /*!< reference count of usage */
332 enum recording_state recording:2; /*!< recording status */
333 unsigned int isdynamic:1; /*!< Created on the fly? */
334 unsigned int locked:1; /*!< Is the conference locked? */
335 pthread_t recordthread; /*!< thread for recording */
336 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
337 pthread_attr_t attr; /*!< thread attribute */
338 const char *recordingfilename; /*!< Filename to record the Conference into */
339 const char *recordingformat; /*!< Format to record the Conference in */
340 char pin[MAX_PIN]; /*!< If protected by a PIN */
341 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
342 struct ast_frame *transframe[32];
343 struct ast_frame *origframe;
344 struct ast_trans_pvt *transpath[32];
345 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
346 AST_LIST_ENTRY(ast_conference) list;
349 static AST_LIST_HEAD_STATIC(confs, ast_conference);
351 static unsigned int conf_map[1024] = {0, };
353 struct volume {
354 int desired; /*!< Desired volume adjustment */
355 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
358 struct ast_conf_user {
359 int user_no; /*!< User Number */
360 int userflags; /*!< Flags as set in the conference */
361 int adminflags; /*!< Flags set by the Admin */
362 struct ast_channel *chan; /*!< Connected channel */
363 int talking; /*!< Is user talking */
364 int zapchannel; /*!< Is a Zaptel channel */
365 char usrvalue[50]; /*!< Custom User Value */
366 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
367 time_t jointime; /*!< Time the user joined the conference */
368 struct volume talk;
369 struct volume listen;
370 AST_LIST_ENTRY(ast_conf_user) list;
373 enum sla_which_trunk_refs {
374 ALL_TRUNK_REFS,
375 INACTIVE_TRUNK_REFS,
378 enum sla_trunk_state {
379 SLA_TRUNK_STATE_IDLE,
380 SLA_TRUNK_STATE_RINGING,
381 SLA_TRUNK_STATE_UP,
382 SLA_TRUNK_STATE_ONHOLD,
383 SLA_TRUNK_STATE_ONHOLD_BYME,
386 enum sla_hold_access {
387 /*! This means that any station can put it on hold, and any station
388 * can retrieve the call from hold. */
389 SLA_HOLD_OPEN,
390 /*! This means that only the station that put the call on hold may
391 * retrieve it from hold. */
392 SLA_HOLD_PRIVATE,
395 struct sla_trunk_ref;
397 struct sla_station {
398 AST_RWLIST_ENTRY(sla_station) entry;
399 AST_DECLARE_STRING_FIELDS(
400 AST_STRING_FIELD(name);
401 AST_STRING_FIELD(device);
402 AST_STRING_FIELD(autocontext);
404 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
405 struct ast_dial *dial;
406 /*! Ring timeout for this station, for any trunk. If a ring timeout
407 * is set for a specific trunk on this station, that will take
408 * priority over this value. */
409 unsigned int ring_timeout;
410 /*! Ring delay for this station, for any trunk. If a ring delay
411 * is set for a specific trunk on this station, that will take
412 * priority over this value. */
413 unsigned int ring_delay;
414 /*! This option uses the values in the sla_hold_access enum and sets the
415 * access control type for hold on this station. */
416 unsigned int hold_access:1;
419 struct sla_station_ref {
420 AST_LIST_ENTRY(sla_station_ref) entry;
421 struct sla_station *station;
424 struct sla_trunk {
425 AST_RWLIST_ENTRY(sla_trunk) entry;
426 AST_DECLARE_STRING_FIELDS(
427 AST_STRING_FIELD(name);
428 AST_STRING_FIELD(device);
429 AST_STRING_FIELD(autocontext);
431 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
432 /*! Number of stations that use this trunk */
433 unsigned int num_stations;
434 /*! Number of stations currently on a call with this trunk */
435 unsigned int active_stations;
436 /*! Number of stations that have this trunk on hold. */
437 unsigned int hold_stations;
438 struct ast_channel *chan;
439 unsigned int ring_timeout;
440 /*! If set to 1, no station will be able to join an active call with
441 * this trunk. */
442 unsigned int barge_disabled:1;
443 /*! This option uses the values in the sla_hold_access enum and sets the
444 * access control type for hold on this trunk. */
445 unsigned int hold_access:1;
446 /*! Whether this trunk is currently on hold, meaning that once a station
447 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
448 unsigned int on_hold:1;
451 struct sla_trunk_ref {
452 AST_LIST_ENTRY(sla_trunk_ref) entry;
453 struct sla_trunk *trunk;
454 enum sla_trunk_state state;
455 struct ast_channel *chan;
456 /*! Ring timeout to use when this trunk is ringing on this specific
457 * station. This takes higher priority than a ring timeout set at
458 * the station level. */
459 unsigned int ring_timeout;
460 /*! Ring delay to use when this trunk is ringing on this specific
461 * station. This takes higher priority than a ring delay set at
462 * the station level. */
463 unsigned int ring_delay;
466 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
467 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
469 static const char sla_registrar[] = "SLA";
471 /*! \brief Event types that can be queued up for the SLA thread */
472 enum sla_event_type {
473 /*! A station has put the call on hold */
474 SLA_EVENT_HOLD,
475 /*! The state of a dial has changed */
476 SLA_EVENT_DIAL_STATE,
477 /*! The state of a ringing trunk has changed */
478 SLA_EVENT_RINGING_TRUNK,
481 struct sla_event {
482 enum sla_event_type type;
483 struct sla_station *station;
484 struct sla_trunk_ref *trunk_ref;
485 AST_LIST_ENTRY(sla_event) entry;
488 /*! \brief A station that failed to be dialed
489 * \note Only used by the SLA thread. */
490 struct sla_failed_station {
491 struct sla_station *station;
492 struct timeval last_try;
493 AST_LIST_ENTRY(sla_failed_station) entry;
496 /*! \brief A trunk that is ringing */
497 struct sla_ringing_trunk {
498 struct sla_trunk *trunk;
499 /*! The time that this trunk started ringing */
500 struct timeval ring_begin;
501 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
502 AST_LIST_ENTRY(sla_ringing_trunk) entry;
505 enum sla_station_hangup {
506 SLA_STATION_HANGUP_NORMAL,
507 SLA_STATION_HANGUP_TIMEOUT,
510 /*! \brief A station that is ringing */
511 struct sla_ringing_station {
512 struct sla_station *station;
513 /*! The time that this station started ringing */
514 struct timeval ring_begin;
515 AST_LIST_ENTRY(sla_ringing_station) entry;
519 * \brief A structure for data used by the sla thread
521 static struct {
522 /*! The SLA thread ID */
523 pthread_t thread;
524 ast_cond_t cond;
525 ast_mutex_t lock;
526 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
527 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
528 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
529 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
530 unsigned int stop:1;
531 /*! Attempt to handle CallerID, even though it is known not to work
532 * properly in some situations. */
533 unsigned int attempt_callerid:1;
534 } sla = {
535 .thread = AST_PTHREADT_NULL,
538 /*! The number of audio buffers to be allocated on pseudo channels
539 * when in a conference */
540 static int audio_buffers;
542 /*! Map 'volume' levels from -5 through +5 into
543 * decibel (dB) settings for channel drivers
544 * Note: these are not a straight linear-to-dB
545 * conversion... the numbers have been modified
546 * to give the user a better level of adjustability
548 static char const gain_map[] = {
549 -15,
550 -13,
551 -10,
563 static int admin_exec(struct ast_channel *chan, void *data);
564 static void *recordthread(void *args);
566 static char *istalking(int x)
568 if (x > 0)
569 return "(talking)";
570 else if (x < 0)
571 return "(unmonitored)";
572 else
573 return "(not talking)";
576 static int careful_write(int fd, unsigned char *data, int len, int block)
578 int res;
579 int x;
581 while (len) {
582 if (block) {
583 x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
584 res = ioctl(fd, DAHDI_IOMUX, &x);
585 } else
586 res = 0;
587 if (res >= 0)
588 res = write(fd, data, len);
589 if (res < 1) {
590 if (errno != EAGAIN) {
591 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
592 return -1;
593 } else
594 return 0;
596 len -= res;
597 data += res;
600 return 0;
603 static int set_talk_volume(struct ast_conf_user *user, int volume)
605 char gain_adjust;
607 /* attempt to make the adjustment in the channel driver;
608 if successful, don't adjust in the frame reading routine
610 gain_adjust = gain_map[volume + 5];
612 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
615 static int set_listen_volume(struct ast_conf_user *user, int volume)
617 char gain_adjust;
619 /* attempt to make the adjustment in the channel driver;
620 if successful, don't adjust in the frame reading routine
622 gain_adjust = gain_map[volume + 5];
624 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
627 static void tweak_volume(struct volume *vol, enum volume_action action)
629 switch (action) {
630 case VOL_UP:
631 switch (vol->desired) {
632 case 5:
633 break;
634 case 0:
635 vol->desired = 2;
636 break;
637 case -2:
638 vol->desired = 0;
639 break;
640 default:
641 vol->desired++;
642 break;
644 break;
645 case VOL_DOWN:
646 switch (vol->desired) {
647 case -5:
648 break;
649 case 2:
650 vol->desired = 0;
651 break;
652 case 0:
653 vol->desired = -2;
654 break;
655 default:
656 vol->desired--;
657 break;
662 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
664 tweak_volume(&user->talk, action);
665 /* attempt to make the adjustment in the channel driver;
666 if successful, don't adjust in the frame reading routine
668 if (!set_talk_volume(user, user->talk.desired))
669 user->talk.actual = 0;
670 else
671 user->talk.actual = user->talk.desired;
674 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
676 tweak_volume(&user->listen, action);
677 /* attempt to make the adjustment in the channel driver;
678 if successful, don't adjust in the frame reading routine
680 if (!set_listen_volume(user, user->listen.desired))
681 user->listen.actual = 0;
682 else
683 user->listen.actual = user->listen.desired;
686 static void reset_volumes(struct ast_conf_user *user)
688 signed char zero_volume = 0;
690 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
691 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
694 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
696 unsigned char *data;
697 int len;
698 int res = -1;
700 if (!chan->_softhangup)
701 res = ast_autoservice_start(chan);
703 AST_LIST_LOCK(&confs);
705 switch(sound) {
706 case ENTER:
707 data = enter;
708 len = sizeof(enter);
709 break;
710 case LEAVE:
711 data = leave;
712 len = sizeof(leave);
713 break;
714 default:
715 data = NULL;
716 len = 0;
718 if (data) {
719 careful_write(conf->fd, data, len, 1);
722 AST_LIST_UNLOCK(&confs);
724 if (!res)
725 ast_autoservice_stop(chan);
729 * \brief Find or create a conference
731 * \param confno The conference name/number
732 * \param pin The regular user pin
733 * \param pinadmin The admin pin
734 * \param make Make the conf if it doesn't exist
735 * \param dynamic Mark the newly created conference as dynamic
736 * \param refcount How many references to mark on the conference
738 * \return A pointer to the conference struct, or NULL if it wasn't found and
739 * make or dynamic were not set.
741 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
743 struct ast_conference *cnf;
744 struct dahdi_confinfo ztc = { 0, };
745 int confno_int = 0;
747 AST_LIST_LOCK(&confs);
749 AST_LIST_TRAVERSE(&confs, cnf, list) {
750 if (!strcmp(confno, cnf->confno))
751 break;
754 if (cnf || (!make && !dynamic))
755 goto cnfout;
757 /* Make a new one */
758 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
759 goto cnfout;
761 ast_mutex_init(&cnf->playlock);
762 ast_mutex_init(&cnf->listenlock);
763 cnf->recordthread = AST_PTHREADT_NULL;
764 ast_mutex_init(&cnf->recordthreadlock);
765 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
766 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
767 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
769 /* Setup a new zap conference */
770 ztc.confno = -1;
771 ztc.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
772 #ifdef HAVE_ZAPTEL
773 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
774 #else
775 cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
776 #endif
777 if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &ztc)) {
778 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
779 if (cnf->fd >= 0)
780 close(cnf->fd);
781 free(cnf);
782 cnf = NULL;
783 goto cnfout;
786 cnf->zapconf = ztc.confno;
788 /* Setup a new channel for playback of audio files */
789 cnf->chan = ast_request(dahdi_chan_name, AST_FORMAT_SLINEAR, "pseudo", NULL);
790 if (cnf->chan) {
791 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
792 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
793 ztc.chan = 0;
794 ztc.confno = cnf->zapconf;
795 ztc.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
796 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &ztc)) {
797 ast_log(LOG_WARNING, "Error setting conference\n");
798 if (cnf->chan)
799 ast_hangup(cnf->chan);
800 else
801 close(cnf->fd);
802 free(cnf);
803 cnf = NULL;
804 goto cnfout;
808 /* Fill the conference struct */
809 cnf->start = time(NULL);
810 cnf->isdynamic = dynamic ? 1 : 0;
811 if (option_verbose > 2)
812 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
813 AST_LIST_INSERT_HEAD(&confs, cnf, list);
815 /* Reserve conference number in map */
816 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
817 conf_map[confno_int] = 1;
819 cnfout:
820 if (cnf)
821 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
823 AST_LIST_UNLOCK(&confs);
825 return cnf;
828 static int meetme_cmd(int fd, int argc, char **argv)
830 /* Process the command */
831 struct ast_conference *cnf;
832 struct ast_conf_user *user;
833 int hr, min, sec;
834 int i = 0, total = 0;
835 time_t now;
836 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
837 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
838 char cmdline[1024] = "";
840 if (argc > 8)
841 ast_cli(fd, "Invalid Arguments.\n");
842 /* Check for length so no buffer will overflow... */
843 for (i = 0; i < argc; i++) {
844 if (strlen(argv[i]) > 100)
845 ast_cli(fd, "Invalid Arguments.\n");
847 if (argc == 1) {
848 /* 'MeetMe': List all the conferences */
849 now = time(NULL);
850 AST_LIST_LOCK(&confs);
851 if (AST_LIST_EMPTY(&confs)) {
852 ast_cli(fd, "No active MeetMe conferences.\n");
853 AST_LIST_UNLOCK(&confs);
854 return RESULT_SUCCESS;
856 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
857 AST_LIST_TRAVERSE(&confs, cnf, list) {
858 if (cnf->markedusers == 0)
859 strcpy(cmdline, "N/A ");
860 else
861 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
862 hr = (now - cnf->start) / 3600;
863 min = ((now - cnf->start) % 3600) / 60;
864 sec = (now - cnf->start) % 60;
866 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
868 total += cnf->users;
870 AST_LIST_UNLOCK(&confs);
871 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
872 return RESULT_SUCCESS;
874 if (argc < 3)
875 return RESULT_SHOWUSAGE;
876 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
877 if (strstr(argv[1], "lock")) {
878 if (strcmp(argv[1], "lock") == 0) {
879 /* Lock */
880 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
881 } else {
882 /* Unlock */
883 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
885 } else if (strstr(argv[1], "mute")) {
886 if (argc < 4)
887 return RESULT_SHOWUSAGE;
888 if (strcmp(argv[1], "mute") == 0) {
889 /* Mute */
890 if (strcmp(argv[3], "all") == 0) {
891 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
892 } else {
893 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
894 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
896 } else {
897 /* Unmute */
898 if (strcmp(argv[3], "all") == 0) {
899 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
900 } else {
901 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
902 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
905 } else if (strcmp(argv[1], "kick") == 0) {
906 if (argc < 4)
907 return RESULT_SHOWUSAGE;
908 if (strcmp(argv[3], "all") == 0) {
909 /* Kick all */
910 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
911 } else {
912 /* Kick a single user */
913 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
914 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
916 } else if(strcmp(argv[1], "list") == 0) {
917 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
918 /* List all the users in a conference */
919 if (AST_LIST_EMPTY(&confs)) {
920 if ( !concise )
921 ast_cli(fd, "No active conferences.\n");
922 return RESULT_SUCCESS;
924 /* Find the right conference */
925 AST_LIST_LOCK(&confs);
926 AST_LIST_TRAVERSE(&confs, cnf, list) {
927 if (strcmp(cnf->confno, argv[2]) == 0)
928 break;
930 if (!cnf) {
931 if ( !concise )
932 ast_cli(fd, "No such conference: %s.\n",argv[2]);
933 AST_LIST_UNLOCK(&confs);
934 return RESULT_SUCCESS;
936 /* Show all the users */
937 time(&now);
938 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
939 hr = (now - user->jointime) / 3600;
940 min = ((now - user->jointime) % 3600) / 60;
941 sec = (now - user->jointime) % 60;
942 if ( !concise )
943 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
944 user->user_no,
945 S_OR(user->chan->cid.cid_num, "<unknown>"),
946 S_OR(user->chan->cid.cid_name, "<no name>"),
947 user->chan->name,
948 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
949 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
950 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
951 istalking(user->talking), hr, min, sec);
952 else
953 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
954 user->user_no,
955 S_OR(user->chan->cid.cid_num, ""),
956 S_OR(user->chan->cid.cid_name, ""),
957 user->chan->name,
958 user->userflags & CONFFLAG_ADMIN ? "1" : "",
959 user->userflags & CONFFLAG_MONITOR ? "1" : "",
960 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
961 user->talking, hr, min, sec);
964 if ( !concise )
965 ast_cli(fd,"%d users in that conference.\n",cnf->users);
966 AST_LIST_UNLOCK(&confs);
967 return RESULT_SUCCESS;
968 } else
969 return RESULT_SHOWUSAGE;
970 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
971 admin_exec(NULL, cmdline);
973 return 0;
976 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
978 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
980 int len = strlen(word);
981 int which = 0;
982 struct ast_conference *cnf = NULL;
983 struct ast_conf_user *usr = NULL;
984 char *confno = NULL;
985 char usrno[50] = "";
986 char *myline, *ret = NULL;
988 if (pos == 1) { /* Command */
989 return ast_cli_complete(word, cmds, state);
990 } else if (pos == 2) { /* Conference Number */
991 AST_LIST_LOCK(&confs);
992 AST_LIST_TRAVERSE(&confs, cnf, list) {
993 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
994 ret = cnf->confno;
995 break;
998 ret = ast_strdup(ret); /* dup before releasing the lock */
999 AST_LIST_UNLOCK(&confs);
1000 return ret;
1001 } else if (pos == 3) {
1002 /* User Number || Conf Command option*/
1003 if (strstr(line, "mute") || strstr(line, "kick")) {
1004 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
1005 return strdup("all");
1006 which++;
1007 AST_LIST_LOCK(&confs);
1009 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1010 myline = ast_strdupa(line);
1011 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1012 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1016 AST_LIST_TRAVERSE(&confs, cnf, list) {
1017 if (!strcmp(confno, cnf->confno))
1018 break;
1021 if (cnf) {
1022 /* Search for the user */
1023 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
1024 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1025 if (!strncasecmp(word, usrno, len) && ++which > state)
1026 break;
1029 AST_LIST_UNLOCK(&confs);
1030 return usr ? strdup(usrno) : NULL;
1031 } else if ( strstr(line, "list") && ( 0 == state ) )
1032 return strdup("concise");
1035 return NULL;
1038 static char meetme_usage[] =
1039 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
1040 " Executes a command for the conference or on a conferee\n";
1042 static const char *sla_hold_str(unsigned int hold_access)
1044 const char *hold = "Unknown";
1046 switch (hold_access) {
1047 case SLA_HOLD_OPEN:
1048 hold = "Open";
1049 break;
1050 case SLA_HOLD_PRIVATE:
1051 hold = "Private";
1052 default:
1053 break;
1056 return hold;
1059 static int sla_show_trunks(int fd, int argc, char **argv)
1061 const struct sla_trunk *trunk;
1063 ast_cli(fd, "\n"
1064 "=============================================================\n"
1065 "=== Configured SLA Trunks ===================================\n"
1066 "=============================================================\n"
1067 "===\n");
1068 AST_RWLIST_RDLOCK(&sla_trunks);
1069 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1070 struct sla_station_ref *station_ref;
1071 char ring_timeout[16] = "(none)";
1072 if (trunk->ring_timeout)
1073 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1074 ast_cli(fd, "=== ---------------------------------------------------------\n"
1075 "=== Trunk Name: %s\n"
1076 "=== ==> Device: %s\n"
1077 "=== ==> AutoContext: %s\n"
1078 "=== ==> RingTimeout: %s\n"
1079 "=== ==> BargeAllowed: %s\n"
1080 "=== ==> HoldAccess: %s\n"
1081 "=== ==> Stations ...\n",
1082 trunk->name, trunk->device,
1083 S_OR(trunk->autocontext, "(none)"),
1084 ring_timeout,
1085 trunk->barge_disabled ? "No" : "Yes",
1086 sla_hold_str(trunk->hold_access));
1087 AST_RWLIST_RDLOCK(&sla_stations);
1088 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1089 ast_cli(fd, "=== ==> Station name: %s\n", station_ref->station->name);
1090 AST_RWLIST_UNLOCK(&sla_stations);
1091 ast_cli(fd, "=== ---------------------------------------------------------\n"
1092 "===\n");
1094 AST_RWLIST_UNLOCK(&sla_trunks);
1095 ast_cli(fd, "=============================================================\n"
1096 "\n");
1098 return RESULT_SUCCESS;
1101 static const char *trunkstate2str(enum sla_trunk_state state)
1103 #define S(e) case e: return # e;
1104 switch (state) {
1105 S(SLA_TRUNK_STATE_IDLE)
1106 S(SLA_TRUNK_STATE_RINGING)
1107 S(SLA_TRUNK_STATE_UP)
1108 S(SLA_TRUNK_STATE_ONHOLD)
1109 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1111 return "Uknown State";
1112 #undef S
1115 static const char sla_show_trunks_usage[] =
1116 "Usage: sla show trunks\n"
1117 " This will list all trunks defined in sla.conf\n";
1119 static int sla_show_stations(int fd, int argc, char **argv)
1121 const struct sla_station *station;
1123 ast_cli(fd, "\n"
1124 "=============================================================\n"
1125 "=== Configured SLA Stations =================================\n"
1126 "=============================================================\n"
1127 "===\n");
1128 AST_RWLIST_RDLOCK(&sla_stations);
1129 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1130 struct sla_trunk_ref *trunk_ref;
1131 char ring_timeout[16] = "(none)";
1132 char ring_delay[16] = "(none)";
1133 if (station->ring_timeout) {
1134 snprintf(ring_timeout, sizeof(ring_timeout),
1135 "%u", station->ring_timeout);
1137 if (station->ring_delay) {
1138 snprintf(ring_delay, sizeof(ring_delay),
1139 "%u", station->ring_delay);
1141 ast_cli(fd, "=== ---------------------------------------------------------\n"
1142 "=== Station Name: %s\n"
1143 "=== ==> Device: %s\n"
1144 "=== ==> AutoContext: %s\n"
1145 "=== ==> RingTimeout: %s\n"
1146 "=== ==> RingDelay: %s\n"
1147 "=== ==> HoldAccess: %s\n"
1148 "=== ==> Trunks ...\n",
1149 station->name, station->device,
1150 S_OR(station->autocontext, "(none)"),
1151 ring_timeout, ring_delay,
1152 sla_hold_str(station->hold_access));
1153 AST_RWLIST_RDLOCK(&sla_trunks);
1154 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1155 if (trunk_ref->ring_timeout) {
1156 snprintf(ring_timeout, sizeof(ring_timeout),
1157 "%u", trunk_ref->ring_timeout);
1158 } else
1159 strcpy(ring_timeout, "(none)");
1160 if (trunk_ref->ring_delay) {
1161 snprintf(ring_delay, sizeof(ring_delay),
1162 "%u", trunk_ref->ring_delay);
1163 } else
1164 strcpy(ring_delay, "(none)");
1165 ast_cli(fd, "=== ==> Trunk Name: %s\n"
1166 "=== ==> State: %s\n"
1167 "=== ==> RingTimeout: %s\n"
1168 "=== ==> RingDelay: %s\n",
1169 trunk_ref->trunk->name,
1170 trunkstate2str(trunk_ref->state),
1171 ring_timeout, ring_delay);
1173 AST_RWLIST_UNLOCK(&sla_trunks);
1174 ast_cli(fd, "=== ---------------------------------------------------------\n"
1175 "===\n");
1177 AST_RWLIST_UNLOCK(&sla_stations);
1178 ast_cli(fd, "============================================================\n"
1179 "\n");
1181 return RESULT_SUCCESS;
1184 static const char sla_show_stations_usage[] =
1185 "Usage: sla show stations\n"
1186 " This will list all stations defined in sla.conf\n";
1188 static struct ast_cli_entry cli_meetme[] = {
1189 { { "meetme", NULL, NULL },
1190 meetme_cmd, "Execute a command on a conference or conferee",
1191 meetme_usage, complete_meetmecmd },
1193 { { "sla", "show", "trunks", NULL },
1194 sla_show_trunks, "Show SLA Trunks",
1195 sla_show_trunks_usage, NULL },
1197 { { "sla", "show", "stations", NULL },
1198 sla_show_stations, "Show SLA Stations",
1199 sla_show_stations_usage, NULL },
1202 static void conf_flush(int fd, struct ast_channel *chan)
1204 int x;
1206 /* read any frames that may be waiting on the channel
1207 and throw them away
1209 if (chan) {
1210 struct ast_frame *f;
1212 /* when no frames are available, this will wait
1213 for 1 millisecond maximum
1215 while (ast_waitfor(chan, 1)) {
1216 f = ast_read(chan);
1217 if (f)
1218 ast_frfree(f);
1219 else /* channel was hung up or something else happened */
1220 break;
1224 /* flush any data sitting in the pseudo channel */
1225 x = DAHDI_FLUSH_ALL;
1226 if (ioctl(fd, DAHDI_FLUSH, &x))
1227 ast_log(LOG_WARNING, "Error flushing channel\n");
1231 /* Remove the conference from the list and free it.
1232 We assume that this was called while holding conflock. */
1233 static int conf_free(struct ast_conference *conf)
1235 int x;
1237 AST_LIST_REMOVE(&confs, conf, list);
1239 if (conf->recording == MEETME_RECORD_ACTIVE) {
1240 conf->recording = MEETME_RECORD_TERMINATE;
1241 AST_LIST_UNLOCK(&confs);
1242 while (1) {
1243 usleep(1);
1244 AST_LIST_LOCK(&confs);
1245 if (conf->recording == MEETME_RECORD_OFF)
1246 break;
1247 AST_LIST_UNLOCK(&confs);
1251 for (x=0;x<AST_FRAME_BITS;x++) {
1252 if (conf->transframe[x])
1253 ast_frfree(conf->transframe[x]);
1254 if (conf->transpath[x])
1255 ast_translator_free_path(conf->transpath[x]);
1257 if (conf->origframe)
1258 ast_frfree(conf->origframe);
1259 if (conf->lchan)
1260 ast_hangup(conf->lchan);
1261 if (conf->chan)
1262 ast_hangup(conf->chan);
1263 if (conf->fd >= 0)
1264 close(conf->fd);
1266 ast_mutex_destroy(&conf->playlock);
1267 ast_mutex_destroy(&conf->listenlock);
1268 ast_mutex_destroy(&conf->recordthreadlock);
1269 free(conf);
1271 return 0;
1274 static void conf_queue_dtmf(const struct ast_conference *conf,
1275 const struct ast_conf_user *sender, struct ast_frame *f)
1277 struct ast_conf_user *user;
1279 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1280 if (user == sender)
1281 continue;
1282 if (ast_write(user->chan, f) < 0)
1283 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1287 static void sla_queue_event_full(enum sla_event_type type,
1288 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1290 struct sla_event *event;
1292 if (!(event = ast_calloc(1, sizeof(*event))))
1293 return;
1295 event->type = type;
1296 event->trunk_ref = trunk_ref;
1297 event->station = station;
1299 if (!lock) {
1300 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1301 return;
1304 ast_mutex_lock(&sla.lock);
1305 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1306 ast_cond_signal(&sla.cond);
1307 ast_mutex_unlock(&sla.lock);
1310 static void sla_queue_event_nolock(enum sla_event_type type)
1312 sla_queue_event_full(type, NULL, NULL, 0);
1315 static void sla_queue_event(enum sla_event_type type)
1317 sla_queue_event_full(type, NULL, NULL, 1);
1320 /*! \brief Queue a SLA event from the conference */
1321 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1322 struct ast_conference *conf)
1324 struct sla_station *station;
1325 struct sla_trunk_ref *trunk_ref = NULL;
1326 char *trunk_name;
1328 trunk_name = ast_strdupa(conf->confno);
1329 strsep(&trunk_name, "_");
1330 if (ast_strlen_zero(trunk_name)) {
1331 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1332 return;
1335 AST_RWLIST_RDLOCK(&sla_stations);
1336 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1337 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1338 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1339 break;
1341 if (trunk_ref)
1342 break;
1344 AST_RWLIST_UNLOCK(&sla_stations);
1346 if (!trunk_ref) {
1347 ast_log(LOG_DEBUG, "Trunk not found for event!\n");
1348 return;
1351 sla_queue_event_full(type, trunk_ref, station, 1);
1354 /* Decrement reference counts, as incremented by find_conf() */
1355 static int dispose_conf(struct ast_conference *conf)
1357 int res = 0;
1358 int confno_int = 0;
1360 AST_LIST_LOCK(&confs);
1361 if (ast_atomic_dec_and_test(&conf->refcount)) {
1362 /* Take the conference room number out of an inuse state */
1363 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1364 conf_map[confno_int] = 0;
1365 conf_free(conf);
1366 res = 1;
1368 AST_LIST_UNLOCK(&confs);
1370 return res;
1374 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1376 struct ast_conf_user *user = NULL;
1377 struct ast_conf_user *usr = NULL;
1378 int fd;
1379 struct dahdi_confinfo ztc, ztc_empty;
1380 struct ast_frame *f;
1381 struct ast_channel *c;
1382 struct ast_frame fr;
1383 int outfd;
1384 int ms;
1385 int nfds;
1386 int res;
1387 int flags;
1388 int retryzap;
1389 int origfd;
1390 int musiconhold = 0;
1391 int firstpass = 0;
1392 int lastmarked = 0;
1393 int currentmarked = 0;
1394 int ret = -1;
1395 int x;
1396 int menu_active = 0;
1397 int using_pseudo = 0;
1398 int duration=20;
1399 int hr, min, sec;
1400 int sent_event = 0;
1401 time_t now;
1402 struct ast_dsp *dsp=NULL;
1403 struct ast_app *app;
1404 const char *agifile;
1405 const char *agifiledefault = "conf-background.agi";
1406 char meetmesecs[30] = "";
1407 char exitcontext[AST_MAX_CONTEXT] = "";
1408 char recordingtmp[AST_MAX_EXTENSION] = "";
1409 char members[10] = "";
1410 int dtmf, opt_waitmarked_timeout = 0;
1411 time_t timeout = 0;
1412 struct dahdi_bufferinfo bi;
1413 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1414 char *buf = __buf + AST_FRIENDLY_OFFSET;
1415 int setusercount = 0;
1417 if (!(user = ast_calloc(1, sizeof(*user))))
1418 return ret;
1420 /* Possible timeout waiting for marked user */
1421 if ((confflags & CONFFLAG_WAITMARKED) &&
1422 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1423 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1424 (opt_waitmarked_timeout > 0)) {
1425 timeout = time(NULL) + opt_waitmarked_timeout;
1428 if (confflags & CONFFLAG_RECORDCONF) {
1429 if (!conf->recordingfilename) {
1430 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1431 if (!conf->recordingfilename) {
1432 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1433 conf->recordingfilename = ast_strdupa(recordingtmp);
1435 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1436 if (!conf->recordingformat) {
1437 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1438 conf->recordingformat = ast_strdupa(recordingtmp);
1440 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1441 conf->confno, conf->recordingfilename, conf->recordingformat);
1445 ast_mutex_lock(&conf->recordthreadlock);
1446 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request(dahdi_chan_name, AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1447 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1448 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1449 ztc.chan = 0;
1450 ztc.confno = conf->zapconf;
1451 ztc.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1452 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &ztc)) {
1453 ast_log(LOG_WARNING, "Error starting listen channel\n");
1454 ast_hangup(conf->lchan);
1455 conf->lchan = NULL;
1456 } else {
1457 pthread_attr_init(&conf->attr);
1458 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
1459 ast_pthread_create_background(&conf->recordthread, &conf->attr, recordthread, conf);
1460 pthread_attr_destroy(&conf->attr);
1463 ast_mutex_unlock(&conf->recordthreadlock);
1465 time(&user->jointime);
1467 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1468 /* Sorry, but this confernce is locked! */
1469 if (!ast_streamfile(chan, "conf-locked", chan->language))
1470 ast_waitstream(chan, "");
1471 goto outrun;
1474 ast_mutex_lock(&conf->playlock);
1476 if (AST_LIST_EMPTY(&conf->userlist))
1477 user->user_no = 1;
1478 else
1479 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1481 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1483 user->chan = chan;
1484 user->userflags = confflags;
1485 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1486 user->talking = -1;
1488 ast_mutex_unlock(&conf->playlock);
1490 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1491 char destdir[PATH_MAX];
1493 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
1495 if (mkdir(destdir, 0777) && errno != EEXIST) {
1496 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1497 goto outrun;
1500 snprintf(user->namerecloc, sizeof(user->namerecloc),
1501 "%s/meetme-username-%s-%d", destdir,
1502 conf->confno, user->user_no);
1503 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1504 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1505 else
1506 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1507 if (res == -1)
1508 goto outrun;
1511 ast_mutex_lock(&conf->playlock);
1513 if (confflags & CONFFLAG_MARKEDUSER)
1514 conf->markedusers++;
1515 conf->users++;
1516 /* Update table */
1517 snprintf(members, sizeof(members), "%d", conf->users);
1518 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1519 setusercount = 1;
1521 /* This device changed state now - if this is the first user */
1522 if (conf->users == 1)
1523 ast_device_state_changed("meetme:%s", conf->confno);
1525 ast_mutex_unlock(&conf->playlock);
1527 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1528 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1529 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1530 else if (!ast_strlen_zero(chan->macrocontext))
1531 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1532 else
1533 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1536 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1537 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1538 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1539 ast_waitstream(chan, "");
1540 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1541 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1542 ast_waitstream(chan, "");
1545 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1546 int keepplaying = 1;
1548 if (conf->users == 2) {
1549 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1550 res = ast_waitstream(chan, AST_DIGIT_ANY);
1551 ast_stopstream(chan);
1552 if (res > 0)
1553 keepplaying=0;
1554 else if (res == -1)
1555 goto outrun;
1557 } else {
1558 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1559 res = ast_waitstream(chan, AST_DIGIT_ANY);
1560 ast_stopstream(chan);
1561 if (res > 0)
1562 keepplaying=0;
1563 else if (res == -1)
1564 goto outrun;
1566 if (keepplaying) {
1567 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1568 if (res > 0)
1569 keepplaying=0;
1570 else if (res == -1)
1571 goto outrun;
1573 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1574 res = ast_waitstream(chan, AST_DIGIT_ANY);
1575 ast_stopstream(chan);
1576 if (res > 0)
1577 keepplaying=0;
1578 else if (res == -1)
1579 goto outrun;
1584 ast_indicate(chan, -1);
1586 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1587 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1588 goto outrun;
1591 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1592 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1593 goto outrun;
1596 retryzap = (strcasecmp(chan->tech->type, dahdi_chan_name) || (chan->audiohooks || chan->monitor) ? 1 : 0);
1597 user->zapchannel = !retryzap;
1599 zapretry:
1600 origfd = chan->fds[0];
1601 if (retryzap) {
1602 #ifdef HAVE_ZAPTEL
1603 fd = open("/dev/zap/pseudo", O_RDWR);
1604 #else
1605 fd = open("/dev/dahdi/pseudo", O_RDWR);
1606 #endif
1607 if (fd < 0) {
1608 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1609 goto outrun;
1611 using_pseudo = 1;
1612 /* Make non-blocking */
1613 flags = fcntl(fd, F_GETFL);
1614 if (flags < 0) {
1615 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1616 close(fd);
1617 goto outrun;
1619 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1620 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1621 close(fd);
1622 goto outrun;
1624 /* Setup buffering information */
1625 memset(&bi, 0, sizeof(bi));
1626 bi.bufsize = CONF_SIZE/2;
1627 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
1628 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
1629 bi.numbufs = audio_buffers;
1630 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
1631 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1632 close(fd);
1633 goto outrun;
1635 x = 1;
1636 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
1637 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1638 close(fd);
1639 goto outrun;
1641 nfds = 1;
1642 } else {
1643 /* XXX Make sure we're not running on a pseudo channel XXX */
1644 fd = chan->fds[0];
1645 nfds = 0;
1647 memset(&ztc, 0, sizeof(ztc));
1648 memset(&ztc_empty, 0, sizeof(ztc_empty));
1649 /* Check to see if we're in a conference... */
1650 ztc.chan = 0;
1651 if (ioctl(fd, DAHDI_GETCONF, &ztc)) {
1652 ast_log(LOG_WARNING, "Error getting conference\n");
1653 close(fd);
1654 goto outrun;
1656 if (ztc.confmode) {
1657 /* Whoa, already in a conference... Retry... */
1658 if (!retryzap) {
1659 ast_log(LOG_DEBUG, "%s channel is in a conference already, retrying with pseudo\n", dahdi_chan_name);
1660 retryzap = 1;
1661 goto zapretry;
1664 memset(&ztc, 0, sizeof(ztc));
1665 /* Add us to the conference */
1666 ztc.chan = 0;
1667 ztc.confno = conf->zapconf;
1669 ast_mutex_lock(&conf->playlock);
1671 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1672 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1673 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1674 ast_waitstream(conf->chan, "");
1675 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1676 ast_waitstream(conf->chan, "");
1680 if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
1681 ztc.confmode = DAHDI_CONF_CONF;
1682 else if (confflags & CONFFLAG_MONITOR)
1683 ztc.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
1684 else if (confflags & CONFFLAG_TALKER)
1685 ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
1686 else
1687 ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
1689 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
1690 ast_log(LOG_WARNING, "Error setting conference\n");
1691 close(fd);
1692 ast_mutex_unlock(&conf->playlock);
1693 goto outrun;
1695 ast_log(LOG_DEBUG, "Placed channel %s in %s conf %d\n", chan->name, dahdi_chan_name, conf->zapconf);
1697 if (!sent_event) {
1698 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1699 "Channel: %s\r\n"
1700 "Uniqueid: %s\r\n"
1701 "Meetme: %s\r\n"
1702 "Usernum: %d\r\n",
1703 chan->name, chan->uniqueid, conf->confno, user->user_no);
1704 sent_event = 1;
1707 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1708 firstpass = 1;
1709 if (!(confflags & CONFFLAG_QUIET))
1710 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1711 conf_play(chan, conf, ENTER);
1714 ast_mutex_unlock(&conf->playlock);
1716 conf_flush(fd, chan);
1718 if (confflags & CONFFLAG_AGI) {
1719 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1720 or use default filename of conf-background.agi */
1722 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1723 if (!agifile)
1724 agifile = agifiledefault;
1726 if (user->zapchannel) {
1727 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1728 x = 1;
1729 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1731 /* Find a pointer to the agi app and execute the script */
1732 app = pbx_findapp("agi");
1733 if (app) {
1734 char *s = ast_strdupa(agifile);
1735 ret = pbx_exec(chan, app, s);
1736 } else {
1737 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1738 ret = -2;
1740 if (user->zapchannel) {
1741 /* Remove CONFMUTE mode on Zap channel */
1742 x = 0;
1743 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1745 } else {
1746 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1747 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1748 x = 1;
1749 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1751 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1752 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1753 res = -1;
1755 for(;;) {
1756 int menu_was_active = 0;
1758 outfd = -1;
1759 ms = -1;
1761 if (timeout && time(NULL) >= timeout)
1762 break;
1764 /* if we have just exited from the menu, and the user had a channel-driver
1765 volume adjustment, restore it
1767 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1768 set_talk_volume(user, user->listen.desired);
1770 menu_was_active = menu_active;
1772 currentmarked = conf->markedusers;
1773 if (!(confflags & CONFFLAG_QUIET) &&
1774 (confflags & CONFFLAG_MARKEDUSER) &&
1775 (confflags & CONFFLAG_WAITMARKED) &&
1776 lastmarked == 0) {
1777 if (currentmarked == 1 && conf->users > 1) {
1778 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1779 if (conf->users - 1 == 1) {
1780 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1781 ast_waitstream(chan, "");
1782 } else {
1783 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1784 ast_waitstream(chan, "");
1787 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1788 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1789 ast_waitstream(chan, "");
1792 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1795 /* Update the struct with the actual confflags */
1796 user->userflags = confflags;
1798 if (confflags & CONFFLAG_WAITMARKED) {
1799 if(currentmarked == 0) {
1800 if (lastmarked != 0) {
1801 if (!(confflags & CONFFLAG_QUIET))
1802 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1803 ast_waitstream(chan, "");
1804 if(confflags & CONFFLAG_MARKEDEXIT)
1805 break;
1806 else {
1807 ztc.confmode = DAHDI_CONF_CONF;
1808 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
1809 ast_log(LOG_WARNING, "Error setting conference\n");
1810 close(fd);
1811 goto outrun;
1815 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1816 ast_moh_start(chan, NULL, NULL);
1817 musiconhold = 1;
1819 } else if(currentmarked >= 1 && lastmarked == 0) {
1820 /* Marked user entered, so cancel timeout */
1821 timeout = 0;
1822 if (confflags & CONFFLAG_MONITOR)
1823 ztc.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
1824 else if (confflags & CONFFLAG_TALKER)
1825 ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
1826 else
1827 ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
1828 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
1829 ast_log(LOG_WARNING, "Error setting conference\n");
1830 close(fd);
1831 goto outrun;
1833 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1834 ast_moh_stop(chan);
1835 musiconhold = 0;
1837 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1838 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1839 ast_waitstream(chan, "");
1840 conf_play(chan, conf, ENTER);
1845 /* trying to add moh for single person conf */
1846 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1847 if (conf->users == 1) {
1848 if (musiconhold == 0) {
1849 ast_moh_start(chan, NULL, NULL);
1850 musiconhold = 1;
1852 } else {
1853 if (musiconhold) {
1854 ast_moh_stop(chan);
1855 musiconhold = 0;
1860 /* Leave if the last marked user left */
1861 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1862 ret = -1;
1863 break;
1866 /* Check if my modes have changed */
1868 /* If I should be muted but am still talker, mute me */
1869 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & DAHDI_CONF_TALKER)) {
1870 ztc.confmode ^= DAHDI_CONF_TALKER;
1871 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
1872 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1873 ret = -1;
1874 break;
1877 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1878 "Channel: %s\r\n"
1879 "Uniqueid: %s\r\n"
1880 "Meetme: %s\r\n"
1881 "Usernum: %i\r\n"
1882 "Status: on\r\n",
1883 chan->name, chan->uniqueid, conf->confno, user->user_no);
1886 /* If I should be un-muted but am not talker, un-mute me */
1887 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & DAHDI_CONF_TALKER)) {
1888 ztc.confmode |= DAHDI_CONF_TALKER;
1889 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
1890 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1891 ret = -1;
1892 break;
1895 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1896 "Channel: %s\r\n"
1897 "Uniqueid: %s\r\n"
1898 "Meetme: %s\r\n"
1899 "Usernum: %i\r\n"
1900 "Status: off\r\n",
1901 chan->name, chan->uniqueid, conf->confno, user->user_no);
1904 /* If I have been kicked, exit the conference */
1905 if (user->adminflags & ADMINFLAG_KICKME) {
1906 //You have been kicked.
1907 if (!(confflags & CONFFLAG_QUIET) &&
1908 !ast_streamfile(chan, "conf-kicked", chan->language)) {
1909 ast_waitstream(chan, "");
1911 ret = 0;
1912 break;
1915 /* Perform an extra hangup check just in case */
1916 if (ast_check_hangup(chan))
1917 break;
1919 if (c) {
1920 char dtmfstr[2] = "";
1922 if (c->fds[0] != origfd || (user->zapchannel && (c->audiohooks || c->monitor))) {
1923 if (using_pseudo) {
1924 /* Kill old pseudo */
1925 close(fd);
1926 using_pseudo = 0;
1928 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1929 retryzap = (strcasecmp(c->tech->type, dahdi_chan_name) || (c->audiohooks || c->monitor) ? 1 : 0);
1930 user->zapchannel = !retryzap;
1931 goto zapretry;
1933 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1934 f = ast_read_noaudio(c);
1935 else
1936 f = ast_read(c);
1937 if (!f)
1938 break;
1939 if (f->frametype == AST_FRAME_DTMF) {
1940 dtmfstr[0] = f->subclass;
1941 dtmfstr[1] = '\0';
1944 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1945 if (user->talk.actual)
1946 ast_frame_adjust_volume(f, user->talk.actual);
1948 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1949 int totalsilence;
1951 if (user->talking == -1)
1952 user->talking = 0;
1954 res = ast_dsp_silence(dsp, f, &totalsilence);
1955 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1956 user->talking = 1;
1957 if (confflags & CONFFLAG_MONITORTALKER)
1958 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1959 "Channel: %s\r\n"
1960 "Uniqueid: %s\r\n"
1961 "Meetme: %s\r\n"
1962 "Usernum: %d\r\n"
1963 "Status: on\r\n",
1964 chan->name, chan->uniqueid, conf->confno, user->user_no);
1966 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1967 user->talking = 0;
1968 if (confflags & CONFFLAG_MONITORTALKER)
1969 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1970 "Channel: %s\r\n"
1971 "Uniqueid: %s\r\n"
1972 "Meetme: %s\r\n"
1973 "Usernum: %d\r\n"
1974 "Status: off\r\n",
1975 chan->name, chan->uniqueid, conf->confno, user->user_no);
1978 if (using_pseudo) {
1979 /* Absolutely do _not_ use careful_write here...
1980 it is important that we read data from the channel
1981 as fast as it arrives, and feed it into the conference.
1982 The buffering in the pseudo channel will take care of any
1983 timing differences, unless they are so drastic as to lose
1984 audio frames (in which case carefully writing would only
1985 have delayed the audio even further).
1987 /* As it turns out, we do want to use careful write. We just
1988 don't want to block, but we do want to at least *try*
1989 to write out all the samples.
1991 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1992 careful_write(fd, f->data, f->datalen, 0);
1994 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1995 if (confflags & CONFFLAG_PASS_DTMF)
1996 conf_queue_dtmf(conf, user, f);
1997 ret = 0;
1998 ast_frfree(f);
1999 break;
2000 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
2001 if (confflags & CONFFLAG_PASS_DTMF)
2002 conf_queue_dtmf(conf, user, f);
2003 if (ioctl(fd, DAHDI_SETCONF, &ztc_empty)) {
2004 ast_log(LOG_WARNING, "Error setting conference\n");
2005 close(fd);
2006 ast_frfree(f);
2007 goto outrun;
2010 /* if we are entering the menu, and the user has a channel-driver
2011 volume adjustment, clear it
2013 if (!menu_active && user->talk.desired && !user->talk.actual)
2014 set_talk_volume(user, 0);
2016 if (musiconhold) {
2017 ast_moh_stop(chan);
2019 if ((confflags & CONFFLAG_ADMIN)) {
2020 /* Admin menu */
2021 if (!menu_active) {
2022 menu_active = 1;
2023 /* Record this sound! */
2024 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2025 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2026 ast_stopstream(chan);
2027 } else
2028 dtmf = 0;
2029 } else
2030 dtmf = f->subclass;
2031 if (dtmf) {
2032 switch(dtmf) {
2033 case '1': /* Un/Mute */
2034 menu_active = 0;
2036 /* for admin, change both admin and use flags */
2037 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2038 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2039 else
2040 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2042 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2043 if (!ast_streamfile(chan, "conf-muted", chan->language))
2044 ast_waitstream(chan, "");
2045 } else {
2046 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2047 ast_waitstream(chan, "");
2049 break;
2050 case '2': /* Un/Lock the Conference */
2051 menu_active = 0;
2052 if (conf->locked) {
2053 conf->locked = 0;
2054 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2055 ast_waitstream(chan, "");
2056 } else {
2057 conf->locked = 1;
2058 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2059 ast_waitstream(chan, "");
2061 break;
2062 case '3': /* Eject last user */
2063 menu_active = 0;
2064 usr = AST_LIST_LAST(&conf->userlist);
2065 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2066 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2067 ast_waitstream(chan, "");
2068 } else
2069 usr->adminflags |= ADMINFLAG_KICKME;
2070 ast_stopstream(chan);
2071 break;
2072 case '4':
2073 tweak_listen_volume(user, VOL_DOWN);
2074 break;
2075 case '6':
2076 tweak_listen_volume(user, VOL_UP);
2077 break;
2078 case '7':
2079 tweak_talk_volume(user, VOL_DOWN);
2080 break;
2081 case '8':
2082 menu_active = 0;
2083 break;
2084 case '9':
2085 tweak_talk_volume(user, VOL_UP);
2086 break;
2087 default:
2088 menu_active = 0;
2089 /* Play an error message! */
2090 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2091 ast_waitstream(chan, "");
2092 break;
2095 } else {
2096 /* User menu */
2097 if (!menu_active) {
2098 menu_active = 1;
2099 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2100 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2101 ast_stopstream(chan);
2102 } else
2103 dtmf = 0;
2104 } else
2105 dtmf = f->subclass;
2106 if (dtmf) {
2107 switch(dtmf) {
2108 case '1': /* Un/Mute */
2109 menu_active = 0;
2111 /* user can only toggle the self-muted state */
2112 user->adminflags ^= ADMINFLAG_SELFMUTED;
2114 /* they can't override the admin mute state */
2115 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2116 if (!ast_streamfile(chan, "conf-muted", chan->language))
2117 ast_waitstream(chan, "");
2118 } else {
2119 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2120 ast_waitstream(chan, "");
2122 break;
2123 case '4':
2124 tweak_listen_volume(user, VOL_DOWN);
2125 break;
2126 case '6':
2127 tweak_listen_volume(user, VOL_UP);
2128 break;
2129 case '7':
2130 tweak_talk_volume(user, VOL_DOWN);
2131 break;
2132 case '8':
2133 menu_active = 0;
2134 break;
2135 case '9':
2136 tweak_talk_volume(user, VOL_UP);
2137 break;
2138 default:
2139 menu_active = 0;
2140 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2141 ast_waitstream(chan, "");
2142 break;
2146 if (musiconhold)
2147 ast_moh_start(chan, NULL, NULL);
2149 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
2150 ast_log(LOG_WARNING, "Error setting conference\n");
2151 close(fd);
2152 ast_frfree(f);
2153 goto outrun;
2156 conf_flush(fd, chan);
2157 /* Since this option could absorb dtmf for the previous, we have to check this one last */
2158 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
2159 if (confflags & CONFFLAG_PASS_DTMF)
2160 conf_queue_dtmf(conf, user, f);
2162 if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
2163 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
2164 ret = 0;
2165 ast_frfree(f);
2166 break;
2167 } else if (option_debug > 1)
2168 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension '%s' does not exist in context '%s'\n", dtmfstr, exitcontext);
2169 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2170 && confflags & CONFFLAG_PASS_DTMF) {
2171 conf_queue_dtmf(conf, user, f);
2172 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2173 switch (f->subclass) {
2174 case AST_CONTROL_HOLD:
2175 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2176 break;
2177 default:
2178 break;
2180 } else if (f->frametype == AST_FRAME_NULL) {
2181 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2182 } else if (option_debug) {
2183 ast_log(LOG_DEBUG,
2184 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2185 chan->name, f->frametype, f->subclass);
2187 ast_frfree(f);
2188 } else if (outfd > -1) {
2189 res = read(outfd, buf, CONF_SIZE);
2190 if (res > 0) {
2191 memset(&fr, 0, sizeof(fr));
2192 fr.frametype = AST_FRAME_VOICE;
2193 fr.subclass = AST_FORMAT_SLINEAR;
2194 fr.datalen = res;
2195 fr.samples = res/2;
2196 fr.data = buf;
2197 fr.offset = AST_FRIENDLY_OFFSET;
2198 if (!user->listen.actual &&
2199 ((confflags & CONFFLAG_MONITOR) ||
2200 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2201 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
2202 )) {
2203 int index;
2204 for (index=0;index<AST_FRAME_BITS;index++)
2205 if (chan->rawwriteformat & (1 << index))
2206 break;
2207 if (index >= AST_FRAME_BITS)
2208 goto bailoutandtrynormal;
2209 ast_mutex_lock(&conf->listenlock);
2210 if (!conf->transframe[index]) {
2211 if (conf->origframe) {
2212 if (!conf->transpath[index])
2213 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2214 if (conf->transpath[index]) {
2215 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2216 if (!conf->transframe[index])
2217 conf->transframe[index] = &ast_null_frame;
2221 if (conf->transframe[index]) {
2222 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2223 if (ast_write(chan, conf->transframe[index]))
2224 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2226 } else {
2227 ast_mutex_unlock(&conf->listenlock);
2228 goto bailoutandtrynormal;
2230 ast_mutex_unlock(&conf->listenlock);
2231 } else {
2232 bailoutandtrynormal:
2233 if (user->listen.actual)
2234 ast_frame_adjust_volume(&fr, user->listen.actual);
2235 if (ast_write(chan, &fr) < 0) {
2236 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2239 } else
2240 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2242 lastmarked = currentmarked;
2246 if (musiconhold)
2247 ast_moh_stop(chan);
2249 if (using_pseudo)
2250 close(fd);
2251 else {
2252 /* Take out of conference */
2253 ztc.chan = 0;
2254 ztc.confno = 0;
2255 ztc.confmode = 0;
2256 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
2257 ast_log(LOG_WARNING, "Error setting conference\n");
2261 reset_volumes(user);
2263 AST_LIST_LOCK(&confs);
2264 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2265 conf_play(chan, conf, LEAVE);
2267 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2268 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2269 if ((conf->chan) && (conf->users > 1)) {
2270 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2271 ast_waitstream(conf->chan, "");
2272 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2273 ast_waitstream(conf->chan, "");
2275 ast_filedelete(user->namerecloc, NULL);
2278 AST_LIST_UNLOCK(&confs);
2280 outrun:
2281 AST_LIST_LOCK(&confs);
2283 if (dsp)
2284 ast_dsp_free(dsp);
2286 if (user->user_no) { /* Only cleanup users who really joined! */
2287 now = time(NULL);
2288 hr = (now - user->jointime) / 3600;
2289 min = ((now - user->jointime) % 3600) / 60;
2290 sec = (now - user->jointime) % 60;
2292 if (sent_event) {
2293 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2294 "Channel: %s\r\n"
2295 "Uniqueid: %s\r\n"
2296 "Meetme: %s\r\n"
2297 "Usernum: %d\r\n"
2298 "CallerIDnum: %s\r\n"
2299 "CallerIDname: %s\r\n"
2300 "Duration: %ld\r\n",
2301 chan->name, chan->uniqueid, conf->confno,
2302 user->user_no,
2303 S_OR(user->chan->cid.cid_num, "<unknown>"),
2304 S_OR(user->chan->cid.cid_name, "<unknown>"),
2305 (long)(now - user->jointime));
2308 if (setusercount) {
2309 conf->users--;
2310 /* Update table */
2311 snprintf(members, sizeof(members), "%d", conf->users);
2312 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2313 if (confflags & CONFFLAG_MARKEDUSER)
2314 conf->markedusers--;
2316 /* Remove ourselves from the list */
2317 AST_LIST_REMOVE(&conf->userlist, user, list);
2319 /* Change any states */
2320 if (!conf->users)
2321 ast_device_state_changed("meetme:%s", conf->confno);
2323 /* Return the number of seconds the user was in the conf */
2324 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2325 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2327 free(user);
2328 AST_LIST_UNLOCK(&confs);
2330 return ret;
2333 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2334 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2336 struct ast_variable *var;
2337 struct ast_conference *cnf;
2339 /* Check first in the conference list */
2340 AST_LIST_LOCK(&confs);
2341 AST_LIST_TRAVERSE(&confs, cnf, list) {
2342 if (!strcmp(confno, cnf->confno))
2343 break;
2345 if (cnf) {
2346 cnf->refcount += refcount;
2348 AST_LIST_UNLOCK(&confs);
2350 if (!cnf) {
2351 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2353 var = ast_load_realtime("meetme", "confno", confno, NULL);
2355 if (!var)
2356 return NULL;
2358 while (var) {
2359 if (!strcasecmp(var->name, "pin")) {
2360 pin = ast_strdupa(var->value);
2361 } else if (!strcasecmp(var->name, "adminpin")) {
2362 pinadmin = ast_strdupa(var->value);
2364 var = var->next;
2366 ast_variables_destroy(var);
2368 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
2371 if (cnf) {
2372 if (confflags && !cnf->chan &&
2373 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2374 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2375 ast_log(LOG_WARNING, "No %s channel available for conference, user introduction disabled\n", dahdi_chan_name);
2376 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2379 if (confflags && !cnf->chan &&
2380 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2381 ast_log(LOG_WARNING, "No %s channel available for conference, conference recording disabled\n", dahdi_chan_name);
2382 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2386 return cnf;
2390 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2391 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2393 struct ast_config *cfg;
2394 struct ast_variable *var;
2395 struct ast_conference *cnf;
2396 char *parse;
2397 AST_DECLARE_APP_ARGS(args,
2398 AST_APP_ARG(confno);
2399 AST_APP_ARG(pin);
2400 AST_APP_ARG(pinadmin);
2403 /* Check first in the conference list */
2404 AST_LIST_LOCK(&confs);
2405 AST_LIST_TRAVERSE(&confs, cnf, list) {
2406 if (!strcmp(confno, cnf->confno))
2407 break;
2409 if (cnf){
2410 cnf->refcount += refcount;
2412 AST_LIST_UNLOCK(&confs);
2414 if (!cnf) {
2415 if (dynamic) {
2416 /* No need to parse meetme.conf */
2417 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
2418 if (dynamic_pin) {
2419 if (dynamic_pin[0] == 'q') {
2420 /* Query the user to enter a PIN */
2421 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2422 return NULL;
2424 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
2425 } else {
2426 cnf = build_conf(confno, "", "", make, dynamic, refcount);
2428 } else {
2429 /* Check the config */
2430 cfg = ast_config_load(CONFIG_FILE_NAME);
2431 if (!cfg) {
2432 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2433 return NULL;
2435 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2436 if (strcasecmp(var->name, "conf"))
2437 continue;
2439 if (!(parse = ast_strdupa(var->value)))
2440 return NULL;
2442 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
2443 if (!strcasecmp(args.confno, confno)) {
2444 /* Bingo it's a valid conference */
2445 cnf = build_conf(args.confno,
2446 S_OR(args.pin, ""),
2447 S_OR(args.pinadmin, ""),
2448 make, dynamic, refcount);
2449 break;
2452 if (!var) {
2453 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
2455 ast_config_destroy(cfg);
2457 } else if (dynamic_pin) {
2458 /* Correct for the user selecting 'D' instead of 'd' to have
2459 someone join into a conference that has already been created
2460 with a pin. */
2461 if (dynamic_pin[0] == 'q')
2462 dynamic_pin[0] = '\0';
2465 if (cnf) {
2466 if (confflags && !cnf->chan &&
2467 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2468 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2469 ast_log(LOG_WARNING, "No %s channel available for conference, user introduction disabled\n", dahdi_chan_name);
2470 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2473 if (confflags && !cnf->chan &&
2474 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2475 ast_log(LOG_WARNING, "No %s channel available for conference, conference recording disabled\n", dahdi_chan_name);
2476 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2480 return cnf;
2483 /*! \brief The MeetmeCount application */
2484 static int count_exec(struct ast_channel *chan, void *data)
2486 struct ast_module_user *u;
2487 int res = 0;
2488 struct ast_conference *conf;
2489 int count;
2490 char *localdata;
2491 char val[80] = "0";
2492 AST_DECLARE_APP_ARGS(args,
2493 AST_APP_ARG(confno);
2494 AST_APP_ARG(varname);
2497 if (ast_strlen_zero(data)) {
2498 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2499 return -1;
2502 u = ast_module_user_add(chan);
2504 if (!(localdata = ast_strdupa(data))) {
2505 ast_module_user_remove(u);
2506 return -1;
2509 AST_STANDARD_APP_ARGS(args, localdata);
2511 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2513 if (conf) {
2514 count = conf->users;
2515 dispose_conf(conf);
2516 conf = NULL;
2517 } else
2518 count = 0;
2520 if (!ast_strlen_zero(args.varname)){
2521 /* have var so load it and exit */
2522 snprintf(val, sizeof(val), "%d",count);
2523 pbx_builtin_setvar_helper(chan, args.varname, val);
2524 } else {
2525 if (chan->_state != AST_STATE_UP)
2526 ast_answer(chan);
2527 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2529 ast_module_user_remove(u);
2531 return res;
2534 /*! \brief The meetme() application */
2535 static int conf_exec(struct ast_channel *chan, void *data)
2537 int res=-1;
2538 struct ast_module_user *u;
2539 char confno[MAX_CONFNUM] = "";
2540 int allowretry = 0;
2541 int retrycnt = 0;
2542 struct ast_conference *cnf = NULL;
2543 struct ast_flags confflags = {0};
2544 int dynamic = 0;
2545 int empty = 0, empty_no_pin = 0;
2546 int always_prompt = 0;
2547 char *notdata, *info, the_pin[MAX_PIN] = "";
2548 AST_DECLARE_APP_ARGS(args,
2549 AST_APP_ARG(confno);
2550 AST_APP_ARG(options);
2551 AST_APP_ARG(pin);
2553 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2555 u = ast_module_user_add(chan);
2557 if (ast_strlen_zero(data)) {
2558 allowretry = 1;
2559 notdata = "";
2560 } else {
2561 notdata = data;
2564 if (chan->_state != AST_STATE_UP)
2565 ast_answer(chan);
2567 info = ast_strdupa(notdata);
2569 AST_STANDARD_APP_ARGS(args, info);
2571 if (args.confno) {
2572 ast_copy_string(confno, args.confno, sizeof(confno));
2573 if (ast_strlen_zero(confno)) {
2574 allowretry = 1;
2578 if (args.pin)
2579 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2581 if (args.options) {
2582 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2583 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2584 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2585 strcpy(the_pin, "q");
2587 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2588 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2589 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2592 do {
2593 if (retrycnt > 3)
2594 allowretry = 0;
2595 if (empty) {
2596 int i;
2597 struct ast_config *cfg;
2598 struct ast_variable *var;
2599 int confno_int;
2601 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2602 if ((empty_no_pin) || (!dynamic)) {
2603 cfg = ast_config_load(CONFIG_FILE_NAME);
2604 if (cfg) {
2605 var = ast_variable_browse(cfg, "rooms");
2606 while (var) {
2607 if (!strcasecmp(var->name, "conf")) {
2608 char *stringp = ast_strdupa(var->value);
2609 if (stringp) {
2610 char *confno_tmp = strsep(&stringp, "|,");
2611 int found = 0;
2612 if (!dynamic) {
2613 /* For static: run through the list and see if this conference is empty */
2614 AST_LIST_LOCK(&confs);
2615 AST_LIST_TRAVERSE(&confs, cnf, list) {
2616 if (!strcmp(confno_tmp, cnf->confno)) {
2617 /* The conference exists, therefore it's not empty */
2618 found = 1;
2619 break;
2622 AST_LIST_UNLOCK(&confs);
2623 if (!found) {
2624 /* At this point, we have a confno_tmp (static conference) that is empty */
2625 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2626 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2627 * Case 2: empty_no_pin and pin is blank (but not NULL)
2628 * Case 3: not empty_no_pin
2630 ast_copy_string(confno, confno_tmp, sizeof(confno));
2631 break;
2632 /* XXX the map is not complete (but we do have a confno) */
2638 var = var->next;
2640 ast_config_destroy(cfg);
2644 /* Select first conference number not in use */
2645 if (ast_strlen_zero(confno) && dynamic) {
2646 AST_LIST_LOCK(&confs);
2647 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
2648 if (!conf_map[i]) {
2649 snprintf(confno, sizeof(confno), "%d", i);
2650 conf_map[i] = 1;
2651 break;
2654 AST_LIST_UNLOCK(&confs);
2657 /* Not found? */
2658 if (ast_strlen_zero(confno)) {
2659 res = ast_streamfile(chan, "conf-noempty", chan->language);
2660 if (!res)
2661 ast_waitstream(chan, "");
2662 } else {
2663 if (sscanf(confno, "%d", &confno_int) == 1) {
2664 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2665 if (!res) {
2666 ast_waitstream(chan, "");
2667 res = ast_say_digits(chan, confno_int, "", chan->language);
2669 } else {
2670 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2675 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2676 /* Prompt user for conference number */
2677 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2678 if (res < 0) {
2679 /* Don't try to validate when we catch an error */
2680 confno[0] = '\0';
2681 allowretry = 0;
2682 break;
2685 if (!ast_strlen_zero(confno)) {
2686 /* Check the validity of the conference */
2687 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
2688 sizeof(the_pin), 1, &confflags);
2689 if (!cnf) {
2690 cnf = find_conf_realtime(chan, confno, 1, dynamic,
2691 the_pin, sizeof(the_pin), 1, &confflags);
2694 if (!cnf) {
2695 res = ast_streamfile(chan, "conf-invalid", chan->language);
2696 if (!res)
2697 ast_waitstream(chan, "");
2698 res = -1;
2699 if (allowretry)
2700 confno[0] = '\0';
2701 } else {
2702 if ((!ast_strlen_zero(cnf->pin) &&
2703 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2704 (!ast_strlen_zero(cnf->pinadmin) &&
2705 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2706 char pin[MAX_PIN] = "";
2707 int j;
2709 /* Allow the pin to be retried up to 3 times */
2710 for (j = 0; j < 3; j++) {
2711 if (*the_pin && (always_prompt == 0)) {
2712 ast_copy_string(pin, the_pin, sizeof(pin));
2713 res = 0;
2714 } else {
2715 /* Prompt user for pin if pin is required */
2716 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2718 if (res >= 0) {
2719 if (!strcasecmp(pin, cnf->pin) ||
2720 (!ast_strlen_zero(cnf->pinadmin) &&
2721 !strcasecmp(pin, cnf->pinadmin))) {
2722 /* Pin correct */
2723 allowretry = 0;
2724 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2725 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2726 /* Run the conference */
2727 res = conf_run(chan, cnf, confflags.flags, optargs);
2728 break;
2729 } else {
2730 /* Pin invalid */
2731 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
2732 res = ast_waitstream(chan, AST_DIGIT_ANY);
2733 ast_stopstream(chan);
2735 else {
2736 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2737 break;
2739 if (res < 0)
2740 break;
2741 pin[0] = res;
2742 pin[1] = '\0';
2743 res = -1;
2744 if (allowretry)
2745 confno[0] = '\0';
2747 } else {
2748 /* failed when getting the pin */
2749 res = -1;
2750 allowretry = 0;
2751 /* see if we need to get rid of the conference */
2752 break;
2755 /* Don't retry pin with a static pin */
2756 if (*the_pin && (always_prompt==0)) {
2757 break;
2760 } else {
2761 /* No pin required */
2762 allowretry = 0;
2764 /* Run the conference */
2765 res = conf_run(chan, cnf, confflags.flags, optargs);
2767 dispose_conf(cnf);
2768 cnf = NULL;
2771 } while (allowretry);
2773 if (cnf)
2774 dispose_conf(cnf);
2776 ast_module_user_remove(u);
2778 return res;
2781 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2783 struct ast_conf_user *user = NULL;
2784 int cid;
2786 sscanf(callerident, "%i", &cid);
2787 if (conf && callerident) {
2788 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2789 if (cid == user->user_no)
2790 return user;
2793 return NULL;
2796 /*! \brief The MeetMeadmin application */
2797 /* MeetMeAdmin(confno, command, caller) */
2798 static int admin_exec(struct ast_channel *chan, void *data) {
2799 char *params;
2800 struct ast_conference *cnf;
2801 struct ast_conf_user *user = NULL;
2802 struct ast_module_user *u;
2803 AST_DECLARE_APP_ARGS(args,
2804 AST_APP_ARG(confno);
2805 AST_APP_ARG(command);
2806 AST_APP_ARG(user);
2809 if (ast_strlen_zero(data)) {
2810 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
2811 return -1;
2814 u = ast_module_user_add(chan);
2816 AST_LIST_LOCK(&confs);
2818 params = ast_strdupa(data);
2819 AST_STANDARD_APP_ARGS(args, params);
2821 if (!args.command) {
2822 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2823 AST_LIST_UNLOCK(&confs);
2824 ast_module_user_remove(u);
2825 return -1;
2827 AST_LIST_TRAVERSE(&confs, cnf, list) {
2828 if (!strcmp(cnf->confno, args.confno))
2829 break;
2832 if (!cnf) {
2833 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
2834 AST_LIST_UNLOCK(&confs);
2835 ast_module_user_remove(u);
2836 return 0;
2839 ast_atomic_fetchadd_int(&cnf->refcount, 1);
2841 if (args.user)
2842 user = find_user(cnf, args.user);
2844 switch (*args.command) {
2845 case 76: /* L: Lock */
2846 cnf->locked = 1;
2847 break;
2848 case 108: /* l: Unlock */
2849 cnf->locked = 0;
2850 break;
2851 case 75: /* K: kick all users */
2852 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2853 user->adminflags |= ADMINFLAG_KICKME;
2854 break;
2855 case 101: /* e: Eject last user*/
2856 user = AST_LIST_LAST(&cnf->userlist);
2857 if (!(user->userflags & CONFFLAG_ADMIN))
2858 user->adminflags |= ADMINFLAG_KICKME;
2859 else
2860 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2861 break;
2862 case 77: /* M: Mute */
2863 if (user) {
2864 user->adminflags |= ADMINFLAG_MUTED;
2865 } else
2866 ast_log(LOG_NOTICE, "Specified User not found!\n");
2867 break;
2868 case 78: /* N: Mute all (non-admin) users */
2869 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2870 if (!(user->userflags & CONFFLAG_ADMIN))
2871 user->adminflags |= ADMINFLAG_MUTED;
2873 break;
2874 case 109: /* m: Unmute */
2875 if (user) {
2876 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2877 } else
2878 ast_log(LOG_NOTICE, "Specified User not found!\n");
2879 break;
2880 case 110: /* n: Unmute all users */
2881 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2882 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2883 break;
2884 case 107: /* k: Kick user */
2885 if (user)
2886 user->adminflags |= ADMINFLAG_KICKME;
2887 else
2888 ast_log(LOG_NOTICE, "Specified User not found!\n");
2889 break;
2890 case 118: /* v: Lower all users listen volume */
2891 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2892 tweak_listen_volume(user, VOL_DOWN);
2893 break;
2894 case 86: /* V: Raise all users listen volume */
2895 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2896 tweak_listen_volume(user, VOL_UP);
2897 break;
2898 case 115: /* s: Lower all users speaking volume */
2899 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2900 tweak_talk_volume(user, VOL_DOWN);
2901 break;
2902 case 83: /* S: Raise all users speaking volume */
2903 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2904 tweak_talk_volume(user, VOL_UP);
2905 break;
2906 case 82: /* R: Reset all volume levels */
2907 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2908 reset_volumes(user);
2909 break;
2910 case 114: /* r: Reset user's volume level */
2911 if (user)
2912 reset_volumes(user);
2913 else
2914 ast_log(LOG_NOTICE, "Specified User not found!\n");
2915 break;
2916 case 85: /* U: Raise user's listen volume */
2917 if (user)
2918 tweak_listen_volume(user, VOL_UP);
2919 else
2920 ast_log(LOG_NOTICE, "Specified User not found!\n");
2921 break;
2922 case 117: /* u: Lower user's listen volume */
2923 if (user)
2924 tweak_listen_volume(user, VOL_DOWN);
2925 else
2926 ast_log(LOG_NOTICE, "Specified User not found!\n");
2927 break;
2928 case 84: /* T: Raise user's talk volume */
2929 if (user)
2930 tweak_talk_volume(user, VOL_UP);
2931 else
2932 ast_log(LOG_NOTICE, "Specified User not found!\n");
2933 break;
2934 case 116: /* t: Lower user's talk volume */
2935 if (user)
2936 tweak_talk_volume(user, VOL_DOWN);
2937 else
2938 ast_log(LOG_NOTICE, "Specified User not found!\n");
2939 break;
2942 AST_LIST_UNLOCK(&confs);
2944 dispose_conf(cnf);
2946 ast_module_user_remove(u);
2948 return 0;
2951 static int meetmemute(struct mansession *s, const struct message *m, int mute)
2953 struct ast_conference *conf;
2954 struct ast_conf_user *user;
2955 const char *confid = astman_get_header(m, "Meetme");
2956 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
2957 int userno;
2959 if (ast_strlen_zero(confid)) {
2960 astman_send_error(s, m, "Meetme conference not specified");
2961 return 0;
2964 if (ast_strlen_zero(userid)) {
2965 astman_send_error(s, m, "Meetme user number not specified");
2966 return 0;
2969 userno = strtoul(userid, &userid, 10);
2971 if (*userid) {
2972 astman_send_error(s, m, "Invalid user number");
2973 return 0;
2976 /* Look in the conference list */
2977 AST_LIST_LOCK(&confs);
2978 AST_LIST_TRAVERSE(&confs, conf, list) {
2979 if (!strcmp(confid, conf->confno))
2980 break;
2983 if (!conf) {
2984 AST_LIST_UNLOCK(&confs);
2985 astman_send_error(s, m, "Meetme conference does not exist");
2986 return 0;
2989 AST_LIST_TRAVERSE(&conf->userlist, user, list)
2990 if (user->user_no == userno)
2991 break;
2993 if (!user) {
2994 AST_LIST_UNLOCK(&confs);
2995 astman_send_error(s, m, "User number not found");
2996 return 0;
2999 if (mute)
3000 user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
3001 else
3002 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED); /* request user unmuting */
3004 AST_LIST_UNLOCK(&confs);
3006 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);
3008 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
3009 return 0;
3012 static int action_meetmemute(struct mansession *s, const struct message *m)
3014 return meetmemute(s, m, 1);
3017 static int action_meetmeunmute(struct mansession *s, const struct message *m)
3019 return meetmemute(s, m, 0);
3022 static void *recordthread(void *args)
3024 struct ast_conference *cnf = args;
3025 struct ast_frame *f=NULL;
3026 int flags;
3027 struct ast_filestream *s=NULL;
3028 int res=0;
3029 int x;
3030 const char *oldrecordingfilename = NULL;
3032 if (!cnf || !cnf->lchan) {
3033 pthread_exit(0);
3036 ast_stopstream(cnf->lchan);
3037 flags = O_CREAT|O_TRUNC|O_WRONLY;
3040 cnf->recording = MEETME_RECORD_ACTIVE;
3041 while (ast_waitfor(cnf->lchan, -1) > -1) {
3042 if (cnf->recording == MEETME_RECORD_TERMINATE) {
3043 AST_LIST_LOCK(&confs);
3044 AST_LIST_UNLOCK(&confs);
3045 break;
3047 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
3048 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
3049 oldrecordingfilename = cnf->recordingfilename;
3052 f = ast_read(cnf->lchan);
3053 if (!f) {
3054 res = -1;
3055 break;
3057 if (f->frametype == AST_FRAME_VOICE) {
3058 ast_mutex_lock(&cnf->listenlock);
3059 for (x=0;x<AST_FRAME_BITS;x++) {
3060 /* Free any translations that have occured */
3061 if (cnf->transframe[x]) {
3062 ast_frfree(cnf->transframe[x]);
3063 cnf->transframe[x] = NULL;
3066 if (cnf->origframe)
3067 ast_frfree(cnf->origframe);
3068 cnf->origframe = ast_frdup(f);
3069 ast_mutex_unlock(&cnf->listenlock);
3070 if (s)
3071 res = ast_writestream(s, f);
3072 if (res) {
3073 ast_frfree(f);
3074 break;
3077 ast_frfree(f);
3079 cnf->recording = MEETME_RECORD_OFF;
3080 if (s)
3081 ast_closestream(s);
3083 pthread_exit(0);
3086 /*! \brief Callback for devicestate providers */
3087 static int meetmestate(const char *data)
3089 struct ast_conference *conf;
3091 /* Find conference */
3092 AST_LIST_LOCK(&confs);
3093 AST_LIST_TRAVERSE(&confs, conf, list) {
3094 if (!strcmp(data, conf->confno))
3095 break;
3097 AST_LIST_UNLOCK(&confs);
3098 if (!conf)
3099 return AST_DEVICE_INVALID;
3102 /* SKREP to fill */
3103 if (!conf->users)
3104 return AST_DEVICE_NOT_INUSE;
3106 return AST_DEVICE_INUSE;
3109 static void load_config_meetme(void)
3111 struct ast_config *cfg;
3112 const char *val;
3114 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3116 if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
3117 return;
3119 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
3120 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
3121 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
3122 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3123 } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
3124 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
3125 DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
3126 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3128 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
3129 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
3132 ast_config_destroy(cfg);
3135 /*! \brief Find an SLA trunk by name
3136 * \note This must be called with the sla_trunks container locked
3138 static struct sla_trunk *sla_find_trunk(const char *name)
3140 struct sla_trunk *trunk = NULL;
3142 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
3143 if (!strcasecmp(trunk->name, name))
3144 break;
3147 return trunk;
3150 /*! \brief Find an SLA station by name
3151 * \note This must be called with the sla_stations container locked
3153 static struct sla_station *sla_find_station(const char *name)
3155 struct sla_station *station = NULL;
3157 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
3158 if (!strcasecmp(station->name, name))
3159 break;
3162 return station;
3165 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
3166 const struct sla_station *station)
3168 struct sla_station_ref *station_ref;
3169 struct sla_trunk_ref *trunk_ref;
3171 /* For each station that has this call on hold, check for private hold. */
3172 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
3173 AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
3174 if (trunk_ref->trunk != trunk || station_ref->station == station)
3175 continue;
3176 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
3177 station_ref->station->hold_access == SLA_HOLD_PRIVATE)
3178 return 1;
3179 return 0;
3183 return 0;
3186 /*! \brief Find a trunk reference on a station by name
3187 * \param station the station
3188 * \param name the trunk's name
3189 * \return a pointer to the station's trunk reference. If the trunk
3190 * is not found, it is not idle and barge is disabled, or if
3191 * it is on hold and private hold is set, then NULL will be returned.
3193 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
3194 const char *name)
3196 struct sla_trunk_ref *trunk_ref = NULL;
3198 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3199 if (strcasecmp(trunk_ref->trunk->name, name))
3200 continue;
3202 if ( (trunk_ref->trunk->barge_disabled
3203 && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
3204 (trunk_ref->trunk->hold_stations
3205 && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
3206 && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
3207 sla_check_station_hold_access(trunk_ref->trunk, station) )
3209 trunk_ref = NULL;
3212 break;
3215 return trunk_ref;
3218 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
3220 struct sla_station_ref *station_ref;
3222 if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
3223 return NULL;
3225 station_ref->station = station;
3227 return station_ref;
3230 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
3232 struct sla_ringing_station *ringing_station;
3234 if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
3235 return NULL;
3237 ringing_station->station = station;
3238 ringing_station->ring_begin = ast_tvnow();
3240 return ringing_station;
3243 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
3244 enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
3246 struct sla_station *station;
3247 struct sla_trunk_ref *trunk_ref;
3249 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
3250 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3251 if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
3252 || trunk_ref == exclude)
3253 continue;
3254 trunk_ref->state = state;
3255 ast_device_state_changed("SLA:%s_%s", station->name, trunk->name);
3256 break;
3261 struct run_station_args {
3262 struct sla_station *station;
3263 struct sla_trunk_ref *trunk_ref;
3264 ast_mutex_t *cond_lock;
3265 ast_cond_t *cond;
3268 static void *run_station(void *data)
3270 struct sla_station *station;
3271 struct sla_trunk_ref *trunk_ref;
3272 char conf_name[MAX_CONFNUM];
3273 struct ast_flags conf_flags = { 0 };
3274 struct ast_conference *conf;
3277 struct run_station_args *args = data;
3278 station = args->station;
3279 trunk_ref = args->trunk_ref;
3280 ast_mutex_lock(args->cond_lock);
3281 ast_cond_signal(args->cond);
3282 ast_mutex_unlock(args->cond_lock);
3283 /* args is no longer valid here. */
3286 ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
3287 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
3288 ast_set_flag(&conf_flags,
3289 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
3290 ast_answer(trunk_ref->chan);
3291 conf = build_conf(conf_name, "", "", 0, 0, 1);
3292 if (conf) {
3293 conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
3294 dispose_conf(conf);
3295 conf = NULL;
3297 trunk_ref->chan = NULL;
3298 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
3299 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
3300 strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
3301 admin_exec(NULL, conf_name);
3302 trunk_ref->trunk->hold_stations = 0;
3303 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
3306 ast_dial_join(station->dial);
3307 ast_dial_destroy(station->dial);
3308 station->dial = NULL;
3310 return NULL;
3313 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
3315 char buf[80];
3316 struct sla_station_ref *station_ref;
3318 snprintf(buf, sizeof(buf), "SLA_%s|K", ringing_trunk->trunk->name);
3319 admin_exec(NULL, buf);
3320 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
3322 while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
3323 free(station_ref);
3325 free(ringing_trunk);
3328 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
3329 enum sla_station_hangup hangup)
3331 struct sla_ringing_trunk *ringing_trunk;
3332 struct sla_trunk_ref *trunk_ref;
3333 struct sla_station_ref *station_ref;
3335 ast_dial_join(ringing_station->station->dial);
3336 ast_dial_destroy(ringing_station->station->dial);
3337 ringing_station->station->dial = NULL;
3339 if (hangup == SLA_STATION_HANGUP_NORMAL)
3340 goto done;
3342 /* If the station is being hung up because of a timeout, then add it to the
3343 * list of timed out stations on each of the ringing trunks. This is so
3344 * that when doing further processing to figure out which stations should be
3345 * ringing, which trunk to answer, determining timeouts, etc., we know which
3346 * ringing trunks we should ignore. */
3347 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3348 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3349 if (ringing_trunk->trunk == trunk_ref->trunk)
3350 break;
3352 if (!trunk_ref)
3353 continue;
3354 if (!(station_ref = sla_create_station_ref(ringing_station->station)))
3355 continue;
3356 AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
3359 done:
3360 free(ringing_station);
3363 static void sla_dial_state_callback(struct ast_dial *dial)
3365 sla_queue_event(SLA_EVENT_DIAL_STATE);
3368 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
3369 * \note Assumes sla.lock is locked
3371 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
3372 const struct sla_station *station)
3374 struct sla_station_ref *timed_out_station;
3376 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
3377 if (station == timed_out_station->station)
3378 return 1;
3381 return 0;
3384 /*! \brief Choose the highest priority ringing trunk for a station
3385 * \param station the station
3386 * \param remove remove the ringing trunk once selected
3387 * \param trunk_ref a place to store the pointer to this stations reference to
3388 * the selected trunk
3389 * \return a pointer to the selected ringing trunk, or NULL if none found
3390 * \note Assumes that sla.lock is locked
3392 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
3393 struct sla_trunk_ref **trunk_ref, int remove)
3395 struct sla_trunk_ref *s_trunk_ref;
3396 struct sla_ringing_trunk *ringing_trunk = NULL;
3398 AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
3399 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
3400 /* Make sure this is the trunk we're looking for */
3401 if (s_trunk_ref->trunk != ringing_trunk->trunk)
3402 continue;
3404 /* This trunk on the station is ringing. But, make sure this station
3405 * didn't already time out while this trunk was ringing. */
3406 if (sla_check_timed_out_station(ringing_trunk, station))
3407 continue;
3409 if (remove)
3410 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
3412 if (trunk_ref)
3413 *trunk_ref = s_trunk_ref;
3415 break;
3417 AST_LIST_TRAVERSE_SAFE_END
3419 if (ringing_trunk)
3420 break;
3423 return ringing_trunk;
3426 static void sla_handle_dial_state_event(void)
3428 struct sla_ringing_station *ringing_station;
3430 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3431 struct sla_trunk_ref *s_trunk_ref = NULL;
3432 struct sla_ringing_trunk *ringing_trunk = NULL;
3433 struct run_station_args args;
3434 enum ast_dial_result dial_res;
3435 pthread_attr_t attr;
3436 pthread_t dont_care;
3437 ast_mutex_t cond_lock;
3438 ast_cond_t cond;
3440 switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
3441 case AST_DIAL_RESULT_HANGUP:
3442 case AST_DIAL_RESULT_INVALID:
3443 case AST_DIAL_RESULT_FAILED:
3444 case AST_DIAL_RESULT_TIMEOUT:
3445 case AST_DIAL_RESULT_UNANSWERED:
3446 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3447 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
3448 break;
3449 case AST_DIAL_RESULT_ANSWERED:
3450 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3451 /* Find the appropriate trunk to answer. */
3452 ast_mutex_lock(&sla.lock);
3453 ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
3454 ast_mutex_unlock(&sla.lock);
3455 if (!ringing_trunk) {
3456 ast_log(LOG_DEBUG, "Found no ringing trunk for station '%s' to answer!\n",
3457 ringing_station->station->name);
3458 break;
3460 /* Track the channel that answered this trunk */
3461 s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
3462 /* Actually answer the trunk */
3463 ast_answer(ringing_trunk->trunk->chan);
3464 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
3465 /* Now, start a thread that will connect this station to the trunk. The rest of
3466 * the code here sets up the thread and ensures that it is able to save the arguments
3467 * before they are no longer valid since they are allocated on the stack. */
3468 args.trunk_ref = s_trunk_ref;
3469 args.station = ringing_station->station;
3470 args.cond = &cond;
3471 args.cond_lock = &cond_lock;
3472 free(ringing_trunk);
3473 free(ringing_station);
3474 ast_mutex_init(&cond_lock);
3475 ast_cond_init(&cond, NULL);
3476 pthread_attr_init(&attr);
3477 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
3478 ast_mutex_lock(&cond_lock);
3479 ast_pthread_create_background(&dont_care, &attr, run_station, &args);
3480 ast_cond_wait(&cond, &cond_lock);
3481 ast_mutex_unlock(&cond_lock);
3482 ast_mutex_destroy(&cond_lock);
3483 ast_cond_destroy(&cond);
3484 pthread_attr_destroy(&attr);
3485 break;
3486 case AST_DIAL_RESULT_TRYING:
3487 case AST_DIAL_RESULT_RINGING:
3488 case AST_DIAL_RESULT_PROGRESS:
3489 case AST_DIAL_RESULT_PROCEEDING:
3490 break;
3492 if (dial_res == AST_DIAL_RESULT_ANSWERED) {
3493 /* Queue up reprocessing ringing trunks, and then ringing stations again */
3494 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
3495 sla_queue_event(SLA_EVENT_DIAL_STATE);
3496 break;
3499 AST_LIST_TRAVERSE_SAFE_END
3502 /*! \brief Check to see if this station is already ringing
3503 * \note Assumes sla.lock is locked
3505 static int sla_check_ringing_station(const struct sla_station *station)
3507 struct sla_ringing_station *ringing_station;
3509 AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
3510 if (station == ringing_station->station)
3511 return 1;
3514 return 0;
3517 /*! \brief Check to see if this station has failed to be dialed in the past minute
3518 * \note assumes sla.lock is locked
3520 static int sla_check_failed_station(const struct sla_station *station)
3522 struct sla_failed_station *failed_station;
3523 int res = 0;
3525 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
3526 if (station != failed_station->station)
3527 continue;
3528 if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
3529 AST_LIST_REMOVE_CURRENT(&sla.failed_stations, entry);
3530 free(failed_station);
3531 break;
3533 res = 1;
3535 AST_LIST_TRAVERSE_SAFE_END
3537 return res;
3540 /*! \brief Ring a station
3541 * \note Assumes sla.lock is locked
3543 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
3545 char *tech, *tech_data;
3546 struct ast_dial *dial;
3547 struct sla_ringing_station *ringing_station;
3548 const char *cid_name = NULL, *cid_num = NULL;
3549 enum ast_dial_result res;
3551 if (!(dial = ast_dial_create()))
3552 return -1;
3554 ast_dial_set_state_callback(dial, sla_dial_state_callback);
3555 tech_data = ast_strdupa(station->device);
3556 tech = strsep(&tech_data, "/");
3558 if (ast_dial_append(dial, tech, tech_data) == -1) {
3559 ast_dial_destroy(dial);
3560 return -1;
3563 if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_name)) {
3564 cid_name = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_name);
3565 free(ringing_trunk->trunk->chan->cid.cid_name);
3566 ringing_trunk->trunk->chan->cid.cid_name = NULL;
3568 if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_num)) {
3569 cid_num = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_num);
3570 free(ringing_trunk->trunk->chan->cid.cid_num);
3571 ringing_trunk->trunk->chan->cid.cid_num = NULL;
3574 res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
3576 if (cid_name)
3577 ringing_trunk->trunk->chan->cid.cid_name = ast_strdup(cid_name);
3578 if (cid_num)
3579 ringing_trunk->trunk->chan->cid.cid_num = ast_strdup(cid_num);
3581 if (res != AST_DIAL_RESULT_TRYING) {
3582 struct sla_failed_station *failed_station;
3583 ast_dial_destroy(dial);
3584 if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
3585 return -1;
3586 failed_station->station = station;
3587 failed_station->last_try = ast_tvnow();
3588 AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
3589 return -1;
3591 if (!(ringing_station = sla_create_ringing_station(station))) {
3592 ast_dial_join(dial);
3593 ast_dial_destroy(dial);
3594 return -1;
3597 station->dial = dial;
3599 AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
3601 return 0;
3604 /*! \brief Check to see if a station is in use
3606 static int sla_check_inuse_station(const struct sla_station *station)
3608 struct sla_trunk_ref *trunk_ref;
3610 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3611 if (trunk_ref->chan)
3612 return 1;
3615 return 0;
3618 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
3619 const struct sla_trunk *trunk)
3621 struct sla_trunk_ref *trunk_ref = NULL;
3623 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3624 if (trunk_ref->trunk == trunk)
3625 break;
3628 return trunk_ref;
3631 /*! \brief Calculate the ring delay for a given ringing trunk on a station
3632 * \param station the station
3633 * \param trunk the trunk. If NULL, the highest priority ringing trunk will be used
3634 * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
3636 static int sla_check_station_delay(struct sla_station *station,
3637 struct sla_ringing_trunk *ringing_trunk)
3639 struct sla_trunk_ref *trunk_ref;
3640 unsigned int delay = UINT_MAX;
3641 int time_left, time_elapsed;
3643 if (!ringing_trunk)
3644 ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
3645 else
3646 trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
3648 if (!ringing_trunk || !trunk_ref)
3649 return delay;
3651 /* If this station has a ring delay specific to the highest priority
3652 * ringing trunk, use that. Otherwise, use the ring delay specified
3653 * globally for the station. */
3654 delay = trunk_ref->ring_delay;
3655 if (!delay)
3656 delay = station->ring_delay;
3657 if (!delay)
3658 return INT_MAX;
3660 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3661 time_left = (delay * 1000) - time_elapsed;
3663 return time_left;
3666 /*! \brief Ring stations based on current set of ringing trunks
3667 * \note Assumes that sla.lock is locked
3669 static void sla_ring_stations(void)
3671 struct sla_station_ref *station_ref;
3672 struct sla_ringing_trunk *ringing_trunk;
3674 /* Make sure that every station that uses at least one of the ringing
3675 * trunks, is ringing. */
3676 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3677 AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
3678 int time_left;
3680 /* Is this station already ringing? */
3681 if (sla_check_ringing_station(station_ref->station))
3682 continue;
3684 /* Is this station already in a call? */
3685 if (sla_check_inuse_station(station_ref->station))
3686 continue;
3688 /* Did we fail to dial this station earlier? If so, has it been
3689 * a minute since we tried? */
3690 if (sla_check_failed_station(station_ref->station))
3691 continue;
3693 /* If this station already timed out while this trunk was ringing,
3694 * do not dial it again for this ringing trunk. */
3695 if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
3696 continue;
3698 /* Check for a ring delay in progress */
3699 time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
3700 if (time_left != INT_MAX && time_left > 0)
3701 continue;
3703 /* It is time to make this station begin to ring. Do it! */
3704 sla_ring_station(ringing_trunk, station_ref->station);
3707 /* Now, all of the stations that should be ringing, are ringing. */
3710 static void sla_hangup_stations(void)
3712 struct sla_trunk_ref *trunk_ref;
3713 struct sla_ringing_station *ringing_station;
3715 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3716 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3717 struct sla_ringing_trunk *ringing_trunk;
3718 ast_mutex_lock(&sla.lock);
3719 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3720 if (trunk_ref->trunk == ringing_trunk->trunk)
3721 break;
3723 ast_mutex_unlock(&sla.lock);
3724 if (ringing_trunk)
3725 break;
3727 if (!trunk_ref) {
3728 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3729 ast_dial_join(ringing_station->station->dial);
3730 ast_dial_destroy(ringing_station->station->dial);
3731 ringing_station->station->dial = NULL;
3732 free(ringing_station);
3735 AST_LIST_TRAVERSE_SAFE_END
3738 static void sla_handle_ringing_trunk_event(void)
3740 ast_mutex_lock(&sla.lock);
3741 sla_ring_stations();
3742 ast_mutex_unlock(&sla.lock);
3744 /* Find stations that shouldn't be ringing anymore. */
3745 sla_hangup_stations();
3748 static void sla_handle_hold_event(struct sla_event *event)
3750 ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
3751 event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
3752 ast_device_state_changed("SLA:%s_%s",
3753 event->station->name, event->trunk_ref->trunk->name);
3754 sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
3755 INACTIVE_TRUNK_REFS, event->trunk_ref);
3757 if (event->trunk_ref->trunk->active_stations == 1) {
3758 /* The station putting it on hold is the only one on the call, so start
3759 * Music on hold to the trunk. */
3760 event->trunk_ref->trunk->on_hold = 1;
3761 ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
3764 ast_softhangup(event->trunk_ref->chan, AST_CAUSE_NORMAL);
3765 event->trunk_ref->chan = NULL;
3768 /*! \brief Process trunk ring timeouts
3769 * \note Called with sla.lock locked
3770 * \return non-zero if a change to the ringing trunks was made
3772 static int sla_calc_trunk_timeouts(unsigned int *timeout)
3774 struct sla_ringing_trunk *ringing_trunk;
3775 int res = 0;
3777 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
3778 int time_left, time_elapsed;
3779 if (!ringing_trunk->trunk->ring_timeout)
3780 continue;
3781 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3782 time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
3783 if (time_left <= 0) {
3784 pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
3785 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
3786 sla_stop_ringing_trunk(ringing_trunk);
3787 res = 1;
3788 continue;
3790 if (time_left < *timeout)
3791 *timeout = time_left;
3793 AST_LIST_TRAVERSE_SAFE_END
3795 return res;
3798 /*! \brief Process station ring timeouts
3799 * \note Called with sla.lock locked
3800 * \return non-zero if a change to the ringing stations was made
3802 static int sla_calc_station_timeouts(unsigned int *timeout)
3804 struct sla_ringing_trunk *ringing_trunk;
3805 struct sla_ringing_station *ringing_station;
3806 int res = 0;
3808 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3809 unsigned int ring_timeout = 0;
3810 int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
3811 struct sla_trunk_ref *trunk_ref;
3813 /* If there are any ring timeouts specified for a specific trunk
3814 * on the station, then use the highest per-trunk ring timeout.
3815 * Otherwise, use the ring timeout set for the entire station. */
3816 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3817 struct sla_station_ref *station_ref;
3818 int trunk_time_elapsed, trunk_time_left;
3820 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3821 if (ringing_trunk->trunk == trunk_ref->trunk)
3822 break;
3824 if (!ringing_trunk)
3825 continue;
3827 /* If there is a trunk that is ringing without a timeout, then the
3828 * only timeout that could matter is a global station ring timeout. */
3829 if (!trunk_ref->ring_timeout)
3830 break;
3832 /* This trunk on this station is ringing and has a timeout.
3833 * However, make sure this trunk isn't still ringing from a
3834 * previous timeout. If so, don't consider it. */
3835 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
3836 if (station_ref->station == ringing_station->station)
3837 break;
3839 if (station_ref)
3840 continue;
3842 trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3843 trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
3844 if (trunk_time_left > final_trunk_time_left)
3845 final_trunk_time_left = trunk_time_left;
3848 /* No timeout was found for ringing trunks, and no timeout for the entire station */
3849 if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
3850 continue;
3852 /* Compute how much time is left for a global station timeout */
3853 if (ringing_station->station->ring_timeout) {
3854 ring_timeout = ringing_station->station->ring_timeout;
3855 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
3856 time_left = (ring_timeout * 1000) - time_elapsed;
3859 /* If the time left based on the per-trunk timeouts is smaller than the
3860 * global station ring timeout, use that. */
3861 if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
3862 time_left = final_trunk_time_left;
3864 /* If there is no time left, the station needs to stop ringing */
3865 if (time_left <= 0) {
3866 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3867 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
3868 res = 1;
3869 continue;
3872 /* There is still some time left for this station to ring, so save that
3873 * timeout if it is the first event scheduled to occur */
3874 if (time_left < *timeout)
3875 *timeout = time_left;
3877 AST_LIST_TRAVERSE_SAFE_END
3879 return res;
3882 /*! \brief Calculate the ring delay for a station
3883 * \note Assumes sla.lock is locked
3885 static int sla_calc_station_delays(unsigned int *timeout)
3887 struct sla_station *station;
3888 int res = 0;
3890 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
3891 struct sla_ringing_trunk *ringing_trunk;
3892 int time_left;
3894 /* Ignore stations already ringing */
3895 if (sla_check_ringing_station(station))
3896 continue;
3898 /* Ignore stations already on a call */
3899 if (sla_check_inuse_station(station))
3900 continue;
3902 /* Ignore stations that don't have one of their trunks ringing */
3903 if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
3904 continue;
3906 if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
3907 continue;
3909 /* If there is no time left, then the station needs to start ringing.
3910 * Return non-zero so that an event will be queued up an event to
3911 * make that happen. */
3912 if (time_left <= 0) {
3913 res = 1;
3914 continue;
3917 if (time_left < *timeout)
3918 *timeout = time_left;
3921 return res;
3924 /*! \brief Calculate the time until the next known event
3925 * \note Called with sla.lock locked */
3926 static int sla_process_timers(struct timespec *ts)
3928 unsigned int timeout = UINT_MAX;
3929 struct timeval tv;
3930 unsigned int change_made = 0;
3932 /* Check for ring timeouts on ringing trunks */
3933 if (sla_calc_trunk_timeouts(&timeout))
3934 change_made = 1;
3936 /* Check for ring timeouts on ringing stations */
3937 if (sla_calc_station_timeouts(&timeout))
3938 change_made = 1;
3940 /* Check for station ring delays */
3941 if (sla_calc_station_delays(&timeout))
3942 change_made = 1;
3944 /* queue reprocessing of ringing trunks */
3945 if (change_made)
3946 sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
3948 /* No timeout */
3949 if (timeout == UINT_MAX)
3950 return 0;
3952 if (ts) {
3953 tv = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
3954 ts->tv_sec = tv.tv_sec;
3955 ts->tv_nsec = tv.tv_usec * 1000;
3958 return 1;
3961 static void *sla_thread(void *data)
3963 struct sla_failed_station *failed_station;
3964 struct sla_ringing_station *ringing_station;
3966 ast_mutex_lock(&sla.lock);
3968 while (!sla.stop) {
3969 struct sla_event *event;
3970 struct timespec ts = { 0, };
3971 unsigned int have_timeout = 0;
3973 if (AST_LIST_EMPTY(&sla.event_q)) {
3974 if ((have_timeout = sla_process_timers(&ts)))
3975 ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
3976 else
3977 ast_cond_wait(&sla.cond, &sla.lock);
3978 if (sla.stop)
3979 break;
3982 if (have_timeout)
3983 sla_process_timers(NULL);
3985 while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
3986 ast_mutex_unlock(&sla.lock);
3987 switch (event->type) {
3988 case SLA_EVENT_HOLD:
3989 sla_handle_hold_event(event);
3990 break;
3991 case SLA_EVENT_DIAL_STATE:
3992 sla_handle_dial_state_event();
3993 break;
3994 case SLA_EVENT_RINGING_TRUNK:
3995 sla_handle_ringing_trunk_event();
3996 break;
3998 free(event);
3999 ast_mutex_lock(&sla.lock);
4003 ast_mutex_unlock(&sla.lock);
4005 while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
4006 free(ringing_station);
4008 while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
4009 free(failed_station);
4011 return NULL;
4014 struct dial_trunk_args {
4015 struct sla_trunk_ref *trunk_ref;
4016 struct sla_station *station;
4017 ast_mutex_t *cond_lock;
4018 ast_cond_t *cond;
4021 static void *dial_trunk(void *data)
4023 struct dial_trunk_args *args = data;
4024 struct ast_dial *dial;
4025 char *tech, *tech_data;
4026 enum ast_dial_result dial_res;
4027 char conf_name[MAX_CONFNUM];
4028 struct ast_conference *conf;
4029 struct ast_flags conf_flags = { 0 };
4030 struct sla_trunk_ref *trunk_ref = args->trunk_ref;
4031 const char *cid_name = NULL, *cid_num = NULL;
4033 if (!(dial = ast_dial_create())) {
4034 ast_mutex_lock(args->cond_lock);
4035 ast_cond_signal(args->cond);
4036 ast_mutex_unlock(args->cond_lock);
4037 return NULL;
4040 tech_data = ast_strdupa(trunk_ref->trunk->device);
4041 tech = strsep(&tech_data, "/");
4042 if (ast_dial_append(dial, tech, tech_data) == -1) {
4043 ast_mutex_lock(args->cond_lock);
4044 ast_cond_signal(args->cond);
4045 ast_mutex_unlock(args->cond_lock);
4046 ast_dial_destroy(dial);
4047 return NULL;
4050 if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_name)) {
4051 cid_name = ast_strdupa(trunk_ref->chan->cid.cid_name);
4052 free(trunk_ref->chan->cid.cid_name);
4053 trunk_ref->chan->cid.cid_name = NULL;
4055 if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_num)) {
4056 cid_num = ast_strdupa(trunk_ref->chan->cid.cid_num);
4057 free(trunk_ref->chan->cid.cid_num);
4058 trunk_ref->chan->cid.cid_num = NULL;
4061 dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
4063 if (cid_name)
4064 trunk_ref->chan->cid.cid_name = ast_strdup(cid_name);
4065 if (cid_num)
4066 trunk_ref->chan->cid.cid_num = ast_strdup(cid_num);
4068 if (dial_res != AST_DIAL_RESULT_TRYING) {
4069 ast_mutex_lock(args->cond_lock);
4070 ast_cond_signal(args->cond);
4071 ast_mutex_unlock(args->cond_lock);
4072 ast_dial_destroy(dial);
4073 return NULL;
4076 for (;;) {
4077 unsigned int done = 0;
4078 switch ((dial_res = ast_dial_state(dial))) {
4079 case AST_DIAL_RESULT_ANSWERED:
4080 trunk_ref->trunk->chan = ast_dial_answered(dial);
4081 case AST_DIAL_RESULT_HANGUP:
4082 case AST_DIAL_RESULT_INVALID:
4083 case AST_DIAL_RESULT_FAILED:
4084 case AST_DIAL_RESULT_TIMEOUT:
4085 case AST_DIAL_RESULT_UNANSWERED:
4086 done = 1;
4087 case AST_DIAL_RESULT_TRYING:
4088 case AST_DIAL_RESULT_RINGING:
4089 case AST_DIAL_RESULT_PROGRESS:
4090 case AST_DIAL_RESULT_PROCEEDING:
4091 break;
4093 if (done)
4094 break;
4097 if (!trunk_ref->trunk->chan) {
4098 ast_mutex_lock(args->cond_lock);
4099 ast_cond_signal(args->cond);
4100 ast_mutex_unlock(args->cond_lock);
4101 ast_dial_join(dial);
4102 ast_dial_destroy(dial);
4103 return NULL;
4106 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
4107 ast_set_flag(&conf_flags,
4108 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
4109 CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
4110 conf = build_conf(conf_name, "", "", 1, 1, 1);
4112 ast_mutex_lock(args->cond_lock);
4113 ast_cond_signal(args->cond);
4114 ast_mutex_unlock(args->cond_lock);
4116 if (conf) {
4117 conf_run(trunk_ref->trunk->chan, conf, conf_flags.flags, NULL);
4118 dispose_conf(conf);
4119 conf = NULL;
4122 /* If the trunk is going away, it is definitely now IDLE. */
4123 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4125 trunk_ref->trunk->chan = NULL;
4126 trunk_ref->trunk->on_hold = 0;
4128 ast_dial_join(dial);
4129 ast_dial_destroy(dial);
4131 return NULL;
4134 /*! \brief For a given station, choose the highest priority idle trunk
4136 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
4138 struct sla_trunk_ref *trunk_ref = NULL;
4140 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4141 if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
4142 break;
4145 return trunk_ref;
4148 static int sla_station_exec(struct ast_channel *chan, void *data)
4150 char *station_name, *trunk_name;
4151 struct sla_station *station;
4152 struct sla_trunk_ref *trunk_ref = NULL;
4153 char conf_name[MAX_CONFNUM];
4154 struct ast_flags conf_flags = { 0 };
4155 struct ast_conference *conf;
4157 if (ast_strlen_zero(data)) {
4158 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
4159 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4160 return 0;
4163 trunk_name = ast_strdupa(data);
4164 station_name = strsep(&trunk_name, "_");
4166 if (ast_strlen_zero(station_name)) {
4167 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
4168 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4169 return 0;
4172 AST_RWLIST_RDLOCK(&sla_stations);
4173 station = sla_find_station(station_name);
4174 AST_RWLIST_UNLOCK(&sla_stations);
4176 if (!station) {
4177 ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
4178 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4179 return 0;
4182 AST_RWLIST_RDLOCK(&sla_trunks);
4183 if (!ast_strlen_zero(trunk_name)) {
4184 trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
4185 } else
4186 trunk_ref = sla_choose_idle_trunk(station);
4187 AST_RWLIST_UNLOCK(&sla_trunks);
4189 if (!trunk_ref) {
4190 if (ast_strlen_zero(trunk_name))
4191 ast_log(LOG_NOTICE, "No trunks available for call.\n");
4192 else {
4193 ast_log(LOG_NOTICE, "Can't join existing call on trunk "
4194 "'%s' due to access controls.\n", trunk_name);
4196 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
4197 return 0;
4200 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
4201 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
4202 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4203 else {
4204 trunk_ref->state = SLA_TRUNK_STATE_UP;
4205 ast_device_state_changed("SLA:%s_%s", station->name, trunk_ref->trunk->name);
4207 } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
4208 struct sla_ringing_trunk *ringing_trunk;
4210 ast_mutex_lock(&sla.lock);
4211 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
4212 if (ringing_trunk->trunk == trunk_ref->trunk) {
4213 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
4214 break;
4217 AST_LIST_TRAVERSE_SAFE_END
4218 ast_mutex_unlock(&sla.lock);
4220 if (ringing_trunk) {
4221 ast_answer(ringing_trunk->trunk->chan);
4222 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4224 free(ringing_trunk);
4226 /* Queue up reprocessing ringing trunks, and then ringing stations again */
4227 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4228 sla_queue_event(SLA_EVENT_DIAL_STATE);
4232 trunk_ref->chan = chan;
4234 if (!trunk_ref->trunk->chan) {
4235 ast_mutex_t cond_lock;
4236 ast_cond_t cond;
4237 pthread_t dont_care;
4238 pthread_attr_t attr;
4239 struct dial_trunk_args args = {
4240 .trunk_ref = trunk_ref,
4241 .station = station,
4242 .cond_lock = &cond_lock,
4243 .cond = &cond,
4245 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4246 /* Create a thread to dial the trunk and dump it into the conference.
4247 * However, we want to wait until the trunk has been dialed and the
4248 * conference is created before continuing on here. */
4249 ast_autoservice_start(chan);
4250 ast_mutex_init(&cond_lock);
4251 ast_cond_init(&cond, NULL);
4252 pthread_attr_init(&attr);
4253 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
4254 ast_mutex_lock(&cond_lock);
4255 ast_pthread_create_background(&dont_care, &attr, dial_trunk, &args);
4256 ast_cond_wait(&cond, &cond_lock);
4257 ast_mutex_unlock(&cond_lock);
4258 ast_mutex_destroy(&cond_lock);
4259 ast_cond_destroy(&cond);
4260 pthread_attr_destroy(&attr);
4261 ast_autoservice_stop(chan);
4262 if (!trunk_ref->trunk->chan) {
4263 ast_log(LOG_DEBUG, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
4264 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
4265 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4266 trunk_ref->chan = NULL;
4267 return 0;
4271 if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
4272 trunk_ref->trunk->on_hold) {
4273 trunk_ref->trunk->on_hold = 0;
4274 ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
4275 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4278 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
4279 ast_set_flag(&conf_flags,
4280 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
4281 ast_answer(chan);
4282 conf = build_conf(conf_name, "", "", 0, 0, 1);
4283 if (conf) {
4284 conf_run(chan, conf, conf_flags.flags, NULL);
4285 dispose_conf(conf);
4286 conf = NULL;
4288 trunk_ref->chan = NULL;
4289 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
4290 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
4291 strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
4292 admin_exec(NULL, conf_name);
4293 trunk_ref->trunk->hold_stations = 0;
4294 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4297 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
4299 return 0;
4302 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
4304 struct sla_trunk_ref *trunk_ref;
4306 if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
4307 return NULL;
4309 trunk_ref->trunk = trunk;
4311 return trunk_ref;
4314 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
4316 struct sla_ringing_trunk *ringing_trunk;
4318 if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
4319 return NULL;
4321 ringing_trunk->trunk = trunk;
4322 ringing_trunk->ring_begin = ast_tvnow();
4324 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
4326 ast_mutex_lock(&sla.lock);
4327 AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
4328 ast_mutex_unlock(&sla.lock);
4330 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4332 return ringing_trunk;
4335 static int sla_trunk_exec(struct ast_channel *chan, void *data)
4337 const char *trunk_name = data;
4338 char conf_name[MAX_CONFNUM];
4339 struct ast_conference *conf;
4340 struct ast_flags conf_flags = { 0 };
4341 struct sla_trunk *trunk;
4342 struct sla_ringing_trunk *ringing_trunk;
4344 AST_RWLIST_RDLOCK(&sla_trunks);
4345 trunk = sla_find_trunk(trunk_name);
4346 AST_RWLIST_UNLOCK(&sla_trunks);
4347 if (!trunk) {
4348 ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", trunk_name);
4349 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4350 return 0;
4352 if (trunk->chan) {
4353 ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
4354 trunk_name);
4355 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4356 return 0;
4358 trunk->chan = chan;
4360 if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
4361 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4362 return 0;
4365 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_name);
4366 conf = build_conf(conf_name, "", "", 1, 1, 1);
4367 if (!conf) {
4368 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4369 return 0;
4371 ast_set_flag(&conf_flags,
4372 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF);
4373 ast_indicate(chan, AST_CONTROL_RINGING);
4374 conf_run(chan, conf, conf_flags.flags, NULL);
4375 dispose_conf(conf);
4376 conf = NULL;
4377 trunk->chan = NULL;
4378 trunk->on_hold = 0;
4380 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4382 if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
4383 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
4385 /* Remove the entry from the list of ringing trunks if it is still there. */
4386 ast_mutex_lock(&sla.lock);
4387 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
4388 if (ringing_trunk->trunk == trunk) {
4389 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
4390 break;
4393 AST_LIST_TRAVERSE_SAFE_END
4394 ast_mutex_unlock(&sla.lock);
4395 if (ringing_trunk) {
4396 free(ringing_trunk);
4397 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
4398 /* Queue reprocessing of ringing trunks to make stations stop ringing
4399 * that shouldn't be ringing after this trunk stopped. */
4400 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4403 return 0;
4406 static int sla_state(const char *data)
4408 char *buf, *station_name, *trunk_name;
4409 struct sla_station *station;
4410 struct sla_trunk_ref *trunk_ref;
4411 int res = AST_DEVICE_INVALID;
4413 trunk_name = buf = ast_strdupa(data);
4414 station_name = strsep(&trunk_name, "_");
4416 AST_RWLIST_RDLOCK(&sla_stations);
4417 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
4418 if (strcasecmp(station_name, station->name))
4419 continue;
4420 AST_RWLIST_RDLOCK(&sla_trunks);
4421 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4422 if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
4423 break;
4425 if (!trunk_ref) {
4426 AST_RWLIST_UNLOCK(&sla_trunks);
4427 break;
4429 switch (trunk_ref->state) {
4430 case SLA_TRUNK_STATE_IDLE:
4431 res = AST_DEVICE_NOT_INUSE;
4432 break;
4433 case SLA_TRUNK_STATE_RINGING:
4434 res = AST_DEVICE_RINGING;
4435 break;
4436 case SLA_TRUNK_STATE_UP:
4437 res = AST_DEVICE_INUSE;
4438 break;
4439 case SLA_TRUNK_STATE_ONHOLD:
4440 case SLA_TRUNK_STATE_ONHOLD_BYME:
4441 res = AST_DEVICE_ONHOLD;
4442 break;
4444 AST_RWLIST_UNLOCK(&sla_trunks);
4446 AST_RWLIST_UNLOCK(&sla_stations);
4448 if (res == AST_DEVICE_INVALID) {
4449 ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
4450 trunk_name, station_name);
4453 return res;
4456 static void destroy_trunk(struct sla_trunk *trunk)
4458 struct sla_station_ref *station_ref;
4460 if (!ast_strlen_zero(trunk->autocontext))
4461 ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
4463 while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
4464 free(station_ref);
4466 ast_string_field_free_memory(trunk);
4467 free(trunk);
4470 static void destroy_station(struct sla_station *station)
4472 struct sla_trunk_ref *trunk_ref;
4474 if (!ast_strlen_zero(station->autocontext)) {
4475 AST_RWLIST_RDLOCK(&sla_trunks);
4476 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4477 char exten[AST_MAX_EXTENSION];
4478 char hint[AST_MAX_APP];
4479 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
4480 snprintf(hint, sizeof(hint), "SLA:%s", exten);
4481 ast_context_remove_extension(station->autocontext, exten,
4482 1, sla_registrar);
4483 ast_context_remove_extension(station->autocontext, hint,
4484 PRIORITY_HINT, sla_registrar);
4486 AST_RWLIST_UNLOCK(&sla_trunks);
4489 while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
4490 free(trunk_ref);
4492 ast_string_field_free_memory(station);
4493 free(station);
4496 static void sla_destroy(void)
4498 struct sla_trunk *trunk;
4499 struct sla_station *station;
4501 AST_RWLIST_WRLOCK(&sla_trunks);
4502 while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
4503 destroy_trunk(trunk);
4504 AST_RWLIST_UNLOCK(&sla_trunks);
4506 AST_RWLIST_WRLOCK(&sla_stations);
4507 while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
4508 destroy_station(station);
4509 AST_RWLIST_UNLOCK(&sla_stations);
4511 if (sla.thread != AST_PTHREADT_NULL) {
4512 ast_mutex_lock(&sla.lock);
4513 sla.stop = 1;
4514 ast_cond_signal(&sla.cond);
4515 ast_mutex_unlock(&sla.lock);
4516 pthread_join(sla.thread, NULL);
4519 /* Drop any created contexts from the dialplan */
4520 ast_context_destroy(NULL, sla_registrar);
4522 ast_mutex_destroy(&sla.lock);
4523 ast_cond_destroy(&sla.cond);
4526 static int sla_check_device(const char *device)
4528 char *tech, *tech_data;
4530 tech_data = ast_strdupa(device);
4531 tech = strsep(&tech_data, "/");
4533 if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
4534 return -1;
4536 return 0;
4539 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
4541 struct sla_trunk *trunk;
4542 struct ast_variable *var;
4543 const char *dev;
4545 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
4546 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
4547 return -1;
4550 if (sla_check_device(dev)) {
4551 ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
4552 cat, dev);
4553 return -1;
4556 if (!(trunk = ast_calloc(1, sizeof(*trunk))))
4557 return -1;
4558 if (ast_string_field_init(trunk, 32)) {
4559 free(trunk);
4560 return -1;
4563 ast_string_field_set(trunk, name, cat);
4564 ast_string_field_set(trunk, device, dev);
4566 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
4567 if (!strcasecmp(var->name, "autocontext"))
4568 ast_string_field_set(trunk, autocontext, var->value);
4569 else if (!strcasecmp(var->name, "ringtimeout")) {
4570 if (sscanf(var->value, "%u", &trunk->ring_timeout) != 1) {
4571 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
4572 var->value, trunk->name);
4573 trunk->ring_timeout = 0;
4575 } else if (!strcasecmp(var->name, "barge"))
4576 trunk->barge_disabled = ast_false(var->value);
4577 else if (!strcasecmp(var->name, "hold")) {
4578 if (!strcasecmp(var->value, "private"))
4579 trunk->hold_access = SLA_HOLD_PRIVATE;
4580 else if (!strcasecmp(var->value, "open"))
4581 trunk->hold_access = SLA_HOLD_OPEN;
4582 else {
4583 ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
4584 var->value, trunk->name);
4586 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
4587 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
4588 var->name, var->lineno, SLA_CONFIG_FILE);
4592 if (!ast_strlen_zero(trunk->autocontext)) {
4593 struct ast_context *context;
4594 context = ast_context_find_or_create(NULL, trunk->autocontext, sla_registrar);
4595 if (!context) {
4596 ast_log(LOG_ERROR, "Failed to automatically find or create "
4597 "context '%s' for SLA!\n", trunk->autocontext);
4598 destroy_trunk(trunk);
4599 return -1;
4601 if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
4602 NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free, sla_registrar)) {
4603 ast_log(LOG_ERROR, "Failed to automatically create extension "
4604 "for trunk '%s'!\n", trunk->name);
4605 destroy_trunk(trunk);
4606 return -1;
4610 AST_RWLIST_WRLOCK(&sla_trunks);
4611 AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
4612 AST_RWLIST_UNLOCK(&sla_trunks);
4614 return 0;
4617 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
4619 struct sla_trunk *trunk;
4620 struct sla_trunk_ref *trunk_ref;
4621 struct sla_station_ref *station_ref;
4622 char *trunk_name, *options, *cur;
4624 options = ast_strdupa(var->value);
4625 trunk_name = strsep(&options, ",");
4627 AST_RWLIST_RDLOCK(&sla_trunks);
4628 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
4629 if (!strcasecmp(trunk->name, trunk_name))
4630 break;
4633 AST_RWLIST_UNLOCK(&sla_trunks);
4634 if (!trunk) {
4635 ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
4636 return;
4638 if (!(trunk_ref = create_trunk_ref(trunk)))
4639 return;
4640 trunk_ref->state = SLA_TRUNK_STATE_IDLE;
4642 while ((cur = strsep(&options, ","))) {
4643 char *name, *value = cur;
4644 name = strsep(&value, "=");
4645 if (!strcasecmp(name, "ringtimeout")) {
4646 if (sscanf(value, "%u", &trunk_ref->ring_timeout) != 1) {
4647 ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
4648 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
4649 trunk_ref->ring_timeout = 0;
4651 } else if (!strcasecmp(name, "ringdelay")) {
4652 if (sscanf(value, "%u", &trunk_ref->ring_delay) != 1) {
4653 ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
4654 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
4655 trunk_ref->ring_delay = 0;
4657 } else {
4658 ast_log(LOG_WARNING, "Invalid option '%s' for "
4659 "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
4663 if (!(station_ref = sla_create_station_ref(station))) {
4664 free(trunk_ref);
4665 return;
4667 ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
4668 AST_RWLIST_WRLOCK(&sla_trunks);
4669 AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
4670 AST_RWLIST_UNLOCK(&sla_trunks);
4671 AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
4674 static int sla_build_station(struct ast_config *cfg, const char *cat)
4676 struct sla_station *station;
4677 struct ast_variable *var;
4678 const char *dev;
4680 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
4681 ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
4682 return -1;
4685 if (!(station = ast_calloc(1, sizeof(*station))))
4686 return -1;
4687 if (ast_string_field_init(station, 32)) {
4688 free(station);
4689 return -1;
4692 ast_string_field_set(station, name, cat);
4693 ast_string_field_set(station, device, dev);
4695 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
4696 if (!strcasecmp(var->name, "trunk"))
4697 sla_add_trunk_to_station(station, var);
4698 else if (!strcasecmp(var->name, "autocontext"))
4699 ast_string_field_set(station, autocontext, var->value);
4700 else if (!strcasecmp(var->name, "ringtimeout")) {
4701 if (sscanf(var->value, "%u", &station->ring_timeout) != 1) {
4702 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
4703 var->value, station->name);
4704 station->ring_timeout = 0;
4706 } else if (!strcasecmp(var->name, "ringdelay")) {
4707 if (sscanf(var->value, "%u", &station->ring_delay) != 1) {
4708 ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
4709 var->value, station->name);
4710 station->ring_delay = 0;
4712 } else if (!strcasecmp(var->name, "hold")) {
4713 if (!strcasecmp(var->value, "private"))
4714 station->hold_access = SLA_HOLD_PRIVATE;
4715 else if (!strcasecmp(var->value, "open"))
4716 station->hold_access = SLA_HOLD_OPEN;
4717 else {
4718 ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
4719 var->value, station->name);
4722 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
4723 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
4724 var->name, var->lineno, SLA_CONFIG_FILE);
4728 if (!ast_strlen_zero(station->autocontext)) {
4729 struct ast_context *context;
4730 struct sla_trunk_ref *trunk_ref;
4731 context = ast_context_find_or_create(NULL, station->autocontext, sla_registrar);
4732 if (!context) {
4733 ast_log(LOG_ERROR, "Failed to automatically find or create "
4734 "context '%s' for SLA!\n", station->autocontext);
4735 destroy_station(station);
4736 return -1;
4738 /* The extension for when the handset goes off-hook.
4739 * exten => station1,1,SLAStation(station1) */
4740 if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
4741 NULL, NULL, slastation_app, ast_strdup(station->name), ast_free, sla_registrar)) {
4742 ast_log(LOG_ERROR, "Failed to automatically create extension "
4743 "for trunk '%s'!\n", station->name);
4744 destroy_station(station);
4745 return -1;
4747 AST_RWLIST_RDLOCK(&sla_trunks);
4748 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4749 char exten[AST_MAX_EXTENSION];
4750 char hint[AST_MAX_APP];
4751 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
4752 snprintf(hint, sizeof(hint), "SLA:%s", exten);
4753 /* Extension for this line button
4754 * exten => station1_line1,1,SLAStation(station1_line1) */
4755 if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
4756 NULL, NULL, slastation_app, ast_strdup(exten), ast_free, sla_registrar)) {
4757 ast_log(LOG_ERROR, "Failed to automatically create extension "
4758 "for trunk '%s'!\n", station->name);
4759 destroy_station(station);
4760 return -1;
4762 /* Hint for this line button
4763 * exten => station1_line1,hint,SLA:station1_line1 */
4764 if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
4765 NULL, NULL, hint, NULL, NULL, sla_registrar)) {
4766 ast_log(LOG_ERROR, "Failed to automatically create hint "
4767 "for trunk '%s'!\n", station->name);
4768 destroy_station(station);
4769 return -1;
4772 AST_RWLIST_UNLOCK(&sla_trunks);
4775 AST_RWLIST_WRLOCK(&sla_stations);
4776 AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
4777 AST_RWLIST_UNLOCK(&sla_stations);
4779 return 0;
4782 static int sla_load_config(void)
4784 struct ast_config *cfg;
4785 const char *cat = NULL;
4786 int res = 0;
4787 const char *val;
4789 ast_mutex_init(&sla.lock);
4790 ast_cond_init(&sla.cond, NULL);
4792 if (!(cfg = ast_config_load(SLA_CONFIG_FILE)))
4793 return 0; /* Treat no config as normal */
4795 if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
4796 sla.attempt_callerid = ast_true(val);
4798 while ((cat = ast_category_browse(cfg, cat)) && !res) {
4799 const char *type;
4800 if (!strcasecmp(cat, "general"))
4801 continue;
4802 if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
4803 ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
4804 SLA_CONFIG_FILE);
4805 continue;
4807 if (!strcasecmp(type, "trunk"))
4808 res = sla_build_trunk(cfg, cat);
4809 else if (!strcasecmp(type, "station"))
4810 res = sla_build_station(cfg, cat);
4811 else {
4812 ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
4813 SLA_CONFIG_FILE, type);
4817 ast_config_destroy(cfg);
4819 if (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_stations))
4820 ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
4822 return res;
4825 static int load_config(int reload)
4827 int res = 0;
4829 load_config_meetme();
4830 if (!reload)
4831 res = sla_load_config();
4833 return res;
4836 static int unload_module(void)
4838 int res = 0;
4840 ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
4841 res = ast_manager_unregister("MeetmeMute");
4842 res |= ast_manager_unregister("MeetmeUnmute");
4843 res |= ast_unregister_application(app3);
4844 res |= ast_unregister_application(app2);
4845 res |= ast_unregister_application(app);
4846 res |= ast_unregister_application(slastation_app);
4847 res |= ast_unregister_application(slatrunk_app);
4849 ast_devstate_prov_del("Meetme");
4850 ast_devstate_prov_del("SLA");
4852 ast_module_user_hangup_all();
4854 sla_destroy();
4856 return res;
4859 static int load_module(void)
4861 int res = 0;
4863 res |= load_config(0);
4865 ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
4866 res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL,
4867 action_meetmemute, "Mute a Meetme user");
4868 res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL,
4869 action_meetmeunmute, "Unmute a Meetme user");
4870 res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
4871 res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
4872 res |= ast_register_application(app, conf_exec, synopsis, descrip);
4873 res |= ast_register_application(slastation_app, sla_station_exec,
4874 slastation_synopsis, slastation_desc);
4875 res |= ast_register_application(slatrunk_app, sla_trunk_exec,
4876 slatrunk_synopsis, slatrunk_desc);
4878 res |= ast_devstate_prov_add("Meetme", meetmestate);
4879 res |= ast_devstate_prov_add("SLA", sla_state);
4881 return res;
4884 static int reload(void)
4886 return load_config(1);
4889 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MeetMe conference bridge",
4890 .load = load_module,
4891 .unload = unload_module,
4892 .reload = reload,