Wed Oct 28 11:51:23 2009

Asterisk developer's documentation


app_meetme.c File Reference

Meet me conference bridge and Shared Line Appearances. More...

#include "asterisk.h"
#include <dahdi/user.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/config.h"
#include "asterisk/app.h"
#include "asterisk/dsp.h"
#include "asterisk/musiconhold.h"
#include "asterisk/manager.h"
#include "asterisk/cli.h"
#include "asterisk/say.h"
#include "asterisk/utils.h"
#include "asterisk/translate.h"
#include "asterisk/ulaw.h"
#include "asterisk/astobj2.h"
#include "asterisk/devicestate.h"
#include "asterisk/dial.h"
#include "asterisk/causes.h"
#include "asterisk/paths.h"
#include "enter.h"
#include "leave.h"

Include dependency graph for app_meetme.c:

Go to the source code of this file.

Data Structures

struct  announce_listitem
struct  ast_conf_user
 The MeetMe User object. More...
struct  ast_conference
 The MeetMe Conference object. More...
struct  confs
struct  dial_trunk_args
struct  run_station_args
struct  sla_event
struct  sla_failed_station
 A station that failed to be dialed. More...
struct  sla_ringing_station
 A station that is ringing. More...
struct  sla_ringing_trunk
 A trunk that is ringing. More...
struct  sla_station
struct  sla_station_ref
struct  sla_stations
struct  sla_trunk
struct  sla_trunk_ref
struct  sla_trunks
struct  volume

Defines

#define AST_FRAME_BITS   32
#define CONF_SIZE   320
#define CONFIG_FILE_NAME   "meetme.conf"
#define DATE_FORMAT   "%Y-%m-%d %H:%M:%S"
#define DEFAULT_AUDIO_BUFFERS   32
#define MAX_CONFNUM   80
#define MAX_PIN   80
#define MC_DATA_FORMAT   "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"
#define MC_HEADER_FORMAT   "%-14s %-14s %-10s %-8s %-8s %-6s\n"
#define MEETME_DELAYDETECTENDTALK   1000
#define MEETME_DELAYDETECTTALK   300
#define S(e)   case e: return # e;
#define SLA_CONFIG_FILE   "sla.conf"

Enumerations

enum  { ADMINFLAG_MUTED = (1 << 1), ADMINFLAG_SELFMUTED = (1 << 2), ADMINFLAG_KICKME = (1 << 3), ADMINFLAG_T_REQUEST = (1 << 4) }
enum  {
  CONFFLAG_ADMIN = (1 << 0), CONFFLAG_MONITOR = (1 << 1), CONFFLAG_KEYEXIT = (1 << 2), CONFFLAG_STARMENU = (1 << 3),
  CONFFLAG_TALKER = (1 << 4), CONFFLAG_QUIET = (1 << 5), CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6), CONFFLAG_AGI = (1 << 7),
  CONFFLAG_MOH = (1 << 8), CONFFLAG_MARKEDEXIT = (1 << 9), CONFFLAG_WAITMARKED = (1 << 10), CONFFLAG_EXIT_CONTEXT = (1 << 11),
  CONFFLAG_MARKEDUSER = (1 << 12), CONFFLAG_INTROUSER = (1 << 13), CONFFLAG_RECORDCONF = (1<< 14), CONFFLAG_MONITORTALKER = (1 << 15),
  CONFFLAG_DYNAMIC = (1 << 16), CONFFLAG_DYNAMICPIN = (1 << 17), CONFFLAG_EMPTY = (1 << 18), CONFFLAG_EMPTYNOPIN = (1 << 19),
  CONFFLAG_ALWAYSPROMPT = (1 << 20), CONFFLAG_OPTIMIZETALKER = (1 << 21), CONFFLAG_NOONLYPERSON = (1 << 22), CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
  CONFFLAG_STARTMUTED = (1 << 24), CONFFLAG_PASS_DTMF = (1 << 25), CONFFLAG_SLA_STATION = (1 << 26), CONFFLAG_SLA_TRUNK = (1 << 27),
  CONFFLAG_KICK_CONTINUE = (1 << 28), CONFFLAG_DURATION_STOP = (1 << 29), CONFFLAG_DURATION_LIMIT = (1 << 30), CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 31)
}
enum  {
  OPT_ARG_WAITMARKED = 0, OPT_ARG_EXITKEYS = 1, OPT_ARG_DURATION_STOP = 2, OPT_ARG_DURATION_LIMIT = 3,
  OPT_ARG_MOH_CLASS = 4, OPT_ARG_ARRAY_SIZE = 5
}
enum  { SLA_TRUNK_OPT_MOH = (1 << 0) }
enum  { SLA_TRUNK_OPT_ARG_MOH_CLASS = 0, SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1 }
enum  announcetypes { CONF_HASJOIN, CONF_HASLEFT }
enum  entrance_sound { ENTER, LEAVE }
enum  recording_state { MEETME_RECORD_OFF, MEETME_RECORD_STARTED, MEETME_RECORD_ACTIVE, MEETME_RECORD_TERMINATE }
enum  sla_event_type {
  SLA_EVENT_HOLD, SLA_EVENT_DIAL_STATE, SLA_EVENT_RINGING_TRUNK, SLA_EVENT_RELOAD,
  SLA_EVENT_CHECK_RELOAD
}
 Event types that can be queued up for the SLA thread. More...
enum  sla_hold_access { SLA_HOLD_OPEN, SLA_HOLD_PRIVATE }
enum  sla_station_hangup { SLA_STATION_HANGUP_NORMAL, SLA_STATION_HANGUP_TIMEOUT }
enum  sla_trunk_state {
  SLA_TRUNK_STATE_IDLE, SLA_TRUNK_STATE_RINGING, SLA_TRUNK_STATE_UP, SLA_TRUNK_STATE_ONHOLD,
  SLA_TRUNK_STATE_ONHOLD_BYME
}
enum  sla_which_trunk_refs { ALL_TRUNK_REFS, INACTIVE_TRUNK_REFS }
enum  volume_action { VOL_UP, VOL_DOWN }

Functions

