re-doxygen some comments
[asterisk-bristuff.git] / apps / app_meetme.c
blob63cc0f346b0f978867a979e3ec92fc1c9cd4c94f
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2007, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * SLA Implementation by:
9 * Russell Bryant <russell@digium.com>
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
22 /*! \file
24 * \brief Meet me conference bridge and Shared Line Appearances
26 * \author Mark Spencer <markster@digium.com>
27 * \author (SLA) Russell Bryant <russell@digium.com>
29 * \ingroup applications
32 /*** MODULEINFO
33 <depend>zaptel</depend>
34 ***/
36 #include "asterisk.h"
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <errno.h>
45 #include <sys/ioctl.h>
46 #include <zaptel/zaptel.h>
48 #include "asterisk/lock.h"
49 #include "asterisk/file.h"
50 #include "asterisk/logger.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/module.h"
54 #include "asterisk/config.h"
55 #include "asterisk/app.h"
56 #include "asterisk/dsp.h"
57 #include "asterisk/musiconhold.h"
58 #include "asterisk/manager.h"
59 #include "asterisk/options.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/say.h"
62 #include "asterisk/utils.h"
63 #include "asterisk/translate.h"
64 #include "asterisk/ulaw.h"
65 #include "asterisk/astobj.h"
66 #include "asterisk/devicestate.h"
67 #include "asterisk/dial.h"
68 #include "asterisk/causes.h"
70 #include "enter.h"
71 #include "leave.h"
73 #define CONFIG_FILE_NAME "meetme.conf"
74 #define SLA_CONFIG_FILE "sla.conf"
76 /*! each buffer is 20ms, so this is 640ms total */
77 #define DEFAULT_AUDIO_BUFFERS 32
79 enum {
80 ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */
81 ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
82 ADMINFLAG_KICKME = (1 << 3) /*!< User has been kicked */
85 #define MEETME_DELAYDETECTTALK 300
86 #define MEETME_DELAYDETECTENDTALK 1000
88 #define AST_FRAME_BITS 32
90 enum volume_action {
91 VOL_UP,
92 VOL_DOWN
95 enum entrance_sound {
96 ENTER,
97 LEAVE
100 enum recording_state {
101 MEETME_RECORD_OFF,
102 MEETME_RECORD_STARTED,
103 MEETME_RECORD_ACTIVE,
104 MEETME_RECORD_TERMINATE
107 #define CONF_SIZE 320
109 enum {
110 /*! user has admin access on the conference */
111 CONFFLAG_ADMIN = (1 << 0),
112 /*! If set the user can only receive audio from the conference */
113 CONFFLAG_MONITOR = (1 << 1),
114 /*! If set asterisk will exit conference when '#' is pressed */
115 CONFFLAG_POUNDEXIT = (1 << 2),
116 /*! If set asterisk will provide a menu to the user when '*' is pressed */
117 CONFFLAG_STARMENU = (1 << 3),
118 /*! If set the use can only send audio to the conference */
119 CONFFLAG_TALKER = (1 << 4),
120 /*! If set there will be no enter or leave sounds */
121 CONFFLAG_QUIET = (1 << 5),
122 /*! If set, when user joins the conference, they will be told the number
123 * of users that are already in */
124 CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
125 /*! Set to run AGI Script in Background */
126 CONFFLAG_AGI = (1 << 7),
127 /*! Set to have music on hold when user is alone in conference */
128 CONFFLAG_MOH = (1 << 8),
129 /*! If set the MeetMe will return if all marked with this flag left */
130 CONFFLAG_MARKEDEXIT = (1 << 9),
131 /*! If set, the MeetMe will wait until a marked user enters */
132 CONFFLAG_WAITMARKED = (1 << 10),
133 /*! If set, the MeetMe will exit to the specified context */
134 CONFFLAG_EXIT_CONTEXT = (1 << 11),
135 /*! If set, the user will be marked */
136 CONFFLAG_MARKEDUSER = (1 << 12),
137 /*! If set, user will be ask record name on entry of conference */
138 CONFFLAG_INTROUSER = (1 << 13),
139 /*! If set, the MeetMe will be recorded */
140 CONFFLAG_RECORDCONF = (1<< 14),
141 /*! If set, the user will be monitored if the user is talking or not */
142 CONFFLAG_MONITORTALKER = (1 << 15),
143 CONFFLAG_DYNAMIC = (1 << 16),
144 CONFFLAG_DYNAMICPIN = (1 << 17),
145 CONFFLAG_EMPTY = (1 << 18),
146 CONFFLAG_EMPTYNOPIN = (1 << 19),
147 CONFFLAG_ALWAYSPROMPT = (1 << 20),
148 /*! If set, treats talking users as muted users */
149 CONFFLAG_OPTIMIZETALKER = (1 << 21),
150 /*! If set, won't speak the extra prompt when the first person
151 * enters the conference */
152 CONFFLAG_NOONLYPERSON = (1 << 22),
153 /*! If set, user will be asked to record name on entry of conference
154 * without review */
155 CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
156 /*! If set, the user will be initially self-muted */
157 CONFFLAG_STARTMUTED = (1 << 24),
158 /*! Pass DTMF through the conference */
159 CONFFLAG_PASS_DTMF = (1 << 25),
160 /*! This is a SLA station. (Only for use by the SLA applications.) */
161 CONFFLAG_SLA_STATION = (1 << 26),
162 /*! This is a SLA trunk. (Only for use by the SLA applications.) */
163 CONFFLAG_SLA_TRUNK = (1 << 27),
166 enum {
167 OPT_ARG_WAITMARKED = 0,
168 OPT_ARG_ARRAY_SIZE = 1,
171 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
172 AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
173 AST_APP_OPTION('a', CONFFLAG_ADMIN ),
174 AST_APP_OPTION('b', CONFFLAG_AGI ),
175 AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
176 AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
177 AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
178 AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
179 AST_APP_OPTION('e', CONFFLAG_EMPTY ),
180 AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
181 AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
182 AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
183 AST_APP_OPTION('M', CONFFLAG_MOH ),
184 AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
185 AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
186 AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
187 AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
188 AST_APP_OPTION('q', CONFFLAG_QUIET ),
189 AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
190 AST_APP_OPTION('s', CONFFLAG_STARMENU ),
191 AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
192 AST_APP_OPTION('l', CONFFLAG_MONITOR ),
193 AST_APP_OPTION('t', CONFFLAG_TALKER ),
194 AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
195 AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
196 AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
197 AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
198 END_OPTIONS );
200 static const char *app = "MeetMe";
201 static const char *app2 = "MeetMeCount";
202 static const char *app3 = "MeetMeAdmin";
203 static const char *slastation_app = "SLAStation";
204 static const char *slatrunk_app = "SLATrunk";
206 static const char *synopsis = "MeetMe conference bridge";
207 static const char *synopsis2 = "MeetMe participant count";
208 static const char *synopsis3 = "MeetMe conference Administration";
209 static const char *slastation_synopsis = "Shared Line Appearance Station";
210 static const char *slatrunk_synopsis = "Shared Line Appearance Trunk";
212 static const char *descrip =
213 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe\n"
214 "conference. If the conference number is omitted, the user will be prompted\n"
215 "to enter one. User can exit the conference by hangup, or if the 'p' option\n"
216 "is specified, by pressing '#'.\n"
217 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
218 " must be present for conferencing to operate properly. In addition, the chan_zap\n"
219 " channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
220 "The option string may contain zero or more of the following characters:\n"
221 " 'a' -- set admin mode\n"
222 " 'A' -- set marked mode\n"
223 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
224 " Default: conf-background.agi (Note: This does not work with\n"
225 " non-Zap channels in the same conference)\n"
226 " 'c' -- announce user(s) count on joining a conference\n"
227 " 'd' -- dynamically add conference\n"
228 " 'D' -- dynamically add conference, prompting for a PIN\n"
229 " 'e' -- select an empty conference\n"
230 " 'E' -- select an empty pinless conference\n"
231 " 'F' -- Pass DTMF through the conference.\n"
232 " 'i' -- announce user join/leave with review\n"
233 " 'I' -- announce user join/leave without review\n"
234 " 'l' -- set listen only mode (Listen only, no talking)\n"
235 " 'm' -- set initially muted\n"
236 " 'M' -- enable music on hold when the conference has a single caller\n"
237 " 'o' -- set talker optimization - treats talkers who aren't speaking as\n"
238 " being muted, meaning (a) No encode is done on transmission and\n"
239 " (b) Received audio that is not registered as talking is omitted\n"
240 " causing no buildup in background noise. Note that this option\n"
241 " will be removed in 1.6 and enabled by default.\n"
242 " 'p' -- allow user to exit the conference by pressing '#'\n"
243 " 'P' -- always prompt for the pin even if it is specified\n"
244 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
245 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
246 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
247 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is\n"
248 " wav.\n"
249 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
250 " 't' -- set talk only mode. (Talk only, no listening)\n"
251 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
252 " 'w[(<secs>)]'\n"
253 " -- wait until the marked user enters the conference\n"
254 " 'x' -- close the conference when last marked user exits\n"
255 " 'X' -- allow user to exit the conference by entering a valid single\n"
256 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
257 " if that variable is not defined.\n"
258 " '1' -- do not play message when first person enters\n";
260 static const char *descrip2 =
261 " MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
262 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
263 "will be returned in the variable. Upon app completion, MeetMeCount will hangup\n"
264 "the channel, unless priority n+1 exists, in which case priority progress will\n"
265 "continue.\n"
266 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
268 static const char *descrip3 =
269 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
270 " 'e' -- Eject last user that joined\n"
271 " 'k' -- Kick one user out of conference\n"
272 " 'K' -- Kick all users out of conference\n"
273 " 'l' -- Unlock conference\n"
274 " 'L' -- Lock conference\n"
275 " 'm' -- Unmute one user\n"
276 " 'M' -- Mute one user\n"
277 " 'n' -- Unmute all users in the conference\n"
278 " 'N' -- Mute all non-admin users in the conference\n"
279 " 'r' -- Reset one user's volume settings\n"
280 " 'R' -- Reset all users volume settings\n"
281 " 's' -- Lower entire conference speaking volume\n"
282 " 'S' -- Raise entire conference speaking volume\n"
283 " 't' -- Lower one user's talk volume\n"
284 " 'T' -- Raise one user's talk volume\n"
285 " 'u' -- Lower one user's listen volume\n"
286 " 'U' -- Raise one user's listen volume\n"
287 " 'v' -- Lower entire conference listening volume\n"
288 " 'V' -- Raise entire conference listening volume\n"
291 static const char *slastation_desc =
292 " SLAStation(station):\n"
293 "This application should be executed by an SLA station. The argument depends\n"
294 "on how the call was initiated. If the phone was just taken off hook, then\n"
295 "the argument \"station\" should be just the station name. If the call was\n"
296 "initiated by pressing a line key, then the station name should be preceded\n"
297 "by an underscore and the trunk name associated with that line button.\n"
298 "For example: \"station1_line1\"."
299 " On exit, this application will set the variable SLASTATION_STATUS to\n"
300 "one of the following values:\n"
301 " FAILURE | CONGESTION | SUCCESS\n"
304 static const char *slatrunk_desc =
305 " SLATrunk(trunk):\n"
306 "This application should be executed by an SLA trunk on an inbound call.\n"
307 "The channel calling this application should correspond to the SLA trunk\n"
308 "with the name \"trunk\" that is being passed as an argument.\n"
309 " On exit, this application will set the variable SLATRUNK_STATUS to\n"
310 "one of the following values:\n"
311 " FAILURE | SUCCESS | UNANSWERED | RINGTIMEOUT\n"
314 #define MAX_CONFNUM 80
315 #define MAX_PIN 80
317 /*! \brief The MeetMe Conference object */
318 struct ast_conference {
319 ast_mutex_t playlock; /*!< Conference specific lock (players) */
320 ast_mutex_t listenlock; /*!< Conference specific lock (listeners) */
321 char confno[MAX_CONFNUM]; /*!< Conference */
322 struct ast_channel *chan; /*!< Announcements channel */
323 struct ast_channel *lchan; /*!< Listen/Record channel */
324 int fd; /*!< Announcements fd */
325 int zapconf; /*!< Zaptel Conf # */
326 int users; /*!< Number of active users */
327 int markedusers; /*!< Number of marked users */
328 time_t start; /*!< Start time (s) */
329 int refcount; /*!< reference count of usage */
330 enum recording_state recording:2; /*!< recording status */
331 unsigned int isdynamic:1; /*!< Created on the fly? */
332 unsigned int locked:1; /*!< Is the conference locked? */
333 pthread_t recordthread; /*!< thread for recording */
334 ast_mutex_t recordthreadlock; /*!< control threads trying to start recordthread */
335 pthread_attr_t attr; /*!< thread attribute */
336 const char *recordingfilename; /*!< Filename to record the Conference into */
337 const char *recordingformat; /*!< Format to record the Conference in */
338 char pin[MAX_PIN]; /*!< If protected by a PIN */
339 char pinadmin[MAX_PIN]; /*!< If protected by a admin PIN */
340 struct ast_frame *transframe[32];
341 struct ast_frame *origframe;
342 struct ast_trans_pvt *transpath[32];
343 AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
344 AST_LIST_ENTRY(ast_conference) list;
347 static AST_LIST_HEAD_STATIC(confs, ast_conference);
349 static unsigned int conf_map[1024] = {0, };
351 struct volume {
352 int desired; /*!< Desired volume adjustment */
353 int actual; /*!< Actual volume adjustment (for channels that can't adjust) */
356 struct ast_conf_user {
357 int user_no; /*!< User Number */
358 int userflags; /*!< Flags as set in the conference */
359 int adminflags; /*!< Flags set by the Admin */
360 struct ast_channel *chan; /*!< Connected channel */
361 int talking; /*!< Is user talking */
362 int zapchannel; /*!< Is a Zaptel channel */
363 char usrvalue[50]; /*!< Custom User Value */
364 char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */
365 time_t jointime; /*!< Time the user joined the conference */
366 struct volume talk;
367 struct volume listen;
368 AST_LIST_ENTRY(ast_conf_user) list;
371 enum sla_which_trunk_refs {
372 ALL_TRUNK_REFS,
373 INACTIVE_TRUNK_REFS,
376 enum sla_trunk_state {
377 SLA_TRUNK_STATE_IDLE,
378 SLA_TRUNK_STATE_RINGING,
379 SLA_TRUNK_STATE_UP,
380 SLA_TRUNK_STATE_ONHOLD,
381 SLA_TRUNK_STATE_ONHOLD_BYME,
384 enum sla_hold_access {
385 /*! This means that any station can put it on hold, and any station
386 * can retrieve the call from hold. */
387 SLA_HOLD_OPEN,
388 /*! This means that only the station that put the call on hold may
389 * retrieve it from hold. */
390 SLA_HOLD_PRIVATE,
393 struct sla_trunk_ref;
395 struct sla_station {
396 AST_RWLIST_ENTRY(sla_station) entry;
397 AST_DECLARE_STRING_FIELDS(
398 AST_STRING_FIELD(name);
399 AST_STRING_FIELD(device);
400 AST_STRING_FIELD(autocontext);
402 AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
403 struct ast_dial *dial;
404 /*! Ring timeout for this station, for any trunk. If a ring timeout
405 * is set for a specific trunk on this station, that will take
406 * priority over this value. */
407 unsigned int ring_timeout;
408 /*! Ring delay for this station, for any trunk. If a ring delay
409 * is set for a specific trunk on this station, that will take
410 * priority over this value. */
411 unsigned int ring_delay;
412 /*! This option uses the values in the sla_hold_access enum and sets the
413 * access control type for hold on this station. */
414 unsigned int hold_access:1;
417 struct sla_station_ref {
418 AST_LIST_ENTRY(sla_station_ref) entry;
419 struct sla_station *station;
422 struct sla_trunk {
423 AST_RWLIST_ENTRY(sla_trunk) entry;
424 AST_DECLARE_STRING_FIELDS(
425 AST_STRING_FIELD(name);
426 AST_STRING_FIELD(device);
427 AST_STRING_FIELD(autocontext);
429 AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
430 /*! Number of stations that use this trunk */
431 unsigned int num_stations;
432 /*! Number of stations currently on a call with this trunk */
433 unsigned int active_stations;
434 /*! Number of stations that have this trunk on hold. */
435 unsigned int hold_stations;
436 struct ast_channel *chan;
437 unsigned int ring_timeout;
438 /*! If set to 1, no station will be able to join an active call with
439 * this trunk. */
440 unsigned int barge_disabled:1;
441 /*! This option uses the values in the sla_hold_access enum and sets the
442 * access control type for hold on this trunk. */
443 unsigned int hold_access:1;
444 /*! Whether this trunk is currently on hold, meaning that once a station
445 * connects to it, the trunk channel needs to have UNHOLD indicated to it. */
446 unsigned int on_hold:1;
449 struct sla_trunk_ref {
450 AST_LIST_ENTRY(sla_trunk_ref) entry;
451 struct sla_trunk *trunk;
452 enum sla_trunk_state state;
453 struct ast_channel *chan;
454 /*! Ring timeout to use when this trunk is ringing on this specific
455 * station. This takes higher priority than a ring timeout set at
456 * the station level. */
457 unsigned int ring_timeout;
458 /*! Ring delay to use when this trunk is ringing on this specific
459 * station. This takes higher priority than a ring delay set at
460 * the station level. */
461 unsigned int ring_delay;
464 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
465 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
467 static const char sla_registrar[] = "SLA";
469 /*! \brief Event types that can be queued up for the SLA thread */
470 enum sla_event_type {
471 /*! A station has put the call on hold */
472 SLA_EVENT_HOLD,
473 /*! The state of a dial has changed */
474 SLA_EVENT_DIAL_STATE,
475 /*! The state of a ringing trunk has changed */
476 SLA_EVENT_RINGING_TRUNK,
479 struct sla_event {
480 enum sla_event_type type;
481 struct sla_station *station;
482 struct sla_trunk_ref *trunk_ref;
483 AST_LIST_ENTRY(sla_event) entry;
486 /*! \brief A station that failed to be dialed
487 * \note Only used by the SLA thread. */
488 struct sla_failed_station {
489 struct sla_station *station;
490 struct timeval last_try;
491 AST_LIST_ENTRY(sla_failed_station) entry;
494 /*! \brief A trunk that is ringing */
495 struct sla_ringing_trunk {
496 struct sla_trunk *trunk;
497 /*! The time that this trunk started ringing */
498 struct timeval ring_begin;
499 AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
500 AST_LIST_ENTRY(sla_ringing_trunk) entry;
503 enum sla_station_hangup {
504 SLA_STATION_HANGUP_NORMAL,
505 SLA_STATION_HANGUP_TIMEOUT,
508 /*! \brief A station that is ringing */
509 struct sla_ringing_station {
510 struct sla_station *station;
511 /*! The time that this station started ringing */
512 struct timeval ring_begin;
513 AST_LIST_ENTRY(sla_ringing_station) entry;
517 * \brief A structure for data used by the sla thread
519 static struct {
520 /*! The SLA thread ID */
521 pthread_t thread;
522 ast_cond_t cond;
523 ast_mutex_t lock;
524 AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
525 AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
526 AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
527 AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
528 unsigned int stop:1;
529 /*! Attempt to handle CallerID, even though it is known not to work
530 * properly in some situations. */
531 unsigned int attempt_callerid:1;
532 } sla = {
533 .thread = AST_PTHREADT_NULL,
536 /*! The number of audio buffers to be allocated on pseudo channels
537 * when in a conference */
538 static int audio_buffers;
540 /*! Map 'volume' levels from -5 through +5 into
541 * decibel (dB) settings for channel drivers
542 * Note: these are not a straight linear-to-dB
543 * conversion... the numbers have been modified
544 * to give the user a better level of adjustability
546 static char const gain_map[] = {
547 -15,
548 -13,
549 -10,
561 static int admin_exec(struct ast_channel *chan, void *data);
562 static void *recordthread(void *args);
564 static char *istalking(int x)
566 if (x > 0)
567 return "(talking)";
568 else if (x < 0)
569 return "(unmonitored)";
570 else
571 return "(not talking)";
574 static int careful_write(int fd, unsigned char *data, int len, int block)
576 int res;
577 int x;
579 while (len) {
580 if (block) {
581 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
582 res = ioctl(fd, ZT_IOMUX, &x);
583 } else
584 res = 0;
585 if (res >= 0)
586 res = write(fd, data, len);
587 if (res < 1) {
588 if (errno != EAGAIN) {
589 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
590 return -1;
591 } else
592 return 0;
594 len -= res;
595 data += res;
598 return 0;
601 static int set_talk_volume(struct ast_conf_user *user, int volume)
603 char gain_adjust;
605 /* attempt to make the adjustment in the channel driver;
606 if successful, don't adjust in the frame reading routine
608 gain_adjust = gain_map[volume + 5];
610 return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
613 static int set_listen_volume(struct ast_conf_user *user, int volume)
615 char gain_adjust;
617 /* attempt to make the adjustment in the channel driver;
618 if successful, don't adjust in the frame reading routine
620 gain_adjust = gain_map[volume + 5];
622 return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
625 static void tweak_volume(struct volume *vol, enum volume_action action)
627 switch (action) {
628 case VOL_UP:
629 switch (vol->desired) {
630 case 5:
631 break;
632 case 0:
633 vol->desired = 2;
634 break;
635 case -2:
636 vol->desired = 0;
637 break;
638 default:
639 vol->desired++;
640 break;
642 break;
643 case VOL_DOWN:
644 switch (vol->desired) {
645 case -5:
646 break;
647 case 2:
648 vol->desired = 0;
649 break;
650 case 0:
651 vol->desired = -2;
652 break;
653 default:
654 vol->desired--;
655 break;
660 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
662 tweak_volume(&user->talk, action);
663 /* attempt to make the adjustment in the channel driver;
664 if successful, don't adjust in the frame reading routine
666 if (!set_talk_volume(user, user->talk.desired))
667 user->talk.actual = 0;
668 else
669 user->talk.actual = user->talk.desired;
672 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
674 tweak_volume(&user->listen, action);
675 /* attempt to make the adjustment in the channel driver;
676 if successful, don't adjust in the frame reading routine
678 if (!set_listen_volume(user, user->listen.desired))
679 user->listen.actual = 0;
680 else
681 user->listen.actual = user->listen.desired;
684 static void reset_volumes(struct ast_conf_user *user)
686 signed char zero_volume = 0;
688 ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
689 ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
692 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
694 unsigned char *data;
695 int len;
696 int res = -1;
698 if (!chan->_softhangup)
699 res = ast_autoservice_start(chan);
701 AST_LIST_LOCK(&confs);
703 switch(sound) {
704 case ENTER:
705 data = enter;
706 len = sizeof(enter);
707 break;
708 case LEAVE:
709 data = leave;
710 len = sizeof(leave);
711 break;
712 default:
713 data = NULL;
714 len = 0;
716 if (data) {
717 careful_write(conf->fd, data, len, 1);
720 AST_LIST_UNLOCK(&confs);
722 if (!res)
723 ast_autoservice_stop(chan);
727 * \brief Find or create a conference
729 * \param confno The conference name/number
730 * \param pin The regular user pin
731 * \param pinadmin The admin pin
732 * \param make Make the conf if it doesn't exist
733 * \param dynamic Mark the newly created conference as dynamic
734 * \param refcount How many references to mark on the conference
736 * \return A pointer to the conference struct, or NULL if it wasn't found and
737 * make or dynamic were not set.
739 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
741 struct ast_conference *cnf;
742 struct zt_confinfo ztc = { 0, };
743 int confno_int = 0;
745 AST_LIST_LOCK(&confs);
747 AST_LIST_TRAVERSE(&confs, cnf, list) {
748 if (!strcmp(confno, cnf->confno))
749 break;
752 if (cnf || (!make && !dynamic))
753 goto cnfout;
755 /* Make a new one */
756 if (!(cnf = ast_calloc(1, sizeof(*cnf))))
757 goto cnfout;
759 ast_mutex_init(&cnf->playlock);
760 ast_mutex_init(&cnf->listenlock);
761 cnf->recordthread = AST_PTHREADT_NULL;
762 ast_mutex_init(&cnf->recordthreadlock);
763 ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
764 ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
765 ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
767 /* Setup a new zap conference */
768 ztc.confno = -1;
769 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
770 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
771 if (cnf->fd < 0 || ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
772 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
773 if (cnf->fd >= 0)
774 close(cnf->fd);
775 free(cnf);
776 cnf = NULL;
777 goto cnfout;
780 cnf->zapconf = ztc.confno;
782 /* Setup a new channel for playback of audio files */
783 cnf->chan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
784 if (cnf->chan) {
785 ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
786 ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
787 ztc.chan = 0;
788 ztc.confno = cnf->zapconf;
789 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
790 if (ioctl(cnf->chan->fds[0], ZT_SETCONF, &ztc)) {
791 ast_log(LOG_WARNING, "Error setting conference\n");
792 if (cnf->chan)
793 ast_hangup(cnf->chan);
794 else
795 close(cnf->fd);
796 free(cnf);
797 cnf = NULL;
798 goto cnfout;
802 /* Fill the conference struct */
803 cnf->start = time(NULL);
804 cnf->isdynamic = dynamic ? 1 : 0;
805 if (option_verbose > 2)
806 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
807 AST_LIST_INSERT_HEAD(&confs, cnf, list);
809 /* Reserve conference number in map */
810 if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
811 conf_map[confno_int] = 1;
813 cnfout:
814 if (cnf)
815 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
817 AST_LIST_UNLOCK(&confs);
819 return cnf;
822 static int meetme_cmd(int fd, int argc, char **argv)
824 /* Process the command */
825 struct ast_conference *cnf;
826 struct ast_conf_user *user;
827 int hr, min, sec;
828 int i = 0, total = 0;
829 time_t now;
830 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
831 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
832 char cmdline[1024] = "";
834 if (argc > 8)
835 ast_cli(fd, "Invalid Arguments.\n");
836 /* Check for length so no buffer will overflow... */
837 for (i = 0; i < argc; i++) {
838 if (strlen(argv[i]) > 100)
839 ast_cli(fd, "Invalid Arguments.\n");
841 if (argc == 1) {
842 /* 'MeetMe': List all the conferences */
843 now = time(NULL);
844 AST_LIST_LOCK(&confs);
845 if (AST_LIST_EMPTY(&confs)) {
846 ast_cli(fd, "No active MeetMe conferences.\n");
847 AST_LIST_UNLOCK(&confs);
848 return RESULT_SUCCESS;
850 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
851 AST_LIST_TRAVERSE(&confs, cnf, list) {
852 if (cnf->markedusers == 0)
853 strcpy(cmdline, "N/A ");
854 else
855 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
856 hr = (now - cnf->start) / 3600;
857 min = ((now - cnf->start) % 3600) / 60;
858 sec = (now - cnf->start) % 60;
860 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
862 total += cnf->users;
864 AST_LIST_UNLOCK(&confs);
865 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
866 return RESULT_SUCCESS;
868 if (argc < 3)
869 return RESULT_SHOWUSAGE;
870 ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */
871 if (strstr(argv[1], "lock")) {
872 if (strcmp(argv[1], "lock") == 0) {
873 /* Lock */
874 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
875 } else {
876 /* Unlock */
877 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
879 } else if (strstr(argv[1], "mute")) {
880 if (argc < 4)
881 return RESULT_SHOWUSAGE;
882 if (strcmp(argv[1], "mute") == 0) {
883 /* Mute */
884 if (strcmp(argv[3], "all") == 0) {
885 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
886 } else {
887 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
888 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
890 } else {
891 /* Unmute */
892 if (strcmp(argv[3], "all") == 0) {
893 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
894 } else {
895 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
896 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
899 } else if (strcmp(argv[1], "kick") == 0) {
900 if (argc < 4)
901 return RESULT_SHOWUSAGE;
902 if (strcmp(argv[3], "all") == 0) {
903 /* Kick all */
904 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
905 } else {
906 /* Kick a single user */
907 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
908 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
910 } else if(strcmp(argv[1], "list") == 0) {
911 int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) );
912 /* List all the users in a conference */
913 if (AST_LIST_EMPTY(&confs)) {
914 if ( !concise )
915 ast_cli(fd, "No active conferences.\n");
916 return RESULT_SUCCESS;
918 /* Find the right conference */
919 AST_LIST_LOCK(&confs);
920 AST_LIST_TRAVERSE(&confs, cnf, list) {
921 if (strcmp(cnf->confno, argv[2]) == 0)
922 break;
924 if (!cnf) {
925 if ( !concise )
926 ast_cli(fd, "No such conference: %s.\n",argv[2]);
927 AST_LIST_UNLOCK(&confs);
928 return RESULT_SUCCESS;
930 /* Show all the users */
931 time(&now);
932 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
933 hr = (now - user->jointime) / 3600;
934 min = ((now - user->jointime) % 3600) / 60;
935 sec = (now - user->jointime) % 60;
936 if ( !concise )
937 ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %02d:%02d:%02d\n",
938 user->user_no,
939 S_OR(user->chan->cid.cid_num, "<unknown>"),
940 S_OR(user->chan->cid.cid_name, "<no name>"),
941 user->chan->name,
942 user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
943 user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
944 user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
945 istalking(user->talking), hr, min, sec);
946 else
947 ast_cli(fd, "%d!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
948 user->user_no,
949 S_OR(user->chan->cid.cid_num, ""),
950 S_OR(user->chan->cid.cid_name, ""),
951 user->chan->name,
952 user->userflags & CONFFLAG_ADMIN ? "1" : "",
953 user->userflags & CONFFLAG_MONITOR ? "1" : "",
954 user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
955 user->talking, hr, min, sec);
958 if ( !concise )
959 ast_cli(fd,"%d users in that conference.\n",cnf->users);
960 AST_LIST_UNLOCK(&confs);
961 return RESULT_SUCCESS;
962 } else
963 return RESULT_SHOWUSAGE;
964 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
965 admin_exec(NULL, cmdline);
967 return 0;
970 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
972 static char *cmds[] = {"lock", "unlock", "mute", "unmute", "kick", "list", NULL};
974 int len = strlen(word);
975 int which = 0;
976 struct ast_conference *cnf = NULL;
977 struct ast_conf_user *usr = NULL;
978 char *confno = NULL;
979 char usrno[50] = "";
980 char *myline, *ret = NULL;
982 if (pos == 1) { /* Command */
983 return ast_cli_complete(word, cmds, state);
984 } else if (pos == 2) { /* Conference Number */
985 AST_LIST_LOCK(&confs);
986 AST_LIST_TRAVERSE(&confs, cnf, list) {
987 if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
988 ret = cnf->confno;
989 break;
992 ret = ast_strdup(ret); /* dup before releasing the lock */
993 AST_LIST_UNLOCK(&confs);
994 return ret;
995 } else if (pos == 3) {
996 /* User Number || Conf Command option*/
997 if (strstr(line, "mute") || strstr(line, "kick")) {
998 if (state == 0 && (strstr(line, "kick") || strstr(line,"mute")) && !strncasecmp(word, "all", len))
999 return strdup("all");
1000 which++;
1001 AST_LIST_LOCK(&confs);
1003 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1004 myline = ast_strdupa(line);
1005 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1006 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1010 AST_LIST_TRAVERSE(&confs, cnf, list) {
1011 if (!strcmp(confno, cnf->confno))
1012 break;
1015 if (cnf) {
1016 /* Search for the user */
1017 AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
1018 snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1019 if (!strncasecmp(word, usrno, len) && ++which > state)
1020 break;
1023 AST_LIST_UNLOCK(&confs);
1024 return usr ? strdup(usrno) : NULL;
1025 } else if ( strstr(line, "list") && ( 0 == state ) )
1026 return strdup("concise");
1029 return NULL;
1032 static char meetme_usage[] =
1033 "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
1034 " Executes a command for the conference or on a conferee\n";
1036 static const char *sla_hold_str(unsigned int hold_access)
1038 const char *hold = "Unknown";
1040 switch (hold_access) {
1041 case SLA_HOLD_OPEN:
1042 hold = "Open";
1043 break;
1044 case SLA_HOLD_PRIVATE:
1045 hold = "Private";
1046 default:
1047 break;
1050 return hold;
1053 static int sla_show_trunks(int fd, int argc, char **argv)
1055 const struct sla_trunk *trunk;
1057 ast_cli(fd, "\n"
1058 "=============================================================\n"
1059 "=== Configured SLA Trunks ===================================\n"
1060 "=============================================================\n"
1061 "===\n");
1062 AST_RWLIST_RDLOCK(&sla_trunks);
1063 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1064 struct sla_station_ref *station_ref;
1065 char ring_timeout[16] = "(none)";
1066 if (trunk->ring_timeout)
1067 snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1068 ast_cli(fd, "=== ---------------------------------------------------------\n"
1069 "=== Trunk Name: %s\n"
1070 "=== ==> Device: %s\n"
1071 "=== ==> AutoContext: %s\n"
1072 "=== ==> RingTimeout: %s\n"
1073 "=== ==> BargeAllowed: %s\n"
1074 "=== ==> HoldAccess: %s\n"
1075 "=== ==> Stations ...\n",
1076 trunk->name, trunk->device,
1077 S_OR(trunk->autocontext, "(none)"),
1078 ring_timeout,
1079 trunk->barge_disabled ? "No" : "Yes",
1080 sla_hold_str(trunk->hold_access));
1081 AST_RWLIST_RDLOCK(&sla_stations);
1082 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1083 ast_cli(fd, "=== ==> Station name: %s\n", station_ref->station->name);
1084 AST_RWLIST_UNLOCK(&sla_stations);
1085 ast_cli(fd, "=== ---------------------------------------------------------\n"
1086 "===\n");
1088 AST_RWLIST_UNLOCK(&sla_trunks);
1089 ast_cli(fd, "=============================================================\n"
1090 "\n");
1092 return RESULT_SUCCESS;
1095 static const char *trunkstate2str(enum sla_trunk_state state)
1097 #define S(e) case e: return # e;
1098 switch (state) {
1099 S(SLA_TRUNK_STATE_IDLE)
1100 S(SLA_TRUNK_STATE_RINGING)
1101 S(SLA_TRUNK_STATE_UP)
1102 S(SLA_TRUNK_STATE_ONHOLD)
1103 S(SLA_TRUNK_STATE_ONHOLD_BYME)
1105 return "Uknown State";
1106 #undef S
1109 static const char sla_show_trunks_usage[] =
1110 "Usage: sla show trunks\n"
1111 " This will list all trunks defined in sla.conf\n";
1113 static int sla_show_stations(int fd, int argc, char **argv)
1115 const struct sla_station *station;
1117 ast_cli(fd, "\n"
1118 "=============================================================\n"
1119 "=== Configured SLA Stations =================================\n"
1120 "=============================================================\n"
1121 "===\n");
1122 AST_RWLIST_RDLOCK(&sla_stations);
1123 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1124 struct sla_trunk_ref *trunk_ref;
1125 char ring_timeout[16] = "(none)";
1126 char ring_delay[16] = "(none)";
1127 if (station->ring_timeout) {
1128 snprintf(ring_timeout, sizeof(ring_timeout),
1129 "%u", station->ring_timeout);
1131 if (station->ring_delay) {
1132 snprintf(ring_delay, sizeof(ring_delay),
1133 "%u", station->ring_delay);
1135 ast_cli(fd, "=== ---------------------------------------------------------\n"
1136 "=== Station Name: %s\n"
1137 "=== ==> Device: %s\n"
1138 "=== ==> AutoContext: %s\n"
1139 "=== ==> RingTimeout: %s\n"
1140 "=== ==> RingDelay: %s\n"
1141 "=== ==> HoldAccess: %s\n"
1142 "=== ==> Trunks ...\n",
1143 station->name, station->device,
1144 S_OR(station->autocontext, "(none)"),
1145 ring_timeout, ring_delay,
1146 sla_hold_str(station->hold_access));
1147 AST_RWLIST_RDLOCK(&sla_trunks);
1148 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1149 if (trunk_ref->ring_timeout) {
1150 snprintf(ring_timeout, sizeof(ring_timeout),
1151 "%u", trunk_ref->ring_timeout);
1152 } else
1153 strcpy(ring_timeout, "(none)");
1154 if (trunk_ref->ring_delay) {
1155 snprintf(ring_delay, sizeof(ring_delay),
1156 "%u", trunk_ref->ring_delay);
1157 } else
1158 strcpy(ring_delay, "(none)");
1159 ast_cli(fd, "=== ==> Trunk Name: %s\n"
1160 "=== ==> State: %s\n"
1161 "=== ==> RingTimeout: %s\n"
1162 "=== ==> RingDelay: %s\n",
1163 trunk_ref->trunk->name,
1164 trunkstate2str(trunk_ref->state),
1165 ring_timeout, ring_delay);
1167 AST_RWLIST_UNLOCK(&sla_trunks);
1168 ast_cli(fd, "=== ---------------------------------------------------------\n"
1169 "===\n");
1171 AST_RWLIST_UNLOCK(&sla_stations);
1172 ast_cli(fd, "============================================================\n"
1173 "\n");
1175 return RESULT_SUCCESS;
1178 static const char sla_show_stations_usage[] =
1179 "Usage: sla show stations\n"
1180 " This will list all stations defined in sla.conf\n";
1182 static struct ast_cli_entry cli_meetme[] = {
1183 { { "meetme", NULL, NULL },
1184 meetme_cmd, "Execute a command on a conference or conferee",
1185 meetme_usage, complete_meetmecmd },
1187 { { "sla", "show", "trunks", NULL },
1188 sla_show_trunks, "Show SLA Trunks",
1189 sla_show_trunks_usage, NULL },
1191 { { "sla", "show", "stations", NULL },
1192 sla_show_stations, "Show SLA Stations",
1193 sla_show_stations_usage, NULL },
1196 static void conf_flush(int fd, struct ast_channel *chan)
1198 int x;
1200 /* read any frames that may be waiting on the channel
1201 and throw them away
1203 if (chan) {
1204 struct ast_frame *f;
1206 /* when no frames are available, this will wait
1207 for 1 millisecond maximum
1209 while (ast_waitfor(chan, 1)) {
1210 f = ast_read(chan);
1211 if (f)
1212 ast_frfree(f);
1213 else /* channel was hung up or something else happened */
1214 break;
1218 /* flush any data sitting in the pseudo channel */
1219 x = ZT_FLUSH_ALL;
1220 if (ioctl(fd, ZT_FLUSH, &x))
1221 ast_log(LOG_WARNING, "Error flushing channel\n");
1225 /* Remove the conference from the list and free it.
1226 We assume that this was called while holding conflock. */
1227 static int conf_free(struct ast_conference *conf)
1229 int x;
1231 AST_LIST_REMOVE(&confs, conf, list);
1233 if (conf->recording == MEETME_RECORD_ACTIVE) {
1234 conf->recording = MEETME_RECORD_TERMINATE;
1235 AST_LIST_UNLOCK(&confs);
1236 while (1) {
1237 usleep(1);
1238 AST_LIST_LOCK(&confs);
1239 if (conf->recording == MEETME_RECORD_OFF)
1240 break;
1241 AST_LIST_UNLOCK(&confs);
1245 for (x=0;x<AST_FRAME_BITS;x++) {
1246 if (conf->transframe[x])
1247 ast_frfree(conf->transframe[x]);
1248 if (conf->transpath[x])
1249 ast_translator_free_path(conf->transpath[x]);
1251 if (conf->origframe)
1252 ast_frfree(conf->origframe);
1253 if (conf->lchan)
1254 ast_hangup(conf->lchan);
1255 if (conf->chan)
1256 ast_hangup(conf->chan);
1257 else
1258 close(conf->fd);
1260 ast_mutex_destroy(&conf->playlock);
1261 ast_mutex_destroy(&conf->listenlock);
1262 ast_mutex_destroy(&conf->recordthreadlock);
1263 free(conf);
1265 return 0;
1268 static void conf_queue_dtmf(const struct ast_conference *conf,
1269 const struct ast_conf_user *sender, struct ast_frame *f)
1271 struct ast_conf_user *user;
1273 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
1274 if (user == sender)
1275 continue;
1276 if (ast_write(user->chan, f) < 0)
1277 ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1281 static void sla_queue_event_full(enum sla_event_type type,
1282 struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1284 struct sla_event *event;
1286 if (!(event = ast_calloc(1, sizeof(*event))))
1287 return;
1289 event->type = type;
1290 event->trunk_ref = trunk_ref;
1291 event->station = station;
1293 if (!lock) {
1294 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1295 return;
1298 ast_mutex_lock(&sla.lock);
1299 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1300 ast_cond_signal(&sla.cond);
1301 ast_mutex_unlock(&sla.lock);
1304 static void sla_queue_event_nolock(enum sla_event_type type)
1306 sla_queue_event_full(type, NULL, NULL, 0);
1309 static void sla_queue_event(enum sla_event_type type)
1311 sla_queue_event_full(type, NULL, NULL, 1);
1314 /*! \brief Queue a SLA event from the conference */
1315 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1316 struct ast_conference *conf)
1318 struct sla_station *station;
1319 struct sla_trunk_ref *trunk_ref = NULL;
1320 char *trunk_name;
1322 trunk_name = ast_strdupa(conf->confno);
1323 strsep(&trunk_name, "_");
1324 if (ast_strlen_zero(trunk_name)) {
1325 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1326 return;
1329 AST_RWLIST_RDLOCK(&sla_stations);
1330 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1331 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1332 if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1333 break;
1335 if (trunk_ref)
1336 break;
1338 AST_RWLIST_UNLOCK(&sla_stations);
1340 if (!trunk_ref) {
1341 ast_log(LOG_DEBUG, "Trunk not found for event!\n");
1342 return;
1345 sla_queue_event_full(type, trunk_ref, station, 1);
1348 /* Decrement reference counts, as incremented by find_conf() */
1349 static int dispose_conf(struct ast_conference *conf)
1351 int res = 0;
1352 int confno_int = 0;
1354 AST_LIST_LOCK(&confs);
1355 if (ast_atomic_dec_and_test(&conf->refcount)) {
1356 /* Take the conference room number out of an inuse state */
1357 if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1358 conf_map[confno_int] = 0;
1359 conf_free(conf);
1360 res = 1;
1362 AST_LIST_UNLOCK(&confs);
1364 return res;
1368 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
1370 struct ast_conf_user *user = NULL;
1371 struct ast_conf_user *usr = NULL;
1372 int fd;
1373 struct zt_confinfo ztc, ztc_empty;
1374 struct ast_frame *f;
1375 struct ast_channel *c;
1376 struct ast_frame fr;
1377 int outfd;
1378 int ms;
1379 int nfds;
1380 int res;
1381 int flags;
1382 int retryzap;
1383 int origfd;
1384 int musiconhold = 0;
1385 int firstpass = 0;
1386 int lastmarked = 0;
1387 int currentmarked = 0;
1388 int ret = -1;
1389 int x;
1390 int menu_active = 0;
1391 int using_pseudo = 0;
1392 int duration=20;
1393 int hr, min, sec;
1394 int sent_event = 0;
1395 time_t now;
1396 struct ast_dsp *dsp=NULL;
1397 struct ast_app *app;
1398 const char *agifile;
1399 const char *agifiledefault = "conf-background.agi";
1400 char meetmesecs[30] = "";
1401 char exitcontext[AST_MAX_CONTEXT] = "";
1402 char recordingtmp[AST_MAX_EXTENSION] = "";
1403 char members[10] = "";
1404 int dtmf, opt_waitmarked_timeout = 0;
1405 time_t timeout = 0;
1406 ZT_BUFFERINFO bi;
1407 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
1408 char *buf = __buf + AST_FRIENDLY_OFFSET;
1410 if (!(user = ast_calloc(1, sizeof(*user))))
1411 return ret;
1413 /* Possible timeout waiting for marked user */
1414 if ((confflags & CONFFLAG_WAITMARKED) &&
1415 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
1416 (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
1417 (opt_waitmarked_timeout > 0)) {
1418 timeout = time(NULL) + opt_waitmarked_timeout;
1421 if (confflags & CONFFLAG_RECORDCONF) {
1422 if (!conf->recordingfilename) {
1423 conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
1424 if (!conf->recordingfilename) {
1425 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
1426 conf->recordingfilename = ast_strdupa(recordingtmp);
1428 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
1429 if (!conf->recordingformat) {
1430 snprintf(recordingtmp, sizeof(recordingtmp), "wav");
1431 conf->recordingformat = ast_strdupa(recordingtmp);
1433 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
1434 conf->confno, conf->recordingfilename, conf->recordingformat);
1438 ast_mutex_lock(&conf->recordthreadlock);
1439 if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
1440 ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
1441 ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
1442 ztc.chan = 0;
1443 ztc.confno = conf->zapconf;
1444 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
1445 if (ioctl(conf->lchan->fds[0], ZT_SETCONF, &ztc)) {
1446 ast_log(LOG_WARNING, "Error starting listen channel\n");
1447 ast_hangup(conf->lchan);
1448 conf->lchan = NULL;
1449 } else {
1450 pthread_attr_init(&conf->attr);
1451 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
1452 ast_pthread_create_background(&conf->recordthread, &conf->attr, recordthread, conf);
1453 pthread_attr_destroy(&conf->attr);
1456 ast_mutex_unlock(&conf->recordthreadlock);
1458 time(&user->jointime);
1460 if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
1461 /* Sorry, but this confernce is locked! */
1462 if (!ast_streamfile(chan, "conf-locked", chan->language))
1463 ast_waitstream(chan, "");
1464 goto outrun;
1467 if (confflags & CONFFLAG_MARKEDUSER)
1468 conf->markedusers++;
1470 ast_mutex_lock(&conf->playlock);
1472 if (AST_LIST_EMPTY(&conf->userlist))
1473 user->user_no = 1;
1474 else
1475 user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
1477 AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
1479 user->chan = chan;
1480 user->userflags = confflags;
1481 user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
1482 user->talking = -1;
1483 conf->users++;
1484 /* Update table */
1485 snprintf(members, sizeof(members), "%d", conf->users);
1486 ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL);
1488 /* This device changed state now - if this is the first user */
1489 if (conf->users == 1)
1490 ast_device_state_changed("meetme:%s", conf->confno);
1492 ast_mutex_unlock(&conf->playlock);
1494 if (confflags & CONFFLAG_EXIT_CONTEXT) {
1495 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
1496 ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
1497 else if (!ast_strlen_zero(chan->macrocontext))
1498 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
1499 else
1500 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
1503 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
1504 snprintf(user->namerecloc, sizeof(user->namerecloc),
1505 "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
1506 conf->confno, user->user_no);
1507 if (confflags & CONFFLAG_INTROUSERNOREVIEW)
1508 res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, 128, 0, NULL);
1509 else
1510 res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
1511 if (res == -1)
1512 goto outrun;
1515 if ( !(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON)) ) {
1516 if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
1517 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1518 ast_waitstream(chan, "");
1519 if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
1520 if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
1521 ast_waitstream(chan, "");
1524 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
1525 int keepplaying = 1;
1527 if (conf->users == 2) {
1528 if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
1529 res = ast_waitstream(chan, AST_DIGIT_ANY);
1530 ast_stopstream(chan);
1531 if (res > 0)
1532 keepplaying=0;
1533 else if (res == -1)
1534 goto outrun;
1536 } else {
1537 if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
1538 res = ast_waitstream(chan, AST_DIGIT_ANY);
1539 ast_stopstream(chan);
1540 if (res > 0)
1541 keepplaying=0;
1542 else if (res == -1)
1543 goto outrun;
1545 if (keepplaying) {
1546 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1547 if (res > 0)
1548 keepplaying=0;
1549 else if (res == -1)
1550 goto outrun;
1552 if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
1553 res = ast_waitstream(chan, AST_DIGIT_ANY);
1554 ast_stopstream(chan);
1555 if (res > 0)
1556 keepplaying=0;
1557 else if (res == -1)
1558 goto outrun;
1563 ast_indicate(chan, -1);
1565 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
1566 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
1567 goto outrun;
1570 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
1571 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
1572 goto outrun;
1575 retryzap = (strcasecmp(chan->tech->type, "Zap") || chan->spies ? 1 : 0);
1576 user->zapchannel = !retryzap;
1578 zapretry:
1579 origfd = chan->fds[0];
1580 if (retryzap) {
1581 fd = open("/dev/zap/pseudo", O_RDWR);
1582 if (fd < 0) {
1583 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
1584 goto outrun;
1586 using_pseudo = 1;
1587 /* Make non-blocking */
1588 flags = fcntl(fd, F_GETFL);
1589 if (flags < 0) {
1590 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
1591 close(fd);
1592 goto outrun;
1594 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
1595 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
1596 close(fd);
1597 goto outrun;
1599 /* Setup buffering information */
1600 memset(&bi, 0, sizeof(bi));
1601 bi.bufsize = CONF_SIZE/2;
1602 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
1603 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
1604 bi.numbufs = audio_buffers;
1605 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
1606 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
1607 close(fd);
1608 goto outrun;
1610 x = 1;
1611 if (ioctl(fd, ZT_SETLINEAR, &x)) {
1612 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
1613 close(fd);
1614 goto outrun;
1616 nfds = 1;
1617 } else {
1618 /* XXX Make sure we're not running on a pseudo channel XXX */
1619 fd = chan->fds[0];
1620 nfds = 0;
1622 memset(&ztc, 0, sizeof(ztc));
1623 memset(&ztc_empty, 0, sizeof(ztc_empty));
1624 /* Check to see if we're in a conference... */
1625 ztc.chan = 0;
1626 if (ioctl(fd, ZT_GETCONF, &ztc)) {
1627 ast_log(LOG_WARNING, "Error getting conference\n");
1628 close(fd);
1629 goto outrun;
1631 if (ztc.confmode) {
1632 /* Whoa, already in a conference... Retry... */
1633 if (!retryzap) {
1634 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
1635 retryzap = 1;
1636 goto zapretry;
1639 memset(&ztc, 0, sizeof(ztc));
1640 /* Add us to the conference */
1641 ztc.chan = 0;
1642 ztc.confno = conf->zapconf;
1644 ast_mutex_lock(&conf->playlock);
1646 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
1647 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
1648 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1649 ast_waitstream(conf->chan, "");
1650 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
1651 ast_waitstream(conf->chan, "");
1655 if (confflags & CONFFLAG_MONITOR)
1656 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1657 else if (confflags & CONFFLAG_TALKER)
1658 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1659 else
1660 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1662 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1663 ast_log(LOG_WARNING, "Error setting conference\n");
1664 close(fd);
1665 ast_mutex_unlock(&conf->playlock);
1666 goto outrun;
1668 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
1670 if (!sent_event) {
1671 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
1672 "Channel: %s\r\n"
1673 "Uniqueid: %s\r\n"
1674 "Meetme: %s\r\n"
1675 "Usernum: %d\r\n",
1676 chan->name, chan->uniqueid, conf->confno, user->user_no);
1677 sent_event = 1;
1680 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
1681 firstpass = 1;
1682 if (!(confflags & CONFFLAG_QUIET))
1683 if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
1684 conf_play(chan, conf, ENTER);
1687 ast_mutex_unlock(&conf->playlock);
1689 conf_flush(fd, chan);
1691 if (confflags & CONFFLAG_AGI) {
1692 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
1693 or use default filename of conf-background.agi */
1695 agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
1696 if (!agifile)
1697 agifile = agifiledefault;
1699 if (user->zapchannel) {
1700 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
1701 x = 1;
1702 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1704 /* Find a pointer to the agi app and execute the script */
1705 app = pbx_findapp("agi");
1706 if (app) {
1707 char *s = ast_strdupa(agifile);
1708 ret = pbx_exec(chan, app, s);
1709 } else {
1710 ast_log(LOG_WARNING, "Could not find application (agi)\n");
1711 ret = -2;
1713 if (user->zapchannel) {
1714 /* Remove CONFMUTE mode on Zap channel */
1715 x = 0;
1716 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1718 } else {
1719 if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
1720 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
1721 x = 1;
1722 ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
1724 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(dsp = ast_dsp_new())) {
1725 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
1726 res = -1;
1728 for(;;) {
1729 int menu_was_active = 0;
1731 outfd = -1;
1732 ms = -1;
1734 if (timeout && time(NULL) >= timeout)
1735 break;
1737 /* if we have just exited from the menu, and the user had a channel-driver
1738 volume adjustment, restore it
1740 if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
1741 set_talk_volume(user, user->listen.desired);
1743 menu_was_active = menu_active;
1745 currentmarked = conf->markedusers;
1746 if (!(confflags & CONFFLAG_QUIET) &&
1747 (confflags & CONFFLAG_MARKEDUSER) &&
1748 (confflags & CONFFLAG_WAITMARKED) &&
1749 lastmarked == 0) {
1750 if (currentmarked == 1 && conf->users > 1) {
1751 ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
1752 if (conf->users - 1 == 1) {
1753 if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
1754 ast_waitstream(chan, "");
1755 } else {
1756 if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
1757 ast_waitstream(chan, "");
1760 if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
1761 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
1762 ast_waitstream(chan, "");
1765 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
1768 /* Update the struct with the actual confflags */
1769 user->userflags = confflags;
1771 if (confflags & CONFFLAG_WAITMARKED) {
1772 if(currentmarked == 0) {
1773 if (lastmarked != 0) {
1774 if (!(confflags & CONFFLAG_QUIET))
1775 if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
1776 ast_waitstream(chan, "");
1777 if(confflags & CONFFLAG_MARKEDEXIT)
1778 break;
1779 else {
1780 ztc.confmode = ZT_CONF_CONF;
1781 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1782 ast_log(LOG_WARNING, "Error setting conference\n");
1783 close(fd);
1784 goto outrun;
1788 if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
1789 ast_moh_start(chan, NULL, NULL);
1790 musiconhold = 1;
1792 } else if(currentmarked >= 1 && lastmarked == 0) {
1793 /* Marked user entered, so cancel timeout */
1794 timeout = 0;
1795 if (confflags & CONFFLAG_MONITOR)
1796 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
1797 else if (confflags & CONFFLAG_TALKER)
1798 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
1799 else
1800 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1801 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1802 ast_log(LOG_WARNING, "Error setting conference\n");
1803 close(fd);
1804 goto outrun;
1806 if (musiconhold && (confflags & CONFFLAG_MOH)) {
1807 ast_moh_stop(chan);
1808 musiconhold = 0;
1810 if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
1811 if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
1812 ast_waitstream(chan, "");
1813 conf_play(chan, conf, ENTER);
1818 /* trying to add moh for single person conf */
1819 if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
1820 if (conf->users == 1) {
1821 if (musiconhold == 0) {
1822 ast_moh_start(chan, NULL, NULL);
1823 musiconhold = 1;
1825 } else {
1826 if (musiconhold) {
1827 ast_moh_stop(chan);
1828 musiconhold = 0;
1833 /* Leave if the last marked user left */
1834 if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
1835 ret = -1;
1836 break;
1839 /* Check if my modes have changed */
1841 /* If I should be muted but am still talker, mute me */
1842 if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (ztc.confmode & ZT_CONF_TALKER)) {
1843 ztc.confmode ^= ZT_CONF_TALKER;
1844 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1845 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1846 ret = -1;
1847 break;
1850 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1851 "Channel: %s\r\n"
1852 "Uniqueid: %s\r\n"
1853 "Meetme: %s\r\n"
1854 "Usernum: %i\r\n"
1855 "Status: on\r\n",
1856 chan->name, chan->uniqueid, conf->confno, user->user_no);
1859 /* If I should be un-muted but am not talker, un-mute me */
1860 if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
1861 ztc.confmode |= ZT_CONF_TALKER;
1862 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1863 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1864 ret = -1;
1865 break;
1868 manager_event(EVENT_FLAG_CALL, "MeetmeMute",
1869 "Channel: %s\r\n"
1870 "Uniqueid: %s\r\n"
1871 "Meetme: %s\r\n"
1872 "Usernum: %i\r\n"
1873 "Status: off\r\n",
1874 chan->name, chan->uniqueid, conf->confno, user->user_no);
1877 /* If I have been kicked, exit the conference */
1878 if (user->adminflags & ADMINFLAG_KICKME) {
1879 //You have been kicked.
1880 if (!(confflags & CONFFLAG_QUIET) &&
1881 !ast_streamfile(chan, "conf-kicked", chan->language)) {
1882 ast_waitstream(chan, "");
1884 ret = 0;
1885 break;
1888 /* Perform an extra hangup check just in case */
1889 if (ast_check_hangup(chan))
1890 break;
1892 if (c) {
1893 if (c->fds[0] != origfd || (user->zapchannel && c->spies)) {
1894 if (using_pseudo) {
1895 /* Kill old pseudo */
1896 close(fd);
1897 using_pseudo = 0;
1899 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
1900 retryzap = (strcasecmp(c->tech->type, "Zap") || c->spies ? 1 : 0);
1901 user->zapchannel = !retryzap;
1902 goto zapretry;
1904 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
1905 f = ast_read_noaudio(c);
1906 else
1907 f = ast_read(c);
1908 if (!f)
1909 break;
1910 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
1911 if (user->talk.actual)
1912 ast_frame_adjust_volume(f, user->talk.actual);
1914 if (confflags & (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) {
1915 int totalsilence;
1917 if (user->talking == -1)
1918 user->talking = 0;
1920 res = ast_dsp_silence(dsp, f, &totalsilence);
1921 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
1922 user->talking = 1;
1923 if (confflags & CONFFLAG_MONITORTALKER)
1924 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1925 "Channel: %s\r\n"
1926 "Uniqueid: %s\r\n"
1927 "Meetme: %s\r\n"
1928 "Usernum: %d\r\n"
1929 "Status: on\r\n",
1930 chan->name, chan->uniqueid, conf->confno, user->user_no);
1932 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
1933 user->talking = 0;
1934 if (confflags & CONFFLAG_MONITORTALKER)
1935 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
1936 "Channel: %s\r\n"
1937 "Uniqueid: %s\r\n"
1938 "Meetme: %s\r\n"
1939 "Usernum: %d\r\n"
1940 "Status: off\r\n",
1941 chan->name, chan->uniqueid, conf->confno, user->user_no);
1944 if (using_pseudo) {
1945 /* Absolutely do _not_ use careful_write here...
1946 it is important that we read data from the channel
1947 as fast as it arrives, and feed it into the conference.
1948 The buffering in the pseudo channel will take care of any
1949 timing differences, unless they are so drastic as to lose
1950 audio frames (in which case carefully writing would only
1951 have delayed the audio even further).
1953 /* As it turns out, we do want to use careful write. We just
1954 don't want to block, but we do want to at least *try*
1955 to write out all the samples.
1957 if (user->talking || !(confflags & CONFFLAG_OPTIMIZETALKER))
1958 careful_write(fd, f->data, f->datalen, 0);
1960 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
1961 char tmp[2];
1963 if (confflags & CONFFLAG_PASS_DTMF)
1964 conf_queue_dtmf(conf, user, f);
1966 tmp[0] = f->subclass;
1967 tmp[1] = '\0';
1968 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
1969 ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
1970 ret = 0;
1971 ast_frfree(f);
1972 break;
1973 } else if (option_debug > 1)
1974 ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
1975 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
1976 if (confflags & CONFFLAG_PASS_DTMF)
1977 conf_queue_dtmf(conf, user, f);
1978 ret = 0;
1979 ast_frfree(f);
1980 break;
1981 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
1982 if (confflags & CONFFLAG_PASS_DTMF)
1983 conf_queue_dtmf(conf, user, f);
1984 if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
1985 ast_log(LOG_WARNING, "Error setting conference\n");
1986 close(fd);
1987 ast_frfree(f);
1988 goto outrun;
1991 /* if we are entering the menu, and the user has a channel-driver
1992 volume adjustment, clear it
1994 if (!menu_active && user->talk.desired && !user->talk.actual)
1995 set_talk_volume(user, 0);
1997 if (musiconhold) {
1998 ast_moh_stop(chan);
2000 if ((confflags & CONFFLAG_ADMIN)) {
2001 /* Admin menu */
2002 if (!menu_active) {
2003 menu_active = 1;
2004 /* Record this sound! */
2005 if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
2006 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2007 ast_stopstream(chan);
2008 } else
2009 dtmf = 0;
2010 } else
2011 dtmf = f->subclass;
2012 if (dtmf) {
2013 switch(dtmf) {
2014 case '1': /* Un/Mute */
2015 menu_active = 0;
2017 /* for admin, change both admin and use flags */
2018 if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
2019 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2020 else
2021 user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2023 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2024 if (!ast_streamfile(chan, "conf-muted", chan->language))
2025 ast_waitstream(chan, "");
2026 } else {
2027 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2028 ast_waitstream(chan, "");
2030 break;
2031 case '2': /* Un/Lock the Conference */
2032 menu_active = 0;
2033 if (conf->locked) {
2034 conf->locked = 0;
2035 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
2036 ast_waitstream(chan, "");
2037 } else {
2038 conf->locked = 1;
2039 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
2040 ast_waitstream(chan, "");
2042 break;
2043 case '3': /* Eject last user */
2044 menu_active = 0;
2045 usr = AST_LIST_LAST(&conf->userlist);
2046 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
2047 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
2048 ast_waitstream(chan, "");
2049 } else
2050 usr->adminflags |= ADMINFLAG_KICKME;
2051 ast_stopstream(chan);
2052 break;
2053 case '4':
2054 tweak_listen_volume(user, VOL_DOWN);
2055 break;
2056 case '6':
2057 tweak_listen_volume(user, VOL_UP);
2058 break;
2059 case '7':
2060 tweak_talk_volume(user, VOL_DOWN);
2061 break;
2062 case '8':
2063 menu_active = 0;
2064 break;
2065 case '9':
2066 tweak_talk_volume(user, VOL_UP);
2067 break;
2068 default:
2069 menu_active = 0;
2070 /* Play an error message! */
2071 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2072 ast_waitstream(chan, "");
2073 break;
2076 } else {
2077 /* User menu */
2078 if (!menu_active) {
2079 menu_active = 1;
2080 if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
2081 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
2082 ast_stopstream(chan);
2083 } else
2084 dtmf = 0;
2085 } else
2086 dtmf = f->subclass;
2087 if (dtmf) {
2088 switch(dtmf) {
2089 case '1': /* Un/Mute */
2090 menu_active = 0;
2092 /* user can only toggle the self-muted state */
2093 user->adminflags ^= ADMINFLAG_SELFMUTED;
2095 /* they can't override the admin mute state */
2096 if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
2097 if (!ast_streamfile(chan, "conf-muted", chan->language))
2098 ast_waitstream(chan, "");
2099 } else {
2100 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
2101 ast_waitstream(chan, "");
2103 break;
2104 case '4':
2105 tweak_listen_volume(user, VOL_DOWN);
2106 break;
2107 case '6':
2108 tweak_listen_volume(user, VOL_UP);
2109 break;
2110 case '7':
2111 tweak_talk_volume(user, VOL_DOWN);
2112 break;
2113 case '8':
2114 menu_active = 0;
2115 break;
2116 case '9':
2117 tweak_talk_volume(user, VOL_UP);
2118 break;
2119 default:
2120 menu_active = 0;
2121 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
2122 ast_waitstream(chan, "");
2123 break;
2127 if (musiconhold)
2128 ast_moh_start(chan, NULL, NULL);
2130 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2131 ast_log(LOG_WARNING, "Error setting conference\n");
2132 close(fd);
2133 ast_frfree(f);
2134 goto outrun;
2137 conf_flush(fd, chan);
2138 } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
2139 && confflags & CONFFLAG_PASS_DTMF) {
2140 conf_queue_dtmf(conf, user, f);
2141 } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
2142 switch (f->subclass) {
2143 case AST_CONTROL_HOLD:
2144 sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
2145 break;
2146 default:
2147 break;
2149 } else if (f->frametype == AST_FRAME_NULL) {
2150 /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
2151 } else if (option_debug) {
2152 ast_log(LOG_DEBUG,
2153 "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
2154 chan->name, f->frametype, f->subclass);
2156 ast_frfree(f);
2157 } else if (outfd > -1) {
2158 res = read(outfd, buf, CONF_SIZE);
2159 if (res > 0) {
2160 memset(&fr, 0, sizeof(fr));
2161 fr.frametype = AST_FRAME_VOICE;
2162 fr.subclass = AST_FORMAT_SLINEAR;
2163 fr.datalen = res;
2164 fr.samples = res/2;
2165 fr.data = buf;
2166 fr.offset = AST_FRIENDLY_OFFSET;
2167 if (!user->listen.actual &&
2168 ((confflags & CONFFLAG_MONITOR) ||
2169 (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
2170 (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
2171 )) {
2172 int index;
2173 for (index=0;index<AST_FRAME_BITS;index++)
2174 if (chan->rawwriteformat & (1 << index))
2175 break;
2176 if (index >= AST_FRAME_BITS)
2177 goto bailoutandtrynormal;
2178 ast_mutex_lock(&conf->listenlock);
2179 if (!conf->transframe[index]) {
2180 if (conf->origframe) {
2181 if (!conf->transpath[index])
2182 conf->transpath[index] = ast_translator_build_path((1 << index), AST_FORMAT_SLINEAR);
2183 if (conf->transpath[index]) {
2184 conf->transframe[index] = ast_translate(conf->transpath[index], conf->origframe, 0);
2185 if (!conf->transframe[index])
2186 conf->transframe[index] = &ast_null_frame;
2190 if (conf->transframe[index]) {
2191 if (conf->transframe[index]->frametype != AST_FRAME_NULL) {
2192 if (ast_write(chan, conf->transframe[index]))
2193 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2195 } else {
2196 ast_mutex_unlock(&conf->listenlock);
2197 goto bailoutandtrynormal;
2199 ast_mutex_unlock(&conf->listenlock);
2200 } else {
2201 bailoutandtrynormal:
2202 if (user->listen.actual)
2203 ast_frame_adjust_volume(&fr, user->listen.actual);
2204 if (ast_write(chan, &fr) < 0) {
2205 ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
2208 } else
2209 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
2211 lastmarked = currentmarked;
2215 if (musiconhold)
2216 ast_moh_stop(chan);
2218 if (using_pseudo)
2219 close(fd);
2220 else {
2221 /* Take out of conference */
2222 ztc.chan = 0;
2223 ztc.confno = 0;
2224 ztc.confmode = 0;
2225 if (ioctl(fd, ZT_SETCONF, &ztc)) {
2226 ast_log(LOG_WARNING, "Error setting conference\n");
2230 reset_volumes(user);
2232 AST_LIST_LOCK(&confs);
2233 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
2234 conf_play(chan, conf, LEAVE);
2236 if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
2237 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
2238 if ((conf->chan) && (conf->users > 1)) {
2239 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
2240 ast_waitstream(conf->chan, "");
2241 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
2242 ast_waitstream(conf->chan, "");
2244 ast_filedelete(user->namerecloc, NULL);
2247 AST_LIST_UNLOCK(&confs);
2249 outrun:
2250 AST_LIST_LOCK(&confs);
2252 if (dsp)
2253 ast_dsp_free(dsp);
2255 if (user->user_no) { /* Only cleanup users who really joined! */
2256 now = time(NULL);
2257 hr = (now - user->jointime) / 3600;
2258 min = ((now - user->jointime) % 3600) / 60;
2259 sec = (now - user->jointime) % 60;
2261 if (sent_event) {
2262 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
2263 "Channel: %s\r\n"
2264 "Uniqueid: %s\r\n"
2265 "Meetme: %s\r\n"
2266 "Usernum: %d\r\n"
2267 "CallerIDnum: %s\r\n"
2268 "CallerIDname: %s\r\n"
2269 "Duration: %ld\r\n",
2270 chan->name, chan->uniqueid, conf->confno,
2271 user->user_no,
2272 S_OR(user->chan->cid.cid_num, "<unknown>"),
2273 S_OR(user->chan->cid.cid_name, "<unknown>"),
2274 (long)(now - user->jointime));
2277 conf->users--;
2278 /* Update table */
2279 snprintf(members, sizeof(members), "%d", conf->users);
2280 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2281 if (confflags & CONFFLAG_MARKEDUSER)
2282 conf->markedusers--;
2283 /* Remove ourselves from the list */
2284 AST_LIST_REMOVE(&conf->userlist, user, list);
2286 /* Change any states */
2287 if (!conf->users)
2288 ast_device_state_changed("meetme:%s", conf->confno);
2290 /* Return the number of seconds the user was in the conf */
2291 snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
2292 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
2294 free(user);
2295 AST_LIST_UNLOCK(&confs);
2297 return ret;
2300 static struct ast_conference *find_conf_realtime(struct ast_channel *chan, char *confno, int make, int dynamic,
2301 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2303 struct ast_variable *var;
2304 struct ast_conference *cnf;
2306 /* Check first in the conference list */
2307 AST_LIST_LOCK(&confs);
2308 AST_LIST_TRAVERSE(&confs, cnf, list) {
2309 if (!strcmp(confno, cnf->confno))
2310 break;
2312 if (cnf) {
2313 cnf->refcount += refcount;
2315 AST_LIST_UNLOCK(&confs);
2317 if (!cnf) {
2318 char *pin = NULL, *pinadmin = NULL; /* For temp use */
2320 var = ast_load_realtime("meetme", "confno", confno, NULL);
2322 if (!var)
2323 return NULL;
2325 while (var) {
2326 if (!strcasecmp(var->name, "pin")) {
2327 pin = ast_strdupa(var->value);
2328 } else if (!strcasecmp(var->name, "adminpin")) {
2329 pinadmin = ast_strdupa(var->value);
2331 var = var->next;
2333 ast_variables_destroy(var);
2335 cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount);
2338 if (cnf) {
2339 if (confflags && !cnf->chan &&
2340 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2341 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2342 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2343 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2346 if (confflags && !cnf->chan &&
2347 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2348 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2349 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2353 return cnf;
2357 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic,
2358 char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
2360 struct ast_config *cfg;
2361 struct ast_variable *var;
2362 struct ast_conference *cnf;
2363 char *parse;
2364 AST_DECLARE_APP_ARGS(args,
2365 AST_APP_ARG(confno);
2366 AST_APP_ARG(pin);
2367 AST_APP_ARG(pinadmin);
2370 /* Check first in the conference list */
2371 AST_LIST_LOCK(&confs);
2372 AST_LIST_TRAVERSE(&confs, cnf, list) {
2373 if (!strcmp(confno, cnf->confno))
2374 break;
2376 if (cnf){
2377 cnf->refcount += refcount;
2379 AST_LIST_UNLOCK(&confs);
2381 if (!cnf) {
2382 if (dynamic) {
2383 /* No need to parse meetme.conf */
2384 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
2385 if (dynamic_pin) {
2386 if (dynamic_pin[0] == 'q') {
2387 /* Query the user to enter a PIN */
2388 if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
2389 return NULL;
2391 cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount);
2392 } else {
2393 cnf = build_conf(confno, "", "", make, dynamic, refcount);
2395 } else {
2396 /* Check the config */
2397 cfg = ast_config_load(CONFIG_FILE_NAME);
2398 if (!cfg) {
2399 ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
2400 return NULL;
2402 for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
2403 if (strcasecmp(var->name, "conf"))
2404 continue;
2406 if (!(parse = ast_strdupa(var->value)))
2407 return NULL;
2409 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
2410 if (!strcasecmp(args.confno, confno)) {
2411 /* Bingo it's a valid conference */
2412 cnf = build_conf(args.confno,
2413 S_OR(args.pin, ""),
2414 S_OR(args.pinadmin, ""),
2415 make, dynamic, refcount);
2416 break;
2419 if (!var) {
2420 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
2422 ast_config_destroy(cfg);
2424 } else if (dynamic_pin) {
2425 /* Correct for the user selecting 'D' instead of 'd' to have
2426 someone join into a conference that has already been created
2427 with a pin. */
2428 if (dynamic_pin[0] == 'q')
2429 dynamic_pin[0] = '\0';
2432 if (cnf) {
2433 if (confflags && !cnf->chan &&
2434 !ast_test_flag(confflags, CONFFLAG_QUIET) &&
2435 ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
2436 ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
2437 ast_clear_flag(confflags, CONFFLAG_INTROUSER);
2440 if (confflags && !cnf->chan &&
2441 ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
2442 ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
2443 ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
2447 return cnf;
2450 /*! \brief The MeetmeCount application */
2451 static int count_exec(struct ast_channel *chan, void *data)
2453 struct ast_module_user *u;
2454 int res = 0;
2455 struct ast_conference *conf;
2456 int count;
2457 char *localdata;
2458 char val[80] = "0";
2459 AST_DECLARE_APP_ARGS(args,
2460 AST_APP_ARG(confno);
2461 AST_APP_ARG(varname);
2464 if (ast_strlen_zero(data)) {
2465 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
2466 return -1;
2469 u = ast_module_user_add(chan);
2471 if (!(localdata = ast_strdupa(data))) {
2472 ast_module_user_remove(u);
2473 return -1;
2476 AST_STANDARD_APP_ARGS(args, localdata);
2478 conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
2480 if (conf) {
2481 count = conf->users;
2482 dispose_conf(conf);
2483 conf = NULL;
2484 } else
2485 count = 0;
2487 if (!ast_strlen_zero(args.varname)){
2488 /* have var so load it and exit */
2489 snprintf(val, sizeof(val), "%d",count);
2490 pbx_builtin_setvar_helper(chan, args.varname, val);
2491 } else {
2492 if (chan->_state != AST_STATE_UP)
2493 ast_answer(chan);
2494 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
2496 ast_module_user_remove(u);
2498 return res;
2501 /*! \brief The meetme() application */
2502 static int conf_exec(struct ast_channel *chan, void *data)
2504 int res=-1;
2505 struct ast_module_user *u;
2506 char confno[MAX_CONFNUM] = "";
2507 int allowretry = 0;
2508 int retrycnt = 0;
2509 struct ast_conference *cnf = NULL;
2510 struct ast_flags confflags = {0};
2511 int dynamic = 0;
2512 int empty = 0, empty_no_pin = 0;
2513 int always_prompt = 0;
2514 char *notdata, *info, the_pin[MAX_PIN] = "";
2515 AST_DECLARE_APP_ARGS(args,
2516 AST_APP_ARG(confno);
2517 AST_APP_ARG(options);
2518 AST_APP_ARG(pin);
2520 char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
2522 u = ast_module_user_add(chan);
2524 if (ast_strlen_zero(data)) {
2525 allowretry = 1;
2526 notdata = "";
2527 } else {
2528 notdata = data;
2531 if (chan->_state != AST_STATE_UP)
2532 ast_answer(chan);
2534 info = ast_strdupa(notdata);
2536 AST_STANDARD_APP_ARGS(args, info);
2538 if (args.confno) {
2539 ast_copy_string(confno, args.confno, sizeof(confno));
2540 if (ast_strlen_zero(confno)) {
2541 allowretry = 1;
2545 if (args.pin)
2546 ast_copy_string(the_pin, args.pin, sizeof(the_pin));
2548 if (args.options) {
2549 ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
2550 dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
2551 if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !args.pin)
2552 strcpy(the_pin, "q");
2554 empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
2555 empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
2556 always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
2559 do {
2560 if (retrycnt > 3)
2561 allowretry = 0;
2562 if (empty) {
2563 int i;
2564 struct ast_config *cfg;
2565 struct ast_variable *var;
2566 int confno_int;
2568 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
2569 if ((empty_no_pin) || (!dynamic)) {
2570 cfg = ast_config_load(CONFIG_FILE_NAME);
2571 if (cfg) {
2572 var = ast_variable_browse(cfg, "rooms");
2573 while (var) {
2574 if (!strcasecmp(var->name, "conf")) {
2575 char *stringp = ast_strdupa(var->value);
2576 if (stringp) {
2577 char *confno_tmp = strsep(&stringp, "|,");
2578 int found = 0;
2579 if (!dynamic) {
2580 /* For static: run through the list and see if this conference is empty */
2581 AST_LIST_LOCK(&confs);
2582 AST_LIST_TRAVERSE(&confs, cnf, list) {
2583 if (!strcmp(confno_tmp, cnf->confno)) {
2584 /* The conference exists, therefore it's not empty */
2585 found = 1;
2586 break;
2589 AST_LIST_UNLOCK(&confs);
2590 if (!found) {
2591 /* At this point, we have a confno_tmp (static conference) that is empty */
2592 if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
2593 /* Case 1: empty_no_pin and pin is nonexistent (NULL)
2594 * Case 2: empty_no_pin and pin is blank (but not NULL)
2595 * Case 3: not empty_no_pin
2597 ast_copy_string(confno, confno_tmp, sizeof(confno));
2598 break;
2599 /* XXX the map is not complete (but we do have a confno) */
2605 var = var->next;
2607 ast_config_destroy(cfg);
2611 /* Select first conference number not in use */
2612 if (ast_strlen_zero(confno) && dynamic) {
2613 AST_LIST_LOCK(&confs);
2614 for (i = 0; i < sizeof(conf_map) / sizeof(conf_map[0]); i++) {
2615 if (!conf_map[i]) {
2616 snprintf(confno, sizeof(confno), "%d", i);
2617 conf_map[i] = 1;
2618 break;
2621 AST_LIST_UNLOCK(&confs);
2624 /* Not found? */
2625 if (ast_strlen_zero(confno)) {
2626 res = ast_streamfile(chan, "conf-noempty", chan->language);
2627 if (!res)
2628 ast_waitstream(chan, "");
2629 } else {
2630 if (sscanf(confno, "%d", &confno_int) == 1) {
2631 res = ast_streamfile(chan, "conf-enteringno", chan->language);
2632 if (!res) {
2633 ast_waitstream(chan, "");
2634 res = ast_say_digits(chan, confno_int, "", chan->language);
2636 } else {
2637 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
2642 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
2643 /* Prompt user for conference number */
2644 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
2645 if (res < 0) {
2646 /* Don't try to validate when we catch an error */
2647 confno[0] = '\0';
2648 allowretry = 0;
2649 break;
2652 if (!ast_strlen_zero(confno)) {
2653 /* Check the validity of the conference */
2654 cnf = find_conf(chan, confno, 1, dynamic, the_pin,
2655 sizeof(the_pin), 1, &confflags);
2656 if (!cnf) {
2657 cnf = find_conf_realtime(chan, confno, 1, dynamic,
2658 the_pin, sizeof(the_pin), 1, &confflags);
2661 if (!cnf) {
2662 res = ast_streamfile(chan, "conf-invalid", chan->language);
2663 if (!res)
2664 ast_waitstream(chan, "");
2665 res = -1;
2666 if (allowretry)
2667 confno[0] = '\0';
2668 } else {
2669 if ((!ast_strlen_zero(cnf->pin) &&
2670 !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
2671 (!ast_strlen_zero(cnf->pinadmin) &&
2672 ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
2673 char pin[MAX_PIN] = "";
2674 int j;
2676 /* Allow the pin to be retried up to 3 times */
2677 for (j = 0; j < 3; j++) {
2678 if (*the_pin && (always_prompt == 0)) {
2679 ast_copy_string(pin, the_pin, sizeof(pin));
2680 res = 0;
2681 } else {
2682 /* Prompt user for pin if pin is required */
2683 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
2685 if (res >= 0) {
2686 if (!strcasecmp(pin, cnf->pin) ||
2687 (!ast_strlen_zero(cnf->pinadmin) &&
2688 !strcasecmp(pin, cnf->pinadmin))) {
2689 /* Pin correct */
2690 allowretry = 0;
2691 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
2692 ast_set_flag(&confflags, CONFFLAG_ADMIN);
2693 /* Run the conference */
2694 res = conf_run(chan, cnf, confflags.flags, optargs);
2695 break;
2696 } else {
2697 /* Pin invalid */
2698 if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
2699 res = ast_waitstream(chan, AST_DIGIT_ANY);
2700 ast_stopstream(chan);
2702 else {
2703 ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
2704 break;
2706 if (res < 0)
2707 break;
2708 pin[0] = res;
2709 pin[1] = '\0';
2710 res = -1;
2711 if (allowretry)
2712 confno[0] = '\0';
2714 } else {
2715 /* failed when getting the pin */
2716 res = -1;
2717 allowretry = 0;
2718 /* see if we need to get rid of the conference */
2719 break;
2722 /* Don't retry pin with a static pin */
2723 if (*the_pin && (always_prompt==0)) {
2724 break;
2727 } else {
2728 /* No pin required */
2729 allowretry = 0;
2731 /* Run the conference */
2732 res = conf_run(chan, cnf, confflags.flags, optargs);
2734 dispose_conf(cnf);
2735 cnf = NULL;
2738 } while (allowretry);
2740 if (cnf)
2741 dispose_conf(cnf);
2743 ast_module_user_remove(u);
2745 return res;
2748 static struct ast_conf_user *find_user(struct ast_conference *conf, char *callerident)
2750 struct ast_conf_user *user = NULL;
2751 int cid;
2753 sscanf(callerident, "%i", &cid);
2754 if (conf && callerident) {
2755 AST_LIST_TRAVERSE(&conf->userlist, user, list) {
2756 if (cid == user->user_no)
2757 return user;
2760 return NULL;
2763 /*! \brief The MeetMeadmin application */
2764 /* MeetMeAdmin(confno, command, caller) */
2765 static int admin_exec(struct ast_channel *chan, void *data) {
2766 char *params;
2767 struct ast_conference *cnf;
2768 struct ast_conf_user *user = NULL;
2769 struct ast_module_user *u;
2770 AST_DECLARE_APP_ARGS(args,
2771 AST_APP_ARG(confno);
2772 AST_APP_ARG(command);
2773 AST_APP_ARG(user);
2776 if (ast_strlen_zero(data)) {
2777 ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
2778 return -1;
2781 u = ast_module_user_add(chan);
2783 AST_LIST_LOCK(&confs);
2785 params = ast_strdupa(data);
2786 AST_STANDARD_APP_ARGS(args, params);
2788 if (!args.command) {
2789 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
2790 AST_LIST_UNLOCK(&confs);
2791 ast_module_user_remove(u);
2792 return -1;
2794 AST_LIST_TRAVERSE(&confs, cnf, list) {
2795 if (!strcmp(cnf->confno, args.confno))
2796 break;
2799 if (!cnf) {
2800 ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
2801 AST_LIST_UNLOCK(&confs);
2802 ast_module_user_remove(u);
2803 return 0;
2806 ast_atomic_fetchadd_int(&cnf->refcount, 1);
2808 if (args.user)
2809 user = find_user(cnf, args.user);
2811 switch (*args.command) {
2812 case 76: /* L: Lock */
2813 cnf->locked = 1;
2814 break;
2815 case 108: /* l: Unlock */
2816 cnf->locked = 0;
2817 break;
2818 case 75: /* K: kick all users */
2819 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2820 user->adminflags |= ADMINFLAG_KICKME;
2821 break;
2822 case 101: /* e: Eject last user*/
2823 user = AST_LIST_LAST(&cnf->userlist);
2824 if (!(user->userflags & CONFFLAG_ADMIN))
2825 user->adminflags |= ADMINFLAG_KICKME;
2826 else
2827 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
2828 break;
2829 case 77: /* M: Mute */
2830 if (user) {
2831 user->adminflags |= ADMINFLAG_MUTED;
2832 } else
2833 ast_log(LOG_NOTICE, "Specified User not found!\n");
2834 break;
2835 case 78: /* N: Mute all (non-admin) users */
2836 AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
2837 if (!(user->userflags & CONFFLAG_ADMIN))
2838 user->adminflags |= ADMINFLAG_MUTED;
2840 break;
2841 case 109: /* m: Unmute */
2842 if (user) {
2843 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2844 } else
2845 ast_log(LOG_NOTICE, "Specified User not found!\n");
2846 break;
2847 case 110: /* n: Unmute all users */
2848 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2849 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
2850 break;
2851 case 107: /* k: Kick user */
2852 if (user)
2853 user->adminflags |= ADMINFLAG_KICKME;
2854 else
2855 ast_log(LOG_NOTICE, "Specified User not found!\n");
2856 break;
2857 case 118: /* v: Lower all users listen volume */
2858 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2859 tweak_listen_volume(user, VOL_DOWN);
2860 break;
2861 case 86: /* V: Raise all users listen volume */
2862 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2863 tweak_listen_volume(user, VOL_UP);
2864 break;
2865 case 115: /* s: Lower all users speaking volume */
2866 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2867 tweak_talk_volume(user, VOL_DOWN);
2868 break;
2869 case 83: /* S: Raise all users speaking volume */
2870 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2871 tweak_talk_volume(user, VOL_UP);
2872 break;
2873 case 82: /* R: Reset all volume levels */
2874 AST_LIST_TRAVERSE(&cnf->userlist, user, list)
2875 reset_volumes(user);
2876 break;
2877 case 114: /* r: Reset user's volume level */
2878 if (user)
2879 reset_volumes(user);
2880 else
2881 ast_log(LOG_NOTICE, "Specified User not found!\n");
2882 break;
2883 case 85: /* U: Raise user's listen volume */
2884 if (user)
2885 tweak_listen_volume(user, VOL_UP);
2886 else
2887 ast_log(LOG_NOTICE, "Specified User not found!\n");
2888 break;
2889 case 117: /* u: Lower user's listen volume */
2890 if (user)
2891 tweak_listen_volume(user, VOL_DOWN);
2892 else
2893 ast_log(LOG_NOTICE, "Specified User not found!\n");
2894 break;
2895 case 84: /* T: Raise user's talk volume */
2896 if (user)
2897 tweak_talk_volume(user, VOL_UP);
2898 else
2899 ast_log(LOG_NOTICE, "Specified User not found!\n");
2900 break;
2901 case 116: /* t: Lower user's talk volume */
2902 if (user)
2903 tweak_talk_volume(user, VOL_DOWN);
2904 else
2905 ast_log(LOG_NOTICE, "Specified User not found!\n");
2906 break;
2909 AST_LIST_UNLOCK(&confs);
2911 dispose_conf(cnf);
2913 ast_module_user_remove(u);
2915 return 0;
2918 static int meetmemute(struct mansession *s, const struct message *m, int mute)
2920 struct ast_conference *conf;
2921 struct ast_conf_user *user;
2922 const char *confid = astman_get_header(m, "Meetme");
2923 char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
2924 int userno;
2926 if (ast_strlen_zero(confid)) {
2927 astman_send_error(s, m, "Meetme conference not specified");
2928 return 0;
2931 if (ast_strlen_zero(userid)) {
2932 astman_send_error(s, m, "Meetme user number not specified");
2933 return 0;
2936 userno = strtoul(userid, &userid, 10);
2938 if (*userid) {
2939 astman_send_error(s, m, "Invalid user number");
2940 return 0;
2943 /* Look in the conference list */
2944 AST_LIST_LOCK(&confs);
2945 AST_LIST_TRAVERSE(&confs, conf, list) {
2946 if (!strcmp(confid, conf->confno))
2947 break;
2950 if (!conf) {
2951 AST_LIST_UNLOCK(&confs);
2952 astman_send_error(s, m, "Meetme conference does not exist");
2953 return 0;
2956 AST_LIST_TRAVERSE(&conf->userlist, user, list)
2957 if (user->user_no == userno)
2958 break;
2960 if (!user) {
2961 AST_LIST_UNLOCK(&confs);
2962 astman_send_error(s, m, "User number not found");
2963 return 0;
2966 if (mute)
2967 user->adminflags |= ADMINFLAG_MUTED; /* request user muting */
2968 else
2969 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED); /* request user unmuting */
2971 AST_LIST_UNLOCK(&confs);
2973 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);
2975 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2976 return 0;
2979 static int action_meetmemute(struct mansession *s, const struct message *m)
2981 return meetmemute(s, m, 1);
2984 static int action_meetmeunmute(struct mansession *s, const struct message *m)
2986 return meetmemute(s, m, 0);
2989 static void *recordthread(void *args)
2991 struct ast_conference *cnf = args;
2992 struct ast_frame *f=NULL;
2993 int flags;
2994 struct ast_filestream *s=NULL;
2995 int res=0;
2996 int x;
2997 const char *oldrecordingfilename = NULL;
2999 if (!cnf || !cnf->lchan) {
3000 pthread_exit(0);
3003 ast_stopstream(cnf->lchan);
3004 flags = O_CREAT|O_TRUNC|O_WRONLY;
3007 cnf->recording = MEETME_RECORD_ACTIVE;
3008 while (ast_waitfor(cnf->lchan, -1) > -1) {
3009 if (cnf->recording == MEETME_RECORD_TERMINATE) {
3010 AST_LIST_LOCK(&confs);
3011 AST_LIST_UNLOCK(&confs);
3012 break;
3014 if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
3015 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
3016 oldrecordingfilename = cnf->recordingfilename;
3019 f = ast_read(cnf->lchan);
3020 if (!f) {
3021 res = -1;
3022 break;
3024 if (f->frametype == AST_FRAME_VOICE) {
3025 ast_mutex_lock(&cnf->listenlock);
3026 for (x=0;x<AST_FRAME_BITS;x++) {
3027 /* Free any translations that have occured */
3028 if (cnf->transframe[x]) {
3029 ast_frfree(cnf->transframe[x]);
3030 cnf->transframe[x] = NULL;
3033 if (cnf->origframe)
3034 ast_frfree(cnf->origframe);
3035 cnf->origframe = ast_frdup(f);
3036 ast_mutex_unlock(&cnf->listenlock);
3037 if (s)
3038 res = ast_writestream(s, f);
3039 if (res) {
3040 ast_frfree(f);
3041 break;
3044 ast_frfree(f);
3046 cnf->recording = MEETME_RECORD_OFF;
3047 if (s)
3048 ast_closestream(s);
3050 pthread_exit(0);
3053 /*! \brief Callback for devicestate providers */
3054 static int meetmestate(const char *data)
3056 struct ast_conference *conf;
3058 /* Find conference */
3059 AST_LIST_LOCK(&confs);
3060 AST_LIST_TRAVERSE(&confs, conf, list) {
3061 if (!strcmp(data, conf->confno))
3062 break;
3064 AST_LIST_UNLOCK(&confs);
3065 if (!conf)
3066 return AST_DEVICE_INVALID;
3069 /* SKREP to fill */
3070 if (!conf->users)
3071 return AST_DEVICE_NOT_INUSE;
3073 return AST_DEVICE_INUSE;
3076 static void load_config_meetme(void)
3078 struct ast_config *cfg;
3079 const char *val;
3081 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3083 if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
3084 return;
3086 if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
3087 if ((sscanf(val, "%d", &audio_buffers) != 1)) {
3088 ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
3089 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3090 } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
3091 ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
3092 ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
3093 audio_buffers = DEFAULT_AUDIO_BUFFERS;
3095 if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
3096 ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
3099 ast_config_destroy(cfg);
3102 /*! \brief Find an SLA trunk by name
3103 * \note This must be called with the sla_trunks container locked
3105 static struct sla_trunk *sla_find_trunk(const char *name)
3107 struct sla_trunk *trunk = NULL;
3109 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
3110 if (!strcasecmp(trunk->name, name))
3111 break;
3114 return trunk;
3117 /*! \brief Find an SLA station by name
3118 * \note This must be called with the sla_stations container locked
3120 static struct sla_station *sla_find_station(const char *name)
3122 struct sla_station *station = NULL;
3124 AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
3125 if (!strcasecmp(station->name, name))
3126 break;
3129 return station;
3132 static int sla_check_station_hold_access(const struct sla_trunk *trunk,
3133 const struct sla_station *station)
3135 struct sla_station_ref *station_ref;
3136 struct sla_trunk_ref *trunk_ref;
3138 /* For each station that has this call on hold, check for private hold. */
3139 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
3140 AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
3141 if (trunk_ref->trunk != trunk || station_ref->station == station)
3142 continue;
3143 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
3144 station_ref->station->hold_access == SLA_HOLD_PRIVATE)
3145 return 1;
3146 return 0;
3150 return 0;
3153 /*! \brief Find a trunk reference on a station by name
3154 * \param station the station
3155 * \param name the trunk's name
3156 * \return a pointer to the station's trunk reference. If the trunk
3157 * is not found, it is not idle and barge is disabled, or if
3158 * it is on hold and private hold is set, then NULL will be returned.
3160 static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station *station,
3161 const char *name)
3163 struct sla_trunk_ref *trunk_ref = NULL;
3165 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3166 if (strcasecmp(trunk_ref->trunk->name, name))
3167 continue;
3169 if ( (trunk_ref->trunk->barge_disabled
3170 && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
3171 (trunk_ref->trunk->hold_stations
3172 && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
3173 && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
3174 sla_check_station_hold_access(trunk_ref->trunk, station) )
3176 trunk_ref = NULL;
3179 break;
3182 return trunk_ref;
3185 static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
3187 struct sla_station_ref *station_ref;
3189 if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
3190 return NULL;
3192 station_ref->station = station;
3194 return station_ref;
3197 static struct sla_ringing_station *sla_create_ringing_station(struct sla_station *station)
3199 struct sla_ringing_station *ringing_station;
3201 if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
3202 return NULL;
3204 ringing_station->station = station;
3205 ringing_station->ring_begin = ast_tvnow();
3207 return ringing_station;
3210 static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk_state state,
3211 enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
3213 struct sla_station *station;
3214 struct sla_trunk_ref *trunk_ref;
3216 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
3217 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3218 if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
3219 || trunk_ref == exclude)
3220 continue;
3221 trunk_ref->state = state;
3222 ast_device_state_changed("SLA:%s_%s", station->name, trunk->name);
3223 break;
3228 struct run_station_args {
3229 struct sla_station *station;
3230 struct sla_trunk_ref *trunk_ref;
3231 ast_mutex_t *cond_lock;
3232 ast_cond_t *cond;
3235 static void *run_station(void *data)
3237 struct sla_station *station;
3238 struct sla_trunk_ref *trunk_ref;
3239 char conf_name[MAX_CONFNUM];
3240 struct ast_flags conf_flags = { 0 };
3241 struct ast_conference *conf;
3244 struct run_station_args *args = data;
3245 station = args->station;
3246 trunk_ref = args->trunk_ref;
3247 ast_mutex_lock(args->cond_lock);
3248 ast_cond_signal(args->cond);
3249 ast_mutex_unlock(args->cond_lock);
3250 /* args is no longer valid here. */
3253 ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
3254 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
3255 ast_set_flag(&conf_flags,
3256 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
3257 ast_answer(trunk_ref->chan);
3258 conf = build_conf(conf_name, "", "", 0, 0, 1);
3259 if (conf) {
3260 conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
3261 dispose_conf(conf);
3262 conf = NULL;
3264 trunk_ref->chan = NULL;
3265 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
3266 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
3267 strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
3268 admin_exec(NULL, conf_name);
3269 trunk_ref->trunk->hold_stations = 0;
3270 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
3273 ast_dial_join(station->dial);
3274 ast_dial_destroy(station->dial);
3275 station->dial = NULL;
3277 return NULL;
3280 static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
3282 char buf[80];
3283 struct sla_station_ref *station_ref;
3285 snprintf(buf, sizeof(buf), "SLA_%s|K", ringing_trunk->trunk->name);
3286 admin_exec(NULL, buf);
3287 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
3289 while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
3290 free(station_ref);
3292 free(ringing_trunk);
3295 static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
3296 enum sla_station_hangup hangup)
3298 struct sla_ringing_trunk *ringing_trunk;
3299 struct sla_trunk_ref *trunk_ref;
3300 struct sla_station_ref *station_ref;
3302 ast_dial_join(ringing_station->station->dial);
3303 ast_dial_destroy(ringing_station->station->dial);
3304 ringing_station->station->dial = NULL;
3306 if (hangup == SLA_STATION_HANGUP_NORMAL)
3307 goto done;
3309 /* If the station is being hung up because of a timeout, then add it to the
3310 * list of timed out stations on each of the ringing trunks. This is so
3311 * that when doing further processing to figure out which stations should be
3312 * ringing, which trunk to answer, determining timeouts, etc., we know which
3313 * ringing trunks we should ignore. */
3314 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3315 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3316 if (ringing_trunk->trunk == trunk_ref->trunk)
3317 break;
3319 if (!trunk_ref)
3320 continue;
3321 if (!(station_ref = sla_create_station_ref(ringing_station->station)))
3322 continue;
3323 AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
3326 done:
3327 free(ringing_station);
3330 static void sla_dial_state_callback(struct ast_dial *dial)
3332 sla_queue_event(SLA_EVENT_DIAL_STATE);
3335 /*! \brief Check to see if dialing this station already timed out for this ringing trunk
3336 * \note Assumes sla.lock is locked
3338 static int sla_check_timed_out_station(const struct sla_ringing_trunk *ringing_trunk,
3339 const struct sla_station *station)
3341 struct sla_station_ref *timed_out_station;
3343 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
3344 if (station == timed_out_station->station)
3345 return 1;
3348 return 0;
3351 /*! \brief Choose the highest priority ringing trunk for a station
3352 * \param station the station
3353 * \param remove remove the ringing trunk once selected
3354 * \param trunk_ref a place to store the pointer to this stations reference to
3355 * the selected trunk
3356 * \return a pointer to the selected ringing trunk, or NULL if none found
3357 * \note Assumes that sla.lock is locked
3359 static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *station,
3360 struct sla_trunk_ref **trunk_ref, int remove)
3362 struct sla_trunk_ref *s_trunk_ref;
3363 struct sla_ringing_trunk *ringing_trunk = NULL;
3365 AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
3366 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
3367 /* Make sure this is the trunk we're looking for */
3368 if (s_trunk_ref->trunk != ringing_trunk->trunk)
3369 continue;
3371 /* This trunk on the station is ringing. But, make sure this station
3372 * didn't already time out while this trunk was ringing. */
3373 if (sla_check_timed_out_station(ringing_trunk, station))
3374 continue;
3376 if (remove)
3377 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
3379 if (trunk_ref)
3380 *trunk_ref = s_trunk_ref;
3382 break;
3384 AST_LIST_TRAVERSE_SAFE_END
3386 if (ringing_trunk)
3387 break;
3390 return ringing_trunk;
3393 static void sla_handle_dial_state_event(void)
3395 struct sla_ringing_station *ringing_station;
3397 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3398 struct sla_trunk_ref *s_trunk_ref = NULL;
3399 struct sla_ringing_trunk *ringing_trunk = NULL;
3400 struct run_station_args args;
3401 enum ast_dial_result dial_res;
3402 pthread_attr_t attr;
3403 pthread_t dont_care;
3404 ast_mutex_t cond_lock;
3405 ast_cond_t cond;
3407 switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
3408 case AST_DIAL_RESULT_HANGUP:
3409 case AST_DIAL_RESULT_INVALID:
3410 case AST_DIAL_RESULT_FAILED:
3411 case AST_DIAL_RESULT_TIMEOUT:
3412 case AST_DIAL_RESULT_UNANSWERED:
3413 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3414 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
3415 break;
3416 case AST_DIAL_RESULT_ANSWERED:
3417 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3418 /* Find the appropriate trunk to answer. */
3419 ast_mutex_lock(&sla.lock);
3420 ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
3421 ast_mutex_unlock(&sla.lock);
3422 if (!ringing_trunk) {
3423 ast_log(LOG_DEBUG, "Found no ringing trunk for station '%s' to answer!\n",
3424 ringing_station->station->name);
3425 break;
3427 /* Track the channel that answered this trunk */
3428 s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
3429 /* Actually answer the trunk */
3430 ast_answer(ringing_trunk->trunk->chan);
3431 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
3432 /* Now, start a thread that will connect this station to the trunk. The rest of
3433 * the code here sets up the thread and ensures that it is able to save the arguments
3434 * before they are no longer valid since they are allocated on the stack. */
3435 args.trunk_ref = s_trunk_ref;
3436 args.station = ringing_station->station;
3437 args.cond = &cond;
3438 args.cond_lock = &cond_lock;
3439 free(ringing_trunk);
3440 free(ringing_station);
3441 ast_mutex_init(&cond_lock);
3442 ast_cond_init(&cond, NULL);
3443 pthread_attr_init(&attr);
3444 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
3445 ast_mutex_lock(&cond_lock);
3446 ast_pthread_create_background(&dont_care, &attr, run_station, &args);
3447 ast_cond_wait(&cond, &cond_lock);
3448 ast_mutex_unlock(&cond_lock);
3449 ast_mutex_destroy(&cond_lock);
3450 ast_cond_destroy(&cond);
3451 pthread_attr_destroy(&attr);
3452 break;
3453 case AST_DIAL_RESULT_TRYING:
3454 case AST_DIAL_RESULT_RINGING:
3455 case AST_DIAL_RESULT_PROGRESS:
3456 case AST_DIAL_RESULT_PROCEEDING:
3457 break;
3459 if (dial_res == AST_DIAL_RESULT_ANSWERED) {
3460 /* Queue up reprocessing ringing trunks, and then ringing stations again */
3461 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
3462 sla_queue_event(SLA_EVENT_DIAL_STATE);
3463 break;
3466 AST_LIST_TRAVERSE_SAFE_END
3469 /*! \brief Check to see if this station is already ringing
3470 * \note Assumes sla.lock is locked
3472 static int sla_check_ringing_station(const struct sla_station *station)
3474 struct sla_ringing_station *ringing_station;
3476 AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
3477 if (station == ringing_station->station)
3478 return 1;
3481 return 0;
3484 /*! \brief Check to see if this station has failed to be dialed in the past minute
3485 * \note assumes sla.lock is locked
3487 static int sla_check_failed_station(const struct sla_station *station)
3489 struct sla_failed_station *failed_station;
3490 int res = 0;
3492 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
3493 if (station != failed_station->station)
3494 continue;
3495 if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
3496 AST_LIST_REMOVE_CURRENT(&sla.failed_stations, entry);
3497 free(failed_station);
3498 break;
3500 res = 1;
3502 AST_LIST_TRAVERSE_SAFE_END
3504 return res;
3507 /*! \brief Ring a station
3508 * \note Assumes sla.lock is locked
3510 static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
3512 char *tech, *tech_data;
3513 struct ast_dial *dial;
3514 struct sla_ringing_station *ringing_station;
3515 const char *cid_name = NULL, *cid_num = NULL;
3516 enum ast_dial_result res;
3518 if (!(dial = ast_dial_create()))
3519 return -1;
3521 ast_dial_set_state_callback(dial, sla_dial_state_callback);
3522 tech_data = ast_strdupa(station->device);
3523 tech = strsep(&tech_data, "/");
3525 if (ast_dial_append(dial, tech, tech_data) == -1) {
3526 ast_dial_destroy(dial);
3527 return -1;
3530 if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_name)) {
3531 cid_name = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_name);
3532 free(ringing_trunk->trunk->chan->cid.cid_name);
3533 ringing_trunk->trunk->chan->cid.cid_name = NULL;
3535 if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_num)) {
3536 cid_num = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_num);
3537 free(ringing_trunk->trunk->chan->cid.cid_num);
3538 ringing_trunk->trunk->chan->cid.cid_num = NULL;
3541 res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
3543 if (cid_name)
3544 ringing_trunk->trunk->chan->cid.cid_name = ast_strdup(cid_name);
3545 if (cid_num)
3546 ringing_trunk->trunk->chan->cid.cid_num = ast_strdup(cid_num);
3548 if (res != AST_DIAL_RESULT_TRYING) {
3549 struct sla_failed_station *failed_station;
3550 ast_dial_destroy(dial);
3551 if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
3552 return -1;
3553 failed_station->station = station;
3554 failed_station->last_try = ast_tvnow();
3555 AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
3556 return -1;
3558 if (!(ringing_station = sla_create_ringing_station(station))) {
3559 ast_dial_join(dial);
3560 ast_dial_destroy(dial);
3561 return -1;
3564 station->dial = dial;
3566 AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
3568 return 0;
3571 /*! \brief Check to see if a station is in use
3573 static int sla_check_inuse_station(const struct sla_station *station)
3575 struct sla_trunk_ref *trunk_ref;
3577 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3578 if (trunk_ref->chan)
3579 return 1;
3582 return 0;
3585 static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *station,
3586 const struct sla_trunk *trunk)
3588 struct sla_trunk_ref *trunk_ref = NULL;
3590 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
3591 if (trunk_ref->trunk == trunk)
3592 break;
3595 return trunk_ref;
3598 /*! \brief Calculate the ring delay for a given ringing trunk on a station
3599 * \param station the station
3600 * \param trunk the trunk. If NULL, the highest priority ringing trunk will be used
3601 * \return the number of ms left before the delay is complete, or INT_MAX if there is no delay
3603 static int sla_check_station_delay(struct sla_station *station,
3604 struct sla_ringing_trunk *ringing_trunk)
3606 struct sla_trunk_ref *trunk_ref;
3607 unsigned int delay = UINT_MAX;
3608 int time_left, time_elapsed;
3610 if (!ringing_trunk)
3611 ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
3612 else
3613 trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
3615 if (!ringing_trunk || !trunk_ref)
3616 return delay;
3618 /* If this station has a ring delay specific to the highest priority
3619 * ringing trunk, use that. Otherwise, use the ring delay specified
3620 * globally for the station. */
3621 delay = trunk_ref->ring_delay;
3622 if (!delay)
3623 delay = station->ring_delay;
3624 if (!delay)
3625 return INT_MAX;
3627 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3628 time_left = (delay * 1000) - time_elapsed;
3630 return time_left;
3633 /*! \brief Ring stations based on current set of ringing trunks
3634 * \note Assumes that sla.lock is locked
3636 static void sla_ring_stations(void)
3638 struct sla_station_ref *station_ref;
3639 struct sla_ringing_trunk *ringing_trunk;
3641 /* Make sure that every station that uses at least one of the ringing
3642 * trunks, is ringing. */
3643 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3644 AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
3645 int time_left;
3647 /* Is this station already ringing? */
3648 if (sla_check_ringing_station(station_ref->station))
3649 continue;
3651 /* Is this station already in a call? */
3652 if (sla_check_inuse_station(station_ref->station))
3653 continue;
3655 /* Did we fail to dial this station earlier? If so, has it been
3656 * a minute since we tried? */
3657 if (sla_check_failed_station(station_ref->station))
3658 continue;
3660 /* If this station already timed out while this trunk was ringing,
3661 * do not dial it again for this ringing trunk. */
3662 if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
3663 continue;
3665 /* Check for a ring delay in progress */
3666 time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
3667 if (time_left != INT_MAX && time_left > 0)
3668 continue;
3670 /* It is time to make this station begin to ring. Do it! */
3671 sla_ring_station(ringing_trunk, station_ref->station);
3674 /* Now, all of the stations that should be ringing, are ringing. */
3677 static void sla_hangup_stations(void)
3679 struct sla_trunk_ref *trunk_ref;
3680 struct sla_ringing_station *ringing_station;
3682 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3683 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3684 struct sla_ringing_trunk *ringing_trunk;
3685 ast_mutex_lock(&sla.lock);
3686 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3687 if (trunk_ref->trunk == ringing_trunk->trunk)
3688 break;
3690 ast_mutex_unlock(&sla.lock);
3691 if (ringing_trunk)
3692 break;
3694 if (!trunk_ref) {
3695 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3696 ast_dial_join(ringing_station->station->dial);
3697 ast_dial_destroy(ringing_station->station->dial);
3698 ringing_station->station->dial = NULL;
3699 free(ringing_station);
3702 AST_LIST_TRAVERSE_SAFE_END
3705 static void sla_handle_ringing_trunk_event(void)
3707 ast_mutex_lock(&sla.lock);
3708 sla_ring_stations();
3709 ast_mutex_unlock(&sla.lock);
3711 /* Find stations that shouldn't be ringing anymore. */
3712 sla_hangup_stations();
3715 static void sla_handle_hold_event(struct sla_event *event)
3717 ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
3718 event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
3719 ast_device_state_changed("SLA:%s_%s",
3720 event->station->name, event->trunk_ref->trunk->name);
3721 sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD,
3722 INACTIVE_TRUNK_REFS, event->trunk_ref);
3724 if (event->trunk_ref->trunk->active_stations == 1) {
3725 /* The station putting it on hold is the only one on the call, so start
3726 * Music on hold to the trunk. */
3727 event->trunk_ref->trunk->on_hold = 1;
3728 ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
3731 ast_softhangup(event->trunk_ref->chan, AST_CAUSE_NORMAL);
3732 event->trunk_ref->chan = NULL;
3735 /*! \brief Process trunk ring timeouts
3736 * \note Called with sla.lock locked
3737 * \return non-zero if a change to the ringing trunks was made
3739 static int sla_calc_trunk_timeouts(unsigned int *timeout)
3741 struct sla_ringing_trunk *ringing_trunk;
3742 int res = 0;
3744 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
3745 int time_left, time_elapsed;
3746 if (!ringing_trunk->trunk->ring_timeout)
3747 continue;
3748 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3749 time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
3750 if (time_left <= 0) {
3751 pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
3752 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
3753 sla_stop_ringing_trunk(ringing_trunk);
3754 res = 1;
3755 continue;
3757 if (time_left < *timeout)
3758 *timeout = time_left;
3760 AST_LIST_TRAVERSE_SAFE_END
3762 return res;
3765 /*! \brief Process station ring timeouts
3766 * \note Called with sla.lock locked
3767 * \return non-zero if a change to the ringing stations was made
3769 static int sla_calc_station_timeouts(unsigned int *timeout)
3771 struct sla_ringing_trunk *ringing_trunk;
3772 struct sla_ringing_station *ringing_station;
3773 int res = 0;
3775 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
3776 unsigned int ring_timeout = 0;
3777 int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
3778 struct sla_trunk_ref *trunk_ref;
3780 /* If there are any ring timeouts specified for a specific trunk
3781 * on the station, then use the highest per-trunk ring timeout.
3782 * Otherwise, use the ring timeout set for the entire station. */
3783 AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
3784 struct sla_station_ref *station_ref;
3785 int trunk_time_elapsed, trunk_time_left;
3787 AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
3788 if (ringing_trunk->trunk == trunk_ref->trunk)
3789 break;
3791 if (!ringing_trunk)
3792 continue;
3794 /* If there is a trunk that is ringing without a timeout, then the
3795 * only timeout that could matter is a global station ring timeout. */
3796 if (!trunk_ref->ring_timeout)
3797 break;
3799 /* This trunk on this station is ringing and has a timeout.
3800 * However, make sure this trunk isn't still ringing from a
3801 * previous timeout. If so, don't consider it. */
3802 AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
3803 if (station_ref->station == ringing_station->station)
3804 break;
3806 if (station_ref)
3807 continue;
3809 trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
3810 trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
3811 if (trunk_time_left > final_trunk_time_left)
3812 final_trunk_time_left = trunk_time_left;
3815 /* No timeout was found for ringing trunks, and no timeout for the entire station */
3816 if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
3817 continue;
3819 /* Compute how much time is left for a global station timeout */
3820 if (ringing_station->station->ring_timeout) {
3821 ring_timeout = ringing_station->station->ring_timeout;
3822 time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
3823 time_left = (ring_timeout * 1000) - time_elapsed;
3826 /* If the time left based on the per-trunk timeouts is smaller than the
3827 * global station ring timeout, use that. */
3828 if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
3829 time_left = final_trunk_time_left;
3831 /* If there is no time left, the station needs to stop ringing */
3832 if (time_left <= 0) {
3833 AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, entry);
3834 sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
3835 res = 1;
3836 continue;
3839 /* There is still some time left for this station to ring, so save that
3840 * timeout if it is the first event scheduled to occur */
3841 if (time_left < *timeout)
3842 *timeout = time_left;
3844 AST_LIST_TRAVERSE_SAFE_END
3846 return res;
3849 /*! \brief Calculate the ring delay for a station
3850 * \note Assumes sla.lock is locked
3852 static int sla_calc_station_delays(unsigned int *timeout)
3854 struct sla_station *station;
3855 int res = 0;
3857 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
3858 struct sla_ringing_trunk *ringing_trunk;
3859 int time_left;
3861 /* Ignore stations already ringing */
3862 if (sla_check_ringing_station(station))
3863 continue;
3865 /* Ignore stations already on a call */
3866 if (sla_check_inuse_station(station))
3867 continue;
3869 /* Ignore stations that don't have one of their trunks ringing */
3870 if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
3871 continue;
3873 if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
3874 continue;
3876 /* If there is no time left, then the station needs to start ringing.
3877 * Return non-zero so that an event will be queued up an event to
3878 * make that happen. */
3879 if (time_left <= 0) {
3880 res = 1;
3881 continue;
3884 if (time_left < *timeout)
3885 *timeout = time_left;
3888 return res;
3891 /*! \brief Calculate the time until the next known event
3892 * \note Called with sla.lock locked */
3893 static int sla_process_timers(struct timespec *ts)
3895 unsigned int timeout = UINT_MAX;
3896 struct timeval tv;
3897 unsigned int change_made = 0;
3899 /* Check for ring timeouts on ringing trunks */
3900 if (sla_calc_trunk_timeouts(&timeout))
3901 change_made = 1;
3903 /* Check for ring timeouts on ringing stations */
3904 if (sla_calc_station_timeouts(&timeout))
3905 change_made = 1;
3907 /* Check for station ring delays */
3908 if (sla_calc_station_delays(&timeout))
3909 change_made = 1;
3911 /* queue reprocessing of ringing trunks */
3912 if (change_made)
3913 sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
3915 /* No timeout */
3916 if (timeout == UINT_MAX)
3917 return 0;
3919 if (ts) {
3920 tv = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
3921 ts->tv_sec = tv.tv_sec;
3922 ts->tv_nsec = tv.tv_usec * 1000;
3925 return 1;
3928 static void *sla_thread(void *data)
3930 struct sla_failed_station *failed_station;
3931 struct sla_ringing_station *ringing_station;
3933 ast_mutex_lock(&sla.lock);
3935 while (!sla.stop) {
3936 struct sla_event *event;
3937 struct timespec ts = { 0, };
3938 unsigned int have_timeout = 0;
3940 if (AST_LIST_EMPTY(&sla.event_q)) {
3941 if ((have_timeout = sla_process_timers(&ts)))
3942 ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
3943 else
3944 ast_cond_wait(&sla.cond, &sla.lock);
3945 if (sla.stop)
3946 break;
3949 if (have_timeout)
3950 sla_process_timers(NULL);
3952 while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
3953 ast_mutex_unlock(&sla.lock);
3954 switch (event->type) {
3955 case SLA_EVENT_HOLD:
3956 sla_handle_hold_event(event);
3957 break;
3958 case SLA_EVENT_DIAL_STATE:
3959 sla_handle_dial_state_event();
3960 break;
3961 case SLA_EVENT_RINGING_TRUNK:
3962 sla_handle_ringing_trunk_event();
3963 break;
3965 free(event);
3966 ast_mutex_lock(&sla.lock);
3970 ast_mutex_unlock(&sla.lock);
3972 while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
3973 free(ringing_station);
3975 while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
3976 free(failed_station);
3978 return NULL;
3981 struct dial_trunk_args {
3982 struct sla_trunk_ref *trunk_ref;
3983 struct sla_station *station;
3984 ast_mutex_t *cond_lock;
3985 ast_cond_t *cond;
3988 static void *dial_trunk(void *data)
3990 struct dial_trunk_args *args = data;
3991 struct ast_dial *dial;
3992 char *tech, *tech_data;
3993 enum ast_dial_result dial_res;
3994 char conf_name[MAX_CONFNUM];
3995 struct ast_conference *conf;
3996 struct ast_flags conf_flags = { 0 };
3997 struct sla_trunk_ref *trunk_ref = args->trunk_ref;
3998 const char *cid_name = NULL, *cid_num = NULL;
4000 if (!(dial = ast_dial_create())) {
4001 ast_mutex_lock(args->cond_lock);
4002 ast_cond_signal(args->cond);
4003 ast_mutex_unlock(args->cond_lock);
4004 return NULL;
4007 tech_data = ast_strdupa(trunk_ref->trunk->device);
4008 tech = strsep(&tech_data, "/");
4009 if (ast_dial_append(dial, tech, tech_data) == -1) {
4010 ast_mutex_lock(args->cond_lock);
4011 ast_cond_signal(args->cond);
4012 ast_mutex_unlock(args->cond_lock);
4013 ast_dial_destroy(dial);
4014 return NULL;
4017 if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_name)) {
4018 cid_name = ast_strdupa(trunk_ref->chan->cid.cid_name);
4019 free(trunk_ref->chan->cid.cid_name);
4020 trunk_ref->chan->cid.cid_name = NULL;
4022 if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_num)) {
4023 cid_num = ast_strdupa(trunk_ref->chan->cid.cid_num);
4024 free(trunk_ref->chan->cid.cid_num);
4025 trunk_ref->chan->cid.cid_num = NULL;
4028 dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
4030 if (cid_name)
4031 trunk_ref->chan->cid.cid_name = ast_strdup(cid_name);
4032 if (cid_num)
4033 trunk_ref->chan->cid.cid_num = ast_strdup(cid_num);
4035 if (dial_res != AST_DIAL_RESULT_TRYING) {
4036 ast_mutex_lock(args->cond_lock);
4037 ast_cond_signal(args->cond);
4038 ast_mutex_unlock(args->cond_lock);
4039 ast_dial_destroy(dial);
4040 return NULL;
4043 for (;;) {
4044 unsigned int done = 0;
4045 switch ((dial_res = ast_dial_state(dial))) {
4046 case AST_DIAL_RESULT_ANSWERED:
4047 trunk_ref->trunk->chan = ast_dial_answered(dial);
4048 case AST_DIAL_RESULT_HANGUP:
4049 case AST_DIAL_RESULT_INVALID:
4050 case AST_DIAL_RESULT_FAILED:
4051 case AST_DIAL_RESULT_TIMEOUT:
4052 case AST_DIAL_RESULT_UNANSWERED:
4053 done = 1;
4054 case AST_DIAL_RESULT_TRYING:
4055 case AST_DIAL_RESULT_RINGING:
4056 case AST_DIAL_RESULT_PROGRESS:
4057 case AST_DIAL_RESULT_PROCEEDING:
4058 break;
4060 if (done)
4061 break;
4064 if (!trunk_ref->trunk->chan) {
4065 ast_mutex_lock(args->cond_lock);
4066 ast_cond_signal(args->cond);
4067 ast_mutex_unlock(args->cond_lock);
4068 ast_dial_join(dial);
4069 ast_dial_destroy(dial);
4070 return NULL;
4073 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
4074 ast_set_flag(&conf_flags,
4075 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER |
4076 CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
4077 conf = build_conf(conf_name, "", "", 1, 1, 1);
4079 ast_mutex_lock(args->cond_lock);
4080 ast_cond_signal(args->cond);
4081 ast_mutex_unlock(args->cond_lock);
4083 if (conf) {
4084 conf_run(trunk_ref->trunk->chan, conf, conf_flags.flags, NULL);
4085 dispose_conf(conf);
4086 conf = NULL;
4089 /* If the trunk is going away, it is definitely now IDLE. */
4090 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4092 trunk_ref->trunk->chan = NULL;
4093 trunk_ref->trunk->on_hold = 0;
4095 ast_dial_join(dial);
4096 ast_dial_destroy(dial);
4098 return NULL;
4101 /*! \brief For a given station, choose the highest priority idle trunk
4103 static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
4105 struct sla_trunk_ref *trunk_ref = NULL;
4107 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4108 if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
4109 break;
4112 return trunk_ref;
4115 static int sla_station_exec(struct ast_channel *chan, void *data)
4117 char *station_name, *trunk_name;
4118 struct sla_station *station;
4119 struct sla_trunk_ref *trunk_ref = NULL;
4120 char conf_name[MAX_CONFNUM];
4121 struct ast_flags conf_flags = { 0 };
4122 struct ast_conference *conf;
4124 if (ast_strlen_zero(data)) {
4125 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
4126 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4127 return 0;
4130 trunk_name = ast_strdupa(data);
4131 station_name = strsep(&trunk_name, "_");
4133 if (ast_strlen_zero(station_name)) {
4134 ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
4135 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4136 return 0;
4139 AST_RWLIST_RDLOCK(&sla_stations);
4140 station = sla_find_station(station_name);
4141 AST_RWLIST_UNLOCK(&sla_stations);
4143 if (!station) {
4144 ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
4145 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
4146 return 0;
4149 AST_RWLIST_RDLOCK(&sla_trunks);
4150 if (!ast_strlen_zero(trunk_name)) {
4151 trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
4152 } else
4153 trunk_ref = sla_choose_idle_trunk(station);
4154 AST_RWLIST_UNLOCK(&sla_trunks);
4156 if (!trunk_ref) {
4157 if (ast_strlen_zero(trunk_name))
4158 ast_log(LOG_NOTICE, "No trunks available for call.\n");
4159 else {
4160 ast_log(LOG_NOTICE, "Can't join existing call on trunk "
4161 "'%s' due to access controls.\n", trunk_name);
4163 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
4164 return 0;
4167 if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
4168 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
4169 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4170 else {
4171 trunk_ref->state = SLA_TRUNK_STATE_UP;
4172 ast_device_state_changed("SLA:%s_%s", station->name, trunk_ref->trunk->name);
4174 } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
4175 struct sla_ringing_trunk *ringing_trunk;
4177 ast_mutex_lock(&sla.lock);
4178 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
4179 if (ringing_trunk->trunk == trunk_ref->trunk) {
4180 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
4181 break;
4184 AST_LIST_TRAVERSE_SAFE_END
4185 ast_mutex_unlock(&sla.lock);
4187 if (ringing_trunk) {
4188 ast_answer(ringing_trunk->trunk->chan);
4189 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4191 free(ringing_trunk);
4193 /* Queue up reprocessing ringing trunks, and then ringing stations again */
4194 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4195 sla_queue_event(SLA_EVENT_DIAL_STATE);
4199 trunk_ref->chan = chan;
4201 if (!trunk_ref->trunk->chan) {
4202 ast_mutex_t cond_lock;
4203 ast_cond_t cond;
4204 pthread_t dont_care;
4205 pthread_attr_t attr;
4206 struct dial_trunk_args args = {
4207 .trunk_ref = trunk_ref,
4208 .station = station,
4209 .cond_lock = &cond_lock,
4210 .cond = &cond,
4212 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4213 /* Create a thread to dial the trunk and dump it into the conference.
4214 * However, we want to wait until the trunk has been dialed and the
4215 * conference is created before continuing on here. */
4216 ast_autoservice_start(chan);
4217 ast_mutex_init(&cond_lock);
4218 ast_cond_init(&cond, NULL);
4219 pthread_attr_init(&attr);
4220 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
4221 ast_mutex_lock(&cond_lock);
4222 ast_pthread_create_background(&dont_care, &attr, dial_trunk, &args);
4223 ast_cond_wait(&cond, &cond_lock);
4224 ast_mutex_unlock(&cond_lock);
4225 ast_mutex_destroy(&cond_lock);
4226 ast_cond_destroy(&cond);
4227 pthread_attr_destroy(&attr);
4228 ast_autoservice_stop(chan);
4229 if (!trunk_ref->trunk->chan) {
4230 ast_log(LOG_DEBUG, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
4231 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
4232 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4233 trunk_ref->chan = NULL;
4234 return 0;
4238 if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
4239 trunk_ref->trunk->on_hold) {
4240 trunk_ref->trunk->on_hold = 0;
4241 ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
4242 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
4245 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
4246 ast_set_flag(&conf_flags,
4247 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
4248 ast_answer(chan);
4249 conf = build_conf(conf_name, "", "", 0, 0, 1);
4250 if (conf) {
4251 conf_run(chan, conf, conf_flags.flags, NULL);
4252 dispose_conf(conf);
4253 conf = NULL;
4255 trunk_ref->chan = NULL;
4256 if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
4257 trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
4258 strncat(conf_name, "|K", sizeof(conf_name) - strlen(conf_name) - 1);
4259 admin_exec(NULL, conf_name);
4260 trunk_ref->trunk->hold_stations = 0;
4261 sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4264 pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
4266 return 0;
4269 static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
4271 struct sla_trunk_ref *trunk_ref;
4273 if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
4274 return NULL;
4276 trunk_ref->trunk = trunk;
4278 return trunk_ref;
4281 static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
4283 struct sla_ringing_trunk *ringing_trunk;
4285 if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
4286 return NULL;
4288 ringing_trunk->trunk = trunk;
4289 ringing_trunk->ring_begin = ast_tvnow();
4291 sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
4293 ast_mutex_lock(&sla.lock);
4294 AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
4295 ast_mutex_unlock(&sla.lock);
4297 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4299 return ringing_trunk;
4302 static int sla_trunk_exec(struct ast_channel *chan, void *data)
4304 const char *trunk_name = data;
4305 char conf_name[MAX_CONFNUM];
4306 struct ast_conference *conf;
4307 struct ast_flags conf_flags = { 0 };
4308 struct sla_trunk *trunk;
4309 struct sla_ringing_trunk *ringing_trunk;
4311 AST_RWLIST_RDLOCK(&sla_trunks);
4312 trunk = sla_find_trunk(trunk_name);
4313 AST_RWLIST_UNLOCK(&sla_trunks);
4314 if (!trunk) {
4315 ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", trunk_name);
4316 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4317 return 0;
4319 if (trunk->chan) {
4320 ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
4321 trunk_name);
4322 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4323 return 0;
4325 trunk->chan = chan;
4327 if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
4328 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4329 return 0;
4332 snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_name);
4333 conf = build_conf(conf_name, "", "", 1, 1, 1);
4334 if (!conf) {
4335 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
4336 return 0;
4338 ast_set_flag(&conf_flags,
4339 CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF);
4340 ast_indicate(chan, AST_CONTROL_RINGING);
4341 conf_run(chan, conf, conf_flags.flags, NULL);
4342 dispose_conf(conf);
4343 conf = NULL;
4344 trunk->chan = NULL;
4345 trunk->on_hold = 0;
4347 if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
4348 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");
4350 /* Remove the entry from the list of ringing trunks if it is still there. */
4351 ast_mutex_lock(&sla.lock);
4352 AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
4353 if (ringing_trunk->trunk == trunk) {
4354 AST_LIST_REMOVE_CURRENT(&sla.ringing_trunks, entry);
4355 break;
4358 AST_LIST_TRAVERSE_SAFE_END
4359 ast_mutex_unlock(&sla.lock);
4360 if (ringing_trunk) {
4361 sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
4362 free(ringing_trunk);
4363 pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
4364 /* Queue reprocessing of ringing trunks to make stations stop ringing
4365 * that shouldn't be ringing after this trunk stopped. */
4366 sla_queue_event(SLA_EVENT_RINGING_TRUNK);
4369 return 0;
4372 static int sla_state(const char *data)
4374 char *buf, *station_name, *trunk_name;
4375 struct sla_station *station;
4376 struct sla_trunk_ref *trunk_ref;
4377 int res = AST_DEVICE_INVALID;
4379 trunk_name = buf = ast_strdupa(data);
4380 station_name = strsep(&trunk_name, "_");
4382 AST_RWLIST_RDLOCK(&sla_stations);
4383 AST_LIST_TRAVERSE(&sla_stations, station, entry) {
4384 if (strcasecmp(station_name, station->name))
4385 continue;
4386 AST_RWLIST_RDLOCK(&sla_trunks);
4387 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4388 if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
4389 break;
4391 if (!trunk_ref) {
4392 AST_RWLIST_UNLOCK(&sla_trunks);
4393 break;
4395 switch (trunk_ref->state) {
4396 case SLA_TRUNK_STATE_IDLE:
4397 res = AST_DEVICE_NOT_INUSE;
4398 break;
4399 case SLA_TRUNK_STATE_RINGING:
4400 res = AST_DEVICE_RINGING;
4401 break;
4402 case SLA_TRUNK_STATE_UP:
4403 res = AST_DEVICE_INUSE;
4404 break;
4405 case SLA_TRUNK_STATE_ONHOLD:
4406 case SLA_TRUNK_STATE_ONHOLD_BYME:
4407 res = AST_DEVICE_ONHOLD;
4408 break;
4410 AST_RWLIST_UNLOCK(&sla_trunks);
4412 AST_RWLIST_UNLOCK(&sla_stations);
4414 if (res == AST_DEVICE_INVALID) {
4415 ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
4416 trunk_name, station_name);
4419 return res;
4422 static void destroy_trunk(struct sla_trunk *trunk)
4424 struct sla_station_ref *station_ref;
4426 if (!ast_strlen_zero(trunk->autocontext))
4427 ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
4429 while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
4430 free(station_ref);
4432 ast_string_field_free_memory(trunk);
4433 free(trunk);
4436 static void destroy_station(struct sla_station *station)
4438 struct sla_trunk_ref *trunk_ref;
4440 if (!ast_strlen_zero(station->autocontext)) {
4441 AST_RWLIST_RDLOCK(&sla_trunks);
4442 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4443 char exten[AST_MAX_EXTENSION];
4444 char hint[AST_MAX_APP];
4445 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
4446 snprintf(hint, sizeof(hint), "SLA:%s", exten);
4447 ast_context_remove_extension(station->autocontext, exten,
4448 1, sla_registrar);
4449 ast_context_remove_extension(station->autocontext, hint,
4450 PRIORITY_HINT, sla_registrar);
4452 AST_RWLIST_UNLOCK(&sla_trunks);
4455 while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
4456 free(trunk_ref);
4458 ast_string_field_free_memory(station);
4459 free(station);
4462 static void sla_destroy(void)
4464 struct sla_trunk *trunk;
4465 struct sla_station *station;
4467 AST_RWLIST_WRLOCK(&sla_trunks);
4468 while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
4469 destroy_trunk(trunk);
4470 AST_RWLIST_UNLOCK(&sla_trunks);
4472 AST_RWLIST_WRLOCK(&sla_stations);
4473 while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
4474 destroy_station(station);
4475 AST_RWLIST_UNLOCK(&sla_stations);
4477 if (sla.thread != AST_PTHREADT_NULL) {
4478 ast_mutex_lock(&sla.lock);
4479 sla.stop = 1;
4480 ast_cond_signal(&sla.cond);
4481 ast_mutex_unlock(&sla.lock);
4482 pthread_join(sla.thread, NULL);
4485 ast_mutex_destroy(&sla.lock);
4486 ast_cond_destroy(&sla.cond);
4489 static int sla_check_device(const char *device)
4491 char *tech, *tech_data;
4493 tech_data = ast_strdupa(device);
4494 tech = strsep(&tech_data, "/");
4496 if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
4497 return -1;
4499 return 0;
4502 static int sla_build_trunk(struct ast_config *cfg, const char *cat)
4504 struct sla_trunk *trunk;
4505 struct ast_variable *var;
4506 const char *dev;
4508 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
4509 ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
4510 return -1;
4513 if (sla_check_device(dev)) {
4514 ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
4515 cat, dev);
4516 return -1;
4519 if (!(trunk = ast_calloc(1, sizeof(*trunk))))
4520 return -1;
4521 if (ast_string_field_init(trunk, 32)) {
4522 free(trunk);
4523 return -1;
4526 ast_string_field_set(trunk, name, cat);
4527 ast_string_field_set(trunk, device, dev);
4529 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
4530 if (!strcasecmp(var->name, "autocontext"))
4531 ast_string_field_set(trunk, autocontext, var->value);
4532 else if (!strcasecmp(var->name, "ringtimeout")) {
4533 if (sscanf(var->value, "%u", &trunk->ring_timeout) != 1) {
4534 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
4535 var->value, trunk->name);
4536 trunk->ring_timeout = 0;
4538 } else if (!strcasecmp(var->name, "barge"))
4539 trunk->barge_disabled = ast_false(var->value);
4540 else if (!strcasecmp(var->name, "hold")) {
4541 if (!strcasecmp(var->value, "private"))
4542 trunk->hold_access = SLA_HOLD_PRIVATE;
4543 else if (!strcasecmp(var->value, "open"))
4544 trunk->hold_access = SLA_HOLD_OPEN;
4545 else {
4546 ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
4547 var->value, trunk->name);
4549 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
4550 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
4551 var->name, var->lineno, SLA_CONFIG_FILE);
4555 if (!ast_strlen_zero(trunk->autocontext)) {
4556 struct ast_context *context;
4557 context = ast_context_find_or_create(NULL, trunk->autocontext, sla_registrar);
4558 if (!context) {
4559 ast_log(LOG_ERROR, "Failed to automatically find or create "
4560 "context '%s' for SLA!\n", trunk->autocontext);
4561 destroy_trunk(trunk);
4562 return -1;
4564 if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
4565 NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free, sla_registrar)) {
4566 ast_log(LOG_ERROR, "Failed to automatically create extension "
4567 "for trunk '%s'!\n", trunk->name);
4568 destroy_trunk(trunk);
4569 return -1;
4573 AST_RWLIST_WRLOCK(&sla_trunks);
4574 AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
4575 AST_RWLIST_UNLOCK(&sla_trunks);
4577 return 0;
4580 static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
4582 struct sla_trunk *trunk;
4583 struct sla_trunk_ref *trunk_ref;
4584 struct sla_station_ref *station_ref;
4585 char *trunk_name, *options, *cur;
4587 options = ast_strdupa(var->value);
4588 trunk_name = strsep(&options, ",");
4590 AST_RWLIST_RDLOCK(&sla_trunks);
4591 AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
4592 if (!strcasecmp(trunk->name, trunk_name))
4593 break;
4596 AST_RWLIST_UNLOCK(&sla_trunks);
4597 if (!trunk) {
4598 ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
4599 return;
4601 if (!(trunk_ref = create_trunk_ref(trunk)))
4602 return;
4603 trunk_ref->state = SLA_TRUNK_STATE_IDLE;
4605 while ((cur = strsep(&options, ","))) {
4606 char *name, *value = cur;
4607 name = strsep(&value, "=");
4608 if (!strcasecmp(name, "ringtimeout")) {
4609 if (sscanf(value, "%u", &trunk_ref->ring_timeout) != 1) {
4610 ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
4611 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
4612 trunk_ref->ring_timeout = 0;
4614 } else if (!strcasecmp(name, "ringdelay")) {
4615 if (sscanf(value, "%u", &trunk_ref->ring_delay) != 1) {
4616 ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
4617 "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
4618 trunk_ref->ring_delay = 0;
4620 } else {
4621 ast_log(LOG_WARNING, "Invalid option '%s' for "
4622 "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
4626 if (!(station_ref = sla_create_station_ref(station))) {
4627 free(trunk_ref);
4628 return;
4630 ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
4631 AST_RWLIST_WRLOCK(&sla_trunks);
4632 AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
4633 AST_RWLIST_UNLOCK(&sla_trunks);
4634 AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
4637 static int sla_build_station(struct ast_config *cfg, const char *cat)
4639 struct sla_station *station;
4640 struct ast_variable *var;
4641 const char *dev;
4643 if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
4644 ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
4645 return -1;
4648 if (!(station = ast_calloc(1, sizeof(*station))))
4649 return -1;
4650 if (ast_string_field_init(station, 32)) {
4651 free(station);
4652 return -1;
4655 ast_string_field_set(station, name, cat);
4656 ast_string_field_set(station, device, dev);
4658 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
4659 if (!strcasecmp(var->name, "trunk"))
4660 sla_add_trunk_to_station(station, var);
4661 else if (!strcasecmp(var->name, "autocontext"))
4662 ast_string_field_set(station, autocontext, var->value);
4663 else if (!strcasecmp(var->name, "ringtimeout")) {
4664 if (sscanf(var->value, "%u", &station->ring_timeout) != 1) {
4665 ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
4666 var->value, station->name);
4667 station->ring_timeout = 0;
4669 } else if (!strcasecmp(var->name, "ringdelay")) {
4670 if (sscanf(var->value, "%u", &station->ring_delay) != 1) {
4671 ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
4672 var->value, station->name);
4673 station->ring_delay = 0;
4675 } else if (!strcasecmp(var->name, "hold")) {
4676 if (!strcasecmp(var->value, "private"))
4677 station->hold_access = SLA_HOLD_PRIVATE;
4678 else if (!strcasecmp(var->value, "open"))
4679 station->hold_access = SLA_HOLD_OPEN;
4680 else {
4681 ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
4682 var->value, station->name);
4685 } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
4686 ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
4687 var->name, var->lineno, SLA_CONFIG_FILE);
4691 if (!ast_strlen_zero(station->autocontext)) {
4692 struct ast_context *context;
4693 struct sla_trunk_ref *trunk_ref;
4694 context = ast_context_find_or_create(NULL, station->autocontext, sla_registrar);
4695 if (!context) {
4696 ast_log(LOG_ERROR, "Failed to automatically find or create "
4697 "context '%s' for SLA!\n", station->autocontext);
4698 destroy_station(station);
4699 return -1;
4701 /* The extension for when the handset goes off-hook.
4702 * exten => station1,1,SLAStation(station1) */
4703 if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
4704 NULL, NULL, slastation_app, ast_strdup(station->name), ast_free, sla_registrar)) {
4705 ast_log(LOG_ERROR, "Failed to automatically create extension "
4706 "for trunk '%s'!\n", station->name);
4707 destroy_station(station);
4708 return -1;
4710 AST_RWLIST_RDLOCK(&sla_trunks);
4711 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
4712 char exten[AST_MAX_EXTENSION];
4713 char hint[AST_MAX_APP];
4714 snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
4715 snprintf(hint, sizeof(hint), "SLA:%s", exten);
4716 /* Extension for this line button
4717 * exten => station1_line1,1,SLAStation(station1_line1) */
4718 if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
4719 NULL, NULL, slastation_app, ast_strdup(exten), ast_free, sla_registrar)) {
4720 ast_log(LOG_ERROR, "Failed to automatically create extension "
4721 "for trunk '%s'!\n", station->name);
4722 destroy_station(station);
4723 return -1;
4725 /* Hint for this line button
4726 * exten => station1_line1,hint,SLA:station1_line1 */
4727 if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
4728 NULL, NULL, hint, NULL, NULL, sla_registrar)) {
4729 ast_log(LOG_ERROR, "Failed to automatically create hint "
4730 "for trunk '%s'!\n", station->name);
4731 destroy_station(station);
4732 return -1;
4735 AST_RWLIST_UNLOCK(&sla_trunks);
4738 AST_RWLIST_WRLOCK(&sla_stations);
4739 AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
4740 AST_RWLIST_UNLOCK(&sla_stations);
4742 return 0;
4745 static int sla_load_config(void)
4747 struct ast_config *cfg;
4748 const char *cat = NULL;
4749 int res = 0;
4750 const char *val;
4752 ast_mutex_init(&sla.lock);
4753 ast_cond_init(&sla.cond, NULL);
4755 if (!(cfg = ast_config_load(SLA_CONFIG_FILE)))
4756 return 0; /* Treat no config as normal */
4758 if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
4759 sla.attempt_callerid = ast_true(val);
4761 while ((cat = ast_category_browse(cfg, cat)) && !res) {
4762 const char *type;
4763 if (!strcasecmp(cat, "general"))
4764 continue;
4765 if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
4766 ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
4767 SLA_CONFIG_FILE);
4768 continue;
4770 if (!strcasecmp(type, "trunk"))
4771 res = sla_build_trunk(cfg, cat);
4772 else if (!strcasecmp(type, "station"))
4773 res = sla_build_station(cfg, cat);
4774 else {
4775 ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
4776 SLA_CONFIG_FILE, type);
4780 ast_config_destroy(cfg);
4782 ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
4784 return res;
4787 static int load_config(int reload)
4789 int res = 0;
4791 load_config_meetme();
4792 if (!reload)
4793 res = sla_load_config();
4795 return res;
4798 static int unload_module(void)
4800 int res = 0;
4802 ast_cli_unregister_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
4803 res = ast_manager_unregister("MeetmeMute");
4804 res |= ast_manager_unregister("MeetmeUnmute");
4805 res |= ast_unregister_application(app3);
4806 res |= ast_unregister_application(app2);
4807 res |= ast_unregister_application(app);
4808 res |= ast_unregister_application(slastation_app);
4809 res |= ast_unregister_application(slatrunk_app);
4811 ast_devstate_prov_del("Meetme");
4812 ast_devstate_prov_del("SLA");
4814 ast_module_user_hangup_all();
4816 sla_destroy();
4818 return res;
4821 static int load_module(void)
4823 int res = 0;
4825 res |= load_config(0);
4827 ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
4828 res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL,
4829 action_meetmemute, "Mute a Meetme user");
4830 res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL,
4831 action_meetmeunmute, "Unmute a Meetme user");
4832 res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
4833 res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
4834 res |= ast_register_application(app, conf_exec, synopsis, descrip);
4835 res |= ast_register_application(slastation_app, sla_station_exec,
4836 slastation_synopsis, slastation_desc);
4837 res |= ast_register_application(slatrunk_app, sla_trunk_exec,
4838 slatrunk_synopsis, slatrunk_desc);
4840 res |= ast_devstate_prov_add("Meetme", meetmestate);
4841 res |= ast_devstate_prov_add("SLA", sla_state);
4843 return res;
4846 static int reload(void)
4848 return load_config(1);
4851 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MeetMe conference bridge",
4852 .load = load_module,
4853 .unload = unload_module,
4854 .reload = reload,