static void __fini_sla_stations (void)
static void __fini_sla_trunks (void)
static void __init_sla_stations (void)
static void __init_sla_trunks (void)
static void __reg_module (void)
static void __unreg_module (void)
static int acf_meetme_info (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int acf_meetme_info_eval (char *keyword, struct ast_conference *conf)
static int action_meetmelist (struct mansession *s, const struct message *m)
static int action_meetmemute (struct mansession *s, const struct message *m)
static int action_meetmeunmute (struct mansession *s, const struct message *m)
static int admin_exec (struct ast_channel *chan, void *data)
 The MeetMeadmin application.
static void * announce_thread (void *data)
static void answer_trunk_chan (struct ast_channel *chan)
static struct ast_conferencebuild_conf (char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan)
 Find or create a conference.
static int can_write (struct ast_channel *chan, int confflags)
static int careful_write (int fd, unsigned char *data, int len, int block)
static int channel_admin_exec (struct ast_channel *chan, void *data)
static char * complete_meetmecmd (const char *line, const char *word, int pos, int state)
static int conf_exec (struct ast_channel *chan, void *data)
 The meetme() application.
static void conf_flush (int fd, struct ast_channel *chan)
static int conf_free (struct ast_conference *conf)
static void conf_play (struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
static void conf_queue_dtmf (const struct ast_conference *conf, const struct ast_conf_user *sender, struct ast_frame *f)
static int conf_run (struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
static void conf_start_moh (struct ast_channel *chan, const char *musicclass)
static int count_exec (struct ast_channel *chan, void *data)
 The MeetmeCount application.
static struct sla_trunk_refcreate_trunk_ref (struct sla_trunk *trunk)
static void destroy_station (struct sla_station *station)
static void destroy_trunk (struct sla_trunk *trunk)
static void * dial_trunk (void *data)
static int dispose_conf (struct ast_conference *conf)
static struct ast_conferencefind_conf (struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags)
static struct ast_conferencefind_conf_realtime (struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin, size_t pin_buf_len, int refcount, struct ast_flags *confflags, char *optargs[], int *too_early)
static struct ast_conf_userfind_user (struct ast_conference *conf, char *callerident)
static const char * get_announce_filename (enum announcetypes type)
static char * istalking (int x)
static int load_config (int reload)
static void load_config_meetme (void)
static int load_module (void)
static char * meetme_cmd (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int meetmemute (struct mansession *s, const struct message *m, int mute)
static enum ast_device_state meetmestate (const char *data)
 Callback for devicestate providers.
static struct sla_ringing_trunkqueue_ringing_trunk (struct sla_trunk *trunk)
static void * recordthread (void *args)
static int reload (void)
static void reset_volumes (struct ast_conf_user *user)
static void * run_station (void *data)
static int set_listen_volume (struct ast_conf_user *user, int volume)
static int set_talk_volume (struct ast_conf_user *user, int volume)
static void sla_add_trunk_to_station (struct sla_station *station, struct ast_variable *var)
static int sla_build_station (struct ast_config *cfg, const char *cat)
static int sla_build_trunk (struct ast_config *cfg, const char *cat)
static int sla_calc_station_delays (unsigned int *timeout)
 Calculate the ring delay for a station.
static int sla_calc_station_timeouts (unsigned int *timeout)
 Process station ring timeouts.
static int sla_calc_trunk_timeouts (unsigned int *timeout)
 Process trunk ring timeouts.
static void sla_change_trunk_state (const struct sla_trunk *trunk, enum sla_trunk_state state, enum sla_which_trunk_refs inactive_only, const struct sla_trunk_ref *exclude)
static int sla_check_device (const char *device)
static int sla_check_failed_station (const struct sla_station *station)
 Check to see if this station has failed to be dialed in the past minute.
static int sla_check_inuse_station (const struct sla_station *station)
 Check to see if a station is in use.
static void sla_check_reload (void)
 Check if we can do a reload of SLA, and do it if we can.
static int sla_check_ringing_station (const struct sla_station *station)
 Check to see if this station is already ringing.
static int sla_check_station_delay (struct sla_station *station, struct sla_ringing_trunk *ringing_trunk)
 Calculate the ring delay for a given ringing trunk on a station.
static int sla_check_station_hold_access (const struct sla_trunk *trunk, const struct sla_station *station)
static int sla_check_timed_out_station (const struct sla_ringing_trunk *ringing_trunk, const struct sla_station *station)
 Check to see if dialing this station already timed out for this ringing trunk.
static struct sla_trunk_refsla_choose_idle_trunk (const struct sla_station *station)
 For a given station, choose the highest priority idle trunk.
static struct sla_ringing_trunksla_choose_ringing_trunk (struct sla_station *station, struct sla_trunk_ref **trunk_ref, int rm)
 Choose the highest priority ringing trunk for a station.
static struct sla_ringing_stationsla_create_ringing_station (struct sla_station *station)
static struct sla_station_refsla_create_station_ref (struct sla_station *station)
static void sla_destroy (void)
static void sla_dial_state_callback (struct ast_dial *dial)
static struct sla_stationsla_find_station (const char *name)
 Find an SLA station by name.
static struct sla_trunksla_find_trunk (const char *name)
 Find an SLA trunk by name.
static struct sla_trunk_refsla_find_trunk_ref (const struct sla_station *station, const struct sla_trunk *trunk)
static struct sla_trunk_refsla_find_trunk_ref_byname (const struct sla_station *station, const char *name)
 Find a trunk reference on a station by name.
static void sla_handle_dial_state_event (void)
static void sla_handle_hold_event (struct sla_event *event)
static void sla_handle_ringing_trunk_event (void)
static void sla_hangup_stations (void)
static const char * sla_hold_str (unsigned int hold_access)
static int sla_load_config (int reload)
static int sla_process_timers (struct timespec *ts)
 Calculate the time until the next known event.
static void sla_queue_event (enum sla_event_type type)
static void sla_queue_event_conf (enum sla_event_type type, struct ast_channel *chan, struct ast_conference *conf)
 Queue a SLA event from the conference.
static void sla_queue_event_full (enum sla_event_type type, struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
static void sla_queue_event_nolock (enum sla_event_type type)
static int sla_ring_station (struct sla_ringing_trunk *ringing_trunk, struct sla_station *station)
 Ring a station.
static void sla_ring_stations (void)
 Ring stations based on current set of ringing trunks.
static char * sla_show_stations (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * sla_show_trunks (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static enum ast_device_state sla_state (const char *data)
static enum ast_device_state sla_state_to_devstate (enum sla_trunk_state state)
static int sla_station_exec (struct ast_channel *chan, void *data)
static void sla_stop_ringing_station (struct sla_ringing_station *ringing_station, enum sla_station_hangup hangup)
static void sla_stop_ringing_trunk (struct sla_ringing_trunk *ringing_trunk)
static void * sla_thread (void *data)
static int sla_trunk_exec (struct ast_channel *chan, void *data)
static const char * trunkstate2str (enum sla_trunk_state state)
static void tweak_listen_volume (struct ast_conf_user *user, enum volume_action action)
static void tweak_talk_volume (struct ast_conf_user *user, enum volume_action action)
static void tweak_volume (struct volume *vol, enum volume_action action)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "MeetMe conference bridge" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, }
static const char * app = "MeetMe"
static const char * app2 = "MeetMeCount"
static const char * app3 = "MeetMeAdmin"
static const char * app4 = "MeetMeChannelAdmin"
static struct ast_module_infoast_module_info = &__mod_info
static int audio_buffers
static struct ast_cli_entry cli_meetme []
static unsigned int conf_map [1024] = {0, }
static const char * descrip
static const char * descrip2
static const char * descrip3
static const char * descrip4
static int earlyalert
static int endalert
static int fuzzystart
static char const gain_map []
static char mandescr_meetmelist []
static struct ast_custom_function meetme_info_acf
static struct ast_app_option meetme_opts [128] = { [ 'A' ] = { .flag = CONFFLAG_MARKEDUSER }, [ 'a' ] = { .flag = CONFFLAG_ADMIN }, [ 'b' ] = { .flag = CONFFLAG_AGI }, [ 'c' ] = { .flag = CONFFLAG_ANNOUNCEUSERCOUNT }, [ 'C' ] = { .flag = CONFFLAG_KICK_CONTINUE }, [ 'D' ] = { .flag = CONFFLAG_DYNAMICPIN }, [ 'd' ] = { .flag = CONFFLAG_DYNAMIC }, [ 'E' ] = { .flag = CONFFLAG_EMPTYNOPIN }, [ 'e' ] = { .flag = CONFFLAG_EMPTY }, [ 'F' ] = { .flag = CONFFLAG_PASS_DTMF }, [ 'i' ] = { .flag = CONFFLAG_INTROUSER }, [ 'I' ] = { .flag = CONFFLAG_INTROUSERNOREVIEW }, [ 'M' ] = { .flag = CONFFLAG_MOH , .arg_index = OPT_ARG_MOH_CLASS + 1 }, [ 'm' ] = { .flag = CONFFLAG_STARTMUTED }, [ 'o' ] = { .flag = CONFFLAG_OPTIMIZETALKER }, [ 'P' ] = { .flag = CONFFLAG_ALWAYSPROMPT }, [ 'p' ] = { .flag = CONFFLAG_KEYEXIT , .arg_index = OPT_ARG_EXITKEYS + 1 }, [ 'q' ] = { .flag = CONFFLAG_QUIET }, [ 'r' ] = { .flag = CONFFLAG_RECORDCONF }, [ 's' ] = { .flag = CONFFLAG_STARMENU }, [ 'T' ] = { .flag = CONFFLAG_MONITORTALKER }, [ 'l' ] = { .flag = CONFFLAG_MONITOR }, [ 't' ] = { .flag = CONFFLAG_TALKER }, [ 'w' ] = { .flag = CONFFLAG_WAITMARKED , .arg_index = OPT_ARG_WAITMARKED + 1 }, [ 'X' ] = { .flag = CONFFLAG_EXIT_CONTEXT }, [ 'x' ] = { .flag = CONFFLAG_MARKEDEXIT }, [ '1' ] = { .flag = CONFFLAG_NOONLYPERSON }, [ 'S' ] = { .flag = CONFFLAG_DURATION_STOP , .arg_index = OPT_ARG_DURATION_STOP + 1 }, [ 'L' ] = { .flag = CONFFLAG_DURATION_LIMIT , .arg_index = OPT_ARG_DURATION_LIMIT + 1 }, }
static int rt_log_members
static int rt_schedule
struct {
   unsigned int   attempt_callerid:1
   ast_cond_t   cond
   struct {
      struct sla_event *   first
      struct sla_event *   last
   }   event_q
   struct {
      struct sla_failed_station *   first
      struct sla_failed_station *   last
   }   failed_stations
   ast_mutex_t   lock
   unsigned int   reload:1
   struct {
      struct sla_ringing_station *   first
      struct sla_ringing_station *   last
   }   ringing_stations
   struct {
      struct sla_ringing_trunk *   first
      struct sla_ringing_trunk *   last
   }   ringing_trunks
   unsigned int   stop:1
   pthread_t   thread
sla
 A structure for data used by the sla thread.
static const char sla_registrar [] = "SLA"
static struct ast_app_option sla_trunk_opts [128] = { [ 'M' ] = { .flag = SLA_TRUNK_OPT_MOH , .arg_index = SLA_TRUNK_OPT_ARG_MOH_CLASS + 1 }, }
static const char * slastation_app = "SLAStation"
static const char * slastation_desc
static const char * slastation_synopsis = "Shared Line Appearance Station"
static const char * slatrunk_app = "SLATrunk"
static const char * slatrunk_desc
static const char * slatrunk_synopsis = "Shared Line Appearance Trunk"
static const char * synopsis = "MeetMe conference bridge"
static const char * synopsis2 = "MeetMe participant count"
static const char * synopsis3 = "MeetMe conference Administration"
static const char * synopsis4 = "MeetMe conference Administration (channel specific)"


Detailed Description

Meet me conference bridge and Shared Line Appearances.

Author:
Mark Spencer <markster@digium.com>

(SLA) Russell Bryant <russell@digium.com>

Definition in file app_meetme.c.


Define Documentation

#define AST_FRAME_BITS   32

Definition at line 86 of file app_meetme.c.

Referenced by conf_free(), conf_run(), and recordthread().

#define CONF_SIZE   320

Definition at line 105 of file app_meetme.c.

#define CONFIG_FILE_NAME   "meetme.conf"

Definition at line 66 of file app_meetme.c.

#define DATE_FORMAT   "%Y-%m-%d %H:%M:%S"

String format for scheduled conferences

Definition at line 73 of file app_meetme.c.

Referenced by append_date(), build_radius_record(), execute_cb(), find_conf_realtime(), get_date(), manager_log(), pgsql_log(), and sqlite_log().

#define DEFAULT_AUDIO_BUFFERS   32

each buffer is 20ms, so this is 640ms total

Definition at line 70 of file app_meetme.c.

Referenced by load_config_meetme().

#define MAX_CONFNUM   80

Definition at line 359 of file app_meetme.c.

Referenced by conf_exec(), dial_trunk(), meetme_cmd(), sla_station_exec(), and sla_trunk_exec().

#define MAX_PIN   80

Definition at line 360 of file app_meetme.c.

Referenced by conf_exec().

#define MC_DATA_FORMAT   "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s %-6s\n"

Referenced by meetme_cmd().

#define MC_HEADER_FORMAT   "%-14s %-14s %-10s %-8s %-8s %-6s\n"

Referenced by meetme_cmd().

#define MEETME_DELAYDETECTENDTALK   1000

Definition at line 84 of file app_meetme.c.

Referenced by conf_run().

#define MEETME_DELAYDETECTTALK   300

Definition at line 83 of file app_meetme.c.

Referenced by conf_run().

#define S (  )     case e: return # e;

Referenced by sms_readfile(), and trunkstate2str().

#define SLA_CONFIG_FILE   "sla.conf"

Definition at line 67 of file app_meetme.c.

Referenced by sla_build_station(), sla_build_trunk(), and sla_load_config().


Enumeration Type Documentation

anonymous enum

Enumerator:
ADMINFLAG_MUTED  User is muted
ADMINFLAG_SELFMUTED  User muted self
ADMINFLAG_KICKME  User has been kicked
ADMINFLAG_T_REQUEST  User has requested to speak

Definition at line 75 of file app_meetme.c.

00075      {
00076    ADMINFLAG_MUTED =     (1 << 1), /*!< User is muted */
00077    ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
00078    ADMINFLAG_KICKME =    (1 << 3),  /*!< User has been kicked */
00079    /*! User has requested to speak */
00080    ADMINFLAG_T_REQUEST = (1 << 4),
00081 };

anonymous enum

Enumerator:
CONFFLAG_ADMIN  user has admin access on the conference
CONFFLAG_MONITOR  If set the user can only receive audio from the conference
CONFFLAG_KEYEXIT  If set asterisk will exit conference when key defined in p() option is pressed
CONFFLAG_STARMENU  If set asterisk will provide a menu to the user when '*' is pressed
CONFFLAG_TALKER  If set the use can only send audio to the conference
CONFFLAG_QUIET  If set there will be no enter or leave sounds
CONFFLAG_ANNOUNCEUSERCOUNT  If set, when user joins the conference, they will be told the number of users that are already in
CONFFLAG_AGI  Set to run AGI Script in Background
CONFFLAG_MOH  Set to have music on hold when user is alone in conference
CONFFLAG_MARKEDEXIT  If set the MeetMe will return if all marked with this flag left
CONFFLAG_WAITMARKED  If set, the MeetMe will wait until a marked user enters
CONFFLAG_EXIT_CONTEXT  If set, the MeetMe will exit to the specified context
CONFFLAG_MARKEDUSER  If set, the user will be marked
CONFFLAG_INTROUSER  If set, user will be ask record name on entry of conference
CONFFLAG_RECORDCONF  If set, the MeetMe will be recorded
CONFFLAG_MONITORTALKER  If set, the user will be monitored if the user is talking or not
CONFFLAG_DYNAMIC 
CONFFLAG_DYNAMICPIN 
CONFFLAG_EMPTY 
CONFFLAG_EMPTYNOPIN 
CONFFLAG_ALWAYSPROMPT 
CONFFLAG_OPTIMIZETALKER  If set, treat talking users as muted users
CONFFLAG_NOONLYPERSON  If set, won't speak the extra prompt when the first person enters the conference
CONFFLAG_INTROUSERNOREVIEW  If set, user will be asked to record name on entry of conference without review
CONFFLAG_STARTMUTED  If set, the user will be initially self-muted
CONFFLAG_PASS_DTMF  Pass DTMF through the conference
CONFFLAG_SLA_STATION 
CONFFLAG_SLA_TRUNK 
CONFFLAG_KICK_CONTINUE  If set, the user should continue in the dialplan if kicked out
CONFFLAG_DURATION_STOP 
CONFFLAG_DURATION_LIMIT 
CONFFLAG_NO_AUDIO_UNTIL_UP  Do not write any audio to this channel until the state is up.

Definition at line 107 of file app_meetme.c.

00107      {
00108    /*! user has admin access on the conference */
00109    CONFFLAG_ADMIN = (1 << 0),
00110    /*! If set the user can only receive audio from the conference */
00111    CONFFLAG_MONITOR = (1 << 1),
00112    /*! If set asterisk will exit conference when key defined in p() option is pressed */
00113    CONFFLAG_KEYEXIT = (1 << 2),
00114    /*! If set asterisk will provide a menu to the user when '*' is pressed */
00115    CONFFLAG_STARMENU = (1 << 3),
00116    /*! If set the use can only send audio to the conference */
00117    CONFFLAG_TALKER = (1 << 4),
00118    /*! If set there will be no enter or leave sounds */
00119    CONFFLAG_QUIET = (1 << 5),
00120    /*! If set, when user joins the conference, they will be told the number 
00121     *  of users that are already in */
00122    CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
00123    /*! Set to run AGI Script in Background */
00124    CONFFLAG_AGI = (1 << 7),
00125    /*! Set to have music on hold when user is alone in conference */
00126    CONFFLAG_MOH = (1 << 8),
00127    /*! If set the MeetMe will return if all marked with this flag left */
00128    CONFFLAG_MARKEDEXIT = (1 << 9),
00129    /*! If set, the MeetMe will wait until a marked user enters */
00130    CONFFLAG_WAITMARKED = (1 << 10),
00131    /*! If set, the MeetMe will exit to the specified context */
00132    CONFFLAG_EXIT_CONTEXT = (1 << 11),
00133    /*! If set, the user will be marked */
00134    CONFFLAG_MARKEDUSER = (1 << 12),
00135    /*! If set, user will be ask record name on entry of conference */
00136    CONFFLAG_INTROUSER = (1 << 13),
00137    /*! If set, the MeetMe will be recorded */
00138    CONFFLAG_RECORDCONF = (1<< 14),
00139    /*! If set, the user will be monitored if the user is talking or not */
00140    CONFFLAG_MONITORTALKER = (1 << 15),
00141    CONFFLAG_DYNAMIC = (1 << 16),
00142    CONFFLAG_DYNAMICPIN = (1 << 17),
00143    CONFFLAG_EMPTY = (1 << 18),
00144    CONFFLAG_EMPTYNOPIN = (1 << 19),
00145    CONFFLAG_ALWAYSPROMPT = (1 << 20),
00146    /*! If set, treat talking users as muted users */
00147    CONFFLAG_OPTIMIZETALKER = (1 << 21),
00148    /*! If set, won't speak the extra prompt when the first person 
00149     *  enters the conference */
00150    CONFFLAG_NOONLYPERSON = (1 << 22),
00151    /*! If set, user will be asked to record name on entry of conference 
00152     *  without review */
00153    CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
00154    /*! If set, the user will be initially self-muted */
00155    CONFFLAG_STARTMUTED = (1 << 24),
00156    /*! Pass DTMF through the conference */
00157    CONFFLAG_PASS_DTMF = (1 << 25),
00158    CONFFLAG_SLA_STATION = (1 << 26),
00159    CONFFLAG_SLA_TRUNK = (1 << 27),
00160    /*! If set, the user should continue in the dialplan if kicked out */
00161    CONFFLAG_KICK_CONTINUE = (1 << 28),
00162    CONFFLAG_DURATION_STOP = (1 << 29),
00163    CONFFLAG_DURATION_LIMIT = (1 << 30),
00164    /*! Do not write any audio to this channel until the state is up. */
00165    CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 31),
00166 };

anonymous enum

Enumerator:
OPT_ARG_WAITMARKED 
OPT_ARG_EXITKEYS 
OPT_ARG_DURATION_STOP 
OPT_ARG_DURATION_LIMIT 
OPT_ARG_MOH_CLASS 
OPT_ARG_ARRAY_SIZE 

Definition at line 168 of file app_meetme.c.

00168      {
00169    OPT_ARG_WAITMARKED = 0,
00170    OPT_ARG_EXITKEYS   = 1,
00171    OPT_ARG_DURATION_STOP = 2,
00172    OPT_ARG_DURATION_LIMIT = 3,
00173    OPT_ARG_MOH_CLASS = 4,
00174    OPT_ARG_ARRAY_SIZE = 5,
00175 };

anonymous enum

Enumerator:
SLA_TRUNK_OPT_MOH 

Definition at line 5228 of file app_meetme.c.

05228      {
05229    SLA_TRUNK_OPT_MOH = (1 << 0),
05230 };

anonymous enum

Enumerator:
SLA_TRUNK_OPT_ARG_MOH_CLASS 
SLA_TRUNK_OPT_ARG_ARRAY_SIZE 

Definition at line 5232 of file app_meetme.c.

05232      {
05233    SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
05234    SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
05235 };

Enumerator:
CONF_HASJOIN 
CONF_HASLEFT 

Definition at line 362 of file app_meetme.c.

00362                    {
00363    CONF_HASJOIN,
00364    CONF_HASLEFT
00365 };

Enumerator:
ENTER 
LEAVE 

Definition at line 93 of file app_meetme.c.

00093                     {
00094    ENTER,
00095    LEAVE
00096 };

Enumerator:
MEETME_RECORD_OFF 
MEETME_RECORD_STARTED 
MEETME_RECORD_ACTIVE 
MEETME_RECORD_TERMINATE 

Definition at line 98 of file app_meetme.c.

Event types that can be queued up for the SLA thread.

Enumerator:
SLA_EVENT_HOLD  A station has put the call on hold
SLA_EVENT_DIAL_STATE  The state of a dial has changed
SLA_EVENT_RINGING_TRUNK  The state of a ringing trunk has changed
SLA_EVENT_RELOAD  A reload of configuration has been requested
SLA_EVENT_CHECK_RELOAD  Poke the SLA thread so it can check if it can perform a reload

Definition at line 552 of file app_meetme.c.

00552                     {
00553    /*! A station has put the call on hold */
00554    SLA_EVENT_HOLD,
00555    /*! The state of a dial has changed */
00556    SLA_EVENT_DIAL_STATE,
00557    /*! The state of a ringing trunk has changed */
00558    SLA_EVENT_RINGING_TRUNK,
00559    /*! A reload of configuration has been requested */
00560    SLA_EVENT_RELOAD,
00561    /*! Poke the SLA thread so it can check if it can perform a reload */
00562    SLA_EVENT_CHECK_RELOAD,
00563 };

Enumerator:
SLA_HOLD_OPEN  This means that any station can put it on hold, and any station can retrieve the call from hold.
SLA_HOLD_PRIVATE  This means that only the station that put the call on hold may retrieve it from hold.

Definition at line 462 of file app_meetme.c.

00462                      {
00463    /*! This means that any station can put it on hold, and any station
00464     * can retrieve the call from hold. */
00465    SLA_HOLD_OPEN,
00466    /*! This means that only the station that put the call on hold may
00467     * retrieve it from hold. */
00468    SLA_HOLD_PRIVATE,
00469 };

Enumerator:
SLA_STATION_HANGUP_NORMAL 
SLA_STATION_HANGUP_TIMEOUT 

Definition at line 589 of file app_meetme.c.

Enumerator:
SLA_TRUNK_STATE_IDLE 
SLA_TRUNK_STATE_RINGING 
SLA_TRUNK_STATE_UP 
SLA_TRUNK_STATE_ONHOLD 
SLA_TRUNK_STATE_ONHOLD_BYME 

Definition at line 454 of file app_meetme.c.

Enumerator:
ALL_TRUNK_REFS 
INACTIVE_TRUNK_REFS 

Definition at line 449 of file app_meetme.c.

00449                           {
00450    ALL_TRUNK_REFS,
00451    INACTIVE_TRUNK_REFS,
00452 };

Enumerator:
VOL_UP 
VOL_DOWN 

Definition at line 88 of file app_meetme.c.

00088                    {
00089    VOL_UP,
00090    VOL_DOWN
00091 };


Function Documentation

static void __fini_sla_stations ( void   )  [static]

Definition at line 546 of file app_meetme.c.

00552 {

static void __fini_sla_trunks ( void   )  [static]

Definition at line 547 of file app_meetme.c.

00552 {

static void __init_sla_stations ( void   )  [static]

Definition at line 546 of file app_meetme.c.

00552 {

static void __init_sla_trunks ( void   )  [static]

Definition at line 547 of file app_meetme.c.

00552 {

static void __reg_module ( void   )  [static]

Definition at line 5929 of file app_meetme.c.

static void __unreg_module ( void   )  [static]

Definition at line 5929 of file app_meetme.c.

static int acf_meetme_info ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 5784 of file app_meetme.c.

References acf_meetme_info_eval(), AST_APP_ARG, AST_DECLARE_APP_ARGS, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_conference::confno, ast_conference::list, LOG_ERROR, and parse().

05785 {
05786    struct ast_conference *conf;
05787    char *parse;
05788    int result = -2; /* only non-negative numbers valid, -1 is used elsewhere */
05789    AST_DECLARE_APP_ARGS(args,
05790       AST_APP_ARG(keyword);
05791       AST_APP_ARG(confno);
05792    );
05793 
05794    if (ast_strlen_zero(data)) {
05795       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
05796       return -1;
05797    }
05798 
05799    parse = ast_strdupa(data);
05800    AST_STANDARD_APP_ARGS(args, parse);
05801 
05802    if (ast_strlen_zero(args.keyword)) {
05803       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
05804       return -1;
05805    }
05806 
05807    if (ast_strlen_zero(args.confno)) {
05808       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
05809       return -1;
05810    }
05811 
05812    AST_LIST_LOCK(&confs);
05813    AST_LIST_TRAVERSE(&confs, conf, list) {
05814       if (!strcmp(args.confno, conf->confno)) {
05815          result = acf_meetme_info_eval(args.keyword, conf);
05816          break;
05817       }
05818    }
05819    AST_LIST_UNLOCK(&confs);
05820 
05821    if (result > -1) {
05822       snprintf(buf, len, "%d", result);
05823    } else if (result == -1) {
05824       snprintf(buf, len, "%s %s", "Error: invalid keyword:", args.keyword);
05825    } else if (result == -2) {
05826       snprintf(buf, len, "Error: conference (%s) not found", args.confno);
05827    }
05828 
05829    return 0;
05830 }

static int acf_meetme_info_eval ( char *  keyword,
struct ast_conference conf 
) [static]

Definition at line 5766 of file app_meetme.c.

References ast_conference::isdynamic, ast_conference::locked, ast_conference::start, and ast_conference::users.

Referenced by acf_meetme_info().

05767 {
05768    if (!strcasecmp("lock", keyword)) {
05769       return conf->locked;
05770    } else if (!strcasecmp("parties", keyword)) {
05771       return conf->users;
05772    } else if (!strcasecmp("activity", keyword)) {
05773       time_t now;
05774       now = time(NULL);
05775       return (now - conf->start);
05776    } else if (!strcasecmp("dynamic", keyword)) {
05777       return conf->isdynamic;
05778    } else {
05779       return -1;
05780    }
05781 
05782 }

static int action_meetmelist ( struct mansession s,
const struct message m 
) [static]

Definition at line 3736 of file app_meetme.c.

References ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ast_conf_user::adminflags, AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_error(), astman_send_listack(), ast_conf_user::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, CONFFLAG_ADMIN, CONFFLAG_MARKEDUSER, CONFFLAG_MONITOR, CONFFLAG_TALKER, ast_conference::confno, ast_conf_user::list, ast_channel::name, S_OR, ast_conf_user::talking, total, ast_conf_user::user_no, ast_conf_user::userflags, and ast_conference::userlist.

Referenced by load_module().

03737 {
03738    const char *actionid = astman_get_header(m, "ActionID");
03739    const char *conference = astman_get_header(m, "Conference");
03740    char idText[80] = "";
03741    struct ast_conference *cnf;
03742    struct ast_conf_user *user;
03743    int total = 0;
03744 
03745    if (!ast_strlen_zero(actionid))
03746       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
03747 
03748    if (AST_LIST_EMPTY(&confs)) {
03749       astman_send_error(s, m, "No active conferences.");
03750       return 0;
03751    }
03752 
03753    astman_send_listack(s, m, "Meetme user list will follow", "start");
03754 
03755    /* Find the right conference */
03756    AST_LIST_LOCK(&confs);
03757    AST_LIST_TRAVERSE(&confs, cnf, list) {
03758       /* If we ask for one particular, and this isn't it, skip it */
03759       if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
03760          continue;
03761 
03762       /* Show all the users */
03763       AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
03764          total++;
03765          astman_append(s,
03766          "Event: MeetmeList\r\n"
03767          "%s"
03768          "Conference: %s\r\n"
03769          "UserNumber: %d\r\n"
03770          "CallerIDNum: %s\r\n"
03771          "CallerIDName: %s\r\n"
03772          "Channel: %s\r\n"
03773          "Admin: %s\r\n"
03774          "Role: %s\r\n"
03775          "MarkedUser: %s\r\n"
03776          "Muted: %s\r\n"
03777          "Talking: %s\r\n"
03778          "\r\n",
03779          idText,
03780          cnf->confno,
03781          user->user_no,
03782          S_OR(user->chan->cid.cid_num, "<unknown>"),
03783          S_OR(user->chan->cid.cid_name, "<no name>"),
03784          user->chan->name,
03785          user->userflags & CONFFLAG_ADMIN ? "Yes" : "No",
03786          user->userflags & CONFFLAG_MONITOR ? "Listen only" : user->userflags & CONFFLAG_TALKER ? "Talk only" : "Talk and listen",
03787          user->userflags & CONFFLAG_MARKEDUSER ? "Yes" : "No",
03788          user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
03789          user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored"); 
03790       }
03791    }
03792    AST_LIST_UNLOCK(&confs);
03793    /* Send final confirmation */
03794    astman_append(s,
03795    "Event: MeetmeListComplete\r\n"
03796    "EventList: Complete\r\n"
03797    "ListItems: %d\r\n"
03798    "%s"
03799    "\r\n", total, idText);
03800    return 0;
03801 }

static int action_meetmemute ( struct mansession s,
const struct message m 
) [static]

Definition at line 3718 of file app_meetme.c.

References meetmemute().

Referenced by load_module().

03719 {
03720    return meetmemute(s, m, 1);
03721 }

static int action_meetmeunmute ( struct mansession s,
const struct message m 
) [static]

Definition at line 3723 of file app_meetme.c.

References meetmemute().

Referenced by load_module().

03724 {
03725    return meetmemute(s, m, 0);
03726 }

static int admin_exec ( struct ast_channel chan,
void *  data 
) [static]

The MeetMeadmin application.

Definition at line 3448 of file app_meetme.c.

References ADMINFLAG_KICKME, ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ADMINFLAG_T_REQUEST, ast_conf_user::adminflags, AST_APP_ARG, ast_atomic_fetchadd_int(), AST_DECLARE_APP_ARGS, AST_LIST_LAST, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), CONFFLAG_ADMIN, ast_conference::confno, dispose_conf(), find_user(), ast_conf_user::list, ast_conference::locked, LOG_NOTICE, LOG_WARNING, ast_conference::refcount, reset_volumes(), tweak_listen_volume(), tweak_talk_volume(), ast_conf_user::userflags, ast_conference::userlist, VOL_DOWN, and VOL_UP.

Referenced by load_module(), meetme_cmd(), run_station(), sla_station_exec(), and sla_stop_ringing_trunk().

03448                                                             {
03449    char *params;
03450    struct ast_conference *cnf;
03451    struct ast_conf_user *user = NULL;
03452    AST_DECLARE_APP_ARGS(args,
03453       AST_APP_ARG(confno);
03454       AST_APP_ARG(command);
03455       AST_APP_ARG(user);
03456    );
03457 
03458    if (ast_strlen_zero(data)) {
03459       ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
03460       return -1;
03461    }
03462 
03463    params = ast_strdupa(data);
03464    AST_STANDARD_APP_ARGS(args, params);
03465 
03466    if (!args.command) {
03467       ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
03468       return -1;
03469    }
03470 
03471    AST_LIST_LOCK(&confs);
03472    AST_LIST_TRAVERSE(&confs, cnf, list) {
03473       if (!strcmp(cnf->confno, args.confno))
03474          break;
03475    }
03476 
03477    if (!cnf) {
03478       ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
03479       AST_LIST_UNLOCK(&confs);
03480       return 0;
03481    }
03482 
03483    ast_atomic_fetchadd_int(&cnf->refcount, 1);
03484 
03485    if (args.user)
03486       user = find_user(cnf, args.user);
03487 
03488    switch (*args.command) {
03489    case 76: /* L: Lock */ 
03490       cnf->locked = 1;
03491       break;
03492    case 108: /* l: Unlock */ 
03493       cnf->locked = 0;
03494       break;
03495    case 75: /* K: kick all users */
03496       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03497          user->adminflags |= ADMINFLAG_KICKME;
03498       break;
03499    case 101: /* e: Eject last user*/
03500       user = AST_LIST_LAST(&cnf->userlist);
03501       if (!(user->userflags & CONFFLAG_ADMIN))
03502          user->adminflags |= ADMINFLAG_KICKME;
03503       else
03504          ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
03505       break;
03506    case 77: /* M: Mute */ 
03507       if (user) {
03508          user->adminflags |= ADMINFLAG_MUTED;
03509       } else
03510          ast_log(LOG_NOTICE, "Specified User not found!\n");
03511       break;
03512    case 78: /* N: Mute all (non-admin) users */
03513       AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
03514          if (!(user->userflags & CONFFLAG_ADMIN))
03515             user->adminflags |= ADMINFLAG_MUTED;
03516       }
03517       break;               
03518    case 109: /* m: Unmute */ 
03519       if (user) {
03520          user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
03521       } else
03522          ast_log(LOG_NOTICE, "Specified User not found!\n");
03523       break;
03524    case 110: /* n: Unmute all users */
03525       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03526          user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
03527       break;
03528    case 107: /* k: Kick user */ 
03529       if (user)
03530          user->adminflags |= ADMINFLAG_KICKME;
03531       else
03532          ast_log(LOG_NOTICE, "Specified User not found!\n");
03533       break;
03534    case 118: /* v: Lower all users listen volume */
03535       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03536          tweak_listen_volume(user, VOL_DOWN);
03537       break;
03538    case 86: /* V: Raise all users listen volume */
03539       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03540          tweak_listen_volume(user, VOL_UP);
03541       break;
03542    case 115: /* s: Lower all users speaking volume */
03543       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03544          tweak_talk_volume(user, VOL_DOWN);
03545       break;
03546    case 83: /* S: Raise all users speaking volume */
03547       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03548          tweak_talk_volume(user, VOL_UP);
03549       break;
03550    case 82: /* R: Reset all volume levels */
03551       AST_LIST_TRAVERSE(&cnf->userlist, user, list)
03552          reset_volumes(user);
03553       break;
03554    case 114: /* r: Reset user's volume level */
03555       if (user)
03556          reset_volumes(user);
03557       else
03558          ast_log(LOG_NOTICE, "Specified User not found!\n");
03559       break;
03560    case 85: /* U: Raise user's listen volume */
03561       if (user)
03562          tweak_listen_volume(user, VOL_UP);
03563       else
03564          ast_log(LOG_NOTICE, "Specified User not found!\n");
03565       break;
03566    case 117: /* u: Lower user's listen volume */
03567       if (user)
03568          tweak_listen_volume(user, VOL_DOWN);
03569       else
03570          ast_log(LOG_NOTICE, "Specified User not found!\n");
03571       break;
03572    case 84: /* T: Raise user's talk volume */
03573       if (user)
03574          tweak_talk_volume(user, VOL_UP);
03575       else
03576          ast_log(LOG_NOTICE, "Specified User not found!\n");
03577       break;
03578    case 116: /* t: Lower user's talk volume */
03579       if (user) 
03580          tweak_talk_volume(user, VOL_DOWN);
03581       else 
03582          ast_log(LOG_NOTICE, "Specified User not found!\n");
03583       break;
03584    }
03585 
03586    AST_LIST_UNLOCK(&confs);
03587 
03588    dispose_conf(cnf);
03589    
03590    return 0;
03591 }

static void* announce_thread ( void *  data  )  [static]

Definition at line 1570 of file app_meetme.c.

References ast_conference::announcelist, ast_conference::announcelist_addition, ast_conference::announcelistlock, ast_conference::announcethread_stop, announce_listitem::announcetype, ao2_ref, ast_check_hangup(), ast_cond_wait(), ast_copy_string(), ast_filedelete(), ast_fileexists(), AST_LIST_APPEND_LIST, AST_LIST_EMPTY, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_HEAD_NOLOCK, AST_LIST_REMOVE_HEAD, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_streamfile(), ast_waitstream(), CONF_HASLEFT, announce_listitem::confchan, announce_listitem::confusers, get_announce_filename(), announce_listitem::language, LOG_DEBUG, and announce_listitem::namerecloc.

Referenced by conf_run().

01571 {
01572    struct announce_listitem *current;
01573    struct ast_conference *conf = data;
01574    int res = 0;
01575    char filename[PATH_MAX] = "";
01576    AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
01577    AST_LIST_HEAD_INIT_NOLOCK(&local_list);
01578 
01579    while (!conf->announcethread_stop) {
01580       ast_mutex_lock(&conf->announcelistlock);
01581       if (conf->announcethread_stop) {
01582          ast_mutex_unlock(&conf->announcelistlock);
01583          break;
01584       }
01585       if (AST_LIST_EMPTY(&conf->announcelist))
01586          ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
01587 
01588       AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
01589       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
01590 
01591       ast_mutex_unlock(&conf->announcelistlock);
01592       if (conf->announcethread_stop) {
01593          break;
01594       }
01595 
01596       for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
01597          ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc);
01598          if (!ast_fileexists(current->namerecloc, NULL, NULL))
01599             continue;
01600          if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
01601             if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
01602                res = ast_waitstream(current->confchan, "");
01603             if (!res) {
01604                ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
01605                if (!ast_streamfile(current->confchan, filename, current->language))
01606                   ast_waitstream(current->confchan, "");
01607             }
01608          }
01609          if (current->announcetype == CONF_HASLEFT) {
01610             ast_filedelete(current->namerecloc, NULL);
01611          }
01612       }
01613    }
01614 
01615    /* thread marked to stop, clean up */
01616    while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
01617       ast_filedelete(current->namerecloc, NULL);
01618       ao2_ref(current, -1);
01619    }
01620    return NULL;
01621 }

static void answer_trunk_chan ( struct ast_channel chan  )  [static]

Definition at line 4100 of file app_meetme.c.

References ast_answer(), and ast_indicate().

Referenced by run_station(), sla_handle_dial_state_event(), and sla_station_exec().

04101 {
04102    ast_answer(chan);
04103    ast_indicate(chan, -1);
04104 }

static struct ast_conference* build_conf ( char *  confno,
char *  pin,
char *  pinadmin,
int  make,
int  dynamic,
int  refcount,
const struct ast_channel chan 
) [static, read]

Find or create a conference.

Parameters:
confno The conference name/number
pin The regular user pin
pinadmin The admin pin
make Make the conf if it doesn't exist
dynamic Mark the newly created conference as dynamic
refcount How many references to mark on the conference
chan The asterisk channel
Returns:
A pointer to the conference struct, or NULL if it wasn't found and make or dynamic were not set.

Definition at line 828 of file app_meetme.c.

References ast_conference::announcethread, ast_conference::announcethreadlock, ast_atomic_fetchadd_int(), ast_calloc, ast_copy_string(), AST_FORMAT_SLINEAR, ast_free, ast_hangup(), AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_init(), AST_PTHREADT_NULL, ast_request(), ast_set_read_format(), ast_set_write_format(), ast_verb, ast_conference::chan, conf_map, ast_conference::confno, ast_conference::dahdiconf, ast_conference::fd, ast_channel::fds, ast_conference::isdynamic, ast_conference::listenlock, LOG_WARNING, ast_conference::maxusers, ast_conference::pin, ast_conference::pinadmin, ast_conference::playlock, ast_conference::recordthread, ast_conference::recordthreadlock, ast_conference::refcount, ast_conference::start, ast_channel::uniqueid, and ast_conference::uniqueid.

Referenced by dial_trunk(), find_conf(), find_conf_realtime(), run_station(), sla_station_exec(), and sla_trunk_exec().

00829 {
00830    struct ast_conference *cnf;
00831    struct dahdi_confinfo dahdic = { 0, };
00832    int confno_int = 0;
00833 
00834    AST_LIST_LOCK(&confs);
00835 
00836    AST_LIST_TRAVERSE(&confs, cnf, list) {
00837       if (!strcmp(confno, cnf->confno)) 
00838          break;
00839    }
00840 
00841    if (cnf || (!make && !dynamic))
00842       goto cnfout;
00843 
00844    /* Make a new one */
00845    if (!(cnf = ast_calloc(1, sizeof(*cnf))))
00846       goto cnfout;
00847 
00848    ast_mutex_init(&cnf->playlock);
00849    ast_mutex_init(&cnf->listenlock);
00850    cnf->recordthread = AST_PTHREADT_NULL;
00851    ast_mutex_init(&cnf->recordthreadlock);
00852    cnf->announcethread = AST_PTHREADT_NULL;
00853    ast_mutex_init(&cnf->announcethreadlock);
00854    ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
00855    ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
00856    ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
00857    ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
00858 
00859    /* Setup a new dahdi conference */
00860    dahdic.confno = -1;
00861    dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
00862    cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
00863    if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
00864       ast_log(LOG_WARNING, "Unable to open pseudo device\n");
00865       if (cnf->fd >= 0)
00866          close(cnf->fd);
00867       ast_free(cnf);
00868       cnf = NULL;
00869       goto cnfout;
00870    }
00871 
00872    cnf->dahdiconf = dahdic.confno;
00873 
00874    /* Setup a new channel for playback of audio files */
00875    cnf->chan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL);
00876    if (cnf->chan) {
00877       ast_set_read_format(cnf->chan, AST_FORMAT_SLINEAR);
00878       ast_set_write_format(cnf->chan, AST_FORMAT_SLINEAR);
00879       dahdic.chan = 0;
00880       dahdic.confno = cnf->dahdiconf;
00881       dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
00882       if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
00883          ast_log(LOG_WARNING, "Error setting conference\n");
00884          if (cnf->chan)
00885             ast_hangup(cnf->chan);
00886          else
00887             close(cnf->fd);
00888 
00889          ast_free(cnf);
00890          cnf = NULL;
00891          goto cnfout;
00892       }
00893    }
00894 
00895    /* Fill the conference struct */
00896    cnf->start = time(NULL);
00897    cnf->maxusers = 0x7fffffff;
00898    cnf->isdynamic = dynamic ? 1 : 0;
00899    ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
00900    AST_LIST_INSERT_HEAD(&confs, cnf, list);
00901 
00902    /* Reserve conference number in map */
00903    if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
00904       conf_map[confno_int] = 1;
00905    
00906 cnfout:
00907    if (cnf)
00908       ast_atomic_fetchadd_int(&cnf->refcount, refcount);
00909 
00910    AST_LIST_UNLOCK(&confs);
00911 
00912    return cnf;
00913 }

static int can_write ( struct ast_channel chan,
int  confflags 
) [static]

Definition at line 1623 of file app_meetme.c.

References ast_channel::_state, AST_STATE_UP, and CONFFLAG_NO_AUDIO_UNTIL_UP.

Referenced by conf_run().

01624 {
01625    if (!(confflags & CONFFLAG_NO_AUDIO_UNTIL_UP)) {
01626       return 1;
01627    }
01628 
01629    return (chan->_state == AST_STATE_UP);
01630 }

static int careful_write ( int  fd,
unsigned char *  data,
int  len,
int  block 
) [static]

Definition at line 662 of file app_meetme.c.

References ast_log(), errno, and LOG_WARNING.

00663 {
00664    int res;
00665    int x;
00666 
00667    while (len) {
00668       if (block) {
00669          x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
00670          res = ioctl(fd, DAHDI_IOMUX, &x);
00671       } else
00672          res = 0;
00673       if (res >= 0)
00674          res = write(fd, data, len);
00675       if (res < 1) {
00676          if (errno != EAGAIN) {
00677             ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
00678             return -1;
00679          } else
00680             return 0;
00681       }
00682       len -= res;
00683       data += res;
00684    }
00685 
00686    return 0;
00687 }

static int channel_admin_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 3595 of file app_meetme.c.

References ADMINFLAG_KICKME, ADMINFLAG_MUTED, ast_conf_user::adminflags, AST_APP_ARG, AST_DECLARE_APP_ARGS, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_conf_user::chan, ast_conf_user::list, LOG_NOTICE, LOG_WARNING, ast_channel::name, and ast_conference::userlist.

Referenced by load_module().

03595                                                                     {
03596    char *params;
03597    struct ast_conference *conf = NULL;
03598    struct ast_conf_user *user = NULL;
03599    AST_DECLARE_APP_ARGS(args,
03600       AST_APP_ARG(channel);
03601       AST_APP_ARG(command);
03602    );
03603 
03604    if (ast_strlen_zero(data)) {
03605       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
03606       return -1;
03607    }
03608    
03609    params = ast_strdupa(data);
03610    AST_STANDARD_APP_ARGS(args, params);
03611 
03612    if (!args.channel) {
03613       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
03614       return -1;
03615    }
03616 
03617    if (!args.command) {
03618       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
03619       return -1;
03620    }
03621 
03622    AST_LIST_LOCK(&confs);
03623    AST_LIST_TRAVERSE(&confs, conf, list) {
03624       AST_LIST_TRAVERSE(&conf->userlist, user, list) {
03625          if (!strcmp(user->chan->name, args.channel))
03626             break;
03627       }
03628    }
03629    
03630    if (!user) {
03631       ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
03632       AST_LIST_UNLOCK(&confs);
03633       return 0;
03634    }
03635    
03636    /* perform the specified action */
03637    switch (*args.command) {
03638       case 77: /* M: Mute */ 
03639          user->adminflags |= ADMINFLAG_MUTED;
03640          break;
03641       case 109: /* m: Unmute */ 
03642          user->adminflags &= ~ADMINFLAG_MUTED;
03643          break;
03644       case 107: /* k: Kick user */ 
03645          user->adminflags |= ADMINFLAG_KICKME;
03646          break;
03647       default: /* unknown command */
03648          ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
03649          break;
03650    }
03651 
03652    AST_LIST_UNLOCK(&confs);
03653    
03654    return 0;
03655 }

static char* complete_meetmecmd ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 916 of file app_meetme.c.

References ast_cli_complete(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, ast_strdupa, ast_conference::confno, len(), ast_conf_user::list, strsep(), ast_conf_user::user_no, and ast_conference::userlist.

Referenced by meetme_cmd().

00917 {
00918    static char *cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
00919 
00920    int len = strlen(word);
00921    int which = 0;
00922    struct ast_conference *cnf = NULL;
00923    struct ast_conf_user *usr = NULL;
00924    char *confno = NULL;
00925    char usrno[50] = "";
00926    char *myline, *ret = NULL;
00927    
00928    if (pos == 1) {      /* Command */
00929       return ast_cli_complete(word, cmds, state);
00930    } else if (pos == 2) {  /* Conference Number */
00931       AST_LIST_LOCK(&confs);
00932       AST_LIST_TRAVERSE(&confs, cnf, list) {
00933          if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
00934             ret = cnf->confno;
00935             break;
00936          }
00937       }
00938       ret = ast_strdup(ret); /* dup before releasing the lock */
00939       AST_LIST_UNLOCK(&confs);
00940       return ret;
00941    } else if (pos == 3) {
00942       /* User Number || Conf Command option*/
00943       if (strstr(line, "mute") || strstr(line, "kick")) {
00944          if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
00945             return ast_strdup("all");
00946          which++;
00947          AST_LIST_LOCK(&confs);
00948 
00949          /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
00950          myline = ast_strdupa(line);
00951          if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
00952             while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
00953                ;
00954          }
00955          
00956          AST_LIST_TRAVERSE(&confs, cnf, list) {
00957             if (!strcmp(confno, cnf->confno))
00958                 break;
00959          }
00960 
00961          if (cnf) {
00962             /* Search for the user */
00963             AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
00964                snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
00965                if (!strncasecmp(word, usrno, len) && ++which > state)
00966                   break;
00967             }
00968          }
00969          AST_LIST_UNLOCK(&confs);
00970          return usr ? ast_strdup(usrno) : NULL;
00971       } else if (strstr(line, "list") && (state == 0))
00972          return ast_strdup("concise");
00973    }
00974 
00975    return NULL;
00976 }

static int conf_exec ( struct ast_channel chan,
void *  data 
) [static]

The meetme() application.

Definition at line 3184 of file app_meetme.c.

References ast_channel::_state, ARRAY_LEN, ast_answer(), AST_APP_ARG, ast_app_getdata(), ast_app_parse_options(), ast_config_destroy(), ast_config_load, ast_copy_string(), AST_DECLARE_APP_ARGS, AST_DIGIT_ANY, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_say_digits(), ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stopstream(), ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_variable_browse(), ast_waitstream(), conf_map, conf_run(), CONFFLAG_ADMIN, CONFFLAG_ALWAYSPROMPT, CONFFLAG_DYNAMIC, CONFFLAG_DYNAMICPIN, CONFFLAG_EMPTY, CONFFLAG_EMPTYNOPIN, CONFFLAG_QUIET, CONFIG_FILE_NAME, config_flags, ast_conference::confno, dispose_conf(), find_conf(), find_conf_realtime(), ast_flags::flags, ast_channel::language, LOG_ERROR, LOG_WARNING, MAX_CONFNUM, MAX_PIN, meetme_opts, ast_variable::name, ast_variable::next, OPT_ARG_ARRAY_SIZE, ast_conference::pin, ast_conference::pinadmin, strsep(), ast_variable::value, and var.

03185 {
03186    int res = -1;
03187    char confno[MAX_CONFNUM] = "";
03188    int allowretry = 0;
03189    int retrycnt = 0;
03190    struct ast_conference *cnf = NULL;
03191    struct ast_flags confflags = {0}, config_flags = { 0 };
03192    int dynamic = 0;
03193    int empty = 0, empty_no_pin = 0;
03194    int always_prompt = 0;
03195    char *notdata, *info, the_pin[MAX_PIN] = "";
03196    AST_DECLARE_APP_ARGS(args,
03197       AST_APP_ARG(confno);
03198       AST_APP_ARG(options);
03199       AST_APP_ARG(pin);
03200    );
03201    char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
03202 
03203    if (ast_strlen_zero(data)) {
03204       allowretry = 1;
03205       notdata = "";
03206    } else {
03207       notdata = data;
03208    }
03209    
03210    if (chan->_state != AST_STATE_UP)
03211       ast_answer(chan);
03212 
03213    info = ast_strdupa(notdata);
03214 
03215    AST_STANDARD_APP_ARGS(args, info);  
03216 
03217    if (args.confno) {
03218       ast_copy_string(confno, args.confno, sizeof(confno));
03219       if (ast_strlen_zero(confno)) {
03220          allowretry = 1;
03221       }
03222    }
03223    
03224    if (args.pin)
03225       ast_copy_string(the_pin, args.pin, sizeof(the_pin));
03226 
03227    if (args.options) {
03228       ast_app_parse_options(meetme_opts, &confflags, optargs, args.options);
03229       dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
03230       if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
03231          strcpy(the_pin, "q");
03232 
03233       empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
03234       empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
03235       always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
03236    }
03237 
03238    do {
03239       if (retrycnt > 3)
03240          allowretry = 0;
03241       if (empty) {
03242          int i;
03243          struct ast_config *cfg;
03244          struct ast_variable *var;
03245          int confno_int;
03246 
03247          /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
03248          if ((empty_no_pin) || (!dynamic)) {
03249             cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
03250             if (cfg) {
03251                var = ast_variable_browse(cfg, "rooms");
03252                while (var) {
03253                   if (!strcasecmp(var->name, "conf")) {
03254                      char *stringp = ast_strdupa(var->value);
03255                      if (stringp) {
03256                         char *confno_tmp = strsep(&stringp, "|,");
03257                         int found = 0;
03258                         if (!dynamic) {
03259                            /* For static:  run through the list and see if this conference is empty */
03260                            AST_LIST_LOCK(&confs);
03261                            AST_LIST_TRAVERSE(&confs, cnf, list) {
03262                               if (!strcmp(confno_tmp, cnf->confno)) {
03263                                  /* The conference exists, therefore it's not empty */
03264                                  found = 1;
03265                                  break;
03266                               }
03267                            }
03268                            AST_LIST_UNLOCK(&confs);
03269                            if (!found) {
03270                               /* At this point, we have a confno_tmp (static conference) that is empty */
03271                               if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
03272                                  /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
03273                                   * Case 2:  empty_no_pin and pin is blank (but not NULL)
03274                                   * Case 3:  not empty_no_pin
03275                                   */
03276                                  ast_copy_string(confno, confno_tmp, sizeof(confno));
03277                                  break;
03278                                  /* XXX the map is not complete (but we do have a confno) */
03279                               }
03280                            }
03281                         }
03282                      }
03283                   }
03284                   var = var->next;
03285                }
03286                ast_config_destroy(cfg);
03287             }
03288          }
03289 
03290          /* Select first conference number not in use */
03291          if (ast_strlen_zero(confno) && dynamic) {
03292             AST_LIST_LOCK(&confs);
03293             for (i = 0; i < ARRAY_LEN(conf_map); i++) {
03294                if (!conf_map[i]) {
03295                   snprintf(confno, sizeof(confno), "%d", i);
03296                   conf_map[i] = 1;
03297                   break;
03298                }
03299             }
03300             AST_LIST_UNLOCK(&confs);
03301          }
03302 
03303          /* Not found? */
03304          if (ast_strlen_zero(confno)) {
03305             res = ast_streamfile(chan, "conf-noempty", chan->language);
03306             if (!res)
03307                ast_waitstream(chan, "");
03308          } else {
03309             if (sscanf(confno, "%30d", &confno_int) == 1) {
03310                if (!ast_test_flag(&confflags, CONFFLAG_QUIET)) {
03311                   res = ast_streamfile(chan, "conf-enteringno", chan->language);
03312                   if (!res) {
03313                      ast_waitstream(chan, "");
03314                      res = ast_say_digits(chan, confno_int, "", chan->language);
03315                   }
03316                }
03317             } else {
03318                ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
03319             }
03320          }
03321       }
03322 
03323       while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
03324          /* Prompt user for conference number */
03325          res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
03326          if (res < 0) {
03327             /* Don't try to validate when we catch an error */
03328             confno[0] = '\0';
03329             allowretry = 0;
03330             break;
03331          }
03332       }
03333       if (!ast_strlen_zero(confno)) {
03334          /* Check the validity of the conference */
03335          cnf = find_conf(chan, confno, 1, dynamic, the_pin, 
03336             sizeof(the_pin), 1, &confflags);
03337          if (!cnf) {
03338             int too_early = 0;
03339             cnf = find_conf_realtime(chan, confno, 1, dynamic, 
03340                the_pin, sizeof(the_pin), 1, &confflags, optargs, &too_early);
03341             if (rt_schedule && too_early)
03342                allowretry = 0;
03343          }
03344 
03345          if (!cnf) {
03346             if (allowretry) {
03347                confno[0] = '\0';
03348                res = ast_streamfile(chan, "conf-invalid", chan->language);
03349                if (!res)
03350                   ast_waitstream(chan, "");
03351                res = -1;
03352             }
03353          } else {
03354             if ((!ast_strlen_zero(cnf->pin) &&
03355                  !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
03356                 (!ast_strlen_zero(cnf->pinadmin) &&
03357                  ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
03358                char pin[MAX_PIN] = "";
03359                int j;
03360 
03361                /* Allow the pin to be retried up to 3 times */
03362                for (j = 0; j < 3; j++) {
03363                   if (*the_pin && (always_prompt == 0)) {
03364                      ast_copy_string(pin, the_pin, sizeof(pin));
03365                      res = 0;
03366                   } else {
03367                      /* Prompt user for pin if pin is required */
03368                      res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
03369                   }
03370                   if (res >= 0) {
03371                      if (!strcasecmp(pin, cnf->pin) ||
03372                          (!ast_strlen_zero(cnf->pinadmin) &&
03373                           !strcasecmp(pin, cnf->pinadmin))) {
03374                         /* Pin correct */
03375                         allowretry = 0;
03376                         if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) 
03377                            ast_set_flag(&confflags, CONFFLAG_ADMIN);
03378                         /* Run the conference */
03379                         res = conf_run(chan, cnf, confflags.flags, optargs);
03380                         break;
03381                      } else {
03382                         /* Pin invalid */
03383                         if (!ast_streamfile(chan, "conf-invalidpin", chan->language)) {
03384                            res = ast_waitstream(chan, AST_DIGIT_ANY);
03385                            ast_stopstream(chan);
03386                         }
03387                         else {
03388                            ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
03389                            break;
03390                         }
03391                         if (res < 0)
03392                            break;
03393                         pin[0] = res;
03394                         pin[1] = '\0';
03395                         res = -1;
03396                         if (allowretry)
03397                            confno[0] = '\0';
03398                      }
03399                   } else {
03400                      /* failed when getting the pin */
03401                      res = -1;
03402                      allowretry = 0;
03403                      /* see if we need to get rid of the conference */
03404                      break;
03405                   }
03406 
03407                   /* Don't retry pin with a static pin */
03408                   if (*the_pin && (always_prompt == 0)) {
03409                      break;
03410                   }
03411                }
03412             } else {
03413                /* No pin required */
03414                allowretry = 0;
03415 
03416                /* Run the conference */
03417                res = conf_run(chan, cnf, confflags.flags, optargs);
03418             }
03419             dispose_conf(cnf);
03420             cnf = NULL;
03421          }
03422       }
03423    } while (allowretry);
03424 
03425    if (cnf)
03426       dispose_conf(cnf);
03427    
03428    return res;
03429 }

static void conf_flush ( int  fd,
struct ast_channel chan 
) [static]

Definition at line 1343 of file app_meetme.c.

References ast_frfree, ast_log(), ast_read(), ast_waitfor(), f, and LOG_WARNING.

Referenced by conf_run().

01344 {
01345    int x;
01346 
01347    /* read any frames that may be waiting on the channel
01348       and throw them away
01349    */
01350    if (chan) {
01351       struct ast_frame *f;
01352 
01353       /* when no frames are available, this will wait
01354          for 1 millisecond maximum
01355       */
01356       while (ast_waitfor(chan, 1)) {
01357          f = ast_read(chan);
01358          if (f)
01359             ast_frfree(f);
01360          else /* channel was hung up or something else happened */
01361             break;
01362       }
01363    }
01364 
01365    /* flush any data sitting in the pseudo channel */
01366    x = DAHDI_FLUSH_ALL;
01367    if (ioctl(fd, DAHDI_FLUSH, &x))
01368       ast_log(LOG_WARNING, "Error flushing channel\n");
01369 
01370 }

static int conf_free ( struct ast_conference conf  )  [static]

Definition at line 1374 of file app_meetme.c.

References ast_conference::announcelist, ast_conference::announcelist_addition, ast_conference::announcelistlock, ast_conference::announcethread, ast_conference::announcethread_stop, ast_conference::announcethreadlock, ao2_ref, ast_cond_signal(), ast_filedelete(), AST_FRAME_BITS, ast_free, ast_frfree, ast_hangup(), AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_mutex_destroy(), ast_mutex_lock(), ast_mutex_unlock(), AST_PTHREADT_NULL, ast_softhangup(), AST_SOFTHANGUP_EXPLICIT, ast_translator_free_path(), ast_conference::chan, ast_conference::confno, EVENT_FLAG_CALL, ast_conference::fd, ast_conference::lchan, ast_conference::listenlock, manager_event, MEETME_RECORD_ACTIVE, MEETME_RECORD_OFF, MEETME_RECORD_TERMINATE, announce_listitem::namerecloc, ast_conference::origframe, ast_conference::playlock, ast_conference::recordingfilename, ast_conference::recordingformat, ast_conference::recordthreadlock, ast_conference::transframe, and ast_conference::transpath.

Referenced by dispose_conf().

01375 {
01376    int x;
01377    struct announce_listitem *item;
01378    
01379    AST_LIST_REMOVE(&confs, conf, list);
01380    manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
01381 
01382    if (conf->recording == MEETME_RECORD_ACTIVE) {
01383       conf->recording = MEETME_RECORD_TERMINATE;
01384       AST_LIST_UNLOCK(&confs);
01385       while (1) {
01386          usleep(1);
01387          AST_LIST_LOCK(&confs);
01388          if (conf->recording == MEETME_RECORD_OFF)
01389             break;
01390          AST_LIST_UNLOCK(&confs);
01391       }
01392    }
01393 
01394    for (x = 0; x < AST_FRAME_BITS; x++) {
01395       if (conf->transframe[x])
01396          ast_frfree(conf->transframe[x]);
01397       if (conf->transpath[x])
01398          ast_translator_free_path(conf->transpath[x]);
01399    }
01400    if (conf->announcethread != AST_PTHREADT_NULL) {
01401       ast_mutex_lock(&conf->announcelistlock);
01402       conf->announcethread_stop = 1;
01403       ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
01404       ast_cond_signal(&conf->announcelist_addition);
01405       ast_mutex_unlock(&conf->announcelistlock);
01406       pthread_join(conf->announcethread, NULL);
01407    
01408       while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
01409          ast_filedelete(item->namerecloc, NULL);
01410          ao2_ref(item, -1);
01411       }
01412       ast_mutex_destroy(&conf->announcelistlock);
01413    }
01414    if (conf->origframe)
01415       ast_frfree(conf->origframe);
01416    if (conf->lchan)
01417       ast_hangup(conf->lchan);
01418    if (conf->chan)
01419       ast_hangup(conf->chan);
01420    if (conf->fd >= 0)
01421       close(conf->fd);
01422    if (conf->recordingfilename) {
01423       ast_free(conf->recordingfilename);
01424    }
01425    if (conf->recordingformat) {
01426       ast_free(conf->recordingformat);
01427    }
01428    ast_mutex_destroy(&conf->playlock);
01429    ast_mutex_destroy(&conf->listenlock);
01430    ast_mutex_destroy(&conf->recordthreadlock);
01431    ast_mutex_destroy(&conf->announcethreadlock);
01432    ast_free(conf);
01433 
01434    return 0;
01435 }

static void conf_play ( struct ast_channel chan,
struct ast_conference conf,
enum entrance_sound  sound 
) [static]

Definition at line 780 of file app_meetme.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_check_hangup(), AST_LIST_LOCK, AST_LIST_UNLOCK, careful_write(), enter, ENTER, ast_conference::fd, leave, LEAVE, and len().

Referenced by conf_run().

00781 {
00782    unsigned char *data;
00783    int len;
00784    int res = -1;
00785 
00786    if (!ast_check_hangup(chan))
00787       res = ast_autoservice_start(chan);
00788 
00789    AST_LIST_LOCK(&confs);
00790 
00791    switch(sound) {
00792    case ENTER:
00793       data = enter;
00794       len = sizeof(enter);
00795       break;
00796    case LEAVE:
00797       data = leave;
00798       len = sizeof(leave);
00799       break;
00800    default:
00801       data = NULL;
00802       len = 0;
00803    }
00804    if (data) {
00805       careful_write(conf->fd, data, len, 1);
00806    }
00807 
00808    AST_LIST_UNLOCK(&confs);
00809 
00810    if (!res) 
00811       ast_autoservice_stop(chan);
00812 }

static void conf_queue_dtmf ( const struct ast_conference conf,
const struct ast_conf_user sender,
struct ast_frame f 
) [static]

Definition at line 1437 of file app_meetme.c.

References AST_LIST_TRAVERSE, ast_log(), ast_write(), ast_conf_user::chan, ast_conf_user::list, LOG_WARNING, ast_channel::name, and ast_conference::userlist.

Referenced by conf_run().

01439 {
01440    struct ast_conf_user *user;
01441 
01442    AST_LIST_TRAVERSE(&conf->userlist, user, list) {
01443       if (user == sender)
01444          continue;
01445       if (ast_write(user->chan, f) < 0)
01446          ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
01447    }
01448 }

static int conf_run ( struct ast_channel chan,
struct ast_conference conf,
int  confflags,
char *  optargs[] 
) [static]

Definition at line 1632 of file app_meetme.c.

References volume::actual, ADMINFLAG_KICKME, ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ADMINFLAG_T_REQUEST, ast_conf_user::adminflags, announce_thread(), ast_conference::announcelist, ast_conference::announcelist_addition, ast_conference::announcelistlock, ast_conference::announcethread, ast_conference::announcethreadlock, announce_listitem::announcetype, ao2_alloc, ao2_ref, ast_calloc, ast_channel_lock, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_cond_signal(), ast_config_AST_SPOOL_DIR, AST_CONTROL_HOLD, ast_copy_string(), ast_debug, AST_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, ast_devstate_changed(), AST_DIGIT_ANY, ast_dsp_free(), ast_dsp_get_threshold_from_settings(), ast_dsp_new(), ast_dsp_silence(), ast_exists_extension(), ast_filedelete(), AST_FORMAT_SLINEAR, ast_frame_adjust_volume(), AST_FRAME_BITS, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_DTMF_BEGIN, AST_FRAME_DTMF_END, AST_FRAME_NULL, AST_FRAME_VOICE, ast_free, ast_frfree, AST_FRIENDLY_OFFSET, ast_goto_if_exists(), ast_hangup(), ast_indicate(), AST_LIST_EMPTY, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_LAST, AST_LIST_LOCK, AST_LIST_NEXT, AST_LIST_REMOVE, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_mkdir(), ast_moh_start(), ast_moh_stop(), ast_mutex_init(), ast_mutex_lock(), ast_mutex_unlock(), ast_null_frame, AST_OPTION_TONE_VERIFY, ast_play_and_record(), ast_pthread_create_background, ast_pthread_create_detached_background, AST_PTHREADT_NULL, ast_read(), ast_read_noaudio(), ast_realtime_require_field(), ast_record_review(), ast_request(), ast_safe_sleep(), ast_samp2tv(), ast_say_digits(), ast_say_number(), ast_set_read_format(), ast_set_write_format(), ast_stopstream(), ast_strdup, ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_translate(), ast_translator_build_path(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_tvsub(), ast_tvzero(), ast_update_realtime(), ast_verb, ast_waitfor_nandfds(), ast_waitstream(), ast_write(), ast_channel::audiohooks, buf, can_write(), careful_write(), ast_conference::chan, ast_conf_user::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, conf_flush(), CONF_HASJOIN, CONF_HASLEFT, conf_play(), conf_queue_dtmf(), CONF_SIZE, conf_start_moh(), announce_listitem::confchan, CONFFLAG_ADMIN, CONFFLAG_AGI, CONFFLAG_ANNOUNCEUSERCOUNT, CONFFLAG_DURATION_LIMIT, CONFFLAG_DURATION_STOP, CONFFLAG_EXIT_CONTEXT, CONFFLAG_INTROUSER, CONFFLAG_INTROUSERNOREVIEW, CONFFLAG_KEYEXIT, CONFFLAG_KICK_CONTINUE, CONFFLAG_MARKEDEXIT, CONFFLAG_MARKEDUSER, CONFFLAG_MOH, CONFFLAG_MONITOR, CONFFLAG_MONITORTALKER, CONFFLAG_NO_AUDIO_UNTIL_UP, CONFFLAG_NOONLYPERSON, CONFFLAG_OPTIMIZETALKER, CONFFLAG_PASS_DTMF, CONFFLAG_QUIET, CONFFLAG_RECORDCONF, CONFFLAG_SLA_STATION, CONFFLAG_STARMENU, CONFFLAG_STARTMUTED, CONFFLAG_TALKER, CONFFLAG_WAITMARKED, ast_conference::confno, announce_listitem::confusers, ast_channel::context, ast_conf_user::dahdichannel, ast_conference::dahdiconf, ast_frame::data, ast_frame::datalen, volume::desired, dtmfstr, ast_conf_user::end_sound, ast_conference::endalert, ast_conference::endtime, ENTER, errno, EVENT_FLAG_CALL, exitcontext, f, ast_channel::fds, ast_frame::frame_list, ast_frame::frametype, ast_conf_user::jointime, ast_conf_user::kicktime, announce_listitem::language, ast_channel::language, ast_conference::lchan, LEAVE, ast_conf_user::listen, ast_conference::listenlock, ast_conference::locked, LOG_WARNING, ast_channel::macrocontext, manager_event, ast_conference::markedusers, ast_conference::maxusers, MEETME_DELAYDETECTENDTALK, MEETME_DELAYDETECTTALK, ast_channel::monitor, ast_channel::name, announce_listitem::namerecloc, ast_conf_user::namerecloc, ast_frame::offset, OPT_ARG_DURATION_LIMIT, OPT_ARG_DURATION_STOP, OPT_ARG_EXITKEYS, OPT_ARG_MOH_CLASS, OPT_ARG_WAITMARKED, ast_conference::origframe, parse(), pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), ast_conf_user::play_warning, ast_conference::playlock, ast_frame::ptr, ast_channel::rawwriteformat, ast_conference::recordingfilename, ast_conference::recordingformat, ast_conference::recordthread, ast_conference::recordthreadlock, reset_volumes(), RQ_UINTEGER1, RQ_UINTEGER2, RQ_UINTEGER3, RQ_UINTEGER4, S_OR, ast_frame::samples, sec, set_talk_volume(), SLA_EVENT_HOLD, sla_queue_event_conf(), ast_conf_user::start_time, strsep(), ast_frame::subclass, ast_conf_user::talk, ast_conf_user::talking, ast_channel::tech, THRESHOLD_SILENCE, ast_conf_user::timelimit, ast_conference::transframe, ast_conference::transpath, tweak_listen_volume(), tweak_talk_volume(), ast_channel_tech::type, ast_conference::uniqueid, ast_channel::uniqueid, ast_conf_user::user_no, ast_conf_user::userflags, ast_conference::userlist, ast_conference::users, var, VOL_DOWN, VOL_UP, ast_conf_user::warning_freq, and ast_conf_user::warning_sound.

01633 {
01634    struct ast_conf_user *user = NULL;
01635    struct ast_conf_user *usr = NULL;
01636    int fd;
01637    struct dahdi_confinfo dahdic, dahdic_empty;
01638    struct ast_frame *f;
01639    struct ast_channel *c;
01640    struct ast_frame fr;
01641    int outfd;
01642    int ms;
01643    int nfds;
01644    int res;
01645    int retrydahdi;
01646    int origfd;
01647    int musiconhold = 0, mohtempstopped = 0;
01648    int firstpass = 0;
01649    int lastmarked = 0;
01650    int currentmarked = 0;
01651    int ret = -1;
01652    int x;
01653    int menu_active = 0;
01654    int talkreq_manager = 0;
01655    int using_pseudo = 0;
01656    int duration = 20;
01657    int hr, min, sec;
01658    int sent_event = 0;
01659    int checked = 0;
01660    int announcement_played = 0;
01661    struct timeval now;
01662    struct ast_dsp *dsp = NULL;
01663    struct ast_app *agi_app;
01664    char *agifile;
01665    const char *agifiledefault = "conf-background.agi", *tmpvar;
01666    char meetmesecs[30] = "";
01667    char exitcontext[AST_MAX_CONTEXT] = "";
01668    char recordingtmp[AST_MAX_EXTENSION] = "";
01669    char members[10] = "";
01670    int dtmf, opt_waitmarked_timeout = 0;
01671    time_t timeout = 0;
01672    struct dahdi_bufferinfo bi;
01673    char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
01674    char *buf = __buf + AST_FRIENDLY_OFFSET;
01675    char *exitkeys = NULL;
01676    unsigned int calldurationlimit = 0;
01677    long timelimit = 0;
01678    long play_warning = 0;
01679    long warning_freq = 0;
01680    const char *warning_sound = NULL;
01681    const char *end_sound = NULL;
01682    char *parse;   
01683    long time_left_ms = 0;
01684    struct timeval nexteventts = { 0, };
01685    int to;
01686    int setusercount = 0;
01687    int confsilence = 0, totalsilence = 0;
01688 
01689    if (!(user = ast_calloc(1, sizeof(*user))))
01690       return ret;
01691 
01692    /* Possible timeout waiting for marked user */
01693    if ((confflags & CONFFLAG_WAITMARKED) &&
01694       !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
01695       (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
01696       (opt_waitmarked_timeout > 0)) {
01697       timeout = time(NULL) + opt_waitmarked_timeout;
01698    }
01699       
01700    if ((confflags & CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
01701       calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
01702       ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
01703    }
01704    
01705    if ((confflags & CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
01706       char *limit_str, *warning_str, *warnfreq_str;
01707       const char *var;
01708  
01709       parse = optargs[OPT_ARG_DURATION_LIMIT];
01710       limit_str = strsep(&parse, ":");
01711       warning_str = strsep(&parse, ":");
01712       warnfreq_str = parse;
01713  
01714       timelimit = atol(limit_str);
01715       if (warning_str)
01716          play_warning = atol(warning_str);
01717       if (warnfreq_str)
01718          warning_freq = atol(warnfreq_str);
01719  
01720       if (!timelimit) {
01721          timelimit = play_warning = warning_freq = 0;
01722          warning_sound = NULL;
01723       } else if (play_warning > timelimit) {       
01724          if (!warning_freq) {
01725             play_warning = 0;
01726          } else {
01727             while (play_warning > timelimit)
01728                play_warning -= warning_freq;
01729             if (play_warning < 1)
01730                play_warning = warning_freq = 0;
01731          }
01732       }
01733       
01734       ast_channel_lock(chan);
01735       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
01736          var = ast_strdupa(var);
01737       }
01738       ast_channel_unlock(chan);
01739 
01740       warning_sound = var ? var : "timeleft";
01741       
01742       ast_channel_lock(chan);
01743       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
01744          var = ast_strdupa(var);
01745       }
01746       ast_channel_unlock(chan);
01747       
01748       end_sound = var ? var : NULL;
01749          
01750       /* undo effect of S(x) in case they are both used */
01751       calldurationlimit = 0;
01752       /* more efficient do it like S(x) does since no advanced opts */
01753       if (!play_warning && !end_sound && timelimit) { 
01754          calldurationlimit = timelimit / 1000;
01755          timelimit = play_warning = warning_freq = 0;
01756       } else {
01757          ast_debug(2, "Limit Data for this call:\n");
01758          ast_debug(2, "- timelimit     = %ld\n", timelimit);
01759          ast_debug(2, "- play_warning  = %ld\n", play_warning);
01760          ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
01761          ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
01762          ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
01763       }
01764    }
01765 
01766    /* Get exit keys */
01767    if ((confflags & CONFFLAG_KEYEXIT)) {
01768       if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
01769          exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
01770       else
01771          exitkeys = ast_strdupa("#"); /* Default */
01772    }
01773    
01774    if (confflags & CONFFLAG_RECORDCONF) {
01775       if (!conf->recordingfilename) {
01776          const char *var;
01777          ast_channel_lock(chan);
01778          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
01779             conf->recordingfilename = ast_strdup(var);
01780          }
01781          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
01782             conf->recordingformat = ast_strdup(var);
01783          }
01784          ast_channel_unlock(chan);
01785          if (!conf->recordingfilename) {
01786             snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
01787             conf->recordingfilename = ast_strdup(recordingtmp);
01788          }
01789          if (!conf->recordingformat) {
01790             conf->recordingformat = ast_strdup("wav");
01791          }
01792          ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
01793                 conf->confno, conf->recordingfilename, conf->recordingformat);
01794       }
01795    }
01796 
01797    ast_mutex_lock(&conf->recordthreadlock);
01798    if ((conf->recordthread == AST_PTHREADT_NULL) && (confflags & CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", AST_FORMAT_SLINEAR, "pseudo", NULL)))) {
01799       ast_set_read_format(conf->lchan, AST_FORMAT_SLINEAR);
01800       ast_set_write_format(conf->lchan, AST_FORMAT_SLINEAR);
01801       dahdic.chan = 0;
01802       dahdic.confno = conf->dahdiconf;
01803       dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01804       if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
01805          ast_log(LOG_WARNING, "Error starting listen channel\n");
01806          ast_hangup(conf->lchan);
01807          conf->lchan = NULL;
01808       } else {
01809          ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
01810       }
01811    }
01812    ast_mutex_unlock(&conf->recordthreadlock);
01813 
01814    ast_mutex_lock(&conf->announcethreadlock);
01815    if ((conf->announcethread == AST_PTHREADT_NULL) && !(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
01816       ast_mutex_init(&conf->announcelistlock);
01817       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
01818       ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
01819    }
01820    ast_mutex_unlock(&conf->announcethreadlock);
01821 
01822    time(&user->jointime);
01823    
01824    user->timelimit = timelimit;
01825    user->play_warning = play_warning;
01826    user->warning_freq = warning_freq;
01827    user->warning_sound = warning_sound;
01828    user->end_sound = end_sound;  
01829    
01830    if (calldurationlimit > 0) {
01831       time(&user->kicktime);
01832       user->kicktime = user->kicktime + calldurationlimit;
01833    }
01834    
01835    if (ast_tvzero(user->start_time))
01836       user->start_time = ast_tvnow();
01837    time_left_ms = user->timelimit;
01838    
01839    if (user->timelimit) {
01840       nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
01841       nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
01842    }
01843 
01844    if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
01845       /* Sorry, but this conference is locked! */  
01846       if (!ast_streamfile(chan, "conf-locked", chan->language))
01847          ast_waitstream(chan, "");
01848       goto outrun;
01849    }
01850 
01851       ast_mutex_lock(&conf->playlock);
01852 
01853    if (AST_LIST_EMPTY(&conf->userlist))
01854       user->user_no = 1;
01855    else
01856       user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
01857 
01858    if (rt_schedule && conf->maxusers)
01859       if (conf->users >= conf->maxusers) {
01860          /* Sorry, but this confernce has reached the participant limit! */   
01861          if (!ast_streamfile(chan, "conf-full", chan->language))
01862             ast_waitstream(chan, "");
01863          ast_mutex_unlock(&conf->playlock);
01864          user->user_no = 0;
01865          goto outrun;
01866       }
01867 
01868    AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
01869 
01870    user->chan = chan;
01871    user->userflags = confflags;
01872    user->adminflags = (confflags & CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
01873    user->talking = -1;
01874 
01875    ast_mutex_unlock(&conf->playlock);
01876 
01877    if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) {
01878       char destdir[PATH_MAX];
01879 
01880       snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
01881 
01882       if (ast_mkdir(destdir, 0777) != 0) {
01883          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
01884          goto outrun;
01885       }
01886 
01887       snprintf(user->namerecloc, sizeof(user->namerecloc),
01888           "%s/meetme-username-%s-%d", destdir,
01889           conf->confno, user->user_no);
01890       if (confflags & CONFFLAG_INTROUSERNOREVIEW)
01891          res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
01892       else
01893          res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
01894       if (res == -1)
01895          goto outrun;
01896    }
01897 
01898    ast_mutex_lock(&conf->playlock);
01899 
01900    if (confflags & CONFFLAG_MARKEDUSER)
01901       conf->markedusers++;
01902    conf->users++;
01903    if (rt_log_members) {
01904       /* Update table */
01905       snprintf(members, sizeof(members), "%d", conf->users);
01906       ast_realtime_require_field("meetme",
01907          "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
01908          "members", RQ_UINTEGER1, strlen(members),
01909          NULL);
01910       ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
01911    }
01912    setusercount = 1;
01913 
01914    /* This device changed state now - if this is the first user */
01915    if (conf->users == 1)
01916       ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
01917 
01918    ast_mutex_unlock(&conf->playlock);
01919 
01920    /* return the unique ID of the conference */
01921    pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
01922 
01923    if (confflags & CONFFLAG_EXIT_CONTEXT) {
01924       ast_channel_lock(chan);
01925       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
01926          ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
01927       } else if (!ast_strlen_zero(chan->macrocontext)) {
01928          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
01929       } else {
01930          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
01931       }
01932       ast_channel_unlock(chan);
01933    }
01934 
01935    if (!(confflags & (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
01936       if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
01937          if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
01938             ast_waitstream(chan, "");
01939       if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
01940          if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
01941             ast_waitstream(chan, "");
01942    }
01943 
01944    if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
01945       int keepplaying = 1;
01946 
01947       if (conf->users == 2) { 
01948          if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
01949             res = ast_waitstream(chan, AST_DIGIT_ANY);
01950             ast_stopstream(chan);
01951             if (res > 0)
01952                keepplaying = 0;
01953             else if (res == -1)
01954                goto outrun;
01955          }
01956       } else { 
01957          if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
01958             res = ast_waitstream(chan, AST_DIGIT_ANY);
01959             ast_stopstream(chan);
01960             if (res > 0)
01961                keepplaying = 0;
01962             else if (res == -1)
01963                goto outrun;
01964          }
01965          if (keepplaying) {
01966             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
01967             if (res > 0)
01968                keepplaying = 0;
01969             else if (res == -1)
01970                goto outrun;
01971          }
01972          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
01973             res = ast_waitstream(chan, AST_DIGIT_ANY);
01974             ast_stopstream(chan);
01975             if (res > 0)
01976                keepplaying = 0;
01977             else if (res == -1) 
01978                goto outrun;
01979          }
01980       }
01981    }
01982 
01983    if (!(confflags & CONFFLAG_NO_AUDIO_UNTIL_UP)) {
01984       /* We're leaving this alone until the state gets changed to up */
01985       ast_indicate(chan, -1);
01986    }
01987 
01988    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01989       ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
01990       goto outrun;
01991    }
01992 
01993    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
01994       ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
01995       goto outrun;
01996    }
01997 
01998    retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
01999    user->dahdichannel = !retrydahdi;
02000 
02001  dahdiretry:
02002    origfd = chan->fds[0];
02003    if (retrydahdi) {
02004       /* open pseudo in non-blocking mode */
02005       fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
02006       if (fd < 0) {
02007          ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
02008          goto outrun;
02009       }
02010       using_pseudo = 1;
02011       /* Setup buffering information */
02012       memset(&bi, 0, sizeof(bi));
02013       bi.bufsize = CONF_SIZE / 2;
02014       bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
02015       bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
02016       bi.numbufs = audio_buffers;
02017       if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
02018          ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
02019          close(fd);
02020          goto outrun;
02021       }
02022       x = 1;
02023       if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
02024          ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
02025          close(fd);
02026          goto outrun;
02027       }
02028       nfds = 1;
02029    } else {
02030       /* XXX Make sure we're not running on a pseudo channel XXX */
02031       fd = chan->fds[0];
02032       nfds = 0;
02033    }
02034    memset(&dahdic, 0, sizeof(dahdic));
02035    memset(&dahdic_empty, 0, sizeof(dahdic_empty));
02036    /* Check to see if we're in a conference... */
02037    dahdic.chan = 0;  
02038    if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
02039       ast_log(LOG_WARNING, "Error getting conference\n");
02040       close(fd);
02041       goto outrun;
02042    }
02043    if (dahdic.confmode) {
02044       /* Whoa, already in a conference...  Retry... */
02045       if (!retrydahdi) {
02046          ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
02047          retrydahdi = 1;
02048          goto dahdiretry;
02049       }
02050    }
02051    memset(&dahdic, 0, sizeof(dahdic));
02052    /* Add us to the conference */
02053    dahdic.chan = 0;  
02054    dahdic.confno = conf->dahdiconf;
02055 
02056    if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
02057       struct announce_listitem *item;
02058       if (!(item = ao2_alloc(sizeof(*item), NULL)))
02059          return -1;
02060       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
02061       ast_copy_string(item->language, chan->language, sizeof(item->language));
02062       item->confchan = conf->chan;
02063       item->confusers = conf->users;
02064       item->announcetype = CONF_HASJOIN;
02065       ast_mutex_lock(&conf->announcelistlock);
02066       ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
02067       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
02068       ast_cond_signal(&conf->announcelist_addition);
02069       ast_mutex_unlock(&conf->announcelistlock);
02070 
02071       while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
02072          ;
02073       }
02074       ao2_ref(item, -1);
02075    }
02076 
02077    if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers)
02078       dahdic.confmode = DAHDI_CONF_CONF;
02079    else if (confflags & CONFFLAG_MONITOR)
02080       dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
02081    else if (confflags & CONFFLAG_TALKER)
02082       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
02083    else 
02084       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
02085 
02086    if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02087       ast_log(LOG_WARNING, "Error setting conference\n");
02088       close(fd);
02089       goto outrun;
02090    }
02091    ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
02092 
02093    if (!sent_event) {
02094       manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
02095                  "Channel: %s\r\n"
02096                  "Uniqueid: %s\r\n"
02097             "Meetme: %s\r\n"
02098             "Usernum: %d\r\n"
02099             "CallerIDnum: %s\r\n"
02100                   "CallerIDname: %s\r\n",
02101                   chan->name, chan->uniqueid, conf->confno, 
02102             user->user_no,
02103             S_OR(user->chan->cid.cid_num, "<unknown>"),
02104             S_OR(user->chan->cid.cid_name, "<unknown>")
02105             );
02106       sent_event = 1;
02107    }
02108 
02109    if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
02110       firstpass = 1;
02111       if (!(confflags & CONFFLAG_QUIET))
02112          if (!(confflags & CONFFLAG_WAITMARKED) || ((confflags & CONFFLAG_MARKEDUSER) && (conf->markedusers >= 1)))
02113             conf_play(chan, conf, ENTER);
02114    }
02115 
02116    conf_flush(fd, chan);
02117 
02118    if (!(dsp = ast_dsp_new())) {
02119       ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
02120       res = -1;
02121    }
02122 
02123    if (confflags & CONFFLAG_AGI) {
02124       /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
02125          or use default filename of conf-background.agi */
02126 
02127       ast_channel_lock(chan);
02128       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
02129          agifile = ast_strdupa(tmpvar);
02130       } else {
02131          agifile = ast_strdupa(agifiledefault);
02132       }
02133       ast_channel_unlock(chan);
02134       
02135       if (user->dahdichannel) {
02136          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
02137          x = 1;
02138          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02139       }
02140       /* Find a pointer to the agi app and execute the script */
02141       agi_app = pbx_findapp("agi");
02142       if (agi_app) {
02143          ret = pbx_exec(chan, agi_app, agifile);
02144       } else {
02145          ast_log(LOG_WARNING, "Could not find application (agi)\n");
02146          ret = -2;
02147       }
02148       if (user->dahdichannel) {
02149          /*  Remove CONFMUTE mode on DAHDI channel */
02150          x = 0;
02151          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02152       }
02153    } else {
02154       if (user->dahdichannel && (confflags & CONFFLAG_STARMENU)) {
02155          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
02156          x = 1;
02157          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
02158       }  
02159       for (;;) {
02160          int menu_was_active = 0;
02161 
02162          outfd = -1;
02163          ms = -1;
02164          now = ast_tvnow();
02165 
02166          if (rt_schedule) {
02167             if (now.tv_sec % 60 == 0) {
02168                if (!checked) {
02169                   if (now.tv_sec >= conf->endtime) {
02170                      goto outrun;
02171                   }
02172 
02173                   if (!announcement_played && conf->endalert) {
02174                      if (now.tv_sec + conf->endalert >= conf->endtime) {
02175                         if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
02176                            ast_waitstream(chan, "");
02177                         ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
02178                         if (!ast_streamfile(chan, "minutes", chan->language))
02179                            ast_waitstream(chan, "");
02180                         announcement_played = 1;
02181                      }
02182                   }
02183                   checked = 1;
02184                   
02185                }
02186             } else {
02187                checked = 0;
02188             }
02189          }
02190 
02191          if (user->kicktime && (user->kicktime <= now.tv_sec)) 
02192             break;
02193   
02194          to = -1;
02195          if (user->timelimit) {
02196             int minutes = 0, seconds = 0, remain = 0;
02197  
02198             to = ast_tvdiff_ms(nexteventts, now);
02199             if (to < 0)
02200                to = 0;
02201             time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
02202             if (time_left_ms < to)
02203                to = time_left_ms;
02204    
02205             if (time_left_ms <= 0) {
02206                if (user->end_sound) {                 
02207                   res = ast_streamfile(chan, user->end_sound, chan->language);
02208                   res = ast_waitstream(chan, "");
02209                }
02210                break;
02211             }
02212             
02213             if (!to) {
02214                if (time_left_ms >= 5000) {                  
02215                   
02216                   remain = (time_left_ms + 500) / 1000;
02217                   if (remain / 60 >= 1) {
02218                      minutes = remain / 60;
02219                      seconds = remain % 60;
02220                   } else {
02221                      seconds = remain;
02222                   }
02223                   
02224                   /* force the time left to round up if appropriate */
02225                   if (user->warning_sound && user->play_warning) {
02226                      if (!strcmp(user->warning_sound, "timeleft")) {
02227                         
02228                         res = ast_streamfile(chan, "vm-youhave", chan->language);
02229                         res = ast_waitstream(chan, "");
02230                         if (minutes) {
02231                            res = ast_say_number(chan, minutes, AST_DIGIT_ANY, chan->language, (char *) NULL);
02232                            res = ast_streamfile(chan, "queue-minutes", chan->language);
02233                            res = ast_waitstream(chan, "");
02234                         }
02235                         if (seconds) {
02236                            res = ast_say_number(chan, seconds, AST_DIGIT_ANY, chan->language, (char *) NULL);
02237                            res = ast_streamfile(chan, "queue-seconds", chan->language);
02238                            res = ast_waitstream(chan, "");
02239                         }
02240                      } else {
02241                         res = ast_streamfile(chan, user->warning_sound, chan->language);
02242                         res = ast_waitstream(chan, "");
02243                      }
02244                   }
02245                }
02246                if (user->warning_freq)
02247                   nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
02248                else
02249                   nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
02250             }
02251          }
02252 
02253          now = ast_tvnow();
02254          if (timeout && now.tv_sec >= timeout)
02255             break;
02256 
02257          /* if we have just exited from the menu, and the user had a channel-driver
02258             volume adjustment, restore it
02259          */
02260          if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
02261             set_talk_volume(user, user->listen.desired);
02262 
02263          menu_was_active = menu_active;
02264 
02265          currentmarked = conf->markedusers;
02266          if (!(confflags & CONFFLAG_QUIET) &&
02267              (confflags & CONFFLAG_MARKEDUSER) &&
02268              (confflags & CONFFLAG_WAITMARKED) &&
02269              lastmarked == 0) {
02270             if (currentmarked == 1 && conf->users > 1) {
02271                ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
02272                if (conf->users - 1 == 1) {
02273                   if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
02274                      ast_waitstream(chan, "");
02275                } else {
02276                   if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
02277                      ast_waitstream(chan, "");
02278                }
02279             }
02280             if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
02281                if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
02282                   ast_waitstream(chan, "");
02283          }
02284 
02285          /* Update the struct with the actual confflags */
02286          user->userflags = confflags;
02287 
02288          if (confflags & CONFFLAG_WAITMARKED) {
02289             if (currentmarked == 0) {
02290                if (lastmarked != 0) {
02291                   if (!(confflags & CONFFLAG_QUIET))
02292                      if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
02293                         ast_waitstream(chan, "");
02294                   if (confflags & CONFFLAG_MARKEDEXIT) {
02295                      if (confflags & CONFFLAG_KICK_CONTINUE)
02296                         ret = 0;
02297                      break;
02298                   } else {
02299                      dahdic.confmode = DAHDI_CONF_CONF;
02300                      if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02301                         ast_log(LOG_WARNING, "Error setting conference\n");
02302                         close(fd);
02303                         goto outrun;
02304                      }
02305                   }
02306                }
02307                if (!musiconhold && (confflags & CONFFLAG_MOH)) {
02308                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02309                   musiconhold = 1;
02310                }
02311             } else if (currentmarked >= 1 && lastmarked == 0) {
02312                /* Marked user entered, so cancel timeout */
02313                timeout = 0;
02314                if (confflags & CONFFLAG_MONITOR)
02315                   dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
02316                else if (confflags & CONFFLAG_TALKER)
02317                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
02318                else
02319                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
02320                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02321                   ast_log(LOG_WARNING, "Error setting conference\n");
02322                   close(fd);
02323                   goto outrun;
02324                }
02325                if (musiconhold && (confflags & CONFFLAG_MOH)) {
02326                   ast_moh_stop(chan);
02327                   musiconhold = 0;
02328                }
02329                if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
02330                   if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
02331                      ast_waitstream(chan, "");
02332                   conf_play(chan, conf, ENTER);
02333                }
02334             }
02335          }
02336 
02337          /* trying to add moh for single person conf */
02338          if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
02339             if (conf->users == 1) {
02340                if (!musiconhold) {
02341                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02342                   musiconhold = 1;
02343                } 
02344             } else {
02345                if (musiconhold) {
02346                   ast_moh_stop(chan);
02347                   musiconhold = 0;
02348                }
02349             }
02350          }
02351          
02352          /* Leave if the last marked user left */
02353          if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
02354             if (confflags & CONFFLAG_KICK_CONTINUE)
02355                ret = 0;
02356             else
02357                ret = -1;
02358             break;
02359          }
02360    
02361          /* Check if my modes have changed */
02362 
02363          /* If I should be muted but am still talker, mute me */
02364          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
02365             dahdic.confmode ^= DAHDI_CONF_TALKER;
02366             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02367                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
02368                ret = -1;
02369                break;
02370             }
02371 
02372             manager_event(EVENT_FLAG_CALL, "MeetmeMute", 
02373                   "Channel: %s\r\n"
02374                   "Uniqueid: %s\r\n"
02375                   "Meetme: %s\r\n"
02376                   "Usernum: %i\r\n"
02377                   "Status: on\r\n",
02378                   chan->name, chan->uniqueid, conf->confno, user->user_no);
02379          }
02380 
02381          /* If I should be un-muted but am not talker, un-mute me */
02382          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !(confflags & CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
02383             dahdic.confmode |= DAHDI_CONF_TALKER;
02384             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02385                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
02386                ret = -1;
02387                break;
02388             }
02389 
02390             manager_event(EVENT_FLAG_CALL, "MeetmeMute", 
02391                   "Channel: %s\r\n"
02392                   "Uniqueid: %s\r\n"
02393                   "Meetme: %s\r\n"
02394                   "Usernum: %i\r\n"
02395                   "Status: off\r\n",
02396                   chan->name, chan->uniqueid, conf->confno, user->user_no);
02397          }
02398          
02399          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
02400             (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
02401             talkreq_manager = 1;
02402 
02403             manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest", 
02404                      "Channel: %s\r\n"
02405                            "Uniqueid: %s\r\n"
02406                            "Meetme: %s\r\n"
02407                            "Usernum: %i\r\n"
02408                            "Status: on\r\n",
02409                            chan->name, chan->uniqueid, conf->confno, user->user_no);
02410          }
02411 
02412          
02413          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
02414             !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
02415             talkreq_manager = 0;
02416             manager_event(EVENT_FLAG_CALL, "MeetmeTalkRequest", 
02417                      "Channel: %s\r\n"
02418                            "Uniqueid: %s\r\n"
02419                            "Meetme: %s\r\n"
02420                            "Usernum: %i\r\n"
02421                            "Status: off\r\n",
02422                           chan->name, chan->uniqueid, conf->confno, user->user_no);
02423          }
02424          
02425          /* If I have been kicked, exit the conference */
02426          if (user->adminflags & ADMINFLAG_KICKME) {
02427             /* You have been kicked. */
02428             if (!(confflags & CONFFLAG_QUIET) && 
02429                !ast_streamfile(chan, "conf-kicked", chan->language)) {
02430                ast_waitstream(chan, "");
02431             }
02432             ret = 0;
02433             break;
02434          }
02435 
02436          /* Perform an extra hangup check just in case */
02437          if (ast_check_hangup(chan))
02438             break;
02439 
02440          c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
02441 
02442          if (c) {
02443             char dtmfstr[2] = "";
02444 
02445             if (c->fds[0] != origfd || (user->dahdichannel && (c->audiohooks || c->monitor))) {
02446                if (using_pseudo) {
02447                   /* Kill old pseudo */
02448                   close(fd);
02449                   using_pseudo = 0;
02450                }
02451                ast_debug(1, "Ooh, something swapped out under us, starting over\n");
02452                retrydahdi = (strcasecmp(c->tech->type, "DAHDI") || (c->audiohooks || c->monitor) ? 1 : 0);
02453                user->dahdichannel = !retrydahdi;
02454                goto dahdiretry;
02455             }
02456             if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)))
02457                f = ast_read_noaudio(c);
02458             else
02459                f = ast_read(c);
02460             if (!f)
02461                break;
02462             if (f->frametype == AST_FRAME_DTMF) {
02463                dtmfstr[0] = f->subclass;
02464                dtmfstr[1] = '\0';
02465             }
02466 
02467             if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
02468                if (user->talk.actual)
02469                   ast_frame_adjust_volume(f, user->talk.actual);
02470 
02471                if (confflags & (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER)) {
02472                   if (user->talking == -1)
02473                      user->talking = 0;
02474 
02475                   res = ast_dsp_silence(dsp, f, &totalsilence);
02476                   if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
02477                      user->talking = 1;
02478                      if (confflags & CONFFLAG_MONITORTALKER)
02479                         manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
02480                               "Channel: %s\r\n"
02481                               "Uniqueid: %s\r\n"
02482                               "Meetme: %s\r\n"
02483                               "Usernum: %d\r\n"
02484                               "Status: on\r\n",
02485                               chan->name, chan->uniqueid, conf->confno, user->user_no);
02486                   }
02487                   if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
02488                      user->talking = 0;
02489                      if (confflags & CONFFLAG_MONITORTALKER)
02490                         manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
02491                               "Channel: %s\r\n"
02492                               "Uniqueid: %s\r\n"
02493                               "Meetme: %s\r\n"
02494                               "Usernum: %d\r\n"
02495                               "Status: off\r\n",
02496                               chan->name, chan->uniqueid, conf->confno, user->user_no);
02497                   }
02498                }
02499                if (using_pseudo) {
02500                   /* Absolutely do _not_ use careful_write here...
02501                      it is important that we read data from the channel
02502                      as fast as it arrives, and feed it into the conference.
02503                      The buffering in the pseudo channel will take care of any
02504                      timing differences, unless they are so drastic as to lose
02505                      audio frames (in which case carefully writing would only
02506                      have delayed the audio even further).
02507                   */
02508                   /* As it turns out, we do want to use careful write.  We just
02509                      don't want to block, but we do want to at least *try*
02510                      to write out all the samples.
02511                    */
02512                   if (user->talking && !(confflags & CONFFLAG_OPTIMIZETALKER)) {
02513                      careful_write(fd, f->data.ptr, f->datalen, 0);
02514                   }
02515                }
02516             } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
02517                if (confflags & CONFFLAG_PASS_DTMF)
02518                   conf_queue_dtmf(conf, user, f);
02519                if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
02520                   ast_log(LOG_WARNING, "Error setting conference\n");
02521                   close(fd);
02522                   ast_frfree(f);
02523                   goto outrun;
02524                }
02525 
02526                /* if we are entering the menu, and the user has a channel-driver
02527                   volume adjustment, clear it
02528                */
02529                if (!menu_active && user->talk.desired && !user->talk.actual)
02530                   set_talk_volume(user, 0);
02531 
02532                if (musiconhold) {
02533                      ast_moh_stop(chan);
02534                }
02535                if ((confflags & CONFFLAG_ADMIN)) {
02536                   /* Admin menu */
02537                   if (!menu_active) {
02538                      menu_active = 1;
02539                      /* Record this sound! */
02540                      if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
02541                         dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
02542                         ast_stopstream(chan);
02543                      } else 
02544                         dtmf = 0;
02545                   } else 
02546                      dtmf = f->subclass;
02547                   if (dtmf) {
02548                      switch(dtmf) {
02549                      case '1': /* Un/Mute */
02550                         menu_active = 0;
02551 
02552                         /* for admin, change both admin and use flags */
02553                         if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
02554                            user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02555                         else
02556                            user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02557 
02558                         if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02559                            if (!ast_streamfile(chan, "conf-muted", chan->language))
02560                               ast_waitstream(chan, "");
02561                         } else {
02562                            if (!ast_streamfile(chan, "conf-unmuted", chan->language))
02563                               ast_waitstream(chan, "");
02564                         }
02565                         break;
02566                      case '2': /* Un/Lock the Conference */
02567                         menu_active = 0;
02568                         if (conf->locked) {
02569                            conf->locked = 0;
02570                            if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
02571                               ast_waitstream(chan, "");
02572                         } else {
02573                            conf->locked = 1;
02574                            if (!ast_streamfile(chan, "conf-lockednow", chan->language))
02575                               ast_waitstream(chan, "");
02576                         }
02577                         break;
02578                      case '3': /* Eject last user */
02579                         menu_active = 0;
02580                         usr = AST_LIST_LAST(&conf->userlist);
02581                         if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
02582                            if (!ast_streamfile(chan, "conf-errormenu", chan->language))
02583                               ast_waitstream(chan, "");
02584                         } else 
02585                            usr->adminflags |= ADMINFLAG_KICKME;
02586                         ast_stopstream(chan);
02587                         break;   
02588                      case '4':
02589                         tweak_listen_volume(user, VOL_DOWN);
02590                         break;
02591                      case '6':
02592                         tweak_listen_volume(user, VOL_UP);
02593                         break;
02594                      case '7':
02595                         tweak_talk_volume(user, VOL_DOWN);
02596                         break;
02597                      case '8':
02598                         menu_active = 0;
02599                         break;
02600                      case '9':
02601                         tweak_talk_volume(user, VOL_UP);
02602                         break;
02603                      default:
02604                         menu_active = 0;
02605                         /* Play an error message! */
02606                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
02607                            ast_waitstream(chan, "");
02608                         break;
02609                      }
02610                   }
02611                } else {
02612                   /* User menu */
02613                   if (!menu_active) {
02614                      menu_active = 1;
02615                      if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
02616                         dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
02617                         ast_stopstream(chan);
02618                      } else
02619                         dtmf = 0;
02620                   } else 
02621                      dtmf = f->subclass;
02622                   if (dtmf) {
02623                      switch(dtmf) {
02624                      case '1': /* Un/Mute */
02625                         menu_active = 0;
02626 
02627                         /* user can only toggle the self-muted state */
02628                         user->adminflags ^= ADMINFLAG_SELFMUTED;
02629 
02630                         /* they can't override the admin mute state */
02631                         if ((confflags & CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02632                            if (!ast_streamfile(chan, "conf-muted", chan->language))
02633                               ast_waitstream(chan, "");
02634                         } else {
02635                            if (!ast_streamfile(chan, "conf-unmuted", chan->language))
02636                               ast_waitstream(chan, "");
02637                         }
02638                         break;
02639                      case '2':
02640                         menu_active = 0;
02641                         if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))
02642                            user->adminflags |= ADMINFLAG_T_REQUEST;
02643                            
02644                         if (user->adminflags & ADMINFLAG_T_REQUEST)
02645                            if (!ast_streamfile(chan, "beep", chan->language))
02646                               ast_waitstream(chan, "");
02647                         break;
02648                      case '4':
02649                         tweak_listen_volume(user, VOL_DOWN);
02650                         break;
02651                      case '6':
02652                         tweak_listen_volume(user, VOL_UP);
02653                         break;
02654                      case '7':
02655                         tweak_talk_volume(user, VOL_DOWN);
02656                         break;
02657                      case '8':
02658                         menu_active = 0;
02659                         break;
02660                      case '9':
02661                         tweak_talk_volume(user, VOL_UP);
02662                         break;
02663                      default:
02664                         menu_active = 0;
02665                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
02666                            ast_waitstream(chan, "");
02667                         break;
02668                      }
02669                   }
02670                }
02671                if (musiconhold)
02672                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
02673 
02674                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02675                   ast_log(LOG_WARNING, "Error setting conference\n");
02676                   close(fd);
02677                   ast_frfree(f);
02678                   goto outrun;
02679                }
02680 
02681                conf_flush(fd, chan);
02682             /* Since this option could absorb dtmf meant for the previous (menu), we have to check this one last */
02683             } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
02684                if (confflags & CONFFLAG_PASS_DTMF)
02685                   conf_queue_dtmf(conf, user, f);
02686 
02687                if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
02688                   ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
02689                   ret = 0;
02690                   ast_frfree(f);
02691                   break;
02692                } else {
02693                   ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
02694                }
02695             } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_KEYEXIT) && (strchr(exitkeys, f->subclass))) {
02696                pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
02697                   
02698                if (confflags & CONFFLAG_PASS_DTMF)
02699                   conf_queue_dtmf(conf, user, f);
02700                ret = 0;
02701                ast_frfree(f);
02702                break;
02703             } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
02704                && confflags & CONFFLAG_PASS_DTMF) {
02705                conf_queue_dtmf(conf, user, f);
02706             } else if ((confflags & CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
02707                switch (f->subclass) {
02708                case AST_CONTROL_HOLD:
02709                   sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
02710                   break;
02711                default:
02712                   break;
02713                }
02714             } else if (f->frametype == AST_FRAME_NULL) {
02715                /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
02716             } else {
02717                ast_debug(1, 
02718                   "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
02719                   chan->name, f->frametype, f->subclass);
02720             }
02721             ast_frfree(f);
02722          } else if (outfd > -1) {
02723             res = read(outfd, buf, CONF_SIZE);
02724             if (res > 0) {
02725                memset(&fr, 0, sizeof(fr));
02726                fr.frametype = AST_FRAME_VOICE;
02727                fr.subclass = AST_FORMAT_SLINEAR;
02728                fr.datalen = res;
02729                fr.samples = res / 2;
02730                fr.data.ptr = buf;
02731                fr.offset = AST_FRIENDLY_OFFSET;
02732                if (!user->listen.actual &&
02733                   ((confflags & CONFFLAG_MONITOR) ||
02734                    (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
02735                    (!user->talking && (confflags & CONFFLAG_OPTIMIZETALKER))
02736                    )) {
02737                   int idx;
02738                   for (idx = 0; idx < AST_FRAME_BITS; idx++)
02739                      if (chan->rawwriteformat & (1 << idx))
02740                         break;
02741                   if (idx >= AST_FRAME_BITS)
02742                      goto bailoutandtrynormal;
02743                   ast_mutex_lock(&conf->listenlock);
02744                   if (!conf->transframe[idx]) {
02745                      if (conf->origframe) {
02746                         if (!conf->transpath[idx])
02747                            conf->transpath[idx] = ast_translator_build_path((1 << idx), AST_FORMAT_SLINEAR);
02748                         if (conf->transpath[idx]) {
02749                            conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
02750                            if (!conf->transframe[idx])
02751                               conf->transframe[idx] = &ast_null_frame;
02752                         }
02753                      }
02754                   }
02755                   if (conf->transframe[idx]) {
02756                      if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
02757                          can_write(chan, confflags)) {
02758                         struct ast_frame *cur;
02759                         if (musiconhold && !ast_dsp_silence(dsp, conf->transframe[idx], &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
02760                            ast_moh_stop(chan);
02761                            mohtempstopped = 1;
02762                         }
02763 
02764                         /* the translator may have returned a list of frames, so
02765                            write each one onto the channel
02766                         */
02767                         for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
02768                            if (ast_write(chan, cur)) {
02769                               ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
02770                               break;
02771                            }
02772                         }
02773                         if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
02774                            mohtempstopped = 0;
02775                            ast_moh_start(chan, NULL, NULL);
02776                         }
02777                      }
02778                   } else {
02779                      ast_mutex_unlock(&conf->listenlock);
02780                      goto bailoutandtrynormal;
02781                   }
02782                   ast_mutex_unlock(&conf->listenlock);
02783                } else {
02784 bailoutandtrynormal:
02785                   if (musiconhold && !ast_dsp_silence(dsp, &fr, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
02786                      ast_moh_stop(chan);
02787                      mohtempstopped = 1;
02788                   }
02789                   if (user->listen.actual)
02790                      ast_frame_adjust_volume(&fr, user->listen.actual);
02791                   if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
02792                      ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name);
02793                   }
02794                   if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
02795                      mohtempstopped = 0;
02796                      ast_moh_start(chan, NULL, NULL);
02797                   }
02798                }
02799             } else 
02800                ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
02801          }
02802          lastmarked = currentmarked;
02803       }
02804    }
02805 
02806    if (musiconhold)
02807       ast_moh_stop(chan);
02808    
02809    if (using_pseudo)
02810       close(fd);
02811    else {
02812       /* Take out of conference */
02813       dahdic.chan = 0;  
02814       dahdic.confno = 0;
02815       dahdic.confmode = 0;
02816       if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
02817          ast_log(LOG_WARNING, "Error setting conference\n");
02818       }
02819    }
02820 
02821    reset_volumes(user);
02822 
02823    if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
02824       conf_play(chan, conf, LEAVE);
02825 
02826    if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) {
02827       struct announce_listitem *item;
02828       if (!(item = ao2_alloc(sizeof(*item), NULL)))
02829          return -1;
02830       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
02831       ast_copy_string(item->language, chan->language, sizeof(item->language));
02832       item->confchan = conf->chan;
02833       item->confusers = conf->users;
02834       item->announcetype = CONF_HASLEFT;
02835       ast_mutex_lock(&conf->announcelistlock);
02836       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
02837       ast_cond_signal(&conf->announcelist_addition);
02838       ast_mutex_unlock(&conf->announcelistlock);
02839    } else if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) {
02840       /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
02841       ast_filedelete(user->namerecloc, NULL);
02842    }
02843 
02844  outrun:
02845    AST_LIST_LOCK(&confs);
02846 
02847    if (dsp)
02848       ast_dsp_free(dsp);
02849    
02850    if (user->user_no) { /* Only cleanup users who really joined! */
02851       now = ast_tvnow();
02852       hr = (now.tv_sec - user->jointime) / 3600;
02853       min = ((now.tv_sec - user->jointime) % 3600) / 60;
02854       sec = (now.tv_sec - user->jointime) % 60;
02855 
02856       if (sent_event) {
02857          manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
02858                   "Channel: %s\r\n"
02859                   "Uniqueid: %s\r\n"
02860                   "Meetme: %s\r\n"
02861                   "Usernum: %d\r\n"
02862                   "CallerIDNum: %s\r\n"
02863                   "CallerIDName: %s\r\n"
02864                   "Duration: %ld\r\n",
02865                   chan->name, chan->uniqueid, conf->confno, 
02866                   user->user_no,
02867                   S_OR(user->chan->cid.cid_num, "<unknown>"),
02868                   S_OR(user->chan->cid.cid_name, "<unknown>"),
02869                   (long)(now.tv_sec - user->jointime));
02870       }
02871 
02872       if (setusercount) {
02873          conf->users--;
02874          if (rt_log_members) {
02875             /* Update table */
02876             snprintf(members, sizeof(members), "%d", conf->users);
02877             ast_realtime_require_field("meetme",
02878                "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
02879                "members", RQ_UINTEGER1, strlen(members),
02880                NULL);
02881             ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
02882          }
02883          if (confflags & CONFFLAG_MARKEDUSER) 
02884             conf->markedusers--;
02885       }
02886       /* Remove ourselves from the list */
02887       AST_LIST_REMOVE(&conf->userlist, user, list);
02888 
02889       /* Change any states */
02890       if (!conf->users)
02891          ast_devstate_changed(AST_DEVICE_NOT_INUSE, "meetme:%s", conf->confno);
02892       
02893       /* Return the number of seconds the user was in the conf */
02894       snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
02895       pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
02896    }
02897    ast_free(user);
02898    AST_LIST_UNLOCK(&confs);
02899 
02900    return ret;
02901 }

static void conf_start_moh ( struct ast_channel chan,
const char *  musicclass 
) [static]

Definition at line 1540 of file app_meetme.c.

References ast_channel_lock, ast_channel_unlock, ast_moh_start(), ast_strdupa, ast_string_field_set, and ast_channel::musicclass.

Referenced by conf_run().

01541 {
01542    char *original_moh;
01543 
01544    ast_channel_lock(chan);
01545    original_moh = ast_strdupa(chan->musicclass);
01546    ast_string_field_set(chan, musicclass, musicclass);
01547    ast_channel_unlock(chan);
01548 
01549    ast_moh_start(chan, original_moh, NULL);
01550 
01551    ast_channel_lock(chan);
01552    ast_string_field_set(chan, musicclass, original_moh);
01553    ast_channel_unlock(chan);
01554 }

static int count_exec ( struct ast_channel chan,
void *  data 
) [static]

The MeetmeCount application.

Definition at line 3139 of file app_meetme.c.

References ast_channel::_state, ast_answer(), AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_say_number(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), dispose_conf(), find_conf(), ast_channel::language, LOG_WARNING, pbx_builtin_setvar_helper(), and ast_conference::users.

Referenced by load_module().

03140 {
03141    int res = 0;
03142    struct ast_conference *conf;
03143    int count;
03144    char *localdata;
03145    char val[80] = "0"; 
03146    AST_DECLARE_APP_ARGS(args,
03147       AST_APP_ARG(confno);
03148       AST_APP_ARG(varname);
03149    );
03150 
03151    if (ast_strlen_zero(data)) {
03152       ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
03153       return -1;
03154    }
03155    
03156    if (!(localdata = ast_strdupa(data)))
03157       return -1;
03158 
03159    AST_STANDARD_APP_ARGS(args, localdata);
03160    
03161    conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
03162 
03163    if (conf) {
03164       count = conf->users;
03165       dispose_conf(conf);
03166       conf = NULL;
03167    } else
03168       count = 0;
03169 
03170    if (!ast_strlen_zero(args.varname)) {
03171       /* have var so load it and exit */
03172       snprintf(val, sizeof(val), "%d", count);
03173       pbx_builtin_setvar_helper(chan, args.varname, val);
03174    } else {
03175       if (chan->_state != AST_STATE_UP)
03176          ast_answer(chan);
03177       res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
03178    }
03179 
03180    return res;
03181 }

static struct sla_trunk_ref* create_trunk_ref ( struct sla_trunk trunk  )  [static, read]

Definition at line 5195 of file app_meetme.c.

References ast_calloc, and sla_trunk_ref::trunk.

Referenced by sla_add_trunk_to_station().

05196 {
05197    struct sla_trunk_ref *trunk_ref;
05198 
05199    if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
05200       return NULL;
05201 
05202    trunk_ref->trunk = trunk;
05203 
05204    return trunk_ref;
05205 }

static void destroy_station ( struct sla_station station  )  [static]

Definition at line 5406 of file app_meetme.c.

References ast_context_remove_extension(), ast_free, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, AST_MAX_APP, AST_MAX_EXTENSION, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK, ast_string_field_free_memory, ast_strlen_zero(), sla_station::autocontext, exten, sla_trunk::name, sla_station::name, PRIORITY_HINT, sla_registrar, sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by sla_build_station(), and sla_destroy().

05407 {
05408    struct sla_trunk_ref *trunk_ref;
05409 
05410    if (!ast_strlen_zero(station->autocontext)) {
05411       AST_RWLIST_RDLOCK(&sla_trunks);
05412       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05413          char exten[AST_MAX_EXTENSION];
05414          char hint[AST_MAX_APP];
05415          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
05416          snprintf(hint, sizeof(hint), "SLA:%s", exten);
05417          ast_context_remove_extension(station->autocontext, exten, 
05418             1, sla_registrar);
05419          ast_context_remove_extension(station->autocontext, hint, 
05420             PRIORITY_HINT, sla_registrar);
05421       }
05422       AST_RWLIST_UNLOCK(&sla_trunks);
05423    }
05424 
05425    while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
05426       ast_free(trunk_ref);
05427 
05428    ast_string_field_free_memory(station);
05429    ast_free(station);
05430 }

static void destroy_trunk ( struct sla_trunk trunk  )  [static]

Definition at line 5392 of file app_meetme.c.

References ast_context_remove_extension(), ast_free, AST_LIST_REMOVE_HEAD, ast_string_field_free_memory, ast_strlen_zero(), sla_trunk::autocontext, sla_registrar, and sla_trunk::stations.

Referenced by sla_build_trunk(), and sla_destroy().

05393 {
05394    struct sla_station_ref *station_ref;
05395 
05396    if (!ast_strlen_zero(trunk->autocontext))
05397       ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
05398 
05399    while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
05400       ast_free(station_ref);
05401 
05402    ast_string_field_free_memory(trunk);
05403    ast_free(trunk);
05404 }

static void* dial_trunk ( void *  data  )  [static]

Definition at line 4907 of file app_meetme.c.

References ALL_TRUNK_REFS, ast_cond_signal(), ast_dial_answered(), ast_dial_append(), ast_dial_create(), ast_dial_destroy(), ast_dial_join(), AST_DIAL_RESULT_ANSWERED, AST_DIAL_RESULT_FAILED, AST_DIAL_RESULT_HANGUP, AST_DIAL_RESULT_INVALID, AST_DIAL_RESULT_PROCEEDING, AST_DIAL_RESULT_PROGRESS, AST_DIAL_RESULT_RINGING, AST_DIAL_RESULT_TIMEOUT, AST_DIAL_RESULT_TRYING, AST_DIAL_RESULT_UNANSWERED, ast_dial_run(), ast_dial_state(), ast_free, ast_mutex_lock(), ast_mutex_unlock(), ast_set_flag, ast_strdup, ast_strdupa, ast_strlen_zero(), build_conf(), sla_trunk::chan, sla_trunk_ref::chan, ast_channel::cid, ast_callerid::cid_name, cid_name, ast_callerid::cid_num, cid_num, dial_trunk_args::cond, dial_trunk_args::cond_lock, conf_run(), CONFFLAG_MARKEDEXIT, CONFFLAG_MARKEDUSER, CONFFLAG_PASS_DTMF, CONFFLAG_QUIET, CONFFLAG_SLA_TRUNK, sla_trunk::device, dispose_conf(), ast_flags::flags, MAX_CONFNUM, sla_trunk::name, sla_trunk::on_hold, sla, sla_change_trunk_state(), SLA_TRUNK_STATE_IDLE, strsep(), sla_trunk_ref::trunk, and dial_trunk_args::trunk_ref.

Referenced by sla_station_exec().

04908 {
04909    struct dial_trunk_args *args = data;
04910    struct ast_dial *dial;
04911    char *tech, *tech_data;
04912    enum ast_dial_result dial_res;
04913    char conf_name[MAX_CONFNUM];
04914    struct ast_conference *conf;
04915    struct ast_flags conf_flags = { 0 };
04916    struct sla_trunk_ref *trunk_ref = args->trunk_ref;
04917    const char *cid_name = NULL, *cid_num = NULL;
04918 
04919    if (!(dial = ast_dial_create())) {
04920       ast_mutex_lock(args->cond_lock);
04921       ast_cond_signal(args->cond);
04922       ast_mutex_unlock(args->cond_lock);
04923       return NULL;
04924    }
04925 
04926    tech_data = ast_strdupa(trunk_ref->trunk->device);
04927    tech = strsep(&tech_data, "/");
04928    if (ast_dial_append(dial, tech, tech_data) == -1) {
04929       ast_mutex_lock(args->cond_lock);
04930       ast_cond_signal(args->cond);
04931       ast_mutex_unlock(args->cond_lock);
04932       ast_dial_destroy(dial);
04933       return NULL;
04934    }
04935 
04936    if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_name)) {
04937       cid_name = ast_strdupa(trunk_ref->chan->cid.cid_name);
04938       ast_free(trunk_ref->chan->cid.cid_name);
04939       trunk_ref->chan->cid.cid_name = NULL;
04940    }
04941    if (!sla.attempt_callerid && !ast_strlen_zero(trunk_ref->chan->cid.cid_num)) {
04942       cid_num = ast_strdupa(trunk_ref->chan->cid.cid_num);
04943       ast_free(trunk_ref->chan->cid.cid_num);
04944       trunk_ref->chan->cid.cid_num = NULL;
04945    }
04946 
04947    dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
04948 
04949    if (cid_name)
04950       trunk_ref->chan->cid.cid_name = ast_strdup(cid_name);
04951    if (cid_num)
04952       trunk_ref->chan->cid.cid_num = ast_strdup(cid_num);
04953 
04954    if (dial_res != AST_DIAL_RESULT_TRYING) {
04955       ast_mutex_lock(args->cond_lock);
04956       ast_cond_signal(args->cond);
04957       ast_mutex_unlock(args->cond_lock);
04958       ast_dial_destroy(dial);
04959       return NULL;
04960    }
04961 
04962    for (;;) {
04963       unsigned int done = 0;
04964       switch ((dial_res = ast_dial_state(dial))) {
04965       case AST_DIAL_RESULT_ANSWERED:
04966          trunk_ref->trunk->chan = ast_dial_answered(dial);
04967       case AST_DIAL_RESULT_HANGUP:
04968       case AST_DIAL_RESULT_INVALID:
04969       case AST_DIAL_RESULT_FAILED:
04970       case AST_DIAL_RESULT_TIMEOUT:
04971       case AST_DIAL_RESULT_UNANSWERED:
04972          done = 1;
04973       case AST_DIAL_RESULT_TRYING:
04974       case AST_DIAL_RESULT_RINGING:
04975       case AST_DIAL_RESULT_PROGRESS:
04976       case AST_DIAL_RESULT_PROCEEDING:
04977          break;
04978       }
04979       if (done)
04980          break;
04981    }
04982 
04983    if (!trunk_ref->trunk->chan) {
04984       ast_mutex_lock(args->cond_lock);
04985       ast_cond_signal(args->cond);
04986       ast_mutex_unlock(args->cond_lock);
04987       ast_dial_join(dial);
04988       ast_dial_destroy(dial);
04989       return NULL;
04990    }
04991 
04992    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
04993    ast_set_flag(&conf_flags, 
04994       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | 
04995       CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
04996    conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan);
04997 
04998    ast_mutex_lock(args->cond_lock);
04999    ast_cond_signal(args->cond);
05000    ast_mutex_unlock(args->cond_lock);
05001 
05002    if (conf) {
05003       conf_run(trunk_ref->trunk->chan, conf, conf_flags.flags, NULL);
05004       dispose_conf(conf);
05005       conf = NULL;
05006    }
05007 
05008    /* If the trunk is going away, it is definitely now IDLE. */
05009    sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
05010 
05011    trunk_ref->trunk->chan = NULL;
05012    trunk_ref->trunk->on_hold = 0;
05013 
05014    ast_dial_join(dial);
05015    ast_dial_destroy(dial);
05016 
05017    return NULL;
05018 }

static int dispose_conf ( struct ast_conference conf  )  [static]

Definition at line 1522 of file app_meetme.c.

References ast_atomic_dec_and_test(), AST_LIST_LOCK, AST_LIST_UNLOCK, conf_free(), conf_map, ast_conference::confno, and ast_conference::refcount.

Referenced by admin_exec(), conf_exec(), count_exec(), dial_trunk(), run_station(), sla_station_exec(), and sla_trunk_exec().

01523 {
01524    int res = 0;
01525    int confno_int = 0;
01526 
01527    AST_LIST_LOCK(&confs);
01528    if (ast_atomic_dec_and_test(&conf->refcount)) {
01529       /* Take the conference room number out of an inuse state */
01530       if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
01531          conf_map[confno_int] = 0;
01532       conf_free(conf);
01533       res = 1;
01534    }
01535    AST_LIST_UNLOCK(&confs);
01536 
01537    return res;
01538 }

static struct ast_conference* find_conf ( struct ast_channel chan,
char *  confno,
int  make,
int  dynamic,
char *  dynamic_pin,
size_t  pin_buf_len,
int  refcount,
struct ast_flags confflags 
) [static, read]

Definition at line 3041 of file app_meetme.c.

References AST_APP_ARG, ast_app_getdata(), ast_clear_flag, ast_config_destroy(), ast_config_load, ast_debug, AST_DECLARE_APP_ARGS, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_test_flag, ast_variable_browse(), build_conf(), ast_conference::chan, CONFFLAG_INTROUSER, CONFFLAG_INTROUSERNOREVIEW, CONFFLAG_QUIET, CONFFLAG_RECORDCONF, CONFIG_FILE_NAME, ast_conference::confno, ast_conference::list, LOG_WARNING, ast_variable::name, ast_variable::next, parse(), ast_conference::refcount, S_OR, ast_variable::value, and var.

Referenced by conf_exec(), and count_exec().

03043 {
03044    struct ast_config *cfg;
03045    struct ast_variable *var;
03046    struct ast_flags config_flags = { 0 };
03047    struct ast_conference *cnf;
03048    char *parse;
03049    AST_DECLARE_APP_ARGS(args,
03050       AST_APP_ARG(confno);
03051       AST_APP_ARG(pin);
03052       AST_APP_ARG(pinadmin);
03053    );
03054 
03055    /* Check first in the conference list */
03056    ast_debug(1, "The requested confno is '%s'?\n", confno);
03057    AST_LIST_LOCK(&confs);
03058    AST_LIST_TRAVERSE(&confs, cnf, list) {
03059       ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
03060       if (!strcmp(confno, cnf->confno)) 
03061          break;
03062    }
03063    if (cnf) {
03064       cnf->refcount += refcount;
03065    }
03066    AST_LIST_UNLOCK(&confs);
03067 
03068    if (!cnf) {
03069       if (dynamic) {
03070          /* No need to parse meetme.conf */
03071          ast_debug(1, "Building dynamic conference '%s'\n", confno);
03072          if (dynamic_pin) {
03073             if (dynamic_pin[0] == 'q') {
03074                /* Query the user to enter a PIN */
03075                if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
03076                   return NULL;
03077             }
03078             cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan);
03079          } else {
03080             cnf = build_conf(confno, "", "", make, dynamic, refcount, chan);
03081          }
03082       } else {
03083          /* Check the config */
03084          cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
03085          if (!cfg) {
03086             ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
03087             return NULL;
03088          }
03089          for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
03090             if (strcasecmp(var->name, "conf"))
03091                continue;
03092             
03093             if (!(parse = ast_strdupa(var->value)))
03094                return NULL;
03095             
03096             AST_STANDARD_APP_ARGS(args, parse);
03097             ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
03098             if (!strcasecmp(args.confno, confno)) {
03099                /* Bingo it's a valid conference */
03100                cnf = build_conf(args.confno,
03101                      S_OR(args.pin, ""),
03102                      S_OR(args.pinadmin, ""),
03103                      make, dynamic, refcount, chan);
03104                break;
03105             }
03106          }
03107          if (!var) {
03108             ast_debug(1, "%s isn't a valid conference\n", confno);
03109          }
03110          ast_config_destroy(cfg);
03111       }
03112    } else if (dynamic_pin) {
03113       /* Correct for the user selecting 'D' instead of 'd' to have
03114          someone join into a conference that has already been created
03115          with a pin. */
03116       if (dynamic_pin[0] == 'q')
03117          dynamic_pin[0] = '\0';
03118    }
03119 
03120    if (cnf) {
03121       if (confflags && !cnf->chan &&
03122           !ast_test_flag(confflags, CONFFLAG_QUIET) &&
03123           ast_test_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
03124          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
03125          ast_clear_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
03126       }
03127       
03128       if (confflags && !cnf->chan &&
03129           ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
03130          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
03131          ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
03132       }
03133    }
03134 
03135    return cnf;
03136 }

static struct ast_conference* find_conf_realtime ( struct ast_channel chan,
char *  confno,
int  make,
int  dynamic,
char *  dynamic_pin,
size_t  pin_buf_len,
int  refcount,
struct ast_flags confflags,
char *  optargs[],
int *  too_early 
) [static, read]

Definition at line 2903 of file app_meetme.c.

References ast_clear_flag, ast_copy_string(), ast_debug, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime(), ast_localtime(), ast_log(), ast_mktime(), ast_strdupa, ast_streamfile(), ast_strftime(), ast_test_flag, ast_tvnow(), ast_variables_destroy(), ast_waitstream(), build_conf(), ast_conference::chan, CONFFLAG_INTROUSER, CONFFLAG_INTROUSERNOREVIEW, CONFFLAG_QUIET, CONFFLAG_RECORDCONF, ast_conference::confno, DATE_FORMAT, ast_conference::endalert, ast_conference::endtime, ast_channel::language, ast_conference::list, LOG_WARNING, ast_conference::maxusers, ast_variable::name, ast_variable::next, ast_conference::refcount, ast_variable::value, and var.

Referenced by conf_exec().

02906 {
02907    struct ast_variable *var;
02908    struct ast_conference *cnf;
02909 
02910    *too_early = 0;
02911 
02912    /* Check first in the conference list */
02913    AST_LIST_LOCK(&confs);
02914    AST_LIST_TRAVERSE(&confs, cnf, list) {
02915       if (!strcmp(confno, cnf->confno)) 
02916          break;
02917    }
02918    if (cnf) {
02919       cnf->refcount += refcount;
02920    }
02921    AST_LIST_UNLOCK(&confs);
02922 
02923    if (!cnf) {
02924       char *pin = NULL, *pinadmin = NULL; /* For temp use */
02925       int maxusers = 0;
02926       struct timeval now;
02927       char currenttime[19] = "";
02928       char eatime[19] = "";
02929       char useropts[32] = "";
02930       char adminopts[32] = "";
02931       struct ast_tm tm, etm;
02932       struct timeval endtime = { .tv_sec = 0 };
02933 
02934       if (rt_schedule) {
02935          now = ast_tvnow();
02936 
02937          ast_localtime(&now, &tm, NULL);
02938          ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02939 
02940          ast_debug(1, "Looking for conference %s that starts after %s\n", confno, eatime);
02941 
02942          var = ast_load_realtime("meetme", "confno",
02943             confno, "starttime <= ", currenttime, "endtime >= ",
02944             currenttime, NULL);
02945 
02946          if (!var && fuzzystart) {
02947             now = ast_tvnow();
02948             now.tv_sec += fuzzystart;
02949 
02950             ast_localtime(&now, &tm, NULL);
02951             ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
02952             var = ast_load_realtime("meetme", "confno",
02953                confno, "starttime <= ", currenttime, "endtime >= ",
02954                currenttime, NULL);
02955          }
02956 
02957          if (!var && earlyalert) {
02958             now = ast_tvnow();
02959             now.tv_sec += earlyalert;
02960             ast_localtime(&now, &etm, NULL);
02961             ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
02962             var = ast_load_realtime("meetme", "confno",
02963                confno, "starttime <= ", eatime, "endtime >= ",
02964                currenttime, NULL);
02965             if (var)
02966                *too_early = 1;
02967          }
02968 
02969       } else
02970           var = ast_load_realtime("meetme", "confno", confno, NULL);
02971 
02972       if (!var)
02973          return NULL;
02974 
02975       if (rt_schedule && *too_early) {
02976          /* Announce that the caller is early and exit */
02977          if (!ast_streamfile(chan, "conf-has-not-started", chan->language))
02978             ast_waitstream(chan, "");
02979          ast_variables_destroy(var);
02980          return NULL;
02981       }
02982 
02983       while (var) {
02984          if (!strcasecmp(var->name, "pin")) {
02985             pin = ast_strdupa(var->value);
02986          } else if (!strcasecmp(var->name, "adminpin")) {
02987             pinadmin = ast_strdupa(var->value);
02988          } else if (!strcasecmp(var->name, "opts")) {
02989             ast_copy_string(useropts, var->value, sizeof(useropts));
02990          } else if (!strcasecmp(var->name, "maxusers")) {
02991             maxusers = atoi(var->value);
02992          } else if (!strcasecmp(var->name, "adminopts")) {
02993             ast_copy_string(adminopts, var->value, sizeof(adminopts));
02994          } else if (!strcasecmp(var->name, "endtime")) {
02995             union {
02996                struct ast_tm atm;
02997                struct tm tm;
02998             } t = { { 0, }, };
02999             strptime(var->value, "%Y-%m-%d %H:%M:%S", &t.tm);
03000             /* strptime does not determine if a time is
03001              * in DST or not.  Set tm_isdst to -1 to 
03002              * allow ast_mktime to adjust for DST 
03003              * if needed */
03004             t.tm.tm_isdst = -1; 
03005             endtime = ast_mktime(&t.atm, NULL);
03006          }
03007 
03008          var = var->next;
03009       }
03010 
03011       ast_variables_destroy(var);
03012 
03013       cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan);
03014 
03015       if (cnf) {
03016          cnf->maxusers = maxusers;
03017          cnf->endalert = endalert;
03018          cnf->endtime = endtime.tv_sec;
03019       }
03020    }
03021 
03022    if (cnf) {
03023       if (confflags && !cnf->chan &&
03024           !ast_test_flag(confflags, CONFFLAG_QUIET) &&
03025           ast_test_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW)) {
03026          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
03027          ast_clear_flag(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW);
03028       }
03029       
03030       if (confflags && !cnf->chan &&
03031           ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
03032          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
03033          ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
03034       }
03035    }
03036 
03037    return cnf;
03038 }

static struct ast_conf_user* find_user ( struct ast_conference conf,
char *  callerident 
) [static, read]

Definition at line 3431 of file app_meetme.c.

References AST_LIST_TRAVERSE, ast_conf_user::list, ast_conf_user::user_no, and ast_conference::userlist.

03432 {
03433    struct ast_conf_user *user = NULL;
03434    int cid;
03435    
03436    sscanf(callerident, "%30i", &cid);
03437    if (conf && callerident) {
03438       AST_LIST_TRAVERSE(&conf->userlist, user, list) {
03439          if (cid == user->user_no)
03440             return user;
03441       }
03442    }
03443    return NULL;
03444 }

static const char* get_announce_filename ( enum announcetypes  type  )  [static]

Definition at line 1556 of file app_meetme.c.

References CONF_HASJOIN, and CONF_HASLEFT.

Referenced by announce_thread().

01557 {
01558    switch (type) {
01559    case CONF_HASLEFT:
01560       return "conf-hasleft";
01561       break;
01562    case CONF_HASJOIN:
01563       return "conf-hasjoin";
01564       break;
01565    default:
01566       return "";
01567    }
01568 }

static char* istalking ( int  x  )  [static]

Definition at line 652 of file app_meetme.c.

Referenced by meetme_cmd().

00653 {
00654    if (x > 0)
00655       return "(talking)";
00656    else if (x < 0)
00657       return "(unmonitored)";
00658    else 
00659       return "(not talking)";
00660 }

static int load_config ( int  reload  )  [static]

Definition at line 5848 of file app_meetme.c.

References ast_log(), load_config_meetme(), LOG_NOTICE, SLA_EVENT_RELOAD, sla_load_config(), and sla_queue_event().

05849 {
05850    load_config_meetme();
05851 
05852    if (reload) {
05853       sla_queue_event(SLA_EVENT_RELOAD);
05854       ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
05855          "and will be completed when the system is idle.\n");
05856       return 0;
05857    }
05858    
05859    return sla_load_config(0);
05860 }

static void load_config_meetme ( void   )  [static]

Definition at line 3890 of file app_meetme.c.

References ast_config_destroy(), ast_config_load, ast_log(), ast_true(), ast_variable_retrieve(), CONFIG_FILE_NAME, DEFAULT_AUDIO_BUFFERS, LOG_NOTICE, and LOG_WARNING.

Referenced by load_config().

03891 {
03892    struct ast_config *cfg;
03893    struct ast_flags config_flags = { 0 };
03894    const char *val;
03895 
03896    if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags)))
03897       return;
03898 
03899    audio_buffers = DEFAULT_AUDIO_BUFFERS;
03900 
03901    /*  Scheduling support is off by default */
03902    rt_schedule = 0;
03903    fuzzystart = 0;
03904    earlyalert = 0;
03905    endalert = 0;
03906 
03907    /*  Logging of participants defaults to ON for compatibility reasons */
03908    rt_log_members = 1;  
03909 
03910    if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
03911       if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
03912          ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
03913          audio_buffers = DEFAULT_AUDIO_BUFFERS;
03914       } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
03915          ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
03916             DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
03917          audio_buffers = DEFAULT_AUDIO_BUFFERS;
03918       }
03919       if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
03920          ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
03921    }
03922 
03923    if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
03924       rt_schedule = ast_true(val);
03925    if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
03926       rt_log_members = ast_true(val);
03927    if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
03928       if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
03929          ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
03930          fuzzystart = 0;
03931       } 
03932    }
03933    if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
03934       if ((sscanf(val, "%30d", &earlyalert) != 1)) {
03935          ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
03936          earlyalert = 0;
03937       } 
03938    }
03939    if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
03940       if ((sscanf(val, "%30d", &endalert) != 1)) {
03941          ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
03942          endalert = 0;
03943       } 
03944    }
03945 
03946    ast_config_destroy(cfg);
03947 }

static int load_module ( void   )  [static]

Definition at line 5888 of file app_meetme.c.

References action_meetmelist(), action_meetmemute(), action_meetmeunmute(), admin_exec(), ARRAY_LEN, ast_cli_register_multiple(), ast_custom_function_register, ast_devstate_prov_add(), ast_manager_register, ast_manager_register2(), ast_realtime_require_field(), ast_register_application, channel_admin_exec(), conf_exec(), count_exec(), EVENT_FLAG_CALL, EVENT_FLAG_REPORTING, load_config(), meetmestate(), RQ_UINTEGER1, RQ_UINTEGER2, sla_state(), sla_station_exec(), and sla_trunk_exec().

05889 {
05890    int res = 0;
05891 
05892    res |= load_config(0);
05893 
05894    ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
05895    res |= ast_manager_register("MeetmeMute", EVENT_FLAG_CALL, 
05896                 action_meetmemute, "Mute a Meetme user");
05897    res |= ast_manager_register("MeetmeUnmute", EVENT_FLAG_CALL, 
05898                 action_meetmeunmute, "Unmute a Meetme user");
05899    res |= ast_manager_register2("MeetmeList", EVENT_FLAG_REPORTING, 
05900                 action_meetmelist, "List participants in a conference", mandescr_meetmelist);
05901    res |= ast_register_application(app4, channel_admin_exec, synopsis4, descrip4);
05902    res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
05903    res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
05904    res |= ast_register_application(app, conf_exec, synopsis, descrip);
05905    res |= ast_register_application(slastation_app, sla_station_exec,
05906                slastation_synopsis, slastation_desc);
05907    res |= ast_register_application(slatrunk_app, sla_trunk_exec,
05908                slatrunk_synopsis, slatrunk_desc);
05909 
05910    res |= ast_devstate_prov_add("Meetme", meetmestate);
05911    res |= ast_devstate_prov_add("SLA", sla_state);
05912 
05913    res |= ast_custom_function_register(&meetme_info_acf);
05914    ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
05915 
05916    return res;
05917 }

static char* meetme_cmd ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 978 of file app_meetme.c.

References admin_exec(), ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ADMINFLAG_T_REQUEST, ast_conf_user::adminflags, ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_debug, ast_free, AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_str_append(), ast_str_create(), ast_str_set(), ast_conf_user::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_meetmecmd(), CONFFLAG_ADMIN, CONFFLAG_MONITOR, ast_conference::confno, ast_cli_args::fd, ast_conference::isdynamic, istalking(), ast_conf_user::jointime, ast_cli_args::line, ast_conference::locked, ast_conference::markedusers, MAX_CONFNUM, MC_DATA_FORMAT, MC_HEADER_FORMAT, ast_cli_args::n, ast_channel::name, ast_cli_args::pos, S_OR, sec, ast_conference::start, ast_str::str, ast_conf_user::talking, total, ast_cli_entry::usage, ast_conf_user::user_no, ast_conf_user::userflags, ast_conference::userlist, ast_conference::users, and ast_cli_args::word.

00979 {
00980    /* Process the command */
00981    struct ast_conference *cnf;
00982    struct ast_conf_user *user;
00983    int hr, min, sec;
00984    int i = 0, total = 0;
00985    time_t now;
00986    struct ast_str *cmdline = NULL;
00987 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s  %-8s  %-6s\n"
00988 #define MC_DATA_FORMAT "%-12.12s   %4.4d        %4.4s       %02d:%02d:%02d  %-8s  %-6s\n"
00989 
00990    switch (cmd) {
00991    case CLI_INIT:
00992       e->command = "meetme";
00993       e->usage =
00994          "Usage: meetme (un)lock|(un)mute|kick|list [concise] <confno> <usernumber>\n"
00995          "       Executes a command for the conference or on a conferee\n";
00996       return NULL;
00997    case CLI_GENERATE:
00998       return complete_meetmecmd(a->line, a->word, a->pos, a->n);
00999    }
01000 
01001    if (a->argc > 8)
01002       ast_cli(a->fd, "Invalid Arguments.\n");
01003    /* Check for length so no buffer will overflow... */
01004    for (i = 0; i < a->argc; i++) {
01005       if (strlen(a->argv[i]) > 100)
01006          ast_cli(a->fd, "Invalid Arguments.\n");
01007    }
01008 
01009    /* Max confno length */
01010    if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
01011       return CLI_FAILURE;
01012    }
01013 
01014    if (a->argc == 1 || (a->argc == 2 && !strcasecmp(a->argv[1], "concise"))) {
01015       /* 'MeetMe': List all the conferences */  
01016       int concise = (a->argc == 2 && !strcasecmp(a->argv[1], "concise"));
01017       now = time(NULL);
01018       AST_LIST_LOCK(&confs);
01019       if (AST_LIST_EMPTY(&confs)) {
01020          if (!concise) {
01021             ast_cli(a->fd, "No active MeetMe conferences.\n");
01022          }
01023          AST_LIST_UNLOCK(&confs);
01024          ast_free(cmdline);
01025          return CLI_SUCCESS;
01026       }
01027       if (!concise) {
01028          ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
01029       }
01030       AST_LIST_TRAVERSE(&confs, cnf, list) {
01031          if (cnf->markedusers == 0) {
01032             ast_str_set(&cmdline, 0, "N/A ");
01033          } else {
01034             ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
01035          }
01036          hr = (now - cnf->start) / 3600;
01037          min = ((now - cnf->start) % 3600) / 60;
01038          sec = (now - cnf->start) % 60;
01039          if (!concise) {
01040             ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, cmdline->str, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
01041          } else {
01042             ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
01043                cnf->confno,
01044                cnf->users,
01045                cnf->markedusers,
01046                hr, min, sec,
01047                cnf->isdynamic,
01048                cnf->locked);
01049          }
01050 
01051          total += cnf->users;
01052       }
01053       AST_LIST_UNLOCK(&confs);
01054       if (!concise) {
01055          ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
01056       }
01057       ast_free(cmdline);
01058       return CLI_SUCCESS;
01059    }
01060    if (a->argc < 3) {
01061       ast_free(cmdline);
01062       return CLI_SHOWUSAGE;
01063    }
01064 
01065    ast_str_set(&cmdline, 0, "%s", a->argv[2]);  /* Argv 2: conference number */
01066    if (strstr(a->argv[1], "lock")) {
01067       if (strcmp(a->argv[1], "lock") == 0) {
01068          /* Lock */
01069          ast_str_append(&cmdline, 0, ",L");
01070       } else {
01071          /* Unlock */
01072          ast_str_append(&cmdline, 0, ",l");
01073       }
01074    } else if (strstr(a->argv[1], "mute")) { 
01075       if (a->argc < 4) {
01076          ast_free(cmdline);
01077          return CLI_SHOWUSAGE;
01078       }
01079       if (strcmp(a->argv[1], "mute") == 0) {
01080          /* Mute */
01081          if (strcmp(a->argv[3], "all") == 0) {
01082             ast_str_append(&cmdline, 0, ",N");
01083          } else {
01084             ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);  
01085          }
01086       } else {
01087          /* Unmute */
01088          if (strcmp(a->argv[3], "all") == 0) {
01089             ast_str_append(&cmdline, 0, ",n");
01090          } else {
01091             ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
01092          }
01093       }
01094    } else if (strcmp(a->argv[1], "kick") == 0) {
01095       if (a->argc < 4) {
01096          ast_free(cmdline);
01097          return CLI_SHOWUSAGE;
01098       }
01099       if (strcmp(a->argv[3], "all") == 0) {
01100          /* Kick all */
01101          ast_str_append(&cmdline, 0, ",K");
01102       } else {
01103          /* Kick a single user */
01104          ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
01105       }
01106    } else if (strcmp(a->argv[1], "list") == 0) {
01107       int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
01108       /* List all the users in a conference */
01109       if (AST_LIST_EMPTY(&confs)) {
01110          if (!concise) {
01111             ast_cli(a->fd, "No active conferences.\n");
01112          }
01113          ast_free(cmdline);
01114          return CLI_SUCCESS;  
01115       }
01116       /* Find the right conference */
01117       AST_LIST_LOCK(&confs);
01118       AST_LIST_TRAVERSE(&confs, cnf, list) {
01119          if (strcmp(cnf->confno, a->argv[2]) == 0) {
01120             break;
01121          }
01122       }
01123       if (!cnf) {
01124          if (!concise)
01125             ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
01126          AST_LIST_UNLOCK(&confs);
01127          ast_free(cmdline);
01128          return CLI_SUCCESS;
01129       }
01130       /* Show all the users */
01131       time(&now);
01132       AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
01133          hr = (now - user->jointime) / 3600;
01134          min = ((now - user->jointime) % 3600) / 60;
01135          sec = (now - user->jointime) % 60;
01136          if (!concise) {
01137             ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
01138                user->user_no,
01139                S_OR(user->chan->cid.cid_num, "<unknown>"),
01140                S_OR(user->chan->cid.cid_name, "<no name>"),
01141                user->chan->name,
01142                user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
01143                user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
01144                user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
01145                user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
01146                istalking(user->talking), hr, min, sec); 
01147          } else {
01148             ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
01149                user->user_no,
01150                S_OR(user->chan->cid.cid_num, ""),
01151                S_OR(user->chan->cid.cid_name, ""),
01152                user->chan->name,
01153                user->userflags  & CONFFLAG_ADMIN   ? "1" : "",
01154                user->userflags  & CONFFLAG_MONITOR ? "1" : "",
01155                user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
01156                user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
01157                user->talking, hr, min, sec);
01158          }
01159       }
01160       if (!concise) {
01161          ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
01162       }
01163       AST_LIST_UNLOCK(&confs);
01164       ast_free(cmdline);
01165       return CLI_SUCCESS;
01166    } else {
01167       ast_free(cmdline);
01168       return CLI_SHOWUSAGE;
01169    }
01170 
01171    ast_debug(1, "Cmdline: %s\n", cmdline->str);
01172 
01173    admin_exec(NULL, cmdline->str);
01174    ast_free(cmdline);
01175 
01176    return CLI_SUCCESS;
01177 }

static int meetmemute ( struct mansession s,
const struct message m,
int  mute 
) [static]

Definition at line 3657 of file app_meetme.c.

References ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ADMINFLAG_T_REQUEST, ast_conf_user::adminflags, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strdupa, ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), ast_conf_user::chan, ast_conference::confno, ast_conf_user::list, LOG_NOTICE, ast_channel::name, ast_channel::uniqueid, ast_conf_user::user_no, and ast_conference::userlist.

Referenced by action_meetmemute(), and action_meetmeunmute().

03658 {
03659    struct ast_conference *conf;
03660    struct ast_conf_user *user;
03661    const char *confid = astman_get_header(m, "Meetme");
03662    char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
03663    int userno;
03664 
03665    if (ast_strlen_zero(confid)) {
03666       astman_send_error(s, m, "Meetme conference not specified");
03667       return 0;
03668    }
03669 
03670    if (ast_strlen_zero(userid)) {
03671       astman_send_error(s, m, "Meetme user number not specified");
03672       return 0;
03673    }
03674 
03675    userno = strtoul(userid, &userid, 10);
03676 
03677    if (*userid) {
03678       astman_send_error(s, m, "Invalid user number");
03679       return 0;
03680    }
03681 
03682    /* Look in the conference list */
03683    AST_LIST_LOCK(&confs);
03684    AST_LIST_TRAVERSE(&confs, conf, list) {
03685       if (!strcmp(confid, conf->confno))
03686          break;
03687    }
03688 
03689    if (!conf) {
03690       AST_LIST_UNLOCK(&confs);
03691       astman_send_error(s, m, "Meetme conference does not exist");
03692       return 0;
03693    }
03694 
03695    AST_LIST_TRAVERSE(&conf->userlist, user, list)
03696       if (user->user_no == userno)
03697          break;
03698 
03699    if (!user) {
03700       AST_LIST_UNLOCK(&confs);
03701       astman_send_error(s, m, "User number not found");
03702       return 0;
03703    }
03704 
03705    if (mute)
03706       user->adminflags |= ADMINFLAG_MUTED;   /* request user muting */
03707    else
03708       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST); /* request user unmuting */
03709 
03710    AST_LIST_UNLOCK(&confs);
03711 
03712    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);
03713 
03714    astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
03715    return 0;
03716 }

static enum ast_device_state meetmestate ( const char *  data  )  [static]

Callback for devicestate providers.

Definition at line 3868 of file app_meetme.c.

References AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_conference::confno, ast_conference::list, and ast_conference::users.

Referenced by load_module().

03869 {
03870    struct ast_conference *conf;
03871 
03872    /* Find conference */
03873    AST_LIST_LOCK(&confs);
03874    AST_LIST_TRAVERSE(&confs, conf, list) {
03875       if (!strcmp(data, conf->confno))
03876          break;
03877    }
03878    AST_LIST_UNLOCK(&confs);
03879    if (!conf)
03880       return AST_DEVICE_INVALID;
03881 
03882 
03883    /* SKREP to fill */
03884    if (!conf->users)
03885       return AST_DEVICE_NOT_INUSE;
03886 
03887    return AST_DEVICE_INUSE;
03888 }

static struct sla_ringing_trunk* queue_ringing_trunk ( struct sla_trunk trunk  )  [static, read]

Definition at line 5207 of file app_meetme.c.

References ALL_TRUNK_REFS, ast_calloc, AST_LIST_INSERT_HEAD, ast_mutex_lock(), ast_mutex_unlock(), ast_tvnow(), sla_ringing_trunk::ring_begin, sla, sla_change_trunk_state(), SLA_EVENT_RINGING_TRUNK, sla_queue_event(), SLA_TRUNK_STATE_RINGING, and sla_ringing_trunk::trunk.

Referenced by sla_trunk_exec().

05208 {
05209    struct sla_ringing_trunk *ringing_trunk;
05210 
05211    if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
05212       return NULL;
05213    
05214    ringing_trunk->trunk = trunk;
05215    ringing_trunk->ring_begin = ast_tvnow();
05216 
05217    sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);
05218 
05219    ast_mutex_lock(&sla.lock);
05220    AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
05221    ast_mutex_unlock(&sla.lock);
05222 
05223    sla_queue_event(SLA_EVENT_RINGING_TRUNK);
05224 
05225    return ringing_trunk;
05226 }

static void * recordthread ( void *  args  )  [static]

Definition at line 3803 of file app_meetme.c.

References ast_closestream(), AST_FILE_MODE, AST_FRAME_BITS, AST_FRAME_VOICE, ast_frdup(), ast_frfree, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_stopstream(), ast_waitfor(), ast_writefile(), ast_writestream(), f, ast_frame::flags, ast_frame::frametype, ast_conference::lchan, ast_conference::listenlock, MEETME_RECORD_ACTIVE, MEETME_RECORD_OFF, MEETME_RECORD_TERMINATE, ast_conference::origframe, ast_conference::recordingfilename, ast_conference::recordingformat, s, and ast_conference::transframe.

03804 {
03805    struct ast_conference *cnf = args;
03806    struct ast_frame *f = NULL;
03807    int flags;
03808    struct ast_filestream *s = NULL;
03809    int res = 0;
03810    int x;
03811    const char *oldrecordingfilename = NULL;
03812 
03813    if (!cnf || !cnf->lchan) {
03814       pthread_exit(0);
03815    }
03816 
03817    ast_stopstream(cnf->lchan);
03818    flags = O_CREAT | O_TRUNC | O_WRONLY;
03819 
03820 
03821    cnf->recording = MEETME_RECORD_ACTIVE;
03822    while (ast_waitfor(cnf->lchan, -1) > -1) {
03823       if (cnf->recording == MEETME_RECORD_TERMINATE) {
03824          AST_LIST_LOCK(&confs);
03825          AST_LIST_UNLOCK(&confs);
03826          break;
03827       }
03828       if (!s && cnf->recordingfilename && (cnf->recordingfilename != oldrecordingfilename)) {
03829          s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
03830          oldrecordingfilename = cnf->recordingfilename;
03831       }
03832       
03833       f = ast_read(cnf->lchan);
03834       if (!f) {
03835          res = -1;
03836          break;
03837       }
03838       if (f->frametype == AST_FRAME_VOICE) {
03839          ast_mutex_lock(&cnf->listenlock);
03840          for (x = 0; x < AST_FRAME_BITS; x++) {
03841             /* Free any translations that have occured */
03842             if (cnf->transframe[x]) {
03843                ast_frfree(cnf->transframe[x]);
03844                cnf->transframe[x] = NULL;
03845             }
03846          }
03847          if (cnf->origframe)
03848             ast_frfree(cnf->origframe);
03849          cnf->origframe = ast_frdup(f);
03850          ast_mutex_unlock(&cnf->listenlock);
03851          if (s)
03852             res = ast_writestream(s, f);
03853          if (res) {
03854             ast_frfree(f);
03855             break;
03856          }
03857       }
03858       ast_frfree(f);
03859    }
03860    cnf->recording = MEETME_RECORD_OFF;
03861    if (s)
03862       ast_closestream(s);
03863    
03864    pthread_exit(0);
03865 }

static int reload ( void   )  [static]

Definition at line 5919 of file app_meetme.c.

References ast_unload_realtime(), and load_config().

05920 {
05921    ast_unload_realtime("meetme");
05922    return load_config(1);
05923 }

static void reset_volumes ( struct ast_conf_user user  )  [static]

Definition at line 772 of file app_meetme.c.

References ast_channel_setoption(), AST_OPTION_RXGAIN, AST_OPTION_TXGAIN, and ast_conf_user::chan.

Referenced by admin_exec(), and conf_run().

00773 {
00774    signed char zero_volume = 0;
00775 
00776    ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00777    ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
00778 }

static void* run_station ( void *  data  )  [static]

Definition at line 4106 of file app_meetme.c.

References sla_trunk::active_stations, admin_exec(), ALL_TRUNK_REFS, answer_trunk_chan(), ast_atomic_dec_and_test(), ast_atomic_fetchadd_int(), ast_cond_signal(), ast_dial_destroy(), ast_dial_join(), ast_free, ast_mutex_lock(), ast_mutex_unlock(), ast_set_flag, ast_str_append(), ast_str_create(), ast_str_set(), build_conf(), sla_trunk_ref::chan, run_station_args::cond, run_station_args::cond_lock, conf_run(), CONFFLAG_MARKEDEXIT, CONFFLAG_PASS_DTMF, CONFFLAG_QUIET, CONFFLAG_SLA_STATION, sla_station::dial, dispose_conf(), ast_flags::flags, sla_trunk::hold_stations, sla_trunk::name, sla_change_trunk_state(), SLA_TRUNK_STATE_IDLE, SLA_TRUNK_STATE_ONHOLD_BYME, sla_trunk_ref::state, run_station_args::station, ast_str::str, sla_trunk_ref::trunk, and run_station_args::trunk_ref.

Referenced by sla_handle_dial_state_event().

04107 {
04108    struct sla_station *station;
04109    struct sla_trunk_ref *trunk_ref;
04110    struct ast_str *conf_name = ast_str_create(16);
04111    struct ast_flags conf_flags = { 0 };
04112    struct ast_conference *conf;
04113 
04114    {
04115       struct run_station_args *args = data;
04116       station = args->station;
04117       trunk_ref = args->trunk_ref;
04118       ast_mutex_lock(args->cond_lock);
04119       ast_cond_signal(args->cond);
04120       ast_mutex_unlock(args->cond_lock);
04121       /* args is no longer valid here. */
04122    }
04123 
04124    ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
04125    ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
04126    ast_set_flag(&conf_flags, 
04127       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
04128    answer_trunk_chan(trunk_ref->chan);
04129    conf = build_conf(conf_name->str, "", "", 0, 0, 1, trunk_ref->chan);
04130    if (conf) {
04131       conf_run(trunk_ref->chan, conf, conf_flags.flags, NULL);
04132       dispose_conf(conf);
04133       conf = NULL;
04134    }
04135    trunk_ref->chan = NULL;
04136    if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
04137       trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
04138       ast_str_append(&conf_name, 0, ",K");
04139       admin_exec(NULL, conf_name->str);
04140       trunk_ref->trunk->hold_stations = 0;
04141       sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
04142    }
04143 
04144    ast_dial_join(station->dial);
04145    ast_dial_destroy(station->dial);
04146    station->dial = NULL;
04147    ast_free(conf_name);
04148 
04149    return NULL;
04150 }

static int set_listen_volume ( struct ast_conf_user user,
int  volume 
) [static]

Definition at line 701 of file app_meetme.c.

References ast_channel_setoption(), AST_OPTION_TXGAIN, and ast_conf_user::chan.

Referenced by tweak_listen_volume().

00702 {
00703    char gain_adjust;
00704 
00705    /* attempt to make the adjustment in the channel driver;
00706       if successful, don't adjust in the frame reading routine
00707    */
00708    gain_adjust = gain_map[volume + 5];
00709 
00710    return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
00711 }

static int set_talk_volume ( struct ast_conf_user user,
int  volume 
) [static]

Definition at line 689 of file app_meetme.c.

References ast_channel_setoption(), AST_OPTION_RXGAIN, and ast_conf_user::chan.

Referenced by conf_run(), and tweak_talk_volume().

00690 {
00691    char gain_adjust;
00692 
00693    /* attempt to make the adjustment in the channel driver;
00694       if successful, don't adjust in the frame reading routine
00695    */
00696    gain_adjust = gain_map[volume + 5];
00697 
00698    return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
00699 }

static void sla_add_trunk_to_station ( struct sla_station station,
struct ast_variable var 
) [static]

Definition at line 5553 of file app_meetme.c.

References ast_atomic_fetchadd_int(), ast_free, AST_LIST_INSERT_TAIL, ast_log(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strdupa, create_trunk_ref(), LOG_ERROR, LOG_WARNING, sla_station::name, sla_trunk::name, sla_trunk::num_stations, sla_trunk_ref::ring_delay, sla_trunk_ref::ring_timeout, sla_create_station_ref(), SLA_TRUNK_STATE_IDLE, sla_trunk_ref::state, sla_trunk::stations, strsep(), sla_station::trunks, and ast_variable::value.

Referenced by sla_build_station().

05554 {
05555    struct sla_trunk *trunk;
05556    struct sla_trunk_ref *trunk_ref;
05557    struct sla_station_ref *station_ref;
05558    char *trunk_name, *options, *cur;
05559 
05560    options = ast_strdupa(var->value);
05561    trunk_name = strsep(&options, ",");
05562    
05563    AST_RWLIST_RDLOCK(&sla_trunks);
05564    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
05565       if (!strcasecmp(trunk->name, trunk_name))
05566          break;
05567    }
05568 
05569    AST_RWLIST_UNLOCK(&sla_trunks);
05570    if (!trunk) {
05571       ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
05572       return;
05573    }
05574    if (!(trunk_ref = create_trunk_ref(trunk)))
05575       return;
05576    trunk_ref->state = SLA_TRUNK_STATE_IDLE;
05577 
05578    while ((cur = strsep(&options, ","))) {
05579       char *name, *value = cur;
05580       name = strsep(&value, "=");
05581       if (!strcasecmp(name, "ringtimeout")) {
05582          if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
05583             ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
05584                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
05585             trunk_ref->ring_timeout = 0;
05586          }
05587       } else if (!strcasecmp(name, "ringdelay")) {
05588          if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
05589             ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
05590                "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
05591             trunk_ref->ring_delay = 0;
05592          }
05593       } else {
05594          ast_log(LOG_WARNING, "Invalid option '%s' for "
05595             "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
05596       }
05597    }
05598 
05599    if (!(station_ref = sla_create_station_ref(station))) {
05600       ast_free(trunk_ref);
05601       return;
05602    }
05603    ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
05604    AST_RWLIST_WRLOCK(&sla_trunks);
05605    AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
05606    AST_RWLIST_UNLOCK(&sla_trunks);
05607    AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
05608 }

static int sla_build_station ( struct ast_config cfg,
const char *  cat 
) [static]

Definition at line 5610 of file app_meetme.c.

References ast_add_extension2(), ast_calloc, ast_context_find_or_create(), ast_free, ast_free_ptr, AST_LIST_TRAVERSE, ast_log(), AST_MAX_APP, AST_MAX_EXTENSION, AST_RWLIST_INSERT_TAIL, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strdup, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), sla_station::autocontext, context, destroy_station(), exten, sla_station::hold_access, ast_variable::lineno, LOG_ERROR, LOG_WARNING, sla_trunk::name, sla_station::name, ast_variable::name, ast_variable::next, PRIORITY_HINT, sla_station::ring_delay, sla_station::ring_timeout, sla_add_trunk_to_station(), SLA_CONFIG_FILE, SLA_HOLD_OPEN, SLA_HOLD_PRIVATE, sla_registrar, sla_trunk_ref::trunk, sla_station::trunks, ast_variable::value, and var.

Referenced by sla_load_config().

05611 {
05612    struct sla_station *station;
05613    struct ast_variable *var;
05614    const char *dev;
05615 
05616    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
05617       ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
05618       return -1;
05619    }
05620 
05621    if (!(station = ast_calloc(1, sizeof(*station))))
05622       return -1;
05623    if (ast_string_field_init(station, 32)) {
05624       ast_free(station);
05625       return -1;
05626    }
05627 
05628    ast_string_field_set(station, name, cat);
05629    ast_string_field_set(station, device, dev);
05630 
05631    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05632       if (!strcasecmp(var->name, "trunk"))
05633          sla_add_trunk_to_station(station, var);
05634       else if (!strcasecmp(var->name, "autocontext"))
05635          ast_string_field_set(station, autocontext, var->value);
05636       else if (!strcasecmp(var->name, "ringtimeout")) {
05637          if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
05638             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
05639                var->value, station->name);
05640             station->ring_timeout = 0;
05641          }
05642       } else if (!strcasecmp(var->name, "ringdelay")) {
05643          if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
05644             ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
05645                var->value, station->name);
05646             station->ring_delay = 0;
05647          }
05648       } else if (!strcasecmp(var->name, "hold")) {
05649          if (!strcasecmp(var->value, "private"))
05650             station->hold_access = SLA_HOLD_PRIVATE;
05651          else if (!strcasecmp(var->value, "open"))
05652             station->hold_access = SLA_HOLD_OPEN;
05653          else {
05654             ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
05655                var->value, station->name);
05656          }
05657 
05658       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
05659          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
05660             var->name, var->lineno, SLA_CONFIG_FILE);
05661       }
05662    }
05663 
05664    if (!ast_strlen_zero(station->autocontext)) {
05665       struct ast_context *context;
05666       struct sla_trunk_ref *trunk_ref;
05667       context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
05668       if (!context) {
05669          ast_log(LOG_ERROR, "Failed to automatically find or create "
05670             "context '%s' for SLA!\n", station->autocontext);
05671          destroy_station(station);
05672          return -1;
05673       }
05674       /* The extension for when the handset goes off-hook.
05675        * exten => station1,1,SLAStation(station1) */
05676       if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
05677          NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
05678          ast_log(LOG_ERROR, "Failed to automatically create extension "
05679             "for trunk '%s'!\n", station->name);
05680          destroy_station(station);
05681          return -1;
05682       }
05683       AST_RWLIST_RDLOCK(&sla_trunks);
05684       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05685          char exten[AST_MAX_EXTENSION];
05686          char hint[AST_MAX_APP];
05687          snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
05688          snprintf(hint, sizeof(hint), "SLA:%s", exten);
05689          /* Extension for this line button 
05690           * exten => station1_line1,1,SLAStation(station1_line1) */
05691          if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
05692             NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
05693             ast_log(LOG_ERROR, "Failed to automatically create extension "
05694                "for trunk '%s'!\n", station->name);
05695             destroy_station(station);
05696             return -1;
05697          }
05698          /* Hint for this line button 
05699           * exten => station1_line1,hint,SLA:station1_line1 */
05700          if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
05701             NULL, NULL, hint, NULL, NULL, sla_registrar)) {
05702             ast_log(LOG_ERROR, "Failed to automatically create hint "
05703                "for trunk '%s'!\n", station->name);
05704             destroy_station(station);
05705             return -1;
05706          }
05707       }
05708       AST_RWLIST_UNLOCK(&sla_trunks);
05709    }
05710 
05711    AST_RWLIST_WRLOCK(&sla_stations);
05712    AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
05713    AST_RWLIST_UNLOCK(&sla_stations);
05714 
05715    return 0;
05716 }

static int sla_build_trunk ( struct ast_config cfg,
const char *  cat 
) [static]

Definition at line 5475 of file app_meetme.c.

References ast_add_extension2(), ast_calloc, ast_context_find_or_create(), ast_false(), ast_free, ast_free_ptr, ast_log(), AST_RWLIST_INSERT_TAIL, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strdup, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), sla_trunk::autocontext, sla_trunk::barge_disabled, context, destroy_trunk(), sla_trunk::hold_access, ast_variable::lineno, LOG_ERROR, LOG_WARNING, sla_trunk::name, ast_variable::name, ast_variable::next, sla_trunk::ring_timeout, sla_check_device(), SLA_CONFIG_FILE, SLA_HOLD_OPEN, SLA_HOLD_PRIVATE, sla_registrar, ast_variable::value, and var.

Referenced by sla_load_config().

05476 {
05477    struct sla_trunk *trunk;
05478    struct ast_variable *var;
05479    const char *dev;
05480 
05481    if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
05482       ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
05483       return -1;
05484    }
05485 
05486    if (sla_check_device(dev)) {
05487       ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
05488          cat, dev);
05489       return -1;
05490    }
05491 
05492    if (!(trunk = ast_calloc(1, sizeof(*trunk))))
05493       return -1;
05494    if (ast_string_field_init(trunk, 32)) {
05495       ast_free(trunk);
05496       return -1;
05497    }
05498 
05499    ast_string_field_set(trunk, name, cat);
05500    ast_string_field_set(trunk, device, dev);
05501 
05502    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05503       if (!strcasecmp(var->name, "autocontext"))
05504          ast_string_field_set(trunk, autocontext, var->value);
05505       else if (!strcasecmp(var->name, "ringtimeout")) {
05506          if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
05507             ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
05508                var->value, trunk->name);
05509             trunk->ring_timeout = 0;
05510          }
05511       } else if (!strcasecmp(var->name, "barge"))
05512          trunk->barge_disabled = ast_false(var->value);
05513       else if (!strcasecmp(var->name, "hold")) {
05514          if (!strcasecmp(var->value, "private"))
05515             trunk->hold_access = SLA_HOLD_PRIVATE;
05516          else if (!strcasecmp(var->value, "open"))
05517             trunk->hold_access = SLA_HOLD_OPEN;
05518          else {
05519             ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
05520                var->value, trunk->name);
05521          }
05522       } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
05523          ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
05524             var->name, var->lineno, SLA_CONFIG_FILE);
05525       }
05526    }
05527 
05528    if (!ast_strlen_zero(trunk->autocontext)) {
05529       struct ast_context *context;
05530       context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
05531       if (!context) {
05532          ast_log(LOG_ERROR, "Failed to automatically find or create "
05533             "context '%s' for SLA!\n", trunk->autocontext);
05534          destroy_trunk(trunk);
05535          return -1;
05536       }
05537       if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
05538          NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
05539          ast_log(LOG_ERROR, "Failed to automatically create extension "
05540             "for trunk '%s'!\n", trunk->name);
05541          destroy_trunk(trunk);
05542          return -1;
05543       }
05544    }
05545 
05546    AST_RWLIST_WRLOCK(&sla_trunks);
05547    AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
05548    AST_RWLIST_UNLOCK(&sla_trunks);
05549 
05550    return 0;
05551 }

static int sla_calc_station_delays ( unsigned int *  timeout  )  [static]

Calculate the ring delay for a station.

Note:
Assumes sla.lock is locked

Definition at line 4719 of file app_meetme.c.

References AST_LIST_TRAVERSE, sla_check_inuse_station(), sla_check_ringing_station(), sla_check_station_delay(), and sla_choose_ringing_trunk().

Referenced by sla_process_timers().

04720 {
04721    struct sla_station *station;
04722    int res = 0;
04723 
04724    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
04725       struct sla_ringing_trunk *ringing_trunk;
04726       int time_left;
04727 
04728       /* Ignore stations already ringing */
04729       if (sla_check_ringing_station(station))
04730          continue;
04731 
04732       /* Ignore stations already on a call */
04733       if (sla_check_inuse_station(station))
04734          continue;
04735 
04736       /* Ignore stations that don't have one of their trunks ringing */
04737       if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
04738          continue;
04739 
04740       if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
04741          continue;
04742 
04743       /* If there is no time left, then the station needs to start ringing.
04744        * Return non-zero so that an event will be queued up an event to 
04745        * make that happen. */
04746       if (time_left <= 0) {
04747          res = 1;
04748          continue;
04749       }
04750 
04751       if (time_left < *timeout)
04752          *timeout = time_left;
04753    }
04754 
04755    return res;
04756 }

static int sla_calc_station_timeouts ( unsigned int *  timeout  )  [static]

Process station ring timeouts.

Note:
Called with sla.lock locked
Returns:
non-zero if a change to the ringing stations was made

Definition at line 4636 of file app_meetme.c.

References AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_tvdiff_ms(), ast_tvnow(), sla_ringing_station::ring_begin, sla_ringing_trunk::ring_begin, sla_station::ring_timeout, sla_trunk_ref::ring_timeout, sla, SLA_STATION_HANGUP_TIMEOUT, sla_stop_ringing_station(), sla_station_ref::station, sla_ringing_station::station, sla_ringing_trunk::timed_out_stations, sla_trunk_ref::trunk, sla_ringing_trunk::trunk, and sla_station::trunks.

Referenced by sla_process_timers().

04637 {
04638    struct sla_ringing_trunk *ringing_trunk;
04639    struct sla_ringing_station *ringing_station;
04640    int res = 0;
04641 
04642    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
04643       unsigned int ring_timeout = 0;
04644       int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
04645       struct sla_trunk_ref *trunk_ref;
04646 
04647       /* If there are any ring timeouts specified for a specific trunk
04648        * on the station, then use the highest per-trunk ring timeout.
04649        * Otherwise, use the ring timeout set for the entire station. */
04650       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
04651          struct sla_station_ref *station_ref;
04652          int trunk_time_elapsed, trunk_time_left;
04653 
04654          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
04655             if (ringing_trunk->trunk == trunk_ref->trunk)
04656                break;
04657          }
04658          if (!ringing_trunk)
04659             continue;
04660 
04661          /* If there is a trunk that is ringing without a timeout, then the
04662           * only timeout that could matter is a global station ring timeout. */
04663          if (!trunk_ref->ring_timeout)
04664             break;
04665 
04666          /* This trunk on this station is ringing and has a timeout.
04667           * However, make sure this trunk isn't still ringing from a
04668           * previous timeout.  If so, don't consider it. */
04669          AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
04670             if (station_ref->station == ringing_station->station)
04671                break;
04672          }
04673          if (station_ref)
04674             continue;
04675 
04676          trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
04677          trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
04678          if (trunk_time_left > final_trunk_time_left)
04679             final_trunk_time_left = trunk_time_left;
04680       }
04681 
04682       /* No timeout was found for ringing trunks, and no timeout for the entire station */
04683       if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
04684          continue;
04685 
04686       /* Compute how much time is left for a global station timeout */
04687       if (ringing_station->station->ring_timeout) {
04688          ring_timeout = ringing_station->station->ring_timeout;
04689          time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
04690          time_left = (ring_timeout * 1000) - time_elapsed;
04691       }
04692 
04693       /* If the time left based on the per-trunk timeouts is smaller than the
04694        * global station ring timeout, use that. */
04695       if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
04696          time_left = final_trunk_time_left;
04697 
04698       /* If there is no time left, the station needs to stop ringing */
04699       if (time_left <= 0) {
04700          AST_LIST_REMOVE_CURRENT(entry);
04701          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
04702          res = 1;
04703          continue;
04704       }
04705 
04706       /* There is still some time left for this station to ring, so save that
04707        * timeout if it is the first event scheduled to occur */
04708       if (time_left < *timeout)
04709          *timeout = time_left;
04710    }
04711    AST_LIST_TRAVERSE_SAFE_END;
04712 
04713    return res;
04714 }

static int sla_calc_trunk_timeouts ( unsigned int *  timeout  )  [static]

Process trunk ring timeouts.

Note:
Called with sla.lock locked
Returns:
non-zero if a change to the ringing trunks was made

Definition at line 4606 of file app_meetme.c.

References AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_tvdiff_ms(), ast_tvnow(), sla_trunk::chan, pbx_builtin_setvar_helper(), sla_ringing_trunk::ring_begin, sla_trunk::ring_timeout, sla, sla_stop_ringing_trunk(), and sla_ringing_trunk::trunk.

Referenced by sla_process_timers().

04607 {
04608    struct sla_ringing_trunk *ringing_trunk;
04609    int res = 0;
04610 
04611    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
04612       int time_left, time_elapsed;
04613       if (!ringing_trunk->trunk->ring_timeout)
04614          continue;
04615       time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
04616       time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
04617       if (time_left <= 0) {
04618          pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
04619          AST_LIST_REMOVE_CURRENT(entry);
04620          sla_stop_ringing_trunk(ringing_trunk);
04621          res = 1;
04622          continue;
04623       }
04624       if (time_left < *timeout)
04625          *timeout = time_left;
04626    }
04627    AST_LIST_TRAVERSE_SAFE_END;
04628 
04629    return res;
04630 }

static void sla_change_trunk_state ( const struct sla_trunk trunk,
enum sla_trunk_state  state,
enum sla_which_trunk_refs  inactive_only,
const struct sla_trunk_ref exclude 
) [static]

Definition at line 4074 of file app_meetme.c.

References ast_devstate_changed(), AST_LIST_TRAVERSE, sla_trunk_ref::chan, sla_trunk::name, sla_station::name, sla_state_to_devstate(), sla_trunk_ref::state, sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by dial_trunk(), queue_ringing_trunk(), run_station(), sla_handle_dial_state_event(), sla_handle_hold_event(), sla_station_exec(), sla_stop_ringing_trunk(), and sla_trunk_exec().

04076 {
04077    struct sla_station *station;
04078    struct sla_trunk_ref *trunk_ref;
04079 
04080    AST_LIST_TRAVERSE(&sla_stations, station, entry) {
04081       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04082          if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
04083             || trunk_ref == exclude)
04084             continue;
04085          trunk_ref->state = state;
04086          ast_devstate_changed(sla_state_to_devstate(state), 
04087             "SLA:%s_%s", station->name, trunk->name);
04088          break;
04089       }
04090    }
04091 }

static int sla_check_device ( const char *  device  )  [static]

Definition at line 5462 of file app_meetme.c.

References ast_strdupa, ast_strlen_zero(), and strsep().

Referenced by sla_build_trunk().

05463 {
05464    char *tech, *tech_data;
05465 
05466    tech_data = ast_strdupa(device);
05467    tech = strsep(&tech_data, "/");
05468 
05469    if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
05470       return -1;
05471 
05472    return 0;
05473 }

static int sla_check_failed_station ( const struct sla_station station  )  [static]

Check to see if this station has failed to be dialed in the past minute.

Note:
assumes sla.lock is locked

Definition at line 4354 of file app_meetme.c.

References ast_free, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_tvdiff_ms(), ast_tvnow(), sla_failed_station::last_try, sla, and sla_failed_station::station.

Referenced by sla_ring_stations().

04355 {
04356    struct sla_failed_station *failed_station;
04357    int res = 0;
04358 
04359    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
04360       if (station != failed_station->station)
04361          continue;
04362       if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
04363          AST_LIST_REMOVE_CURRENT(entry);
04364          ast_free(failed_station);
04365          break;
04366       }
04367       res = 1;
04368    }
04369    AST_LIST_TRAVERSE_SAFE_END
04370 
04371    return res;
04372 }

static int sla_check_inuse_station ( const struct sla_station station  )  [static]

Check to see if a station is in use.

Definition at line 4440 of file app_meetme.c.

References AST_LIST_TRAVERSE, sla_trunk_ref::chan, and sla_station::trunks.

Referenced by sla_calc_station_delays(), and sla_ring_stations().

04441 {
04442    struct sla_trunk_ref *trunk_ref;
04443 
04444    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04445       if (trunk_ref->chan)
04446          return 1;
04447    }
04448 
04449    return 0;
04450 }

static void sla_check_reload ( void   )  [static]

Check if we can do a reload of SLA, and do it if we can.

Definition at line 4798 of file app_meetme.c.

References AST_LIST_EMPTY, ast_mutex_lock(), ast_mutex_unlock(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, sla_trunk::ref_count, sla_station::ref_count, sla, and sla_load_config().

Referenced by sla_thread().

04799 {
04800    struct sla_station *station;
04801    struct sla_trunk *trunk;
04802 
04803    ast_mutex_lock(&sla.lock);
04804 
04805    if (!AST_LIST_EMPTY(&sla.event_q) || !AST_LIST_EMPTY(&sla.ringing_trunks) 
04806       || !AST_LIST_EMPTY(&sla.ringing_stations)) {
04807       ast_mutex_unlock(&sla.lock);
04808       return;
04809    }
04810 
04811    AST_RWLIST_RDLOCK(&sla_stations);
04812    AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
04813       if (station->ref_count)
04814          break;
04815    }
04816    AST_RWLIST_UNLOCK(&sla_stations);
04817    if (station) {
04818       ast_mutex_unlock(&sla.lock);
04819       return;
04820    }
04821 
04822    AST_RWLIST_RDLOCK(&sla_trunks);
04823    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
04824       if (trunk->ref_count)
04825          break;
04826    }
04827    AST_RWLIST_UNLOCK(&sla_trunks);
04828    if (trunk) {
04829       ast_mutex_unlock(&sla.lock);
04830       return;
04831    }
04832 
04833    /* yay */
04834    sla_load_config(1);
04835    sla.reload = 0;
04836 
04837    ast_mutex_unlock(&sla.lock);
04838 }

static int sla_check_ringing_station ( const struct sla_station station  )  [static]

Check to see if this station is already ringing.

Note:
Assumes sla.lock is locked

Definition at line 4339 of file app_meetme.c.

References AST_LIST_TRAVERSE, sla, and sla_ringing_station::station.

Referenced by sla_calc_station_delays(), and sla_ring_stations().

04340 {
04341    struct sla_ringing_station *ringing_station;
04342 
04343    AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
04344       if (station == ringing_station->station)
04345          return 1;
04346    }
04347 
04348    return 0;
04349 }

static int sla_check_station_delay ( struct sla_station station,
struct sla_ringing_trunk ringing_trunk 
) [static]

Calculate the ring delay for a given ringing trunk on a station.

Parameters:
station the station
ringing_trunk the trunk. If NULL, the highest priority ringing trunk will be used
Returns:
the number of ms left before the delay is complete, or INT_MAX if there is no delay

Definition at line 4470 of file app_meetme.c.

References ast_tvdiff_ms(), ast_tvnow(), sla_ringing_trunk::ring_begin, sla_station::ring_delay, sla_trunk_ref::ring_delay, sla_choose_ringing_trunk(), sla_find_trunk_ref(), and sla_ringing_trunk::trunk.

Referenced by sla_calc_station_delays(), and sla_ring_stations().

04472 {
04473    struct sla_trunk_ref *trunk_ref;
04474    unsigned int delay = UINT_MAX;
04475    int time_left, time_elapsed;
04476 
04477    if (!ringing_trunk)
04478       ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
04479    else
04480       trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);
04481 
04482    if (!ringing_trunk || !trunk_ref)
04483       return delay;
04484 
04485    /* If this station has a ring delay specific to the highest priority
04486     * ringing trunk, use that.  Otherwise, use the ring delay specified
04487     * globally for the station. */
04488    delay = trunk_ref->ring_delay;
04489    if (!delay)
04490       delay = station->ring_delay;
04491    if (!delay)
04492       return INT_MAX;
04493 
04494    time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
04495    time_left = (delay * 1000) - time_elapsed;
04496 
04497    return time_left;
04498 }

static int sla_check_station_hold_access ( const struct sla_trunk trunk,
const struct sla_station station 
) [static]

Definition at line 3979 of file app_meetme.c.

References AST_LIST_TRAVERSE, sla_station::hold_access, SLA_HOLD_PRIVATE, SLA_TRUNK_STATE_ONHOLD_BYME, sla_trunk_ref::state, sla_station_ref::station, sla_trunk::stations, sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by sla_find_trunk_ref_byname().

03981 {
03982    struct sla_station_ref *station_ref;
03983    struct sla_trunk_ref *trunk_ref;
03984 
03985    /* For each station that has this call on hold, check for private hold. */
03986    AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
03987       AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
03988          if (trunk_ref->trunk != trunk || station_ref->station == station)
03989             continue;
03990          if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
03991             station_ref->station->hold_access == SLA_HOLD_PRIVATE)
03992             return 1;
03993          return 0;
03994       }
03995    }
03996 
03997    return 0;
03998 }

static int sla_check_timed_out_station ( const struct sla_ringing_trunk ringing_trunk,
const struct sla_station station 
) [static]

Check to see if dialing this station already timed out for this ringing trunk.

Note:
Assumes sla.lock is locked

Definition at line 4210 of file app_meetme.c.

References AST_LIST_TRAVERSE, sla_station_ref::station, and sla_ringing_trunk::timed_out_stations.

Referenced by sla_choose_ringing_trunk(), and sla_ring_stations().

04212 {
04213    struct sla_station_ref *timed_out_station;
04214 
04215    AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
04216       if (station == timed_out_station->station)
04217          return 1;
04218    }
04219 
04220    return 0;
04221 }

static struct sla_trunk_ref* sla_choose_idle_trunk ( const struct sla_station station  )  [static, read]

For a given station, choose the highest priority idle trunk.

Definition at line 5022 of file app_meetme.c.

References AST_LIST_TRAVERSE, SLA_TRUNK_STATE_IDLE, sla_trunk_ref::state, and sla_station::trunks.

Referenced by sla_station_exec().

05023 {
05024    struct sla_trunk_ref *trunk_ref = NULL;
05025 
05026    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
05027       if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
05028          break;
05029    }
05030 
05031    return trunk_ref;
05032 }

static struct sla_ringing_trunk* sla_choose_ringing_trunk ( struct sla_station station,
struct sla_trunk_ref **  trunk_ref,
int  rm 
) [static, read]

Choose the highest priority ringing trunk for a station.

Parameters:
station the station
remove remove the ringing trunk once selected
trunk_ref a place to store the pointer to this stations reference to the selected trunk
Returns:
a pointer to the selected ringing trunk, or NULL if none found
Note:
Assumes that sla.lock is locked

Definition at line 4231 of file app_meetme.c.

References AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, sla, sla_check_timed_out_station(), sla_ringing_trunk::trunk, sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by sla_calc_station_delays(), sla_check_station_delay(), and sla_handle_dial_state_event().

04233 {
04234    struct sla_trunk_ref *s_trunk_ref;
04235    struct sla_ringing_trunk *ringing_trunk = NULL;
04236 
04237    AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
04238       AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
04239          /* Make sure this is the trunk we're looking for */
04240          if (s_trunk_ref->trunk != ringing_trunk->trunk)
04241             continue;
04242 
04243          /* This trunk on the station is ringing.  But, make sure this station
04244           * didn't already time out while this trunk was ringing. */
04245          if (sla_check_timed_out_station(ringing_trunk, station))
04246             continue;
04247 
04248          if (rm)
04249             AST_LIST_REMOVE_CURRENT(entry);
04250 
04251          if (trunk_ref)
04252             *trunk_ref = s_trunk_ref;
04253 
04254          break;
04255       }
04256       AST_LIST_TRAVERSE_SAFE_END;
04257    
04258       if (ringing_trunk)
04259          break;
04260    }
04261 
04262    return ringing_trunk;
04263 }

static struct sla_ringing_station* sla_create_ringing_station ( struct sla_station station  )  [static, read]

Definition at line 4044 of file app_meetme.c.

References ast_calloc, ast_tvnow(), sla_ringing_station::ring_begin, and sla_ringing_station::station.

Referenced by sla_ring_station().

04045 {
04046    struct sla_ringing_station *ringing_station;
04047 
04048    if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
04049       return NULL;
04050 
04051    ringing_station->station = station;
04052    ringing_station->ring_begin = ast_tvnow();
04053 
04054    return ringing_station;
04055 }

static struct sla_station_ref* sla_create_station_ref ( struct sla_station station  )  [static, read]

Definition at line 4032 of file app_meetme.c.

References ast_calloc, and sla_station_ref::station.

Referenced by sla_add_trunk_to_station(), and sla_stop_ringing_station().

04033 {
04034    struct sla_station_ref *station_ref;
04035 
04036    if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
04037       return NULL;
04038 
04039    station_ref->station = station;
04040 
04041    return station_ref;
04042 }

static void sla_destroy ( void   )  [static]

Definition at line 5432 of file app_meetme.c.

References ast_cond_destroy(), ast_cond_signal(), ast_context_destroy(), ast_mutex_destroy(), ast_mutex_lock(), ast_mutex_unlock(), AST_PTHREADT_NULL, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, destroy_station(), destroy_trunk(), sla, and sla_registrar.

Referenced by unload_module().

05433 {
05434    struct sla_trunk *trunk;
05435    struct sla_station *station;
05436 
05437    AST_RWLIST_WRLOCK(&sla_trunks);
05438    while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
05439       destroy_trunk(trunk);
05440    AST_RWLIST_UNLOCK(&sla_trunks);
05441 
05442    AST_RWLIST_WRLOCK(&sla_stations);
05443    while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
05444       destroy_station(station);
05445    AST_RWLIST_UNLOCK(&sla_stations);
05446 
05447    if (sla.thread != AST_PTHREADT_NULL) {
05448       ast_mutex_lock(&sla.lock);
05449       sla.stop = 1;
05450       ast_cond_signal(&sla.cond);
05451       ast_mutex_unlock(&sla.lock);
05452       pthread_join(sla.thread, NULL);
05453    }
05454 
05455    /* Drop any created contexts from the dialplan */
05456    ast_context_destroy(NULL, sla_registrar);
05457 
05458    ast_mutex_destroy(&sla.lock);
05459    ast_cond_destroy(&sla.cond);
05460 }

static void sla_dial_state_callback ( struct ast_dial dial  )  [static]

Definition at line 4202 of file app_meetme.c.

References SLA_EVENT_DIAL_STATE, and sla_queue_event().

Referenced by sla_ring_station().

04203 {
04204    sla_queue_event(SLA_EVENT_DIAL_STATE);
04205 }

static struct sla_station* sla_find_station ( const char *  name  )  [static, read]

Find an SLA station by name.

Note:
This must be called with the sla_stations container locked

Definition at line 3967 of file app_meetme.c.

References AST_RWLIST_TRAVERSE, and sla_station::name.

Referenced by sla_station_exec().

03968 {
03969    struct sla_station *station = NULL;
03970 
03971    AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
03972       if (!strcasecmp(station->name, name))
03973          break;
03974    }
03975 
03976    return station;
03977 }

static struct sla_trunk* sla_find_trunk ( const char *  name  )  [static, read]

Find an SLA trunk by name.

Note:
This must be called with the sla_trunks container locked

Definition at line 3952 of file app_meetme.c.

References AST_RWLIST_TRAVERSE, and sla_trunk::name.

Referenced by sla_trunk_exec().

03953 {
03954    struct sla_trunk *trunk = NULL;
03955 
03956    AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
03957       if (!strcasecmp(trunk->name, name))
03958          break;
03959    }
03960 
03961    return trunk;
03962 }

static struct sla_trunk_ref* sla_find_trunk_ref ( const struct sla_station station,
const struct sla_trunk trunk 
) [static, read]

Definition at line 4452 of file app_meetme.c.

References AST_LIST_TRAVERSE, sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by sla_check_station_delay().

04454 {
04455    struct sla_trunk_ref *trunk_ref = NULL;
04456 
04457    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04458       if (trunk_ref->trunk == trunk)
04459          break;
04460    }
04461 
04462    return trunk_ref;
04463 }

static struct sla_trunk_ref* sla_find_trunk_ref_byname ( const struct sla_station station,
const char *  name 
) [static, read]

Find a trunk reference on a station by name.

Parameters:
station the station
name the trunk's name
Returns:
a pointer to the station's trunk reference. If the trunk is not found, it is not idle and barge is disabled, or if it is on hold and private hold is set, then NULL will be returned.

Definition at line 4007 of file app_meetme.c.

References AST_LIST_TRAVERSE, sla_trunk::barge_disabled, sla_trunk::hold_access, sla_trunk::hold_stations, sla_trunk::name, sla_check_station_hold_access(), SLA_HOLD_PRIVATE, SLA_TRUNK_STATE_ONHOLD_BYME, SLA_TRUNK_STATE_UP, sla_trunk_ref::state, sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by sla_station_exec().

04009 {
04010    struct sla_trunk_ref *trunk_ref = NULL;
04011 
04012    AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
04013       if (strcasecmp(trunk_ref->trunk->name, name))
04014          continue;
04015 
04016       if ( (trunk_ref->trunk->barge_disabled 
04017          && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
04018          (trunk_ref->trunk->hold_stations 
04019          && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
04020          && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
04021          sla_check_station_hold_access(trunk_ref->trunk, station) ) 
04022       {
04023          trunk_ref = NULL;
04024       }
04025 
04026       break;
04027    }
04028 
04029    return trunk_ref;
04030 }

static void sla_handle_dial_state_event ( void   )  [static]

Definition at line 4265 of file app_meetme.c.

References ALL_TRUNK_REFS, answer_trunk_chan(), ast_cond_destroy(), ast_cond_init(), ast_cond_wait(), ast_debug, ast_dial_answered(), AST_DIAL_RESULT_ANSWERED, AST_DIAL_RESULT_FAILED, AST_DIAL_RESULT_HANGUP, AST_DIAL_RESULT_INVALID, AST_DIAL_RESULT_PROCEEDING, AST_DIAL_RESULT_PROGRESS, AST_DIAL_RESULT_RINGING, AST_DIAL_RESULT_TIMEOUT, AST_DIAL_RESULT_TRYING, AST_DIAL_RESULT_UNANSWERED, ast_dial_state(), ast_free, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_mutex_destroy(), ast_mutex_init(), ast_mutex_lock(), ast_mutex_unlock(), ast_pthread_create_detached_background, sla_trunk::chan, sla_trunk_ref::chan, run_station_args::cond, run_station_args::cond_lock, sla_station::dial, sla_station::name, run_station(), sla, sla_change_trunk_state(), sla_choose_ringing_trunk(), SLA_EVENT_DIAL_STATE, SLA_EVENT_RINGING_TRUNK, sla_queue_event(), SLA_STATION_HANGUP_NORMAL, sla_stop_ringing_station(), SLA_TRUNK_STATE_UP, run_station_args::station, sla_ringing_station::station, sla_ringing_trunk::trunk, and run_station_args::trunk_ref.

Referenced by sla_thread().

04266 {
04267    struct sla_ringing_station *ringing_station;
04268 
04269    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
04270       struct sla_trunk_ref *s_trunk_ref = NULL;
04271       struct sla_ringing_trunk *ringing_trunk = NULL;
04272       struct run_station_args args;
04273       enum ast_dial_result dial_res;
04274       pthread_t dont_care;
04275       ast_mutex_t cond_lock;
04276       ast_cond_t cond;
04277 
04278       switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
04279       case AST_DIAL_RESULT_HANGUP:
04280       case AST_DIAL_RESULT_INVALID:
04281       case AST_DIAL_RESULT_FAILED:
04282       case AST_DIAL_RESULT_TIMEOUT:
04283       case AST_DIAL_RESULT_UNANSWERED:
04284          AST_LIST_REMOVE_CURRENT(entry);
04285          sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
04286          break;
04287       case AST_DIAL_RESULT_ANSWERED:
04288          AST_LIST_REMOVE_CURRENT(entry);
04289          /* Find the appropriate trunk to answer. */
04290          ast_mutex_lock(&sla.lock);
04291          ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
04292          ast_mutex_unlock(&sla.lock);
04293          if (!ringing_trunk) {
04294             ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
04295             break;
04296          }
04297          /* Track the channel that answered this trunk */
04298          s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
04299          /* Actually answer the trunk */
04300          answer_trunk_chan(ringing_trunk->trunk->chan);
04301          sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
04302          /* Now, start a thread that will connect this station to the trunk.  The rest of
04303           * the code here sets up the thread and ensures that it is able to save the arguments
04304           * before they are no longer valid since they are allocated on the stack. */
04305          args.trunk_ref = s_trunk_ref;
04306          args.station = ringing_station->station;
04307          args.cond = &cond;
04308          args.cond_lock = &cond_lock;
04309          ast_free(ringing_trunk);
04310          ast_free(ringing_station);
04311          ast_mutex_init(&cond_lock);
04312          ast_cond_init(&cond, NULL);
04313          ast_mutex_lock(&cond_lock);
04314          ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
04315          ast_cond_wait(&cond, &cond_lock);
04316          ast_mutex_unlock(&cond_lock);
04317          ast_mutex_destroy(&cond_lock);
04318          ast_cond_destroy(&cond);
04319          break;
04320       case AST_DIAL_RESULT_TRYING:
04321       case AST_DIAL_RESULT_RINGING:
04322       case AST_DIAL_RESULT_PROGRESS:
04323       case AST_DIAL_RESULT_PROCEEDING:
04324          break;
04325       }
04326       if (dial_res == AST_DIAL_RESULT_ANSWERED) {
04327          /* Queue up reprocessing ringing trunks, and then ringing stations again */
04328          sla_queue_event(SLA_EVENT_RINGING_TRUNK);
04329          sla_queue_event(SLA_EVENT_DIAL_STATE);
04330          break;
04331       }
04332    }
04333    AST_LIST_TRAVERSE_SAFE_END;
04334 }

static void sla_handle_hold_event ( struct sla_event event  )  [static]

Definition at line 4582 of file app_meetme.c.

References sla_trunk::active_stations, ast_atomic_fetchadd_int(), AST_CONTROL_HOLD, AST_DEVICE_ONHOLD, ast_devstate_changed(), ast_indicate(), ast_softhangup(), AST_SOFTHANGUP_DEV, sla_trunk_ref::chan, sla_trunk::chan, sla_trunk::hold_stations, INACTIVE_TRUNK_REFS, sla_trunk::name, sla_station::name, sla_change_trunk_state(), SLA_TRUNK_STATE_ONHOLD, SLA_TRUNK_STATE_ONHOLD_BYME, sla_event::station, sla_trunk_ref::trunk, and sla_event::trunk_ref.

Referenced by sla_thread().

04583 {
04584    ast_atomic_fetchadd_int((int *) &event->trunk_ref->trunk->hold_stations, 1);
04585    event->trunk_ref->state = SLA_TRUNK_STATE_ONHOLD_BYME;
04586    ast_devstate_changed(AST_DEVICE_ONHOLD, "SLA:%s_%s", 
04587       event->station->name, event->trunk_ref->trunk->name);
04588    sla_change_trunk_state(event->trunk_ref->trunk, SLA_TRUNK_STATE_ONHOLD, 
04589       INACTIVE_TRUNK_REFS, event->trunk_ref);
04590 
04591    if (event->trunk_ref->trunk->active_stations == 1) {
04592       /* The station putting it on hold is the only one on the call, so start
04593        * Music on hold to the trunk. */
04594       event->trunk_ref->trunk->on_hold = 1;
04595       ast_indicate(event->trunk_ref->trunk->chan, AST_CONTROL_HOLD);
04596    }
04597 
04598    ast_softhangup(event->trunk_ref->chan, AST_SOFTHANGUP_DEV);
04599    event->trunk_ref->chan = NULL;
04600 }

static void sla_handle_ringing_trunk_event ( void   )  [static]

Definition at line 4572 of file app_meetme.c.

References ast_mutex_lock(), ast_mutex_unlock(), sla, sla_hangup_stations(), and sla_ring_stations().

Referenced by sla_thread().

04573 {
04574    ast_mutex_lock(&sla.lock);
04575    sla_ring_stations();
04576    ast_mutex_unlock(&sla.lock);
04577 
04578    /* Find stations that shouldn't be ringing anymore. */
04579    sla_hangup_stations();
04580 }

static void sla_hangup_stations ( void   )  [static]

Definition at line 4544 of file app_meetme.c.

References ast_dial_destroy(), ast_dial_join(), ast_free, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_mutex_lock(), ast_mutex_unlock(), sla_station::dial, sla, sla_ringing_station::station, sla_ringing_trunk::trunk, sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by sla_handle_ringing_trunk_event().

04545 {
04546    struct sla_trunk_ref *trunk_ref;
04547    struct sla_ringing_station *ringing_station;
04548 
04549    AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
04550       AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
04551          struct sla_ringing_trunk *ringing_trunk;
04552          ast_mutex_lock(&sla.lock);
04553          AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
04554             if (trunk_ref->trunk == ringing_trunk->trunk)
04555                break;
04556          }
04557          ast_mutex_unlock(&sla.lock);
04558          if (ringing_trunk)
04559             break;
04560       }
04561       if (!trunk_ref) {
04562          AST_LIST_REMOVE_CURRENT(entry);
04563          ast_dial_join(ringing_station->station->dial);
04564          ast_dial_destroy(ringing_station->station->dial);
04565          ringing_station->station->dial = NULL;
04566          ast_free(ringing_station);
04567       }
04568    }
04569    AST_LIST_TRAVERSE_SAFE_END
04570 }

static const char* sla_hold_str ( unsigned int  hold_access  )  [static]

Definition at line 1179 of file app_meetme.c.

References SLA_HOLD_OPEN, and SLA_HOLD_PRIVATE.

Referenced by sla_show_stations(), and sla_show_trunks().

01180 {
01181    const char *hold = "Unknown";
01182 
01183    switch (hold_access) {
01184    case SLA_HOLD_OPEN:
01185       hold = "Open";
01186       break;
01187    case SLA_HOLD_PRIVATE:
01188       hold = "Private";
01189    default:
01190       break;
01191    }
01192 
01193    return hold;
01194 }

static int sla_load_config ( int  reload  )  [static]

Definition at line 5718 of file app_meetme.c.

References ast_category_browse(), ast_cond_init(), ast_config_destroy(), ast_config_load, AST_LIST_EMPTY, ast_log(), ast_mutex_init(), ast_pthread_create, ast_true(), ast_variable_retrieve(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEUNCHANGED, LOG_WARNING, sla, sla_build_station(), sla_build_trunk(), SLA_CONFIG_FILE, sla_thread(), and type.

Referenced by load_config(), and sla_check_reload().

05719 {
05720    struct ast_config *cfg;
05721    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05722    const char *cat = NULL;
05723    int res = 0;
05724    const char *val;
05725 
05726    if (!reload) {
05727       ast_mutex_init(&sla.lock);
05728       ast_cond_init(&sla.cond, NULL);
05729    }
05730 
05731    if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags)))
05732       return 0; /* Treat no config as normal */
05733    else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
05734       return 0;
05735 
05736    if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
05737       sla.attempt_callerid = ast_true(val);
05738 
05739    while ((cat = ast_category_browse(cfg, cat)) && !res) {
05740       const char *type;
05741       if (!strcasecmp(cat, "general"))
05742          continue;
05743       if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
05744          ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
05745             SLA_CONFIG_FILE);
05746          continue;
05747       }
05748       if (!strcasecmp(type, "trunk"))
05749          res = sla_build_trunk(cfg, cat);
05750       else if (!strcasecmp(type, "station"))
05751          res = sla_build_station(cfg, cat);
05752       else {
05753          ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
05754             SLA_CONFIG_FILE, type);
05755       }
05756    }
05757 
05758    ast_config_destroy(cfg);
05759 
05760    if (!reload && (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_stations)))
05761       ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
05762 
05763    return res;
05764 }

static int sla_process_timers ( struct timespec *  ts  )  [static]

Calculate the time until the next known event.

Note:
Called with sla.lock locked

Definition at line 4760 of file app_meetme.c.

References ast_samp2tv(), ast_tvadd(), ast_tvnow(), sla_calc_station_delays(), sla_calc_station_timeouts(), sla_calc_trunk_timeouts(), SLA_EVENT_RINGING_TRUNK, and sla_queue_event_nolock().

Referenced by sla_thread().

04761 {
04762    unsigned int timeout = UINT_MAX;
04763    struct timeval wait;
04764    unsigned int change_made = 0;
04765 
04766    /* Check for ring timeouts on ringing trunks */
04767    if (sla_calc_trunk_timeouts(&timeout))
04768       change_made = 1;
04769 
04770    /* Check for ring timeouts on ringing stations */
04771    if (sla_calc_station_timeouts(&timeout))
04772       change_made = 1;
04773 
04774    /* Check for station ring delays */
04775    if (sla_calc_station_delays(&timeout))
04776       change_made = 1;
04777 
04778    /* queue reprocessing of ringing trunks */
04779    if (change_made)
04780       sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);
04781 
04782    /* No timeout */
04783    if (timeout == UINT_MAX)
04784       return 0;
04785 
04786    if (ts) {
04787       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
04788       ts->tv_sec = wait.tv_sec;
04789       ts->tv_nsec = wait.tv_usec * 1000;
04790    }
04791 
04792    return 1;
04793 }

static void sla_queue_event ( enum sla_event_type  type  )  [static]

static void sla_queue_event_conf ( enum sla_event_type  type,
struct ast_channel chan,
struct ast_conference conf 
) [static]

Queue a SLA event from the conference.

Definition at line 1488 of file app_meetme.c.

References ast_debug, AST_LIST_TRAVERSE, ast_log(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_strdupa, ast_strlen_zero(), sla_trunk_ref::chan, ast_conference::confno, LOG_ERROR, sla_trunk::name, sla_queue_event_full(), strsep(), sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by conf_run().

01490 {
01491    struct sla_station *station;
01492    struct sla_trunk_ref *trunk_ref = NULL;
01493    char *trunk_name;
01494 
01495    trunk_name = ast_strdupa(conf->confno);
01496    strsep(&trunk_name, "_");
01497    if (ast_strlen_zero(trunk_name)) {
01498       ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
01499       return;
01500    }
01501 
01502    AST_RWLIST_RDLOCK(&sla_stations);
01503    AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
01504       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01505          if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
01506             break;
01507       }
01508       if (trunk_ref)
01509          break;
01510    }
01511    AST_RWLIST_UNLOCK(&sla_stations);
01512 
01513    if (!trunk_ref) {
01514       ast_debug(1, "Trunk not found for event!\n");
01515       return;
01516    }
01517 
01518    sla_queue_event_full(type, trunk_ref, station, 1);
01519 }

static void sla_queue_event_full ( enum sla_event_type  type,
struct sla_trunk_ref trunk_ref,
struct sla_station station,
int  lock 
) [static]

Definition at line 1450 of file app_meetme.c.

References ast_calloc, ast_cond_signal(), AST_LIST_INSERT_TAIL, ast_mutex_lock(), ast_mutex_unlock(), AST_PTHREADT_NULL, and sla.

Referenced by sla_queue_event(), sla_queue_event_conf(), and sla_queue_event_nolock().

01452 {
01453    struct sla_event *event;
01454 
01455    if (sla.thread == AST_PTHREADT_NULL) {
01456       return;
01457    }
01458 
01459    if (!(event = ast_calloc(1, sizeof(*event))))
01460       return;
01461 
01462    event->type = type;
01463    event->trunk_ref = trunk_ref;
01464    event->station = station;
01465 
01466    if (!lock) {
01467       AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
01468       return;
01469    }
01470 
01471    ast_mutex_lock(&sla.lock);
01472    AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
01473    ast_cond_signal(&sla.cond);
01474    ast_mutex_unlock(&sla.lock);
01475 }

static void sla_queue_event_nolock ( enum sla_event_type  type  )  [static]

Definition at line 1477 of file app_meetme.c.

References sla_queue_event_full().

Referenced by sla_process_timers().

01478 {
01479    sla_queue_event_full(type, NULL, NULL, 0);
01480 }

static int sla_ring_station ( struct sla_ringing_trunk ringing_trunk,
struct sla_station station 
) [static]

Ring a station.

Note:
Assumes sla.lock is locked

Definition at line 4377 of file app_meetme.c.

References ast_calloc, ast_dial_append(), ast_dial_create(), ast_dial_destroy(), ast_dial_join(), AST_DIAL_RESULT_TRYING, ast_dial_run(), ast_dial_set_state_callback(), ast_free, AST_LIST_INSERT_HEAD, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_tvnow(), sla_trunk::chan, ast_channel::cid, ast_callerid::cid_name, cid_name, ast_callerid::cid_num, cid_num, sla_station::device, sla_station::dial, sla_failed_station::last_try, sla, sla_create_ringing_station(), sla_dial_state_callback(), sla_failed_station::station, strsep(), and sla_ringing_trunk::trunk.

Referenced by sla_ring_stations().

04378 {
04379    char *tech, *tech_data;
04380    struct ast_dial *dial;
04381    struct sla_ringing_station *ringing_station;
04382    const char *cid_name = NULL, *cid_num = NULL;
04383    enum ast_dial_result res;
04384 
04385    if (!(dial = ast_dial_create()))
04386       return -1;
04387 
04388    ast_dial_set_state_callback(dial, sla_dial_state_callback);
04389    tech_data = ast_strdupa(station->device);
04390    tech = strsep(&tech_data, "/");
04391 
04392    if (ast_dial_append(dial, tech, tech_data) == -1) {
04393       ast_dial_destroy(dial);
04394       return -1;
04395    }
04396 
04397    if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_name)) {
04398       cid_name = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_name);
04399       ast_free(ringing_trunk->trunk->chan->cid.cid_name);
04400       ringing_trunk->trunk->chan->cid.cid_name = NULL;
04401    }
04402    if (!sla.attempt_callerid && !ast_strlen_zero(ringing_trunk->trunk->chan->cid.cid_num)) {
04403       cid_num = ast_strdupa(ringing_trunk->trunk->chan->cid.cid_num);
04404       ast_free(ringing_trunk->trunk->chan->cid.cid_num);
04405       ringing_trunk->trunk->chan->cid.cid_num = NULL;
04406    }
04407 
04408    res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
04409    
04410    if (cid_name)
04411       ringing_trunk->trunk->chan->cid.cid_name = ast_strdup(cid_name);
04412    if (cid_num)
04413       ringing_trunk->trunk->chan->cid.cid_num = ast_strdup(cid_num);
04414    
04415    if (res != AST_DIAL_RESULT_TRYING) {
04416       struct sla_failed_station *failed_station;
04417       ast_dial_destroy(dial);
04418       if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
04419          return -1;
04420       failed_station->station = station;
04421       failed_station->last_try = ast_tvnow();
04422       AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
04423       return -1;
04424    }
04425    if (!(ringing_station = sla_create_ringing_station(station))) {
04426       ast_dial_join(dial);
04427       ast_dial_destroy(dial);
04428       return -1;
04429    }
04430 
04431    station->dial = dial;
04432 
04433    AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);
04434 
04435    return 0;
04436 }

static void sla_ring_stations ( void   )  [static]

Ring stations based on current set of ringing trunks.

Note:
Assumes that sla.lock is locked

Definition at line 4503 of file app_meetme.c.

References AST_LIST_TRAVERSE, sla, sla_check_failed_station(), sla_check_inuse_station(), sla_check_ringing_station(), sla_check_station_delay(), sla_check_timed_out_station(), sla_ring_station(), sla_station_ref::station, sla_trunk::stations, and sla_ringing_trunk::trunk.

Referenced by sla_handle_ringing_trunk_event().

04504 {
04505    struct sla_station_ref *station_ref;
04506    struct sla_ringing_trunk *ringing_trunk;
04507 
04508    /* Make sure that every station that uses at least one of the ringing
04509     * trunks, is ringing. */
04510    AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
04511       AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
04512          int time_left;
04513 
04514          /* Is this station already ringing? */
04515          if (sla_check_ringing_station(station_ref->station))
04516             continue;
04517 
04518          /* Is this station already in a call? */
04519          if (sla_check_inuse_station(station_ref->station))
04520             continue;
04521 
04522          /* Did we fail to dial this station earlier?  If so, has it been
04523           * a minute since we tried? */
04524          if (sla_check_failed_station(station_ref->station))
04525             continue;
04526 
04527          /* If this station already timed out while this trunk was ringing,
04528           * do not dial it again for this ringing trunk. */
04529          if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
04530             continue;
04531 
04532          /* Check for a ring delay in progress */
04533          time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
04534          if (time_left != INT_MAX && time_left > 0)
04535             continue;
04536 
04537          /* It is time to make this station begin to ring.  Do it! */
04538          sla_ring_station(ringing_trunk, station_ref->station);
04539       }
04540    }
04541    /* Now, all of the stations that should be ringing, are ringing. */
04542 }

static char* sla_show_stations ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1261 of file app_meetme.c.

References ast_cli(), AST_LIST_TRAVERSE, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, sla_station::autocontext, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, sla_station::device, ast_cli_args::fd, sla_station::hold_access, sla_trunk::name, sla_station::name, sla_trunk_ref::ring_delay, sla_station::ring_delay, sla_trunk_ref::ring_timeout, sla_station::ring_timeout, S_OR, sla_hold_str(), sla_trunk_ref::state, sla_trunk_ref::trunk, sla_station::trunks, trunkstate2str(), and ast_cli_entry::usage.

01262 {
01263    const struct sla_station *station;
01264 
01265    switch (cmd) {
01266    case CLI_INIT:
01267       e->command = "sla show stations";
01268       e->usage =
01269          "Usage: sla show stations\n"
01270          "       This will list all stations defined in sla.conf\n";
01271       return NULL;
01272    case CLI_GENERATE:
01273       return NULL;
01274    }
01275 
01276    ast_cli(a->fd, "\n" 
01277                "=============================================================\n"
01278                "=== Configured SLA Stations =================================\n"
01279                "=============================================================\n"
01280                "===\n");
01281    AST_RWLIST_RDLOCK(&sla_stations);
01282    AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
01283       struct sla_trunk_ref *trunk_ref;
01284       char ring_timeout[16] = "(none)";
01285       char ring_delay[16] = "(none)";
01286       if (station->ring_timeout) {
01287          snprintf(ring_timeout, sizeof(ring_timeout), 
01288             "%u", station->ring_timeout);
01289       }
01290       if (station->ring_delay) {
01291          snprintf(ring_delay, sizeof(ring_delay), 
01292             "%u", station->ring_delay);
01293       }
01294       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
01295                   "=== Station Name:    %s\n"
01296                   "=== ==> Device:      %s\n"
01297                   "=== ==> AutoContext: %s\n"
01298                   "=== ==> RingTimeout: %s\n"
01299                   "=== ==> RingDelay:   %s\n"
01300                   "=== ==> HoldAccess:  %s\n"
01301                   "=== ==> Trunks ...\n",
01302                   station->name, station->device,
01303                   S_OR(station->autocontext, "(none)"), 
01304                   ring_timeout, ring_delay,
01305                   sla_hold_str(station->hold_access));
01306       AST_RWLIST_RDLOCK(&sla_trunks);
01307       AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
01308          if (trunk_ref->ring_timeout) {
01309             snprintf(ring_timeout, sizeof(ring_timeout),
01310                "%u", trunk_ref->ring_timeout);
01311          } else
01312             strcpy(ring_timeout, "(none)");
01313          if (trunk_ref->ring_delay) {
01314             snprintf(ring_delay, sizeof(ring_delay),
01315                "%u", trunk_ref->ring_delay);
01316          } else
01317             strcpy(ring_delay, "(none)");
01318             ast_cli(a->