Sat Apr 26 2014 22:01:46

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 "asterisk/data.h"
#include "asterisk/test.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
 A reference to a station. More...
struct  sla_trunk
struct  sla_trunk_ref
 A station's reference to a trunk. More...
struct  volume

Defines

#define AST_FRAME_BITS   32
#define CONF_SIZE   320
#define CONFFLAG_DONT_DENOISE   (1ULL << 35)
#define CONFFLAG_INTROMSG   (1ULL << 32)
#define CONFFLAG_INTROUSER_VMREC   (1ULL << 33)
#define CONFFLAG_KILL_LAST_MAN_STANDING   (1ULL << 34)
#define CONFFLAG_NO_AUDIO_UNTIL_UP   (1ULL << 31)
#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 MAX_SETTINGS   (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
#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_DATA_EXPORT(MEMBER)
#define MEETME_DELAYDETECTENDTALK   1000
#define MEETME_DELAYDETECTTALK   300
#define MEETME_USER_DATA_EXPORT(MEMBER)
#define OPTIONS_LEN   100
#define S(e)   case e: return # e;
#define SLA_CONFIG_FILE   "sla.conf"
#define STR_CONCISE   "concise"

Enumerations

enum  {
  ADMINFLAG_MUTED = (1 << 1), ADMINFLAG_SELFMUTED = (1 << 2), ADMINFLAG_KICKME = (1 << 3), ADMINFLAG_T_REQUEST = (1 << 4),
  ADMINFLAG_HANGUP = (1 << 5)
}
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)
}
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_INTROMSG = 5, OPT_ARG_INTROUSER_VMREC = 6, OPT_ARG_ARRAY_SIZE = 7
}
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  menu_modes { MENU_DISABLED = 0, MENU_NORMAL, MENU_ADMIN, MENU_ADMIN_EXTENDED }
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 }
 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 __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 (const char *keyword, const struct ast_conference *conf)
static int action_meetmelist (struct mansession *s, const struct message *m)
static int action_meetmelistrooms (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, const char *data)
 The MeetMeadmin application.
static void * announce_thread (void *data)
static void answer_trunk_chan (struct ast_channel *chan)
 AST_DATA_STRUCTURE (ast_conference, MEETME_DATA_EXPORT)
 AST_DATA_STRUCTURE (ast_conf_user, MEETME_USER_DATA_EXPORT)
static struct ast_conferencebuild_conf (const char *confno, const char *pin, const char *pinadmin, int make, int dynamic, int refcount, const struct ast_channel *chan, struct ast_test *test)
 Find or create a conference.
static int can_write (struct ast_channel *chan, struct ast_flags64 *confflags)
static int careful_write (int fd, unsigned char *data, int len, int block)
static int channel_admin_exec (struct ast_channel *chan, const char *data)
 The MeetMeChannelAdmin application MeetMeChannelAdmin(channel, command)
static char * complete_confno (const char *word, int state)
static char * complete_meetmecmd_list (const char *line, const char *word, int pos, int state)
static char * complete_meetmecmd_lock (const char *word, int pos, int state)
static char * complete_meetmecmd_mute_kick (const char *line, const char *word, int pos, int state)
static char * complete_userno (struct ast_conference *cnf, const char *word, int state)
static int conf_exec (struct ast_channel *chan, const char *data)
 The meetme() application.
static void conf_flush (int fd, struct ast_channel *chan)
static int conf_free (struct ast_conference *conf)
 Remove the conference from the list and free it.
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, struct ast_flags64 *confflags, char *optargs[])
static void conf_start_moh (struct ast_channel *chan, const char *musicclass)
static int count_exec (struct ast_channel *chan, const char *data)
 The MeetmeCount application.
static struct sla_trunk_refcreate_trunk_ref (struct sla_trunk *trunk)
static void * dial_trunk (void *data)
static int dispose_conf (struct ast_conference *conf)
 Decrement reference counts, as incremented by find_conf()
static void filename_parse (char *filename, char *buffer)
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_flags64 *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_flags64 *confflags, int *too_early, char **optargs)
static struct ast_conf_userfind_user (struct ast_conference *conf, const char *callerident)
static const char * get_announce_filename (enum announcetypes type)
static const 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_helper (struct ast_cli_args *a)
static int meetme_data_provider_get (const struct ast_data_search *search, struct ast_data *data_root)
static char * meetme_kick_cmd (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * meetme_lock_cmd (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void meetme_menu (enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size, struct dahdi_confinfo *dahdic, struct ast_format_cap *cap_slin)
static void meetme_menu_admin (enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user)
static void meetme_menu_admin_extended (enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user, char *recordingtmp, int recordingtmp_size, struct dahdi_confinfo *dahdic, struct ast_format_cap *cap_slin)
static void meetme_menu_normal (enum menu_modes *menu_mode, int *dtmf, struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan, struct ast_conf_user *user)
static char * meetme_mute_cmd (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * meetme_show_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 int rt_extend_conf (const char *confno)
static void * run_station (void *data)
static void send_talking_event (struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
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 set_user_talking (struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
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 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_failed_stationsla_create_failed_station (struct sla_station *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 void sla_event_destroy (struct sla_event *event)
static void sla_failed_station_destroy (struct sla_failed_station *failed_station)
static struct sla_stationsla_find_station (const char *name)
static struct sla_trunksla_find_trunk (const char *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_in_use (void)
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 void sla_ringing_station_destroy (struct sla_ringing_station *ringing_station)
static void sla_ringing_trunk_destroy (struct sla_ringing_trunk *ringing_trunk)
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_cmp (void *obj, void *arg, int flags)
static void sla_station_destructor (void *obj)
static int sla_station_exec (struct ast_channel *chan, const char *data)
static int sla_station_hash (const void *obj, const int flags)
static int sla_station_is_marked (void *obj, void *arg, int flags)
static int sla_station_mark (void *obj, void *arg, int flags)
static void sla_station_ref_destructor (void *obj)
static int sla_station_release_refs (void *obj, void *arg, int flags)
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_cmp (void *obj, void *arg, int flags)
static void sla_trunk_destructor (void *obj)
static int sla_trunk_exec (struct ast_channel *chan, const char *data)
static int sla_trunk_hash (const void *obj, const int flags)
static int sla_trunk_is_marked (void *obj, void *arg, int flags)
static int sla_trunk_mark (void *obj, void *arg, int flags)
static void sla_trunk_ref_destructor (void *obj)
static int sla_trunk_release_refs (void *obj, void *arg, int flags)
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)
static int user_add_provider_cb (void *obj, void *arg, int flags)
static int user_chan_cb (void *obj, void *args, int flags)
static int user_listen_voldown_cb (void *obj, void *unused, int flags)
static int user_listen_volup_cb (void *obj, void *unused, int flags)
static int user_max_cmp (void *obj, void *arg, int flags)
static int user_no_cmp (void *obj, void *arg, int flags)
static int user_reset_vol_cb (void *obj, void *unused, int flags)
static int user_set_hangup_cb (void *obj, void *check_admin_arg, int flags)
static int user_set_kickme_cb (void *obj, void *check_admin_arg, int flags)
static int user_set_muted_cb (void *obj, void *check_admin_arg, int flags)
static int user_set_unmuted_cb (void *obj, void *check_admin_arg, int flags)
static int user_talk_voldown_cb (void *obj, void *unused, int flags)
static int user_talk_volup_cb (void *obj, void *unused, int flags)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .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, .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, }
static const char *const app = "MeetMe"
static const char *const app2 = "MeetMeCount"
static const char *const app3 = "MeetMeAdmin"
static const char *const app4 = "MeetMeChannelAdmin"
static struct ast_module_infoast_module_info = &__mod_info
static int audio_buffers
 The number of audio buffers to be allocated on pseudo channels when in a conference.
static struct ast_cli_entry cli_meetme []
static unsigned int conf_map [1024] = {0, }
static struct confs confs
static int earlyalert
static int endalert
static int extendby
static int fuzzystart
static const char gain_map []
 Map 'volume' levels from -5 through +5 into decibel (dB) settings for channel drivers.
static struct ast_data_handler meetme_data_provider
static struct ast_data_entry meetme_data_providers []
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 }, [ 'G' ] = { .flag = (1ULL << 32) , .arg_index = OPT_ARG_INTROMSG + 1 }, [ 'v' ] = { .flag = (1ULL << 33) , .arg_index = OPT_ARG_INTROUSER_VMREC + 1 }, [ 'i' ] = { .flag = CONFFLAG_INTROUSER }, [ 'I' ] = { .flag = CONFFLAG_INTROUSERNOREVIEW }, [ 'k' ] = { .flag = (1ULL << 34) }, [ 'M' ] = { .flag = CONFFLAG_MOH , .arg_index = OPT_ARG_MOH_CLASS + 1 }, [ 'm' ] = { .flag = CONFFLAG_STARTMUTED }, [ 'n' ] = { .flag = (1ULL << 35) }, [ '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
   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 ao2_containersla_stations
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 struct ao2_containersla_trunks
static const char *const slastation_app = "SLAStation"
static const char *const slatrunk_app = "SLATrunk"

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 571 of file app_meetme.c.

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

#define CONF_SIZE   320

Definition at line 590 of file app_meetme.c.

Referenced by conf_run().

#define CONFFLAG_DONT_DENOISE   (1ULL << 35)

If set, don't enable a denoiser for the channel

Definition at line 660 of file app_meetme.c.

Referenced by conf_run().

#define CONFFLAG_INTROMSG   (1ULL << 32)

If set play an intro announcement at start of conference

Definition at line 655 of file app_meetme.c.

Referenced by conf_run().

#define CONFFLAG_INTROUSER_VMREC   (1ULL << 33)

Definition at line 656 of file app_meetme.c.

Referenced by conf_run(), find_conf(), and find_conf_realtime().

#define CONFFLAG_KILL_LAST_MAN_STANDING   (1ULL << 34)

If there's only one person left in a conference when someone leaves, kill the conference

Definition at line 658 of file app_meetme.c.

Referenced by conf_run().

#define CONFFLAG_NO_AUDIO_UNTIL_UP   (1ULL << 31)

Do not write any audio to this channel until the state is up.

Definition at line 654 of file app_meetme.c.

Referenced by can_write(), conf_run(), and sla_trunk_exec().

#define CONFIG_FILE_NAME   "meetme.conf"

Definition at line 549 of file app_meetme.c.

Referenced by conf_exec(), find_conf(), and load_config_meetme().

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

String format for scheduled conferences

Definition at line 557 of file app_meetme.c.

Referenced by conf_run(), find_conf_realtime(), and rt_extend_conf().

#define DEFAULT_AUDIO_BUFFERS   32

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

Definition at line 554 of file app_meetme.c.

Referenced by load_config_meetme().

#define MAX_CONFNUM   80
#define MAX_PIN   80

Definition at line 727 of file app_meetme.c.

Referenced by conf_exec(), and conf_get_pin().

#define MAX_SETTINGS   (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)

Definition at line 731 of file app_meetme.c.

Referenced by conf_exec(), and find_conf().

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

Referenced by meetme_show_cmd().

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

Referenced by meetme_show_cmd().

#define MEETME_DATA_EXPORT (   MEMBER)

Definition at line 7794 of file app_meetme.c.

#define MEETME_DELAYDETECTENDTALK   1000

Definition at line 569 of file app_meetme.c.

Referenced by conf_run().

#define MEETME_DELAYDETECTTALK   300

Definition at line 568 of file app_meetme.c.

Referenced by conf_run().

#define MEETME_USER_DATA_EXPORT (   MEMBER)

Definition at line 7811 of file app_meetme.c.

#define OPTIONS_LEN   100

Definition at line 728 of file app_meetme.c.

Referenced by find_conf_realtime().

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

Referenced by sms_readfile(), and trunkstate2str().

#define SLA_CONFIG_FILE   "sla.conf"

Definition at line 550 of file app_meetme.c.

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

#define STR_CONCISE   "concise"

Definition at line 551 of file app_meetme.c.

Referenced by complete_meetmecmd_list(), and meetme_show_cmd().


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

ADMINFLAG_HANGUP 

User will be leaving the conference

Definition at line 559 of file app_meetme.c.

     {
   ADMINFLAG_MUTED =     (1 << 1), /*!< User is muted */
   ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
   ADMINFLAG_KICKME =    (1 << 3),  /*!< User has been kicked */
   /*! User has requested to speak */
   ADMINFLAG_T_REQUEST = (1 << 4),
   ADMINFLAG_HANGUP = (1 << 5),  /*!< User will be leaving the conference */
};
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 channel will leave the conference if all marked users leave

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 

Definition at line 592 of file app_meetme.c.

     {
   /*! user has admin access on the conference */
   CONFFLAG_ADMIN = (1 << 0),
   /*! If set the user can only receive audio from the conference */
   CONFFLAG_MONITOR = (1 << 1),
   /*! If set asterisk will exit conference when key defined in p() option is pressed */
   CONFFLAG_KEYEXIT = (1 << 2),
   /*! If set asterisk will provide a menu to the user when '*' is pressed */
   CONFFLAG_STARMENU = (1 << 3),
   /*! If set the use can only send audio to the conference */
   CONFFLAG_TALKER = (1 << 4),
   /*! If set there will be no enter or leave sounds */
   CONFFLAG_QUIET = (1 << 5),
   /*! If set, when user joins the conference, they will be told the number 
    *  of users that are already in */
   CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
   /*! Set to run AGI Script in Background */
   CONFFLAG_AGI = (1 << 7),
   /*! Set to have music on hold when user is alone in conference */
   CONFFLAG_MOH = (1 << 8),
   /*! If set, the channel will leave the conference if all marked users leave */
   CONFFLAG_MARKEDEXIT = (1 << 9),
   /*! If set, the MeetMe will wait until a marked user enters */
   CONFFLAG_WAITMARKED = (1 << 10),
   /*! If set, the MeetMe will exit to the specified context */
   CONFFLAG_EXIT_CONTEXT = (1 << 11),
   /*! If set, the user will be marked */
   CONFFLAG_MARKEDUSER = (1 << 12),
   /*! If set, user will be ask record name on entry of conference */
   CONFFLAG_INTROUSER = (1 << 13),
   /*! If set, the MeetMe will be recorded */
   CONFFLAG_RECORDCONF = (1<< 14),
   /*! If set, the user will be monitored if the user is talking or not */
   CONFFLAG_MONITORTALKER = (1 << 15),
   CONFFLAG_DYNAMIC = (1 << 16),
   CONFFLAG_DYNAMICPIN = (1 << 17),
   CONFFLAG_EMPTY = (1 << 18),
   CONFFLAG_EMPTYNOPIN = (1 << 19),
   CONFFLAG_ALWAYSPROMPT = (1 << 20),
   /*! If set, treat talking users as muted users */
   CONFFLAG_OPTIMIZETALKER = (1 << 21),
   /*! If set, won't speak the extra prompt when the first person 
    *  enters the conference */
   CONFFLAG_NOONLYPERSON = (1 << 22),
   /*! If set, user will be asked to record name on entry of conference 
    *  without review */
   CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
   /*! If set, the user will be initially self-muted */
   CONFFLAG_STARTMUTED = (1 << 24),
   /*! Pass DTMF through the conference */
   CONFFLAG_PASS_DTMF = (1 << 25),
   CONFFLAG_SLA_STATION = (1 << 26),
   CONFFLAG_SLA_TRUNK = (1 << 27),
   /*! If set, the user should continue in the dialplan if kicked out */
   CONFFLAG_KICK_CONTINUE = (1 << 28),
   CONFFLAG_DURATION_STOP = (1 << 29),
   CONFFLAG_DURATION_LIMIT = (1 << 30),
};
anonymous enum
Enumerator:
OPT_ARG_WAITMARKED 
OPT_ARG_EXITKEYS 
OPT_ARG_DURATION_STOP 
OPT_ARG_DURATION_LIMIT 
OPT_ARG_MOH_CLASS 
OPT_ARG_INTROMSG 
OPT_ARG_INTROUSER_VMREC 
OPT_ARG_ARRAY_SIZE 

Definition at line 662 of file app_meetme.c.

anonymous enum
Enumerator:
SLA_TRUNK_OPT_MOH 

Definition at line 6991 of file app_meetme.c.

     {
   SLA_TRUNK_OPT_MOH = (1 << 0),
};
anonymous enum
Enumerator:
SLA_TRUNK_OPT_ARG_MOH_CLASS 
SLA_TRUNK_OPT_ARG_ARRAY_SIZE 

Definition at line 6995 of file app_meetme.c.

Enumerator:
CONF_HASJOIN 
CONF_HASLEFT 

Definition at line 733 of file app_meetme.c.

Enumerator:
ENTER 
LEAVE 

Definition at line 578 of file app_meetme.c.

                    {
   ENTER,
   LEAVE
};
enum menu_modes
Enumerator:
MENU_DISABLED 
MENU_NORMAL 
MENU_ADMIN 
MENU_ADMIN_EXTENDED 

Definition at line 2416 of file app_meetme.c.

Enumerator:
MEETME_RECORD_OFF 
MEETME_RECORD_STARTED 
MEETME_RECORD_ACTIVE 
MEETME_RECORD_TERMINATE 

Definition at line 583 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

Definition at line 945 of file app_meetme.c.

                    {
   /*! A station has put the call on hold */
   SLA_EVENT_HOLD,
   /*! The state of a dial has changed */
   SLA_EVENT_DIAL_STATE,
   /*! The state of a ringing trunk has changed */
   SLA_EVENT_RINGING_TRUNK,
};
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 838 of file app_meetme.c.

                     {
   /*! This means that any station can put it on hold, and any station
    * can retrieve the call from hold. */
   SLA_HOLD_OPEN,
   /*! This means that only the station that put the call on hold may
    * retrieve it from hold. */
   SLA_HOLD_PRIVATE,
};
Enumerator:
SLA_STATION_HANGUP_NORMAL 
SLA_STATION_HANGUP_TIMEOUT 

Definition at line 978 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 830 of file app_meetme.c.

Enumerator:
ALL_TRUNK_REFS 
INACTIVE_TRUNK_REFS 

Definition at line 825 of file app_meetme.c.

Enumerator:
VOL_UP 
VOL_DOWN 

Definition at line 573 of file app_meetme.c.


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 8047 of file app_meetme.c.

static void __unreg_module ( void  ) [static]

Definition at line 8047 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 7731 of file app_meetme.c.

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

{
   struct ast_conference *conf;
   char *parse;
   int result = -2; /* only non-negative numbers valid, -1 is used elsewhere */
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(keyword);
      AST_APP_ARG(confno);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
      return -1;
   }

   parse = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, parse);

   if (ast_strlen_zero(args.keyword)) {
      ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
      return -1;
   }

   if (ast_strlen_zero(args.confno)) {
      ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
      return -1;
   }

   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, conf, list) {
      if (!strcmp(args.confno, conf->confno)) {
         result = acf_meetme_info_eval(args.keyword, conf);
         break;
      }
   }
   AST_LIST_UNLOCK(&confs);

   if (result > -1) {
      snprintf(buf, len, "%d", result);
   } else if (result == -1) {
      ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
      snprintf(buf, len, "0");
   } else if (result == -2) {
      ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno); 
      snprintf(buf, len, "0");
   }

   return 0;
}
static int acf_meetme_info_eval ( const char *  keyword,
const struct ast_conference conf 
) [static]

Definition at line 7713 of file app_meetme.c.

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

Referenced by acf_meetme_info().

{
   if (!strcasecmp("lock", keyword)) {
      return conf->locked;
   } else if (!strcasecmp("parties", keyword)) {
      return conf->users;
   } else if (!strcasecmp("activity", keyword)) {
      time_t now;
      now = time(NULL);
      return (now - conf->start);
   } else if (!strcasecmp("dynamic", keyword)) {
      return conf->isdynamic;
   } else {
      return -1;
   }

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

Definition at line 5302 of file app_meetme.c.

References ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ast_conf_user::adminflags, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_channel_caller(), ast_channel_connected(), ast_channel_name(), AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), ast_test_flag64, astman_append(), astman_get_header(), astman_send_error(), astman_send_listack(), ast_conf_user::chan, CONFFLAG_ADMIN, CONFFLAG_MARKEDUSER, CONFFLAG_MONITOR, CONFFLAG_TALKER, ast_conference::confno, ast_party_caller::id, ast_party_connected_line::id, ast_party_id::name, ast_party_id::number, S_COR, ast_party_name::str, ast_party_number::str, ast_conf_user::talking, total, user, ast_conf_user::user_no, ast_conference::usercontainer, ast_conf_user::userflags, ast_party_name::valid, and ast_party_number::valid.

Referenced by load_module().

{
   const char *actionid = astman_get_header(m, "ActionID");
   const char *conference = astman_get_header(m, "Conference");
   char idText[80] = "";
   struct ast_conference *cnf;
   struct ast_conf_user *user;
   struct ao2_iterator user_iter;
   int total = 0;

   if (!ast_strlen_zero(actionid))
      snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);

   if (AST_LIST_EMPTY(&confs)) {
      astman_send_error(s, m, "No active conferences.");
      return 0;
   }

   astman_send_listack(s, m, "Meetme user list will follow", "start");

   /* Find the right conference */
   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, cnf, list) {
      /* If we ask for one particular, and this isn't it, skip it */
      if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
         continue;

      /* Show all the users */
      user_iter = ao2_iterator_init(cnf->usercontainer, 0);
      while ((user = ao2_iterator_next(&user_iter))) {
         total++;
         astman_append(s,
            "Event: MeetmeList\r\n"
            "%s"
            "Conference: %s\r\n"
            "UserNumber: %d\r\n"
            "CallerIDNum: %s\r\n"
            "CallerIDName: %s\r\n"
            "ConnectedLineNum: %s\r\n"
            "ConnectedLineName: %s\r\n"
            "Channel: %s\r\n"
            "Admin: %s\r\n"
            "Role: %s\r\n"
            "MarkedUser: %s\r\n"
            "Muted: %s\r\n"
            "Talking: %s\r\n"
            "\r\n",
            idText,
            cnf->confno,
            user->user_no,
            S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
            S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
            S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
            S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<no name>"),
            ast_channel_name(user->chan),
            ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
            ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
            ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
            user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
            user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
         ao2_ref(user, -1);
      }
      ao2_iterator_destroy(&user_iter);
   }
   AST_LIST_UNLOCK(&confs);
   /* Send final confirmation */
   astman_append(s,
   "Event: MeetmeListComplete\r\n"
   "EventList: Complete\r\n"
   "ListItems: %d\r\n"
   "%s"
   "\r\n", total, idText);
   return 0;
}
static int action_meetmelistrooms ( struct mansession s,
const struct message m 
) [static]

Definition at line 5377 of file app_meetme.c.

References 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_conference::confno, ast_conference::isdynamic, ast_conference::list, ast_conference::locked, ast_conference::markedusers, ast_conference::start, and ast_conference::users.

Referenced by load_module().

{
   const char *actionid = astman_get_header(m, "ActionID");
   char idText[80] = "";
   struct ast_conference *cnf;
   int totalitems = 0;
   int hr, min, sec;
   time_t now;
   char markedusers[5];

   if (!ast_strlen_zero(actionid)) {
      snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
   }

   if (AST_LIST_EMPTY(&confs)) {
      astman_send_error(s, m, "No active conferences.");
      return 0;
   }

   astman_send_listack(s, m, "Meetme conferences will follow", "start");

   now = time(NULL);

   /* Traverse the conference list */
   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, cnf, list) {
      totalitems++;

      if (cnf->markedusers == 0) {
         strcpy(markedusers, "N/A");
      } else {
         sprintf(markedusers, "%.4d", cnf->markedusers);
      }
      hr = (now - cnf->start) / 3600;
      min = ((now - cnf->start) % 3600) / 60;
      sec = (now - cnf->start) % 60;

      astman_append(s,
      "Event: MeetmeListRooms\r\n"
      "%s"
      "Conference: %s\r\n"
      "Parties: %d\r\n"
      "Marked: %s\r\n"
      "Activity: %2.2d:%2.2d:%2.2d\r\n"
      "Creation: %s\r\n"
      "Locked: %s\r\n"
      "\r\n",
      idText,
      cnf->confno,
      cnf->users,
      markedusers,
      hr,  min, sec,
      cnf->isdynamic ? "Dynamic" : "Static",
      cnf->locked ? "Yes" : "No"); 
   }
   AST_LIST_UNLOCK(&confs);

   /* Send final confirmation */
   astman_append(s,
   "Event: MeetmeListRoomsComplete\r\n"
   "EventList: Complete\r\n"
   "ListItems: %d\r\n"
   "%s"
   "\r\n", totalitems, idText);
   return 0;
}
static int action_meetmemute ( struct mansession s,
const struct message m 
) [static]

Definition at line 5292 of file app_meetme.c.

References meetmemute().

Referenced by load_module().

{
   return meetmemute(s, m, 1);
}
static int action_meetmeunmute ( struct mansession s,
const struct message m 
) [static]

Definition at line 5297 of file app_meetme.c.

References meetmemute().

Referenced by load_module().

{
   return meetmemute(s, m, 0);
}
static int admin_exec ( struct ast_channel chan,
const char *  data 
) [static]

The MeetMeadmin application.

MeetMeAdmin(confno, command, caller)

Definition at line 5006 of file app_meetme.c.

References ADMINFLAG_KICKME, ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ADMINFLAG_T_REQUEST, ast_conf_user::adminflags, ao2_callback, ao2_cleanup, ao2_find, ao2_ref, args, AST_APP_ARG, ast_atomic_fetchadd_int(), AST_DECLARE_APP_ARGS, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_test_flag64, CONFFLAG_ADMIN, ast_conference::confno, dispose_conf(), find_user(), ast_conf_user::list, ast_conference::locked, LOG_NOTICE, LOG_WARNING, OBJ_NODATA, pbx_builtin_setvar_helper(), ast_conference::refcount, reset_volumes(), rt_extend_conf(), tweak_listen_volume(), tweak_talk_volume(), user_listen_voldown_cb(), user_listen_volup_cb(), user_max_cmp(), user_reset_vol_cb(), user_set_kickme_cb(), user_set_muted_cb(), user_set_unmuted_cb(), user_talk_voldown_cb(), user_talk_volup_cb(), ast_conference::usercontainer, VOL_DOWN, and VOL_UP.

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

                                                                  {
   char *params;
   struct ast_conference *cnf;
   struct ast_conf_user *user = NULL;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(confno);
      AST_APP_ARG(command);
      AST_APP_ARG(user);
   );
   int res = 0;

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
      pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
      return -1;
   }

   params = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, params);

   if (!args.command) {
      ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
      pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
      return -1;
   }

   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, cnf, list) {
      if (!strcmp(cnf->confno, args.confno))
         break;
   }

   if (!cnf) {
      ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
      AST_LIST_UNLOCK(&confs);
      pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
      return 0;
   }

   ast_atomic_fetchadd_int(&cnf->refcount, 1);

   if (args.user) {
      user = find_user(cnf, args.user);
      if (!user) {
         ast_log(LOG_NOTICE, "Specified User not found!\n");
         res = -2;
         goto usernotfound;
      }
   } else {
      /* fail for commands that require a user */
      switch (*args.command) {
      case 'm': /* Unmute */
      case 'M': /* Mute */
      case 't': /* Lower user's talk volume */
      case 'T': /* Raise user's talk volume */
      case 'u': /* Lower user's listen volume */
      case 'U': /* Raise user's listen volume */
      case 'r': /* Reset user's volume level */
      case 'k': /* Kick user */
         res = -2;
         ast_log(LOG_NOTICE, "No user specified!\n");
         goto usernotfound;
      default:
         break;
      }
   }

   switch (*args.command) {
   case 76: /* L: Lock */ 
      cnf->locked = 1;
      break;
   case 108: /* l: Unlock */ 
      cnf->locked = 0;
      break;
   case 75: /* K: kick all users */
      ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
      break;
   case 101: /* e: Eject last user*/
   {
      int max_no = 0;
      RAII_VAR(struct ast_conf_user *, eject_user, NULL, ao2_cleanup);

      ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
      eject_user = ao2_find(cnf->usercontainer, &max_no, 0);
      if (!eject_user) {
         res = -1;
         ast_log(LOG_NOTICE, "No last user to kick!\n");
         break;
      }

      if (!ast_test_flag64(&eject_user->userflags, CONFFLAG_ADMIN)) {
         eject_user->adminflags |= ADMINFLAG_KICKME;
      } else {
         res = -1;
         ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
      }
      break;
   }
   case 77: /* M: Mute */ 
      user->adminflags |= ADMINFLAG_MUTED;
      break;
   case 78: /* N: Mute all (non-admin) users */
      ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, &cnf);
      break;               
   case 109: /* m: Unmute */ 
      user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
      break;
   case 110: /* n: Unmute all users */
      ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
      break;
   case 107: /* k: Kick user */ 
      user->adminflags |= ADMINFLAG_KICKME;
      break;
   case 118: /* v: Lower all users listen volume */
      ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
      break;
   case 86: /* V: Raise all users listen volume */
      ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
      break;
   case 115: /* s: Lower all users speaking volume */
      ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
      break;
   case 83: /* S: Raise all users speaking volume */
      ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
      break;
   case 82: /* R: Reset all volume levels */
      ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
      break;
   case 114: /* r: Reset user's volume level */
      reset_volumes(user);
      break;
   case 85: /* U: Raise user's listen volume */
      tweak_listen_volume(user, VOL_UP);
      break;
   case 117: /* u: Lower user's listen volume */
      tweak_listen_volume(user, VOL_DOWN);
      break;
   case 84: /* T: Raise user's talk volume */
      tweak_talk_volume(user, VOL_UP);
      break;
   case 116: /* t: Lower user's talk volume */
      tweak_talk_volume(user, VOL_DOWN);
      break;
   case 'E': /* E: Extend conference */
      if (rt_extend_conf(args.confno)) {
         res = -1;
      }
      break;
   }

   if (args.user) {
      /* decrement reference from find_user */
      ao2_ref(user, -1);
   }
usernotfound:
   AST_LIST_UNLOCK(&confs);

   dispose_conf(cnf);
   pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");

   return 0;
}
static void* announce_thread ( void *  data) [static]

Definition at line 2260 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_debug, 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_mutex_lock, ast_mutex_unlock, ast_streamfile(), ast_waitstream(), CONF_HASLEFT, announce_listitem::confchan, announce_listitem::confusers, get_announce_filename(), announce_listitem::language, announce_listitem::namerecloc, and announce_listitem::vmrec.

Referenced by conf_run().

{
   struct announce_listitem *current;
   struct ast_conference *conf = data;
   int res;
   char filename[PATH_MAX] = "";
   AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
   AST_LIST_HEAD_INIT_NOLOCK(&local_list);

   while (!conf->announcethread_stop) {
      ast_mutex_lock(&conf->announcelistlock);
      if (conf->announcethread_stop) {
         ast_mutex_unlock(&conf->announcelistlock);
         break;
      }
      if (AST_LIST_EMPTY(&conf->announcelist))
         ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);

      AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
      AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);

      ast_mutex_unlock(&conf->announcelistlock);
      if (conf->announcethread_stop) {
         break;
      }

      for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
         ast_debug(1, "About to play %s\n", current->namerecloc);
         if (!ast_fileexists(current->namerecloc, NULL, NULL))
            continue;
         if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
            if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
               res = ast_waitstream(current->confchan, "");
            if (!res) {
               ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
               if (!ast_streamfile(current->confchan, filename, current->language))
                  ast_waitstream(current->confchan, "");
            }
         }
         if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
            /* only remove it if it isn't a VM recording file */
            ast_filedelete(current->namerecloc, NULL);
         }
      }
   }

   /* thread marked to stop, clean up */
   while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
      /* only delete if it's a vm rec */
      if (!current->vmrec) {
         ast_filedelete(current->namerecloc, NULL);
      }
      ao2_ref(current, -1);
   }
   return NULL;
}
static void answer_trunk_chan ( struct ast_channel chan) [static]

Definition at line 5838 of file app_meetme.c.

References ast_answer(), and ast_indicate().

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

{
   ast_answer(chan);
   ast_indicate(chan, -1);
}
static struct ast_conference* build_conf ( const char *  confno,
const char *  pin,
const char *  pinadmin,
int  make,
int  dynamic,
int  refcount,
const struct ast_channel chan,
struct ast_test *  test 
) [static, read]

Find or create a conference.

Parameters:
confnoThe conference name/number
pinThe regular user pin
pinadminThe admin pin
makeMake the conf if it doesn't exist
dynamicMark the newly created conference as dynamic
refcountHow many references to mark on the conference
chanThe 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 1247 of file app_meetme.c.

References ast_conference::announcethread, ast_conference::announcethreadlock, ao2_container_alloc, ao2_ref, ast_atomic_fetchadd_int(), ast_calloc, ast_channel_fd(), ast_channel_uniqueid(), ast_copy_string(), ast_format_cap_add(), ast_format_cap_alloc_nolock(), ast_format_cap_destroy(), ast_format_set(), AST_FORMAT_SLINEAR, ast_free, ast_hangup(), AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_destroy, ast_mutex_init, AST_PTHREADT_NULL, ast_request(), ast_set_read_format_by_id(), ast_set_write_format_by_id(), ast_test_status_update, ast_verb, ast_conference::chan, conf_map, ast_conference::confno, ast_conference::dahdiconf, ast_conference::fd, 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_conference::uniqueid, user_no_cmp(), and ast_conference::usercontainer.

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

{
   struct ast_conference *cnf;
   struct dahdi_confinfo dahdic = { 0, };
   int confno_int = 0;
   struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
   struct ast_format tmp_fmt;

   AST_LIST_LOCK(&confs);

   AST_LIST_TRAVERSE(&confs, cnf, list) {
      if (!strcmp(confno, cnf->confno)) 
         break;
   }

   if (cnf || (!make && !dynamic) || !cap_slin)
      goto cnfout;

   ast_format_cap_add(cap_slin, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0));
   /* Make a new one */
   if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
      !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
      goto cnfout;
   }

   ast_mutex_init(&cnf->playlock);
   ast_mutex_init(&cnf->listenlock);
   cnf->recordthread = AST_PTHREADT_NULL;
   ast_mutex_init(&cnf->recordthreadlock);
   cnf->announcethread = AST_PTHREADT_NULL;
   ast_mutex_init(&cnf->announcethreadlock);
   ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
   ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
   ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
   ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan), sizeof(cnf->uniqueid));

   /* Setup a new dahdi conference */
   dahdic.confno = -1;
   dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
   cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
   if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
      if (test) {
         /* if we are creating a conference for a unit test, it is not neccesary
          * to open a pseudo channel, so, if we fail continue creating
          * the conference. */
         ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
      } else {
         ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
         if (cnf->fd >= 0)
            close(cnf->fd);
         ao2_ref(cnf->usercontainer, -1);
         ast_mutex_destroy(&cnf->playlock);
         ast_mutex_destroy(&cnf->listenlock);
         ast_mutex_destroy(&cnf->recordthreadlock);
         ast_mutex_destroy(&cnf->announcethreadlock);
         ast_free(cnf);
         cnf = NULL;
         goto cnfout;
      }
   }

   cnf->dahdiconf = dahdic.confno;

   /* Setup a new channel for playback of audio files */
   cnf->chan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL);
   if (cnf->chan) {
      ast_set_read_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
      ast_set_write_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
      dahdic.chan = 0;
      dahdic.confno = cnf->dahdiconf;
      dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
      if (ioctl(ast_channel_fd(cnf->chan, 0), DAHDI_SETCONF, &dahdic)) {
         if (test) {
            ast_test_status_update(test, "Error setting conference on pseudo channel\n");
         }
         ast_log(LOG_WARNING, "Error setting conference\n");
         if (cnf->chan)
            ast_hangup(cnf->chan);
         else
            close(cnf->fd);
         ao2_ref(cnf->usercontainer, -1);
         ast_mutex_destroy(&cnf->playlock);
         ast_mutex_destroy(&cnf->listenlock);
         ast_mutex_destroy(&cnf->recordthreadlock);
         ast_mutex_destroy(&cnf->announcethreadlock);
         ast_free(cnf);
         cnf = NULL;
         goto cnfout;
      }
   }

   /* Fill the conference struct */
   cnf->start = time(NULL);
   cnf->maxusers = 0x7fffffff;
   cnf->isdynamic = dynamic ? 1 : 0;
   ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
   AST_LIST_INSERT_HEAD(&confs, cnf, list);

   /* Reserve conference number in map */
   if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
      conf_map[confno_int] = 1;
   
cnfout:
   cap_slin = ast_format_cap_destroy(cap_slin);
   if (cnf)
      ast_atomic_fetchadd_int(&cnf->refcount, refcount);

   AST_LIST_UNLOCK(&confs);

   return cnf;
}
static int can_write ( struct ast_channel chan,
struct ast_flags64 confflags 
) [static]

Definition at line 2317 of file app_meetme.c.

References AST_STATE_UP, ast_test_flag64, and CONFFLAG_NO_AUDIO_UNTIL_UP.

Referenced by conf_run().

{
   if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
      return 1;
   }

   return (ast_channel_state(chan) == AST_STATE_UP);
}
static int careful_write ( int  fd,
unsigned char *  data,
int  len,
int  block 
) [static]

Definition at line 1050 of file app_meetme.c.

References ast_log(), errno, and LOG_WARNING.

Referenced by conf_play(), and conf_run().

{
   int res;
   int x;

   while (len) {
      if (block) {
         x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
         res = ioctl(fd, DAHDI_IOMUX, &x);
      } else
         res = 0;
      if (res >= 0)
         res = write(fd, data, len);
      if (res < 1) {
         if (errno != EAGAIN) {
            ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
            return -1;
         } else
            return 0;
      }
      len -= res;
      data += res;
   }

   return 0;
}
static int channel_admin_exec ( struct ast_channel chan,
const char *  data 
) [static]

The MeetMeChannelAdmin application MeetMeChannelAdmin(channel, command)

Definition at line 5171 of file app_meetme.c.

References ADMINFLAG_KICKME, ADMINFLAG_MUTED, ast_conf_user::adminflags, ao2_callback, ao2_ref, args, AST_APP_ARG, AST_DECLARE_APP_ARGS, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_conf_user::list, LOG_NOTICE, LOG_WARNING, user_chan_cb(), and ast_conference::usercontainer.

Referenced by load_module().

                                                                          {
   char *params;
   struct ast_conference *conf = NULL;
   struct ast_conf_user *user = NULL;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(channel);
      AST_APP_ARG(command);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
      return -1;
   }
   
   params = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, params);

   if (!args.channel) {
      ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
      return -1;
   }

   if (!args.command) {
      ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
      return -1;
   }

   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, conf, list) {
      if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
         break;
      }
   }
   
   if (!user) {
      ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
      AST_LIST_UNLOCK(&confs);
      return 0;
   }
   
   /* perform the specified action */
   switch (*args.command) {
      case 77: /* M: Mute */ 
         user->adminflags |= ADMINFLAG_MUTED;
         break;
      case 109: /* m: Unmute */ 
         user->adminflags &= ~ADMINFLAG_MUTED;
         break;
      case 107: /* k: Kick user */ 
         user->adminflags |= ADMINFLAG_KICKME;
         break;
      default: /* unknown command */
         ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
         break;
   }
   ao2_ref(user, -1);
   AST_LIST_UNLOCK(&confs);
   
   return 0;
}
static char* complete_confno ( const char *  word,
int  state 
) [static]

Definition at line 1361 of file app_meetme.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, ast_conference::confno, len(), and ast_conference::list.

Referenced by complete_meetmecmd_list(), complete_meetmecmd_lock(), and complete_meetmecmd_mute_kick().

{
   struct ast_conference *cnf;
   char *ret = NULL;
   int which = 0;
   int len = strlen(word);

   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, cnf, list) {
      if (!strncmp(word, cnf->confno, len) && ++which > state) {
         /* dup before releasing the lock */
         ret = ast_strdup(cnf->confno);
         break;
      }
   }
   AST_LIST_UNLOCK(&confs);
   return ret;
}
static char* complete_meetmecmd_list ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 1450 of file app_meetme.c.

References ast_strdup, complete_confno(), ast_conference::confno, len(), state, and STR_CONCISE.

Referenced by meetme_show_cmd().

{
   int len;

   if (pos == 2) {
      len = strlen(word);
      if (!strncasecmp(word, STR_CONCISE, len)) {
         if (state == 0) {
            return ast_strdup(STR_CONCISE);
         }
         --state;
      }

      return complete_confno(word, state);
   }
   if (pos == 3 && state == 0) {
      char *saved = NULL;
      char *myline;
      char *confno;

      /* Extract the confno from the command line. */
      myline = ast_strdupa(line);
      strtok_r(myline, " ", &saved);
      strtok_r(NULL, " ", &saved);
      confno = strtok_r(NULL, " ", &saved);

      if (!strcasecmp(confno, STR_CONCISE)) {
         /* There is nothing valid in this position now. */
         return NULL;
      }

      len = strlen(word);
      if (!strncasecmp(word, STR_CONCISE, len)) {
         return ast_strdup(STR_CONCISE);
      }
   }
   return NULL;
}
static char* complete_meetmecmd_lock ( const char *  word,
int  pos,
int  state 
) [static]

Definition at line 1442 of file app_meetme.c.

References complete_confno().

Referenced by meetme_lock_cmd().

{
   if (pos == 2) {
      return complete_confno(word, state);
   }
   return NULL;
}
static char* complete_meetmecmd_mute_kick ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 1402 of file app_meetme.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, complete_confno(), complete_userno(), ast_conference::confno, len(), ast_conference::list, and state.

Referenced by meetme_kick_cmd(), and meetme_mute_cmd().

{
   if (pos == 2) {
      return complete_confno(word, state);
   }
   if (pos == 3) {
      int len = strlen(word);
      char *ret = NULL;
      char *saved = NULL;
      char *myline;
      char *confno;
      struct ast_conference *cnf;

      if (!strncasecmp(word, "all", len)) {
         if (state == 0) {
            return ast_strdup("all");
         }
         --state;
      }

      /* Extract the confno from the command line. */
      myline = ast_strdupa(line);
      strtok_r(myline, " ", &saved);
      strtok_r(NULL, " ", &saved);
      confno = strtok_r(NULL, " ", &saved);

      AST_LIST_LOCK(&confs);
      AST_LIST_TRAVERSE(&confs, cnf, list) {
         if (!strcmp(confno, cnf->confno)) {
            ret = complete_userno(cnf, word, state);
            break;
         }
      }
      AST_LIST_UNLOCK(&confs);

      return ret;
   }
   return NULL;
}
static char* complete_userno ( struct ast_conference cnf,
const char *  word,
int  state 
) [static]

Definition at line 1380 of file app_meetme.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_strdup, len(), ast_conf_user::user_no, and ast_conference::usercontainer.

Referenced by complete_meetmecmd_mute_kick().

{
   char usrno[50];
   struct ao2_iterator iter;
   struct ast_conf_user *usr;
   char *ret = NULL;
   int which = 0;
   int len = strlen(word);

   iter = ao2_iterator_init(cnf->usercontainer, 0);
   for (; (usr = ao2_iterator_next(&iter)); ao2_ref(usr, -1)) {
      snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
      if (!strncmp(word, usrno, len) && ++which > state) {
         ao2_ref(usr, -1);
         ret = ast_strdup(usrno);
         break;
      }
   }
   ao2_iterator_destroy(&iter);
   return ret;
}
static int conf_exec ( struct ast_channel chan,
const char *  data 
) [static]

The meetme() application.

Definition at line 4618 of file app_meetme.c.

References ast_conference::adminopts, args, ARRAY_LEN, ast_answer(), AST_APP_ARG, ast_app_getdata(), ast_app_parse_options64(), ast_category_browse(), ast_channel_language(), ast_channel_name(), 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_load_realtime_multientry(), ast_log(), ast_say_digits(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stopstream(), ast_streamfile(), ast_strlen_zero(), ast_test_flag64, ast_test_suite_event_notify, ast_variable_browse(), ast_variable_retrieve(), ast_verb, ast_waitstream(), conf_map, conf_run(), CONFFLAG_ADMIN, CONFFLAG_ALWAYSPROMPT, CONFFLAG_DYNAMIC, CONFFLAG_DYNAMICPIN, CONFFLAG_EMPTY, CONFFLAG_EMPTYNOPIN, CONFFLAG_QUIET, CONFIG_FILE_NAME, CONFIG_STATUS_FILEINVALID, ast_conference::confno, dispose_conf(), find_conf(), find_conf_realtime(), ast_conference::isdynamic, LOG_ERROR, LOG_WARNING, MAX_CONFNUM, MAX_PIN, MAX_SETTINGS, meetme_opts, ast_variable::name, ast_variable::next, OPT_ARG_ARRAY_SIZE, parse(), ast_conference::pin, ast_conference::pinadmin, ast_conference::recordingfilename, ast_conference::recordingformat, SENTINEL, ast_conference::useropts, ast_conference::users, ast_variable::value, and var.

Referenced by load_module().

{
   int res = -1;
   char confno[MAX_CONFNUM] = "";
   int allowretry = 0;
   int retrycnt = 0;
   struct ast_conference *cnf = NULL;
   struct ast_flags64 confflags = {0};
   struct ast_flags config_flags = { 0 };
   int dynamic = 0;
   int empty = 0, empty_no_pin = 0;
   int always_prompt = 0;
   const char *notdata;
   char *info, the_pin[MAX_PIN] = "";
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(confno);
      AST_APP_ARG(options);
      AST_APP_ARG(pin);
   );
   char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };

   if (ast_strlen_zero(data)) {
      allowretry = 1;
      notdata = "";
   } else {
      notdata = data;
   }
   
   if (ast_channel_state(chan) != AST_STATE_UP)
      ast_answer(chan);

   info = ast_strdupa(notdata);

   AST_STANDARD_APP_ARGS(args, info);  

   if (args.confno) {
      ast_copy_string(confno, args.confno, sizeof(confno));
      if (ast_strlen_zero(confno)) {
         allowretry = 1;
      }
   }
   
   if (args.pin)
      ast_copy_string(the_pin, args.pin, sizeof(the_pin));

   if (args.options) {
      ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
      dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
      if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
         strcpy(the_pin, "q");

      empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
      empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
      always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
   }

   do {
      if (retrycnt > 3)
         allowretry = 0;
      if (empty) {
         int i;
         struct ast_config *cfg;
         struct ast_variable *var;
         int confno_int;

         /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
         if ((empty_no_pin) || (!dynamic)) {
            cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
            if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
               var = ast_variable_browse(cfg, "rooms");
               while (var) {
                  char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
                  if (!strcasecmp(var->name, "conf")) {
                     int found = 0;
                     ast_copy_string(parse, var->value, sizeof(parse));
                     confno_tmp = strsep(&stringp, "|,");
                     if (!dynamic) {
                        /* For static:  run through the list and see if this conference is empty */
                        AST_LIST_LOCK(&confs);
                        AST_LIST_TRAVERSE(&confs, cnf, list) {
                           if (!strcmp(confno_tmp, cnf->confno)) {
                              /* The conference exists, therefore it's not empty */
                              found = 1;
                              break;
                           }
                        }
                        AST_LIST_UNLOCK(&confs);
                        cnf = NULL;
                        if (!found) {
                           /* At this point, we have a confno_tmp (static conference) that is empty */
                           if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
                              /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
                               * Case 2:  empty_no_pin and pin is blank (but not NULL)
                               * Case 3:  not empty_no_pin
                               */
                              ast_copy_string(confno, confno_tmp, sizeof(confno));
                              break;
                           }
                        }
                     }
                  }
                  var = var->next;
               }
               ast_config_destroy(cfg);
            }

            if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
               const char *catg;
               for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
                  const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
                  const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
                  if (ast_strlen_zero(confno_tmp)) {
                     continue;
                  }
                  if (!dynamic) {
                     int found = 0;
                     /* For static:  run through the list and see if this conference is empty */
                     AST_LIST_LOCK(&confs);
                     AST_LIST_TRAVERSE(&confs, cnf, list) {
                        if (!strcmp(confno_tmp, cnf->confno)) {
                           /* The conference exists, therefore it's not empty */
                           found = 1;
                           break;
                        }
                     }
                     AST_LIST_UNLOCK(&confs);
                     if (!found) {
                        /* At this point, we have a confno_tmp (realtime conference) that is empty */
                        if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
                           /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
                            * Case 2:  empty_no_pin and pin is blank (but not NULL)
                            * Case 3:  not empty_no_pin
                            */
                           ast_copy_string(confno, confno_tmp, sizeof(confno));
                           break;
                        }
                     }
                  }
               }
               ast_config_destroy(cfg);
            }
         }

         /* Select first conference number not in use */
         if (ast_strlen_zero(confno) && dynamic) {
            AST_LIST_LOCK(&confs);
            for (i = 0; i < ARRAY_LEN(conf_map); i++) {
               if (!conf_map[i]) {
                  snprintf(confno, sizeof(confno), "%d", i);
                  conf_map[i] = 1;
                  break;
               }
            }
            AST_LIST_UNLOCK(&confs);
         }

         /* Not found? */
         if (ast_strlen_zero(confno)) {
            res = ast_streamfile(chan, "conf-noempty", ast_channel_language(chan));
            ast_test_suite_event_notify("PLAYBACK", "Message: conf-noempty");
            if (!res)
               ast_waitstream(chan, "");
         } else {
            if (sscanf(confno, "%30d", &confno_int) == 1) {
               if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
                  res = ast_streamfile(chan, "conf-enteringno", ast_channel_language(chan));
                  if (!res) {
                     ast_waitstream(chan, "");
                     res = ast_say_digits(chan, confno_int, "", ast_channel_language(chan));
                  }
               }
            } else {
               ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
            }
         }
      }

      while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
         /* Prompt user for conference number */
         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
         if (res < 0) {
            /* Don't try to validate when we catch an error */
            confno[0] = '\0';
            allowretry = 0;
            break;
         }
      }
      if (!ast_strlen_zero(confno)) {
         /* Check the validity of the conference */
         cnf = find_conf(chan, confno, 1, dynamic, the_pin, 
            sizeof(the_pin), 1, &confflags);
         if (!cnf) {
            int too_early = 0;

            cnf = find_conf_realtime(chan, confno, 1, dynamic, 
               the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
            if (rt_schedule && too_early)
               allowretry = 0;
         }

         if (!cnf) {
            if (allowretry) {
               confno[0] = '\0';
               res = ast_streamfile(chan, "conf-invalid", ast_channel_language(chan));
               if (!res)
                  ast_waitstream(chan, "");
               res = -1;
            }
         } else {
            /* Conference requires a pin for specified access level */
            int req_pin = !ast_strlen_zero(cnf->pin) ||
               (!ast_strlen_zero(cnf->pinadmin) &&
                  ast_test_flag64(&confflags, CONFFLAG_ADMIN));
            /* The following logic was derived from a
             * 4 variable truth table and defines which
             * circumstances are not exempt from pin
             * checking.
             * If this needs to be modified, write the
             * truth table back out from the boolean
             * expression AB+A'D+C', change the erroneous
             * result, and rederive the expression.
             * Variables:
             *  A: pin provided?
             *  B: always prompt?
             *  C: dynamic?
             *  D: has users? */
            int not_exempt = !cnf->isdynamic;
            not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
            not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
            if (req_pin && not_exempt) {
               char pin[MAX_PIN] = "";
               int j;

               /* Allow the pin to be retried up to 3 times */
               for (j = 0; j < 3; j++) {
                  if (*the_pin && (always_prompt == 0)) {
                     ast_copy_string(pin, the_pin, sizeof(pin));
                     res = 0;
                  } else {
                     /* Prompt user for pin if pin is required */
                     ast_test_suite_event_notify("PLAYBACK", "Message: conf-getpin\r\n"
                        "Channel: %s",
                        ast_channel_name(chan));
                     res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
                  }
                  if (res >= 0) {
                     if ((!strcasecmp(pin, cnf->pin) &&
                          (ast_strlen_zero(cnf->pinadmin) ||
                           !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
                          (!ast_strlen_zero(cnf->pinadmin) &&
                           !strcasecmp(pin, cnf->pinadmin))) {
                        /* Pin correct */
                        allowretry = 0;
                        if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
                           if (!ast_strlen_zero(cnf->adminopts)) {
                              char *opts = ast_strdupa(cnf->adminopts);
                              ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
                           }
                        } else {
                           if (!ast_strlen_zero(cnf->useropts)) {
                              char *opts = ast_strdupa(cnf->useropts);
                              ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
                           }
                        }
                        /* Run the conference */
                        ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
                        res = conf_run(chan, cnf, &confflags, optargs);
                        break;
                     } else {
                        /* Pin invalid */
                        if (!ast_streamfile(chan, "conf-invalidpin", ast_channel_language(chan))) {
                           res = ast_waitstream(chan, AST_DIGIT_ANY);
                           ast_stopstream(chan);
                        } else {
                           ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
                           break;
                        }
                        if (res < 0)
                           break;
                        pin[0] = res;
                        pin[1] = '\0';
                        res = -1;
                        if (allowretry)
                           confno[0] = '\0';
                     }
                  } else {
                     /* failed when getting the pin */
                     res = -1;
                     allowretry = 0;
                     /* see if we need to get rid of the conference */
                     break;
                  }

                  /* Don't retry pin with a static pin */
                  if (*the_pin && (always_prompt == 0)) {
                     break;
                  }
               }
            } else {
               /* No pin required */
               allowretry = 0;

               /* For RealTime conferences without a pin 
                * should still support loading options
                */
               if (!ast_strlen_zero(cnf->useropts)) {
                  char *opts = ast_strdupa(cnf->useropts);
                  ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
               }

               /* Run the conference */
               res = conf_run(chan, cnf, &confflags, optargs);
            }
            dispose_conf(cnf);
            cnf = NULL;
         }
      }
   } while (allowretry);

   if (cnf)
      dispose_conf(cnf);
   
   return res;
}
static void conf_flush ( int  fd,
struct ast_channel chan 
) [static]

Definition at line 1932 of file app_meetme.c.

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

Referenced by conf_run().

{
   int x;

   /* read any frames that may be waiting on the channel
      and throw them away
   */
   if (chan) {
      struct ast_frame *f;

      /* when no frames are available, this will wait
         for 1 millisecond maximum
      */
      while (ast_waitfor(chan, 1) > 0) {
         f = ast_read(chan);
         if (f)
            ast_frfree(f);
         else /* channel was hung up or something else happened */
            break;
      }
   }

   /* flush any data sitting in the pseudo channel */
   x = DAHDI_FLUSH_ALL;
   if (ioctl(fd, DAHDI_FLUSH, &x))
      ast_log(LOG_WARNING, "Error flushing channel\n");

}
static int conf_free ( struct ast_conference conf) [static]

Remove the conference from the list and free it.

We assume that this was called while holding conflock.

Definition at line 1964 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::recording, ast_conference::recordingfilename, ast_conference::recordingformat, ast_conference::recordthreadlock, ast_conference::transframe, ast_conference::transpath, ast_conference::usercontainer, and announce_listitem::vmrec.

Referenced by dispose_conf().

{
   int x;
   struct announce_listitem *item;
   
   AST_LIST_REMOVE(&confs, conf, list);
   /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when a MeetMe conference ends.</synopsis>
         <syntax>
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
         </syntax>
         <see-also>
            <ref type="managerEvent">MeetmeJoin</ref>
         </see-also>
      </managerEventInstance>
   ***/
   manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);

   if (conf->recording == MEETME_RECORD_ACTIVE) {
      conf->recording = MEETME_RECORD_TERMINATE;
      AST_LIST_UNLOCK(&confs);
      while (1) {
         usleep(1);
         AST_LIST_LOCK(&confs);
         if (conf->recording == MEETME_RECORD_OFF)
            break;
         AST_LIST_UNLOCK(&confs);
      }
   }

   for (x = 0; x < AST_FRAME_BITS; x++) {
      if (conf->transframe[x])
         ast_frfree(conf->transframe[x]);
      if (conf->transpath[x])
         ast_translator_free_path(conf->transpath[x]);
   }
   if (conf->announcethread != AST_PTHREADT_NULL) {
      ast_mutex_lock(&conf->announcelistlock);
      conf->announcethread_stop = 1;
      ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
      ast_cond_signal(&conf->announcelist_addition);
      ast_mutex_unlock(&conf->announcelistlock);
      pthread_join(conf->announcethread, NULL);
   
      while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
         /* If it's a voicemail greeting file we don't want to remove it */
         if (!item->vmrec){
            ast_filedelete(item->namerecloc, NULL);
         }
         ao2_ref(item, -1);
      }
      ast_mutex_destroy(&conf->announcelistlock);
   }

   if (conf->origframe)
      ast_frfree(conf->origframe);
   if (conf->lchan)
      ast_hangup(conf->lchan);
   if (conf->chan)
      ast_hangup(conf->chan);
   if (conf->fd >= 0)
      close(conf->fd);
   if (conf->recordingfilename) {
      ast_free(conf->recordingfilename);
   }
   if (conf->usercontainer) {
      ao2_ref(conf->usercontainer, -1);
   }
   if (conf->recordingformat) {
      ast_free(conf->recordingformat);
   }
   ast_mutex_destroy(&conf->playlock);
   ast_mutex_destroy(&conf->listenlock);
   ast_mutex_destroy(&conf->recordthreadlock);
   ast_mutex_destroy(&conf->announcethreadlock);
   ast_free(conf);

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

Definition at line 1168 of file app_meetme.c.

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

Referenced by conf_run().

{
   unsigned char *data;
   int len;
   int res = -1;

   ast_test_suite_event_notify("CONFPLAY", "Channel: %s\r\n"
      "Conference: %s\r\n"
      "Marked: %d",
      ast_channel_name(chan),
      conf->confno,
      conf->markedusers);

   if (!ast_check_hangup(chan))
      res = ast_autoservice_start(chan);

   AST_LIST_LOCK(&confs);

   switch(sound) {
   case ENTER:
      data = enter;
      len = sizeof(enter);
      break;
   case LEAVE:
      data = leave;
      len = sizeof(leave);
      break;
   default:
      data = NULL;
      len = 0;
   }
   if (data) {
      careful_write(conf->fd, data, len, 1);
   }

   AST_LIST_UNLOCK(&confs);

   if (!res) 
      ast_autoservice_stop(chan);
}
static void conf_queue_dtmf ( const struct ast_conference conf,
const struct ast_conf_user sender,
struct ast_frame f 
) [static]

Definition at line 2045 of file app_meetme.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_channel_name(), ast_log(), ast_write(), ast_conf_user::chan, LOG_WARNING, user, and ast_conference::usercontainer.

Referenced by conf_run().

{
   struct ast_conf_user *user;
   struct ao2_iterator user_iter;

   user_iter = ao2_iterator_init(conf->usercontainer, 0);
   while ((user = ao2_iterator_next(&user_iter))) {
      if (user == sender) {
         ao2_ref(user, -1);
         continue;
      }
      if (ast_write(user->chan, f) < 0)
         ast_log(LOG_WARNING, "Error writing frame to channel %s\n", ast_channel_name(user->chan));
      ao2_ref(user, -1);
   }
   ao2_iterator_destroy(&user_iter);
}
static int conf_run ( struct ast_channel chan,
struct ast_conference conf,
struct ast_flags64 confflags,
char *  optargs[] 
) [static]

Definition at line 2839 of file app_meetme.c.

References volume::actual, ADMINFLAG_HANGUP, 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_callback, ao2_link, ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, ast_channel_audiohooks(), ast_channel_caller(), ast_channel_connected(), ast_channel_context(), ast_channel_fd(), ast_channel_language(), ast_channel_lock, ast_channel_macrocontext(), ast_channel_monitor(), ast_channel_name(), ast_channel_rawwriteformat(), ast_channel_setoption(), ast_channel_tech(), ast_channel_uniqueid(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag64, ast_cond_signal, ast_config_AST_SPOOL_DIR, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HOLD, ast_copy_string(), ast_debug, AST_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), AST_DEVSTATE_NOT_CACHABLE, AST_DIGIT_ANY, ast_dsp_free(), ast_dsp_get_threshold_from_settings(), ast_dsp_new(), ast_dsp_silence(), ast_exists_extension(), ast_filedelete(), ast_fileexists(), ast_format_cap_add(), ast_format_cap_alloc_nolock(), ast_format_cap_destroy(), ast_format_from_old_bitfield(), ast_format_set(), AST_FORMAT_SLINEAR, ast_format_to_old_bitfield(), 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_func_write(), ast_goto_if_exists(), ast_hangup(), ast_indicate(), AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_NEXT, AST_LIST_UNLOCK, ast_load_realtime(), ast_localtime(), ast_log(), ast_manager_event, AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_mkdir(), ast_mktime(), ast_module_helper(), 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_by_id(), ast_set_write_format_by_id(), ast_stopstream(), ast_strdup, ast_streamfile(), ast_strftime(), ast_strlen_zero(), ast_strptime(), ast_test_flag64, ast_test_suite_event_notify, ast_translate(), ast_translator_build_path(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_tvsub(), ast_tvzero(), ast_update_realtime(), ast_variables_destroy(), ast_verb, ast_verbose(), ast_waitfor_nandfds(), ast_waitstream(), ast_write(), audio_buffers, ast_conference::bookid, can_write(), careful_write(), ast_conference::chan, ast_conf_user::chan, 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_DONT_DENOISE, CONFFLAG_DURATION_LIMIT, CONFFLAG_DURATION_STOP, CONFFLAG_EXIT_CONTEXT, CONFFLAG_INTROMSG, CONFFLAG_INTROUSER, CONFFLAG_INTROUSER_VMREC, CONFFLAG_INTROUSERNOREVIEW, CONFFLAG_KEYEXIT, CONFFLAG_KICK_CONTINUE, CONFFLAG_KILL_LAST_MAN_STANDING, 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, context, ast_conf_user::dahdichannel, ast_conference::dahdiconf, ast_frame::data, ast_frame::datalen, DATE_FORMAT, volume::desired, dtmfstr, ast_conf_user::end_sound, ast_conference::endalert, ast_conference::endtime, ENTER, errno, EVENT_FLAG_CALL, exitcontext, f, ast_frame_subclass::format, ast_frame::frametype, ast_conference::gmuted, ast_format::id, ast_party_caller::id, ast_party_connected_line::id, ast_frame_subclass::integer, ast_conference::isdynamic, ast_conf_user::jointime, ast_conf_user::kicktime, announce_listitem::language, ast_conference::lchan, LEAVE, ast_conf_user::listen, ast_conference::listenlock, ast_conference::locked, LOG_WARNING, mailbox, ast_conference::markedusers, ast_conference::maxusers, MEETME_DELAYDETECTENDTALK, MEETME_DELAYDETECTTALK, meetme_menu(), MENU_ADMIN, MENU_DISABLED, MENU_NORMAL, ast_variable::name, ast_party_id::name, announce_listitem::namerecloc, ast_conf_user::namerecloc, ast_variable::next, ast_party_id::number, OBJ_NODATA, ast_frame::offset, OPT_ARG_DURATION_LIMIT, OPT_ARG_DURATION_STOP, OPT_ARG_EXITKEYS, OPT_ARG_INTROMSG, OPT_ARG_INTROUSER_VMREC, 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_conference::recordingfilename, ast_conference::recordingformat, ast_conference::recordthread, ast_conference::recordthreadlock, reset_volumes(), RQ_UINTEGER1, RQ_UINTEGER2, RQ_UINTEGER3, RQ_UINTEGER4, S_COR, ast_frame::samples, set_talk_volume(), set_user_talking(), SLA_EVENT_HOLD, sla_queue_event_conf(), ast_conf_user::start_time, ast_party_name::str, ast_party_number::str, ast_frame::subclass, ast_conf_user::talk, ast_conf_user::talking, THRESHOLD_SILENCE, ast_conf_user::timelimit, ast_conference::transframe, ast_conference::transpath, type, ast_conference::uniqueid, user_max_cmp(), ast_conf_user::user_no, user_set_hangup_cb(), ast_conference::usercontainer, ast_conf_user::userflags, ast_conference::users, ast_party_name::valid, ast_party_number::valid, ast_variable::value, var, announce_listitem::vmrec, ast_conf_user::warning_freq, and ast_conf_user::warning_sound.

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

{
   struct ast_conf_user *user = NULL;
   int fd;
   struct dahdi_confinfo dahdic, dahdic_empty;
   struct ast_frame *f;
   struct ast_channel *c;
   struct ast_frame fr;
   int outfd;
   int ms;
   int nfds;
   int res;
   int retrydahdi;
   int origfd;
   int musiconhold = 0, mohtempstopped = 0;
   int firstpass = 0;
   int lastmarked = 0;
   int currentmarked = 0;
   int ret = -1;
   int x;
   enum menu_modes menu_mode = MENU_DISABLED;
   int talkreq_manager = 0;
   int using_pseudo = 0;
   int duration = 20;
   int sent_event = 0;
   int checked = 0;
   int announcement_played = 0;
   struct timeval now;
   struct ast_dsp *dsp = NULL;
   struct ast_app *agi_app;
   char *agifile, *mod_speex;
   const char *agifiledefault = "conf-background.agi", *tmpvar;
   char meetmesecs[30] = "";
   char exitcontext[AST_MAX_CONTEXT] = "";
   char recordingtmp[AST_MAX_EXTENSION] = "";
   char members[10] = "";
   int dtmf = 0, opt_waitmarked_timeout = 0;
   time_t timeout = 0;
   struct dahdi_bufferinfo bi;
   char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
   char *buf = __buf + AST_FRIENDLY_OFFSET;
   char *exitkeys = NULL;
   unsigned int calldurationlimit = 0;
   long timelimit = 0;
   long play_warning = 0;
   long warning_freq = 0;
   const char *warning_sound = NULL;
   const char *end_sound = NULL;
   char *parse;
   long time_left_ms = 0;
   struct timeval nexteventts = { 0, };
   int to;
   int setusercount = 0;
   int confsilence = 0, totalsilence = 0;
   char *mailbox, *context;
   struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
   struct ast_format tmpfmt;

   if (!cap_slin) {
      goto conf_run_cleanup;
   }
   ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));

   if (!(user = ao2_alloc(sizeof(*user), NULL))) {
      goto conf_run_cleanup;
   }

   /* Possible timeout waiting for marked user */
   if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
      !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
      (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
      (opt_waitmarked_timeout > 0)) {
      timeout = time(NULL) + opt_waitmarked_timeout;
   }

   if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
      calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
      ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
   }

   if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
      char *limit_str, *warning_str, *warnfreq_str;
      const char *var;

      parse = optargs[OPT_ARG_DURATION_LIMIT];
      limit_str = strsep(&parse, ":");
      warning_str = strsep(&parse, ":");
      warnfreq_str = parse;

      timelimit = atol(limit_str);
      if (warning_str)
         play_warning = atol(warning_str);
      if (warnfreq_str)
         warning_freq = atol(warnfreq_str);

      if (!timelimit) {
         timelimit = play_warning = warning_freq = 0;
         warning_sound = NULL;
      } else if (play_warning > timelimit) {
         if (!warning_freq) {
            play_warning = 0;
         } else {
            while (play_warning > timelimit)
               play_warning -= warning_freq;
            if (play_warning < 1)
               play_warning = warning_freq = 0;
         }
      }

      ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
      if (play_warning) {
         ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
      }
      if (warning_freq) {
         ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
      }

      ast_channel_lock(chan);
      if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
         var = ast_strdupa(var);
      }
      ast_channel_unlock(chan);

      warning_sound = var ? var : "timeleft";

      ast_channel_lock(chan);
      if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
         var = ast_strdupa(var);
      }
      ast_channel_unlock(chan);

      end_sound = var ? var : NULL;

      /* undo effect of S(x) in case they are both used */
      calldurationlimit = 0;
      /* more efficient do it like S(x) does since no advanced opts */
      if (!play_warning && !end_sound && timelimit) {
         calldurationlimit = timelimit / 1000;
         timelimit = play_warning = warning_freq = 0;
      } else {
         ast_debug(2, "Limit Data for this call:\n");
         ast_debug(2, "- timelimit     = %ld\n", timelimit);
         ast_debug(2, "- play_warning  = %ld\n", play_warning);
         ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
         ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
         ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
      }
   }

   /* Get exit keys */
   if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
      if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
         exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
      else
         exitkeys = ast_strdupa("#"); /* Default */
   }
   
   if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
      if (!conf->recordingfilename) {
         const char *var;
         ast_channel_lock(chan);
         if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
            conf->recordingfilename = ast_strdup(var);
         }
         if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
            conf->recordingformat = ast_strdup(var);
         }
         ast_channel_unlock(chan);
         if (!conf->recordingfilename) {
            snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
            conf->recordingfilename = ast_strdup(recordingtmp);
         }
         if (!conf->recordingformat) {
            conf->recordingformat = ast_strdup("wav");
         }
         ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
                conf->confno, conf->recordingfilename, conf->recordingformat);
      }
   }

   ast_mutex_lock(&conf->recordthreadlock);
   if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
      ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
      ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
      ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
      dahdic.chan = 0;
      dahdic.confno = conf->dahdiconf;
      dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
      if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
         ast_log(LOG_WARNING, "Error starting listen channel\n");
         ast_hangup(conf->lchan);
         conf->lchan = NULL;
      } else {
         ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
      }
   }
   ast_mutex_unlock(&conf->recordthreadlock);

   ast_mutex_lock(&conf->announcethreadlock);
   if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
      ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
      ast_mutex_init(&conf->announcelistlock);
      AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
      ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
   }
   ast_mutex_unlock(&conf->announcethreadlock);

   time(&user->jointime);
   
   user->timelimit = timelimit;
   user->play_warning = play_warning;
   user->warning_freq = warning_freq;
   user->warning_sound = warning_sound;
   user->end_sound = end_sound;  
   
   if (calldurationlimit > 0) {
      time(&user->kicktime);
      user->kicktime = user->kicktime + calldurationlimit;
   }
   
   if (ast_tvzero(user->start_time))
      user->start_time = ast_tvnow();
   time_left_ms = user->timelimit;
   
   if (user->timelimit) {
      nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
      nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
   }

   if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
      /* Sorry, but this conference is locked! */  
      if (!ast_streamfile(chan, "conf-locked", ast_channel_language(chan)))
         ast_waitstream(chan, "");
      goto outrun;
   }

      ast_mutex_lock(&conf->playlock);

   if (rt_schedule && conf->maxusers) {
      if (conf->users >= conf->maxusers) {
         /* Sorry, but this confernce has reached the participant limit! */   
         if (!ast_streamfile(chan, "conf-full", ast_channel_language(chan)))
            ast_waitstream(chan, "");
         ast_mutex_unlock(&conf->playlock);
         goto outrun;
      }
   }

   ao2_lock(conf->usercontainer);
   ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
   user->user_no++;
   ao2_link(conf->usercontainer, user);
   ao2_unlock(conf->usercontainer);

   user->chan = chan;
   user->userflags = *confflags;
   user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
   user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
   user->talking = -1;

   ast_mutex_unlock(&conf->playlock);

   if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
      char destdir[PATH_MAX];

      snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);

      if (ast_mkdir(destdir, 0777) != 0) {
         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
         goto outrun;
      }

      if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
         context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
         mailbox = strsep(&context, "@");

         if (ast_strlen_zero(mailbox)) {
            /* invalid input, clear the v flag*/
            ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
            ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
         } else {
            if (ast_strlen_zero(context)) {
                context = "default";
            }
            /* if there is no mailbox we don't need to do this logic  */
            snprintf(user->namerecloc, sizeof(user->namerecloc),
                "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);

            /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
            if (!ast_fileexists(user->namerecloc, NULL, NULL)){
               snprintf(user->namerecloc, sizeof(user->namerecloc),
                   "%s/meetme-username-%s-%d", destdir,
                   conf->confno, user->user_no);
               ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
            }
         }
      } else {
         snprintf(user->namerecloc, sizeof(user->namerecloc),
             "%s/meetme-username-%s-%d", destdir,
             conf->confno, user->user_no);
      }

      res = 0;
      if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
         res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
      else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
         res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
      if (res == -1)
         goto outrun;

   }

   ast_mutex_lock(&conf->playlock);

   if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
      conf->markedusers++;
   conf->users++;
   if (rt_log_members) {
      /* Update table */
      snprintf(members, sizeof(members), "%d", conf->users);
      ast_realtime_require_field("meetme",
         "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
         "members", RQ_UINTEGER1, strlen(members),
         NULL);
      ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
   }
   setusercount = 1;

   /* This device changed state now - if this is the first user */
   if (conf->users == 1)
      ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);

   ast_mutex_unlock(&conf->playlock);

   /* return the unique ID of the conference */
   pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);

   if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
      ast_channel_lock(chan);
      if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
         ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
      } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
         ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
      } else {
         ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
      }
      ast_channel_unlock(chan);
   }

   /* Play an arbitrary intro message */
   if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
         !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
      if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], ast_channel_language(chan))) {
         ast_waitstream(chan, "");
      }
   }

   if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
      if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
         if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan)))
            ast_waitstream(chan, "");
      if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
         if (!ast_streamfile(chan, "conf-waitforleader", ast_channel_language(chan)))
            ast_waitstream(chan, "");
   }

   if (ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
      int keepplaying = 1;

      if (conf->users == 2) { 
         if (!ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
            res = ast_waitstream(chan, AST_DIGIT_ANY);
            ast_stopstream(chan);
            if (res > 0)
               keepplaying = 0;
            else if (res == -1)
               goto outrun;
         }
      } else { 
         if (!ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
            res = ast_waitstream(chan, AST_DIGIT_ANY);
            ast_stopstream(chan);
            if (res > 0)
               keepplaying = 0;
            else if (res == -1)
               goto outrun;
         }
         if (keepplaying) {
            res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
            if (res > 0)
               keepplaying = 0;
            else if (res == -1)
               goto outrun;
         }
         if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
            res = ast_waitstream(chan, AST_DIGIT_ANY);
            ast_stopstream(chan);
            if (res > 0)
               keepplaying = 0;
            else if (res == -1) 
               goto outrun;
         }
      }
   }

   if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
      /* We're leaving this alone until the state gets changed to up */
      ast_indicate(chan, -1);
   }

   if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
      ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", ast_channel_name(chan));
      goto outrun;
   }

   if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
      ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", ast_channel_name(chan));
      goto outrun;
   }

   /* Reduce background noise from each participant */
   if (!ast_test_flag64(confflags, CONFFLAG_DONT_DENOISE) &&
      (mod_speex = ast_module_helper("", "func_speex", 0, 0, 0, 0))) {
      ast_free(mod_speex);
      ast_func_write(chan, "DENOISE(rx)", "on");
   }

   retrydahdi = (strcasecmp(ast_channel_tech(chan)->type, "DAHDI") || (ast_channel_audiohooks(chan) || ast_channel_monitor(chan)) ? 1 : 0);
   user->dahdichannel = !retrydahdi;

 dahdiretry:
   origfd = ast_channel_fd(chan, 0);
   if (retrydahdi) {
      /* open pseudo in non-blocking mode */
      fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
      if (fd < 0) {
         ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
         goto outrun;
      }
      using_pseudo = 1;
      /* Setup buffering information */
      memset(&bi, 0, sizeof(bi));
      bi.bufsize = CONF_SIZE / 2;
      bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
      bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
      bi.numbufs = audio_buffers;
      if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
         close(fd);
         goto outrun;
      }
      x = 1;
      if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
         close(fd);
         goto outrun;
      }
      nfds = 1;
   } else {
      /* XXX Make sure we're not running on a pseudo channel XXX */
      fd = ast_channel_fd(chan, 0);
      nfds = 0;
   }
   memset(&dahdic, 0, sizeof(dahdic));
   memset(&dahdic_empty, 0, sizeof(dahdic_empty));
   /* Check to see if we're in a conference... */
   dahdic.chan = 0;
   if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
      ast_log(LOG_WARNING, "Error getting conference\n");
      close(fd);
      goto outrun;
   }
   if (dahdic.confmode) {
      /* Whoa, already in a conference...  Retry... */
      if (!retrydahdi) {
         ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
         retrydahdi = 1;
         goto dahdiretry;
      }
   }
   memset(&dahdic, 0, sizeof(dahdic));
   /* Add us to the conference */
   dahdic.chan = 0;
   dahdic.confno = conf->dahdiconf;

   if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
         ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
      struct announce_listitem *item;
      if (!(item = ao2_alloc(sizeof(*item), NULL)))
         goto outrun;
      ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
      ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
      item->confchan = conf->chan;
      item->confusers = conf->users;
      if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
         item->vmrec = 1;
      }
      item->announcetype = CONF_HASJOIN;
      ast_mutex_lock(&conf->announcelistlock);
      ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
      AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
      ast_cond_signal(&conf->announcelist_addition);
      ast_mutex_unlock(&conf->announcelistlock);

      while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
         ;
      }
      ao2_ref(item, -1);
   }

   if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
      dahdic.confmode = DAHDI_CONF_CONF;
   else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
      dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
   else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
      dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
   else
      dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;

   if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
      ast_log(LOG_WARNING, "Error setting conference\n");
      close(fd);
      goto outrun;
   }
   ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf);

   if (!sent_event) {
      /*** DOCUMENTATION
         <managerEventInstance>
            <synopsis>Raised when a user joins a MeetMe conference.</synopsis>
            <syntax>
               <parameter name="Meetme">
                  <para>The identifier for the MeetMe conference.</para>
               </parameter>
               <parameter name="Usernum">
                  <para>The identifier of the MeetMe user who joined.</para>
               </parameter>
            </syntax>
            <see-also>
               <ref type="managerEvent">MeetmeLeave</ref>
               <ref type="application">MeetMe</ref>
            </see-also>
         </managerEventInstance>
      ***/
      ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
         "Channel: %s\r\n"
         "Uniqueid: %s\r\n"
         "Meetme: %s\r\n"
         "Usernum: %d\r\n"
         "CallerIDnum: %s\r\n"
         "CallerIDname: %s\r\n"
         "ConnectedLineNum: %s\r\n"
         "ConnectedLineName: %s\r\n",
         ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno,
         user->user_no,
         S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
         S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<unknown>"),
         S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
         S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<unknown>")
         );
      sent_event = 1;
   }

   if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
      !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
      firstpass = 1;
      if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
         if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
            (conf->markedusers >= 1))) {
            conf_play(chan, conf, ENTER);
         }
   }

   conf_flush(fd, chan);

   if (dsp)
      ast_dsp_free(dsp);

   if (!(dsp = ast_dsp_new())) {
      ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
      res = -1;
   }

   if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
      /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
         or use default filename of conf-background.agi */

      ast_channel_lock(chan);
      if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
         agifile = ast_strdupa(tmpvar);
      } else {
         agifile = ast_strdupa(agifiledefault);
      }
      ast_channel_unlock(chan);
      
      if (user->dahdichannel) {
         /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
         x = 1;
         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
      }
      /* Find a pointer to the agi app and execute the script */
      agi_app = pbx_findapp("agi");
      if (agi_app) {
         ret = pbx_exec(chan, agi_app, agifile);
      } else {
         ast_log(LOG_WARNING, "Could not find application (agi)\n");
         ret = -2;
      }
      if (user->dahdichannel) {
         /*  Remove CONFMUTE mode on DAHDI channel */
         x = 0;
         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
      }
   } else {
      int lastusers = conf->users;
      if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
         /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
         x = 1;
         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
      }

      for (;;) {
         int menu_was_active = 0;

         outfd = -1;
         ms = -1;
         now = ast_tvnow();

         if (rt_schedule && conf->endtime) {
            char currenttime[32];
            long localendtime = 0;
            int extended = 0;
            struct ast_tm tm;
            struct ast_variable *var, *origvar;
            struct timeval tmp;

            if (now.tv_sec % 60 == 0) {
               if (!checked) {
                  ast_localtime(&now, &tm, NULL);
                  ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
                  var = origvar = ast_load_realtime("meetme", "confno",
                     conf->confno, "starttime <=", currenttime,
                      "endtime >=", currenttime, NULL);

                  for ( ; var; var = var->next) {
                     if (!strcasecmp(var->name, "endtime")) {
                        struct ast_tm endtime_tm;
                        ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
                        tmp = ast_mktime(&endtime_tm, NULL);
                        localendtime = tmp.tv_sec;
                     }
                  }
                  ast_variables_destroy(origvar);

                  /* A conference can be extended from the
                     Admin/User menu or by an external source */
                  if (localendtime > conf->endtime){
                     conf->endtime = localendtime;
                     extended = 1;
                  }

                  if (conf->endtime && (now.tv_sec >= conf->endtime)) {
                     ast_verbose("Quitting time...\n");
                     goto outrun;
                  }

                  if (!announcement_played && conf->endalert) {
                     if (now.tv_sec + conf->endalert >= conf->endtime) {
                        if (!ast_streamfile(chan, "conf-will-end-in", ast_channel_language(chan)))
                           ast_waitstream(chan, "");
                        ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", ast_channel_language(chan));
                        if (!ast_streamfile(chan, "minutes", ast_channel_language(chan)))
                           ast_waitstream(chan, "");
                        if (musiconhold) {
                           conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
                        }
                        announcement_played = 1;
                     }
                  }

                  if (extended) {
                     announcement_played = 0;
                  }

                  checked = 1;
               }
            } else {
               checked = 0;
            }
         }

         if (user->kicktime && (user->kicktime <= now.tv_sec)) {
            if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
               ret = 0;
            } else {
               ret = -1;
            }
            break;
         }
  
         to = -1;
         if (user->timelimit) {
            int minutes = 0, seconds = 0, remain = 0;
 
            to = ast_tvdiff_ms(nexteventts, now);
            if (to < 0) {
               to = 0;
            }
            time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
            if (time_left_ms < to) {
               to = time_left_ms;
            }
   
            if (time_left_ms <= 0) {
               if (user->end_sound) {                 
                  res = ast_streamfile(chan, user->end_sound, ast_channel_language(chan));
                  res = ast_waitstream(chan, "");
               }
               if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
                  ret = 0;
               } else {
                  ret = -1;
               }
               break;
            }
            
            if (!to) {
               if (time_left_ms >= 5000) {                  
                  
                  remain = (time_left_ms + 500) / 1000;
                  if (remain / 60 >= 1) {
                     minutes = remain / 60;
                     seconds = remain % 60;
                  } else {
                     seconds = remain;
                  }
                  
                  /* force the time left to round up if appropriate */
                  if (user->warning_sound && user->play_warning) {
                     if (!strcmp(user->warning_sound, "timeleft")) {
                        
                        res = ast_streamfile(chan, "vm-youhave", ast_channel_language(chan));
                        res = ast_waitstream(chan, "");
                        if (minutes) {
                           res = ast_say_number(chan, minutes, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
                           res = ast_streamfile(chan, "queue-minutes", ast_channel_language(chan));
                           res = ast_waitstream(chan, "");
                        }
                        if (seconds) {
                           res = ast_say_number(chan, seconds, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
                           res = ast_streamfile(chan, "queue-seconds", ast_channel_language(chan));
                           res = ast_waitstream(chan, "");
                        }
                     } else {
                        res = ast_streamfile(chan, user->warning_sound, ast_channel_language(chan));
                        res = ast_waitstream(chan, "");
                     }
                     if (musiconhold) {
                        conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
                     }
                  }
               }
               if (user->warning_freq) {
                  nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
               } else {
                  nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
               }
            }
         }

         now = ast_tvnow();
         if (timeout && now.tv_sec >= timeout) {
            if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
               ret = 0;
            } else {
               ret = -1;
            }
            break;
         }

         /* if we have just exited from the menu, and the user had a channel-driver
            volume adjustment, restore it
         */
         if (!menu_mode && menu_was_active && user->listen.desired && !user->listen.actual) {
            set_talk_volume(user, user->listen.desired);
         }

         menu_was_active = menu_mode;

         currentmarked = conf->markedusers;
         if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
             ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
             ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
             lastmarked == 0) {
            if (currentmarked == 1 && conf->users > 1) {
               ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
               if (conf->users - 1 == 1) {
                  if (!ast_streamfile(chan, "conf-userwilljoin", ast_channel_language(chan))) {
                     ast_waitstream(chan, "");
                  }
               } else {
                  if (!ast_streamfile(chan, "conf-userswilljoin", ast_channel_language(chan))) {
                     ast_waitstream(chan, "");
                  }
               }
            }
            if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
               if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
                  ast_waitstream(chan, "");
               }
            }
         }

         /* Update the struct with the actual confflags */
         user->userflags = *confflags;

         if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
            if (currentmarked == 0) {
               if (lastmarked != 0) {
                  if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
                     if (!ast_streamfile(chan, "conf-leaderhasleft", ast_channel_language(chan))) {
                        ast_waitstream(chan, "");
                     }
                  }
                  if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
                     if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
                        ret = 0;
                     }
                     break;
                  } else {
                     dahdic.confmode = DAHDI_CONF_CONF;
                     if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
                        ast_log(LOG_WARNING, "Error setting conference\n");
                        close(fd);
                        goto outrun;
                     }
                  }
               }
               if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
                  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
                  musiconhold = 1;
               }
            } else if (currentmarked >= 1 && lastmarked == 0) {
               /* Marked user entered, so cancel timeout */
               timeout = 0;
               if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
                  dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
               } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
                  dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
               } else {
                  dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
               }
               if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
                  ast_log(LOG_WARNING, "Error setting conference\n");
                  close(fd);
                  goto outrun;
               }
               if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
                  ast_moh_stop(chan);
                  musiconhold = 0;
               }
               if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
                  !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
                  if (!ast_streamfile(chan, "conf-placeintoconf", ast_channel_language(chan))) {
                     ast_waitstream(chan, "");
                  }
                  conf_play(chan, conf, ENTER);
               }
            }
         }

         /* trying to add moh for single person conf */
         if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
            if (conf->users == 1) {
               if (!musiconhold) {
                  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
                  musiconhold = 1;
               } 
            } else {
               if (musiconhold) {
                  ast_moh_stop(chan);
                  musiconhold = 0;
               }
            }
         }
         
         /* Leave if the last marked user left */
         if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
            if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
               ret = 0;
            } else {
               ret = -1;
            }
            break;
         }

         /* Throw a TestEvent if a user exit did not cause this user to leave the conference */
         if (conf->users != lastusers) {
            if (conf->users < lastusers) {
               ast_test_suite_event_notify("NOEXIT", "Message: CONFFLAG_MARKEDEXIT\r\nLastUsers: %d\r\nUsers: %d", lastusers, conf->users);
            }
            lastusers = conf->users;
         }

         /* Check if my modes have changed */

         /* If I should be muted but am still talker, mute me */
         if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
            dahdic.confmode ^= DAHDI_CONF_TALKER;
            if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
               ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
               ret = -1;
               break;
            }

            /* Indicate user is not talking anymore - change him to unmonitored state */
            if (ast_test_flag64(confflags,  (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
               set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
            }
            /*** DOCUMENTATION
            <managerEventInstance>
               <synopsis>Raised when a MeetMe user is muted.</synopsis>
               <syntax>
                  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
                  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
                  <parameter name="Status">
                     <enumlist>
                        <enum name="on"/>
                        <enum name="off"/>
                     </enumlist>
                  </parameter>
               </syntax>
            </managerEventInstance>
            ***/
            ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
               "Channel: %s\r\n"
               "Uniqueid: %s\r\n"
               "Meetme: %s\r\n"
               "Usernum: %d\r\n"
               "Status: on\r\n",
               ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
         }

         /* If I should be un-muted but am not talker, un-mute me */
         if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
            dahdic.confmode |= DAHDI_CONF_TALKER;
            if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
               ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
               ret = -1;
               break;
            }
            /*** DOCUMENTATION
            <managerEventInstance>
               <synopsis>Raised when a MeetMe user is unmuted.</synopsis>
            </managerEventInstance>
            ***/
            ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
               "Channel: %s\r\n"
               "Uniqueid: %s\r\n"
               "Meetme: %s\r\n"
               "Usernum: %d\r\n"
               "Status: off\r\n",
               ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
         }
         
         if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
            (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
            talkreq_manager = 1;

            /*** DOCUMENTATION
            <managerEventInstance>
               <synopsis>Raised when a MeetMe user has started talking.</synopsis>
               <syntax>
                  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
                  <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
                  <parameter name="Status">
                     <enumlist>
                        <enum name="on"/>
                        <enum name="off"/>
                     </enumlist>
                  </parameter>
               </syntax>
            </managerEventInstance>
            ***/
            ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
               "Channel: %s\r\n"
               "Uniqueid: %s\r\n"
               "Meetme: %s\r\n"
               "Usernum: %d\r\n"
               "Status: on\r\n",
               ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
         }

         if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
            !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
            talkreq_manager = 0;
            /*** DOCUMENTATION
            <managerEventInstance>
               <synopsis>Raised when a MeetMe user has finished talking.</synopsis>
            </managerEventInstance>
            ***/
            ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
               "Channel: %s\r\n"
               "Uniqueid: %s\r\n"
               "Meetme: %s\r\n"
               "Usernum: %d\r\n"
               "Status: off\r\n",
               ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
         }

         /* If user have been hung up, exit the conference */
         if (user->adminflags & ADMINFLAG_HANGUP) {
            ret = 0;
            break;
         }

         /* If I have been kicked, exit the conference */
         if (user->adminflags & ADMINFLAG_KICKME) {
            /* You have been kicked. */
            if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
               !ast_streamfile(chan, "conf-kicked", ast_channel_language(chan))) {
               ast_waitstream(chan, "");
            }
            ret = 0;
            break;
         }

         /* Perform a hangup check here since ast_waitfor_nandfds will not always be able to get a channel after a hangup has occurred */
         if (ast_check_hangup(chan)) {
            break;
         }

         c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);

         if (c) {
            char dtmfstr[2] = "";

            if (ast_channel_fd(c, 0) != origfd || (user->dahdichannel && (ast_channel_audiohooks(c) || ast_channel_monitor(c)))) {
               if (using_pseudo) {
                  /* Kill old pseudo */
                  close(fd);
                  using_pseudo = 0;
               }
               ast_debug(1, "Ooh, something swapped out under us, starting over\n");
               retrydahdi = (strcasecmp(ast_channel_tech(c)->type, "DAHDI") || (ast_channel_audiohooks(c) || ast_channel_monitor(c)) ? 1 : 0);
               user->dahdichannel = !retrydahdi;
               goto dahdiretry;
            }
            if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
               f = ast_read_noaudio(c);
            } else {
               f = ast_read(c);
            }
            if (!f) {
               break;
            }
            if (f->frametype == AST_FRAME_DTMF) {
               dtmfstr[0] = f->subclass.integer;
               dtmfstr[1] = '\0';
            }

            if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id == AST_FORMAT_SLINEAR)) {
               if (user->talk.actual) {
                  ast_frame_adjust_volume(f, user->talk.actual);
               }

               if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
                  if (user->talking == -1) {
                     user->talking = 0;
                  }

                  res = ast_dsp_silence(dsp, f, &totalsilence);
                  if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
                     set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
                  }

                  if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
                     set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
                  }
               }
               if (using_pseudo) {
                  /* Absolutely do _not_ use careful_write here...
                     it is important that we read data from the channel
                     as fast as it arrives, and feed it into the conference.
                     The buffering in the pseudo channel will take care of any
                     timing differences, unless they are so drastic as to lose
                     audio frames (in which case carefully writing would only
                     have delayed the audio even further).
                  */
                  /* As it turns out, we do want to use careful write.  We just
                     don't want to block, but we do want to at least *try*
                     to write out all the samples.
                   */
                  if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
                     careful_write(fd, f->data.ptr, f->datalen, 0);
                  }
               }
            } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_mode)) {
               if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
                  conf_queue_dtmf(conf, user, f);
               }
               if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
                  ast_log(LOG_WARNING, "Error setting conference\n");
                  close(fd);
                  ast_frfree(f);
                  goto outrun;
               }

               /* if we are entering the menu, and the user has a channel-driver
                  volume adjustment, clear it
               */
               if (!menu_mode && user->talk.desired && !user->talk.actual) {
                  set_talk_volume(user, 0);
               }

               if (musiconhold) {
                  ast_moh_stop(chan);
               } else if (!menu_mode) {
                  char *menu_to_play;
                  if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
                     menu_mode = MENU_ADMIN;
                     menu_to_play = "conf-adminmenu-18";
                  } else {
                     menu_mode = MENU_NORMAL;
                     menu_to_play = "conf-usermenu-162";
                  }

                  if (!ast_streamfile(chan, menu_to_play, ast_channel_language(chan))) {
                     dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
                     ast_stopstream(chan);
                  } else {
                     dtmf = 0;
                  }
               } else {
                  dtmf = f->subclass.integer;
               }

               if (dtmf > 0) {
                  meetme_menu(&menu_mode, &dtmf, conf, confflags, chan, user, recordingtmp, sizeof(recordingtmp), &dahdic, cap_slin);
               }

               if (musiconhold && !menu_mode) {
                  conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
               }

               if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
                  ast_log(LOG_WARNING, "Error setting conference\n");
                  close(fd);
                  ast_frfree(f);
                  goto outrun;
               }

               conf_flush(fd, chan);
            /*
             * Since options using DTMF could absorb DTMF meant for the
             * conference menu, we have to check them after the menu.
             */
            } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
               if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
                  conf_queue_dtmf(conf, user, f);
               }

               if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
                  ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
                  ret = 0;
                  ast_frfree(f);
                  break;
               } else {
                  ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
               }
            } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
               (strchr(exitkeys, f->subclass.integer))) {
               pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);

               if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
                  conf_queue_dtmf(conf, user, f);
               }
               ret = 0;
               ast_frfree(f);
               break;
            } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
               && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
               conf_queue_dtmf(conf, user, f);
            } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
               switch (f->subclass.integer) {
               case AST_CONTROL_HOLD:
                  sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
                  break;
               default:
                  break;
               }
            } else if (f->frametype == AST_FRAME_NULL) {
               /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
            } else if (f->frametype == AST_FRAME_CONTROL) {
               switch (f->subclass.integer) {
               case AST_CONTROL_BUSY:
               case AST_CONTROL_CONGESTION:
                  ast_frfree(f);
                  goto outrun;
                  break;
               default:
                  ast_debug(1,
                     "Got ignored control frame on channel %s, f->frametype=%d,f->subclass=%d\n",
                     ast_channel_name(chan), f->frametype, f->subclass.integer);
               }
            } else {
               ast_debug(1,
                  "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
                  ast_channel_name(chan), f->frametype, f->subclass.integer);
            }
            ast_frfree(f);
         } else if (outfd > -1) {
            res = read(outfd, buf, CONF_SIZE);
            if (res > 0) {
               memset(&fr, 0, sizeof(fr));
               fr.frametype = AST_FRAME_VOICE;
               ast_format_set(&fr.subclass.format, AST_FORMAT_SLINEAR, 0);
               fr.datalen = res;
               fr.samples = res / 2;
               fr.data.ptr = buf;
               fr.offset = AST_FRIENDLY_OFFSET;
               if (!user->listen.actual &&
                  (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
                   (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
                   (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
                   )) {
                  int idx;
                  for (idx = 0; idx < AST_FRAME_BITS; idx++) {
                     if (ast_format_to_old_bitfield(ast_channel_rawwriteformat(chan)) & (1 << idx)) {
                        break;
                     }
                  }
                  if (idx >= AST_FRAME_BITS) {
                     goto bailoutandtrynormal;
                  }
                  ast_mutex_lock(&conf->listenlock);
                  if (!conf->transframe[idx]) {
                     if (conf->origframe) {
                        if (musiconhold && !ast_dsp_silence(dsp, conf->origframe, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
                           ast_moh_stop(chan);
                           mohtempstopped = 1;
                        }
                        if (!conf->transpath[idx]) {
                           struct ast_format src;
                           struct ast_format dst;
                           ast_format_set(&src, AST_FORMAT_SLINEAR, 0);
                           ast_format_from_old_bitfield(&dst, (1 << idx));
                           conf->transpath[idx] = ast_translator_build_path(&dst, &src);
                        }
                        if (conf->transpath[idx]) {
                           conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
                           if (!conf->transframe[idx]) {
                              conf->transframe[idx] = &ast_null_frame;
                           }
                        }
                     }
                  }
                  if (conf->transframe[idx]) {
                     if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
                         can_write(chan, confflags)) {
                        struct ast_frame *cur;
                        /* the translator may have returned a list of frames, so
                           write each one onto the channel
                        */
                        for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
                           if (ast_write(chan, cur)) {
                              ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
                              break;
                           }
                        }
                        if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
                           mohtempstopped = 0;
                           conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
                        }
                     }
                  } else {
                     ast_mutex_unlock(&conf->listenlock);
                     goto bailoutandtrynormal;
                  }
                  ast_mutex_unlock(&conf->listenlock);
               } else {
bailoutandtrynormal:
                  if (musiconhold && !ast_dsp_silence(dsp, &fr, &confsilence) && confsilence < MEETME_DELAYDETECTTALK) {
                     ast_moh_stop(chan);
                     mohtempstopped = 1;
                  }
                  if (user->listen.actual) {
                     ast_frame_adjust_volume(&fr, user->listen.actual);
                  }
                  if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
                     ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
                  }
                  if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
                     mohtempstopped = 0;
                     conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
                  }
               }
            } else {
               ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
            }
         }
         lastmarked = currentmarked;
      }
   }

   if (musiconhold) {
      ast_moh_stop(chan);
   }
   
   if (using_pseudo) {
      close(fd);
   } else {
      /* Take out of conference */
      dahdic.chan = 0;  
      dahdic.confno = 0;
      dahdic.confmode = 0;
      if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
         ast_log(LOG_WARNING, "Error setting conference\n");
      }
   }

   reset_volumes(user);

   if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
      !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
      conf_play(chan, conf, LEAVE);
   }

   if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER |CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC) && conf->users > 1) {
      struct announce_listitem *item;
      if (!(item = ao2_alloc(sizeof(*item), NULL)))
         goto outrun;
      ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
      ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
      item->confchan = conf->chan;
      item->confusers = conf->users;
      item->announcetype = CONF_HASLEFT;
      if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
         item->vmrec = 1;
      }
      ast_mutex_lock(&conf->announcelistlock);
      AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
      ast_cond_signal(&conf->announcelist_addition);
      ast_mutex_unlock(&conf->announcelistlock);
   } else if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) && !ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC) && conf->users == 1) {
      /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
      ast_filedelete(user->namerecloc, NULL);
   }

 outrun:
   AST_LIST_LOCK(&confs);

   if (dsp) {
      ast_dsp_free(dsp);
   }
   
   if (user->user_no) {
      /* Only cleanup users who really joined! */
      now = ast_tvnow();

      if (sent_event) {
         /*** DOCUMENTATION
         <managerEventInstance>
            <synopsis>Raised when a user leaves a MeetMe conference.</synopsis>
            <syntax>
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
               <parameter name="Duration">
                  <para>The length of time in seconds that the Meetme user was in the conference.</para>
               </parameter>
            </syntax>
            <see-also>
               <ref type="managerEvent">MeetmeJoin</ref>
            </see-also>
         </managerEventInstance>
         ***/
         ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeLeave",
            "Channel: %s\r\n"
            "Uniqueid: %s\r\n"
            "Meetme: %s\r\n"
            "Usernum: %d\r\n"
            "CallerIDNum: %s\r\n"
            "CallerIDName: %s\r\n"
            "ConnectedLineNum: %s\r\n"
            "ConnectedLineName: %s\r\n"
            "Duration: %ld\r\n",
            ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno,
            user->user_no,
            S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
            S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<unknown>"),
            S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
            S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<unknown>"),
            (long)(now.tv_sec - user->jointime));
      }

      if (setusercount) {
         conf->users--;
         if (rt_log_members) {
            /* Update table */
            snprintf(members, sizeof(members), "%d", conf->users);
            ast_realtime_require_field("meetme",
               "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
               "members", RQ_UINTEGER1, strlen(members),
               NULL);
            ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
         }
         if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
            conf->markedusers--;
         }
      }
      /* Remove ourselves from the container */
      ao2_unlink(conf->usercontainer, user); 

      /* Change any states */
      if (!conf->users) {
         ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
      }

      /* This flag is meant to kill a conference with only one participant remaining.  */
      if (conf->users == 1 && ast_test_flag64(confflags, CONFFLAG_KILL_LAST_MAN_STANDING)) {
         ao2_callback(conf->usercontainer, 0, user_set_hangup_cb, NULL);
      }

      /* Return the number of seconds the user was in the conf */
      snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
      pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);

      /* Return the RealTime bookid for CDR linking */
      if (rt_schedule) {
         pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
      }
   }
   ao2_ref(user, -1);
   AST_LIST_UNLOCK(&confs);


conf_run_cleanup:
   cap_slin = ast_format_cap_destroy(cap_slin);

   return ret;
}
static void conf_start_moh ( struct ast_channel chan,
const char *  musicclass 
) [static]

Definition at line 2230 of file app_meetme.c.

References ast_channel_lock, ast_channel_musicclass(), ast_channel_unlock, and ast_moh_start().

Referenced by conf_run().

{
   char *original_moh;

   ast_channel_lock(chan);
   original_moh = ast_strdupa(ast_channel_musicclass(chan));
   ast_channel_musicclass_set(chan, musicclass);
   ast_channel_unlock(chan);

   ast_moh_start(chan, original_moh, NULL);

   ast_channel_lock(chan);
   ast_channel_musicclass_set(chan, original_moh);
   ast_channel_unlock(chan);
}
static int count_exec ( struct ast_channel chan,
const char *  data 
) [static]

The MeetmeCount application.

Definition at line 4573 of file app_meetme.c.

References args, ast_answer(), AST_APP_ARG, ast_channel_language(), AST_DECLARE_APP_ARGS, ast_log(), ast_say_number(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strlen_zero(), dispose_conf(), find_conf(), LOG_WARNING, pbx_builtin_setvar_helper(), and ast_conference::users.

Referenced by load_module().

{
   int res = 0;
   struct ast_conference *conf;
   int count;
   char *localdata;
   char val[80] = "0"; 
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(confno);
      AST_APP_ARG(varname);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
      return -1;
   }
   
   localdata = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, localdata);
   
   conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);

   if (conf) {
      count = conf->users;
      dispose_conf(conf);
      conf = NULL;
   } else
      count = 0;

   if (!ast_strlen_zero(args.varname)) {
      /* have var so load it and exit */
      snprintf(val, sizeof(val), "%d", count);
      pbx_builtin_setvar_helper(chan, args.varname, val);
   } else {
      if (ast_channel_state(chan) != AST_STATE_UP) {
         ast_answer(chan);
      }
      res = ast_say_number(chan, count, "", ast_channel_language(chan), (char *) NULL); /* Needs gender */
   }

   return res;
}
static struct sla_trunk_ref* create_trunk_ref ( struct sla_trunk trunk) [static, read]

Definition at line 6944 of file app_meetme.c.

References ao2_alloc, ao2_ref, sla_trunk_ref_destructor(), and sla_trunk_ref::trunk.

Referenced by sla_add_trunk_to_station().

{
   struct sla_trunk_ref *trunk_ref;

   if (!(trunk_ref = ao2_alloc(sizeof(*trunk_ref), sla_trunk_ref_destructor))) {
      return NULL;
   }

   ao2_ref(trunk, 1);
   trunk_ref->trunk = trunk;

   return trunk_ref;
}
static void* dial_trunk ( void *  data) [static]

Definition at line 6628 of file app_meetme.c.

References ALL_TRUNK_REFS, ao2_cleanup, args, ast_channel_caller(), ast_channel_caller_set(), ast_channel_name(), ast_cond_signal, AST_CONTROL_PROGRESS, AST_CONTROL_RINGING, ast_debug, AST_DEVICE_NOT_INUSE, 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_indicate(), ast_mutex_lock, ast_mutex_unlock, ast_party_caller_free(), ast_party_caller_init(), ast_safe_sleep(), ast_set_flag64, build_conf(), dial_trunk_args::cond, dial_trunk_args::cond_lock, conf_run(), CONFFLAG_MARKEDEXIT, CONFFLAG_MARKEDUSER, CONFFLAG_PASS_DTMF, CONFFLAG_QUIET, CONFFLAG_SLA_TRUNK, dispose_conf(), MAX_CONFNUM, sla, sla_change_trunk_state(), SLA_TRUNK_STATE_IDLE, dial_trunk_args::station, and dial_trunk_args::trunk_ref.

Referenced by sla_station_exec().

{
   struct dial_trunk_args *args = data;
   struct ast_dial *dial;
   char *tech, *tech_data;
   enum ast_dial_result dial_res;
   char conf_name[MAX_CONFNUM];
   struct ast_conference *conf;
   struct ast_flags64 conf_flags = { 0 };
   RAII_VAR(struct sla_trunk_ref *, trunk_ref, args->trunk_ref, ao2_cleanup);
   RAII_VAR(struct sla_station *, station, args->station, ao2_cleanup);
   int caller_is_saved;
   struct ast_party_caller caller;
   int last_state = 0;
   int current_state = 0;

   if (!(dial = ast_dial_create())) {
      ast_mutex_lock(args->cond_lock);
      ast_cond_signal(args->cond);
      ast_mutex_unlock(args->cond_lock);
      return NULL;
   }

   tech_data = ast_strdupa(trunk_ref->trunk->device);
   tech = strsep(&tech_data, "/");
   if (ast_dial_append(dial, tech, tech_data) == -1) {
      ast_mutex_lock(args->cond_lock);
      ast_cond_signal(args->cond);
      ast_mutex_unlock(args->cond_lock);
      ast_dial_destroy(dial);
      return NULL;
   }

   /* Do we need to save of the caller ID data? */
   caller_is_saved = 0;
   if (!sla.attempt_callerid) {
      caller_is_saved = 1;
      caller = *ast_channel_caller(trunk_ref->chan);
      ast_party_caller_init(ast_channel_caller(trunk_ref->chan));
   }

   dial_res = ast_dial_run(dial, trunk_ref->chan, 1);

   /* Restore saved caller ID */
   if (caller_is_saved) {
      ast_party_caller_free(ast_channel_caller(trunk_ref->chan));
      ast_channel_caller_set(trunk_ref->chan, &caller);
   }

   if (dial_res != AST_DIAL_RESULT_TRYING) {
      ast_mutex_lock(args->cond_lock);
      ast_cond_signal(args->cond);
      ast_mutex_unlock(args->cond_lock);
      ast_dial_destroy(dial);
      return NULL;
   }

   for (;;) {
      unsigned int done = 0;
      switch ((dial_res = ast_dial_state(dial))) {
      case AST_DIAL_RESULT_ANSWERED:
         trunk_ref->trunk->chan = ast_dial_answered(dial);
      case AST_DIAL_RESULT_HANGUP:
      case AST_DIAL_RESULT_INVALID:
      case AST_DIAL_RESULT_FAILED:
      case AST_DIAL_RESULT_TIMEOUT:
      case AST_DIAL_RESULT_UNANSWERED:
         done = 1;
         break;
      case AST_DIAL_RESULT_TRYING:
         current_state = AST_CONTROL_PROGRESS;
         break;
      case AST_DIAL_RESULT_RINGING:
      case AST_DIAL_RESULT_PROGRESS:
      case AST_DIAL_RESULT_PROCEEDING:
         current_state = AST_CONTROL_RINGING;
         break;
      }
      if (done)
         break;

      /* check that SLA station that originated trunk call is still alive */
      if (station && ast_device_state(station->device) == AST_DEVICE_NOT_INUSE) {
         ast_debug(3, "Originating station device %s no longer active\n", station->device);
         trunk_ref->trunk->chan = NULL;
         break;
      }

      /* If trunk line state changed, send indication back to originating SLA Station channel */
      if (current_state != last_state) {
         ast_debug(3, "Indicating State Change %d to channel %s\n", current_state, ast_channel_name(trunk_ref->chan));
         ast_indicate(trunk_ref->chan, current_state);
         last_state = current_state;
      }

      /* avoid tight loop... sleep for 1/10th second */
      ast_safe_sleep(trunk_ref->chan, 100);
   }

   if (!trunk_ref->trunk->chan) {
      ast_mutex_lock(args->cond_lock);
      ast_cond_signal(args->cond);
      ast_mutex_unlock(args->cond_lock);
      ast_dial_join(dial);
      ast_dial_destroy(dial);
      return NULL;
   }

   snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
   ast_set_flag64(&conf_flags, 
      CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | 
      CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
   conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);

   ast_mutex_lock(args->cond_lock);
   ast_cond_signal(args->cond);
   ast_mutex_unlock(args->cond_lock);

   if (conf) {
      conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
      dispose_conf(conf);
      conf = NULL;
   }

   /* If the trunk is going away, it is definitely now IDLE. */
   sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);

   trunk_ref->trunk->chan = NULL;
   trunk_ref->trunk->on_hold = 0;

   ast_dial_join(dial);
   ast_dial_destroy(dial);

   return NULL;
}
static int dispose_conf ( struct ast_conference conf) [static]

Decrement reference counts, as incremented by find_conf()

Definition at line 2149 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().

{
   int res = 0;
   int confno_int = 0;

   AST_LIST_LOCK(&confs);
   if (ast_atomic_dec_and_test(&conf->refcount)) {
      /* Take the conference room number out of an inuse state */
      if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
         conf_map[confno_int] = 0;
      }
      conf_free(conf);
      res = 1;
   }
   AST_LIST_UNLOCK(&confs);

   return res;
}
static void filename_parse ( char *  filename,
char *  buffer 
) [static]

Definition at line 5450 of file app_meetme.c.

References ast_config_AST_SPOOL_DIR, ast_copy_string(), ast_log(), ast_mkdir(), ast_strlen_zero(), and LOG_WARNING.

Referenced by recordthread().

{
   char *slash;
   if (ast_strlen_zero(filename)) {
      ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
   } else if (filename[0] != '/') {
      snprintf(buffer, PATH_MAX, "%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
   } else {
      ast_copy_string(buffer, filename, PATH_MAX);
   }

   slash = buffer;
   if ((slash = strrchr(slash, '/'))) {
      *slash = '\0';
      ast_mkdir(buffer, 0777);
      *slash = '/';
   }
}
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_flags64 confflags 
) [static, read]

Definition at line 4469 of file app_meetme.c.

References args, AST_APP_ARG, ast_app_getdata(), ast_clear_flag64, ast_config_destroy(), ast_config_load, ast_copy_string(), ast_debug, AST_DECLARE_APP_ARGS, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_STANDARD_APP_ARGS, ast_test_flag64, ast_variable_browse(), build_conf(), ast_conference::chan, CONFFLAG_INTROUSER, CONFFLAG_INTROUSER_VMREC, CONFFLAG_INTROUSERNOREVIEW, CONFFLAG_QUIET, CONFFLAG_RECORDCONF, CONFIG_FILE_NAME, CONFIG_STATUS_FILEINVALID, ast_conference::confno, ast_conference::list, LOG_ERROR, LOG_WARNING, MAX_SETTINGS, ast_variable::name, ast_variable::next, parse(), ast_conference::refcount, S_OR, ast_variable::value, and var.

Referenced by conf_exec(), and count_exec().

{
   struct ast_config *cfg;
   struct ast_variable *var;
   struct ast_flags config_flags = { 0 };
   struct ast_conference *cnf;

   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(confno);
      AST_APP_ARG(pin);
      AST_APP_ARG(pinadmin);
   );

   /* Check first in the conference list */
   ast_debug(1, "The requested confno is '%s'?\n", confno);
   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, cnf, list) {
      ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
      if (!strcmp(confno, cnf->confno))
         break;
   }
   if (cnf) {
      cnf->refcount += refcount;
   }
   AST_LIST_UNLOCK(&confs);

   if (!cnf) {
      if (dynamic) {
         /* No need to parse meetme.conf */
         ast_debug(1, "Building dynamic conference '%s'\n", confno);
         if (dynamic_pin) {
            if (dynamic_pin[0] == 'q') {
               /* Query the user to enter a PIN */
               if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
                  return NULL;
            }
            cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
         } else {
            cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
         }
      } else {
         /* Check the config */
         cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
         if (!cfg) {
            ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
            return NULL;
         } else if (cfg == CONFIG_STATUS_FILEINVALID) {
            ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
            return NULL;
         }

         for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
            char parse[MAX_SETTINGS];

            if (strcasecmp(var->name, "conf"))
               continue;

            ast_copy_string(parse, var->value, sizeof(parse));

            AST_STANDARD_APP_ARGS(args, parse);
            ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
            if (!strcasecmp(args.confno, confno)) {
               /* Bingo it's a valid conference */
               cnf = build_conf(args.confno,
                     S_OR(args.pin, ""),
                     S_OR(args.pinadmin, ""),
                     make, dynamic, refcount, chan, NULL);
               break;
            }
         }
         if (!var) {
            ast_debug(1, "%s isn't a valid conference\n", confno);
         }
         ast_config_destroy(cfg);
      }
   } else if (dynamic_pin) {
      /* Correct for the user selecting 'D' instead of 'd' to have
         someone join into a conference that has already been created
         with a pin. */
      if (dynamic_pin[0] == 'q') {
         dynamic_pin[0] = '\0';
      }
   }

   if (cnf) {
      if (confflags && !cnf->chan &&
          !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
          ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW  | CONFFLAG_INTROUSER_VMREC)) {
         ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
         ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
      }
      
      if (confflags && !cnf->chan &&
          ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
         ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
         ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
      }
   }

   return cnf;
}
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_flags64 confflags,
int *  too_early,
char **  optargs 
) [static, read]

Definition at line 4281 of file app_meetme.c.

References ast_conference::adminopts, ast_app_parse_options64(), ast_channel_language(), ast_channel_lock, ast_channel_uniqueid(), ast_channel_unlock, ast_clear_flag64, ast_copy_flags64, ast_copy_string(), ast_debug, ast_free, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime(), ast_localtime(), ast_log(), AST_MAX_EXTENSION, ast_mktime(), ast_strdup, ast_streamfile(), ast_strftime(), ast_strlen_zero(), ast_strptime(), ast_test_flag64, ast_tvnow(), ast_variables_destroy(), ast_verb, ast_waitstream(), ast_conference::bookid, build_conf(), ast_conference::chan, CONFFLAG_INTROUSER, CONFFLAG_INTROUSER_VMREC, CONFFLAG_INTROUSERNOREVIEW, CONFFLAG_QUIET, CONFFLAG_RECORDCONF, ast_conference::confno, DATE_FORMAT, earlyalert, endalert, ast_conference::endalert, ast_conference::endtime, ast_flags64::flags, fuzzystart, ast_conference::list, LOG_WARNING, ast_conference::maxusers, meetme_opts, ast_variable::name, ast_variable::next, OPTIONS_LEN, pbx_builtin_getvar_helper(), ast_conference::recordingfilename, ast_conference::recordingformat, ast_conference::refcount, ast_conference::useropts, ast_variable::value, and var.

Referenced by conf_exec().

{
   struct ast_variable *var, *origvar;
   struct ast_conference *cnf;

   *too_early = 0;

   /* Check first in the conference list */
   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, cnf, list) {
      if (!strcmp(confno, cnf->confno)) {
         break;
      }
   }
   if (cnf) {
      cnf->refcount += refcount;
   }
   AST_LIST_UNLOCK(&confs);

   if (!cnf) {
      char *pin = NULL, *pinadmin = NULL; /* For temp use */
      int maxusers = 0;
      struct timeval now;
      char recordingfilename[256] = "";
      char recordingformat[11] = "";
      char currenttime[32] = "";
      char eatime[32] = "";
      char bookid[51] = "";
      char recordingtmp[AST_MAX_EXTENSION] = "";
      char useropts[OPTIONS_LEN + 1] = ""; /* Used for RealTime conferences */
      char adminopts[OPTIONS_LEN + 1] = "";
      struct ast_tm tm, etm;
      struct timeval endtime = { .tv_sec = 0 };
      const char *var2;

      if (rt_schedule) {
         now = ast_tvnow();

         ast_localtime(&now, &tm, NULL);
         ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);

         ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);

         var = ast_load_realtime("meetme", "confno",
            confno, "starttime <= ", currenttime, "endtime >= ",
            currenttime, NULL);

         if (!var && fuzzystart) {
            now = ast_tvnow();
            now.tv_sec += fuzzystart;

            ast_localtime(&now, &tm, NULL);
            ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
            var = ast_load_realtime("meetme", "confno",
               confno, "starttime <= ", currenttime, "endtime >= ",
               currenttime, NULL);
         }

         if (!var && earlyalert) {
            now = ast_tvnow();
            now.tv_sec += earlyalert;
            ast_localtime(&now, &etm, NULL);
            ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
            var = ast_load_realtime("meetme", "confno",
               confno, "starttime <= ", eatime, "endtime >= ",
               currenttime, NULL);
            if (var) {
               *too_early = 1;
            }
         }

      } else {
          var = ast_load_realtime("meetme", "confno", confno, NULL);
      }

      if (!var) {
         return NULL;
      }

      if (rt_schedule && *too_early) {
         /* Announce that the caller is early and exit */
         if (!ast_streamfile(chan, "conf-has-not-started", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
         ast_variables_destroy(var);
         return NULL;
      }

      for (origvar = var; var; var = var->next) {
         if (!strcasecmp(var->name, "pin")) {
            pin = ast_strdupa(var->value);
         } else if (!strcasecmp(var->name, "adminpin")) {
            pinadmin = ast_strdupa(var->value);
         } else if (!strcasecmp(var->name, "bookId")) {
            ast_copy_string(bookid, var->value, sizeof(bookid));
         } else if (!strcasecmp(var->name, "opts")) {
            ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
         } else if (!strcasecmp(var->name, "maxusers")) {
            maxusers = atoi(var->value);
         } else if (!strcasecmp(var->name, "adminopts")) {
            ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
         } else if (!strcasecmp(var->name, "recordingfilename")) {
            ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
         } else if (!strcasecmp(var->name, "recordingformat")) {
            ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
         } else if (!strcasecmp(var->name, "endtime")) {
            struct ast_tm endtime_tm;
            ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
            endtime = ast_mktime(&endtime_tm, NULL);
         }
      }

      ast_variables_destroy(origvar);

      cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);

      if (cnf) {
         struct ast_flags64 tmp_flags;

         cnf->maxusers = maxusers;
         cnf->endalert = endalert;
         cnf->endtime = endtime.tv_sec;
         cnf->useropts = ast_strdup(useropts);
         cnf->adminopts = ast_strdup(adminopts);
         cnf->bookid = ast_strdup(bookid);
         if (!ast_strlen_zero(recordingfilename)) {
            cnf->recordingfilename = ast_strdup(recordingfilename);
         }
         if (!ast_strlen_zero(recordingformat)) {
            cnf->recordingformat = ast_strdup(recordingformat);
         }

         /* Parse the other options into confflags -- need to do this in two
          * steps, because the parse_options routine zeroes the buffer. */
         ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
         ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);

         if (strchr(cnf->useropts, 'r')) {
            if (ast_strlen_zero(recordingfilename)) { /* If the recordingfilename in the database is empty, use the channel definition or use the default. */
               ast_channel_lock(chan);
               if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
                  ast_free(cnf->recordingfilename);
                  cnf->recordingfilename = ast_strdup(var2);
               }
               ast_channel_unlock(chan);
               if (ast_strlen_zero(cnf->recordingfilename)) {
                  snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, ast_channel_uniqueid(chan));
                  ast_free(cnf->recordingfilename);
                  cnf->recordingfilename = ast_strdup(recordingtmp);
               }
            }
            if (ast_strlen_zero(cnf->recordingformat)) {/* If the recording format is empty, use the wav as default */
               ast_channel_lock(chan);
               if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
                  ast_free(cnf->recordingformat);
                  cnf->recordingformat = ast_strdup(var2);
               }
               ast_channel_unlock(chan);
               if (ast_strlen_zero(cnf->recordingformat)) {
                  ast_free(cnf->recordingformat);
                  cnf->recordingformat = ast_strdup("wav");
               }
            }
            ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
         }
      }
   }

   if (cnf) {
      if (confflags->flags && !cnf->chan &&
          !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
          ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) | CONFFLAG_INTROUSER_VMREC) {
         ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
         ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
      }

      if (confflags && !cnf->chan &&
          ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
         ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
         ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
      }
   }

   return cnf;
}
static struct ast_conf_user* find_user ( struct ast_conference conf,
const char *  callerident 
) [static, read]

Definition at line 4943 of file app_meetme.c.

References ao2_find, user, and ast_conference::usercontainer.

Referenced by admin_exec().

{
   struct ast_conf_user *user = NULL;
   int cid;

   if (conf && callerident && sscanf(callerident, "%30d", &cid) == 1) {
      user = ao2_find(conf->usercontainer, &cid, 0);
      /* reference decremented later in admin_exec */
      return user;
   }
   return NULL;
}
static const char* get_announce_filename ( enum announcetypes  type) [static]

Definition at line 2246 of file app_meetme.c.

References CONF_HASJOIN, and CONF_HASLEFT.

Referenced by announce_thread().

{
   switch (type) {
   case CONF_HASLEFT:
      return "conf-hasleft";
      break;
   case CONF_HASJOIN:
      return "conf-hasjoin";
      break;
   default:
      return "";
   }
}
static const char* istalking ( int  x) [static]

Definition at line 1040 of file app_meetme.c.

Referenced by meetme_show_cmd().

{
   if (x > 0)
      return "(talking)";
   else if (x < 0)
      return "(unmonitored)";
   else 
      return "(not talking)";
}
static int load_config ( int  reload) [static]

Definition at line 7788 of file app_meetme.c.

References load_config_meetme(), and sla_load_config().

Referenced by load_module(), and reload().

static void load_config_meetme ( void  ) [static]

Definition at line 5560 of file app_meetme.c.

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

Referenced by load_config().

{
   struct ast_config *cfg;
   struct ast_flags config_flags = { 0 };
   const char *val;

   if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
      return;
   } else if (cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
      return;
   }

   audio_buffers = DEFAULT_AUDIO_BUFFERS;

   /*  Scheduling support is off by default */
   rt_schedule = 0;
   fuzzystart = 0;
   earlyalert = 0;
   endalert = 0;
   extendby = 0;

   /*  Logging of participants defaults to ON for compatibility reasons */
   rt_log_members = 1;  

   if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
      if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
         ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
         audio_buffers = DEFAULT_AUDIO_BUFFERS;
      } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
         ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
            DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
         audio_buffers = DEFAULT_AUDIO_BUFFERS;
      }
      if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
         ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
   }

   if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
      rt_schedule = ast_true(val);
   if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
      rt_log_members = ast_true(val);
   if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
      if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
         ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
         fuzzystart = 0;
      } 
   }
   if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
      if ((sscanf(val, "%30d", &earlyalert) != 1)) {
         ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
         earlyalert = 0;
      } 
   }
   if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
      if ((sscanf(val, "%30d", &endalert) != 1)) {
         ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
         endalert = 0;
      } 
   }
   if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
      if ((sscanf(val, "%30d", &extendby) != 1)) {
         ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
         extendby = 0;
      } 
   }

   ast_config_destroy(cfg);
}
static int load_module ( void  ) [static]

Definition at line 8004 of file app_meetme.c.

References action_meetmelist(), action_meetmelistrooms(), action_meetmemute(), action_meetmeunmute(), admin_exec(), ARRAY_LEN, ast_cli_register_multiple(), ast_custom_function_register, ast_data_register_multiple, ast_devstate_prov_add(), ast_manager_register_xml, ast_realtime_require_field(), ast_register_application_xml, AST_TEST_REGISTER, 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().

static char* meetme_cmd_helper ( struct ast_cli_args a) [static]

Definition at line 1632 of file app_meetme.c.

References admin_exec(), ast_cli_args::argv, ast_debug, ast_free, ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_set(), CLI_FAILURE, CLI_SHOWUSAGE, CLI_SUCCESS, and MAX_CONFNUM.

Referenced by meetme_kick_cmd(), meetme_lock_cmd(), and meetme_mute_cmd().

{
   /* Process the command */
   struct ast_str *cmdline;

   /* Max confno length */
   if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
      return CLI_FAILURE;
   }

   ast_str_set(&cmdline, 0, "%s", a->argv[2]);  /* Argv 2: conference number */
   if (strcasestr(a->argv[1], "lock")) {
      if (strcasecmp(a->argv[1], "lock") == 0) {
         /* Lock */
         ast_str_append(&cmdline, 0, ",L");
      } else {
         /* Unlock */
         ast_str_append(&cmdline, 0, ",l");
      }
   } else if (strcasestr(a->argv[1], "mute")) { 
      if (strcasecmp(a->argv[1], "mute") == 0) {
         /* Mute */
         if (strcasecmp(a->argv[3], "all") == 0) {
            ast_str_append(&cmdline, 0, ",N");
         } else {
            ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);  
         }
      } else {
         /* Unmute */
         if (strcasecmp(a->argv[3], "all") == 0) {
            ast_str_append(&cmdline, 0, ",n");
         } else {
            ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
         }
      }
   } else if (strcasecmp(a->argv[1], "kick") == 0) {
      if (strcasecmp(a->argv[3], "all") == 0) {
         /* Kick all */
         ast_str_append(&cmdline, 0, ",K");
      } else {
         /* Kick a single user */
         ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
      }
   } else {
      /*
       * Should never get here because it is already filtered by the
       * callers.
       */
      ast_free(cmdline);
      return CLI_SHOWUSAGE;
   }

   ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));

   admin_exec(NULL, ast_str_buffer(cmdline));
   ast_free(cmdline);

   return CLI_SUCCESS;
}
static int meetme_data_provider_get ( const struct ast_data_search search,
struct ast_data data_root 
) [static]

Definition at line 7869 of file app_meetme.c.

References ao2_callback, ao2_container_count(), ast_data_add_node(), ast_data_add_structure, ast_data_remove_node(), ast_data_search_match(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, OBJ_NODATA, user_add_provider_cb(), and ast_conference::usercontainer.

{
   struct ast_conference *cnf;
   struct ast_data *data_meetme, *data_meetme_users;

   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, cnf, list) {
      data_meetme = ast_data_add_node(data_root, "meetme");
      if (!data_meetme) {
         continue;
      }

      ast_data_add_structure(ast_conference, data_meetme, cnf);

      if (ao2_container_count(cnf->usercontainer)) {
         data_meetme_users = ast_data_add_node(data_meetme, "users");
         if (!data_meetme_users) {
            ast_data_remove_node(data_root, data_meetme);
            continue;
         }

         ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users); 
      }

      if (!ast_data_search_match(search, data_meetme)) {
         ast_data_remove_node(data_root, data_meetme);
      }
   }
   AST_LIST_UNLOCK(&confs);

   return 0;
}
static char* meetme_kick_cmd ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1712 of file app_meetme.c.

References ast_cli_args::argc, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, ast_cli_entry::command, complete_meetmecmd_mute_kick(), ast_cli_args::line, meetme_cmd_helper(), ast_cli_args::n, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.

{
   switch (cmd) {
   case CLI_INIT:
      e->command = "meetme kick";
      e->usage =
         "Usage: meetme kick <confno> all|<userno>\n"
         "       Kick a conference or a user in a conference.\n";
      return NULL;
   case CLI_GENERATE:
      return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
   }

   if (a->argc != 4) {
      return CLI_SHOWUSAGE;
   }

   return meetme_cmd_helper(a);
}
static char* meetme_lock_cmd ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1692 of file app_meetme.c.

References ast_cli_args::argc, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, ast_cli_entry::command, complete_meetmecmd_lock(), meetme_cmd_helper(), ast_cli_args::n, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.

{
   switch (cmd) {
   case CLI_INIT:
      e->command = "meetme {lock|unlock}";
      e->usage =
         "Usage: meetme lock|unlock <confno>\n"
         "       Lock or unlock a conference to new users.\n";
      return NULL;
   case CLI_GENERATE:
      return complete_meetmecmd_lock(a->word, a->pos, a->n);
   }

   if (a->argc != 3) {
      return CLI_SHOWUSAGE;
   }

   return meetme_cmd_helper(a);
}
static void meetme_menu ( enum menu_modes menu_mode,
int *  dtmf,
struct ast_conference conf,
struct ast_flags64 confflags,
struct ast_channel chan,
struct ast_conf_user user,
char *  recordingtmp,
int  recordingtmp_size,
struct dahdi_confinfo *  dahdic,
struct ast_format_cap cap_slin 
) [static]

Definition at line 2819 of file app_meetme.c.

References meetme_menu_admin(), meetme_menu_admin_extended(), meetme_menu_normal(), MENU_ADMIN, MENU_ADMIN_EXTENDED, MENU_DISABLED, and MENU_NORMAL.

Referenced by conf_run().

{
   switch (*menu_mode) {
   case MENU_DISABLED:
      break;
   case MENU_NORMAL:
      meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
      break;
   case MENU_ADMIN:
      meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
      /* Admin Menu is capable of branching into another menu, in which case it will reset dtmf and change the menu mode. */
      if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
         break;
      }
   case MENU_ADMIN_EXTENDED:
      meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user, recordingtmp, recordingtmp_size, dahdic, cap_slin);
      break;
   }
}
static void meetme_menu_admin ( enum menu_modes menu_mode,
int *  dtmf,
struct ast_conference conf,
struct ast_flags64 confflags,
struct ast_channel chan,
struct ast_conf_user user 
) [static]

Definition at line 2513 of file app_meetme.c.

References ADMINFLAG_KICKME, ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ast_conf_user::adminflags, ao2_callback, ao2_find, ao2_ref, ast_channel_language(), ast_channel_name(), AST_DIGIT_ANY, ast_stopstream(), ast_streamfile(), ast_test_flag64, ast_waitstream(), ast_conf_user::chan, CONFFLAG_ADMIN, CONFFLAG_MONITOR, ast_conference::confno, ast_conference::locked, MENU_ADMIN_EXTENDED, MENU_DISABLED, OBJ_NODATA, rt_extend_conf(), tweak_listen_volume(), tweak_talk_volume(), user_max_cmp(), ast_conference::usercontainer, ast_conf_user::userflags, VOL_DOWN, and VOL_UP.

Referenced by meetme_menu().

{
   switch(*dtmf) {
   case '1': /* Un/Mute */
      *menu_mode = MENU_DISABLED;
      /* for admin, change both admin and use flags */
      if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
         user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
      } else {
         user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
      }

      if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
         if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      } else {
         if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      }
      break;

   case '2': /* Un/Lock the Conference */
      *menu_mode = MENU_DISABLED;
      if (conf->locked) {
         conf->locked = 0;
         if (!ast_streamfile(chan, "conf-unlockednow", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      } else {
         conf->locked = 1;
         if (!ast_streamfile(chan, "conf-lockednow", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      }
      break;

   case '3': /* Eject last user */
   {
      struct ast_conf_user *usr = NULL;
      int max_no = 0;
      ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
      *menu_mode = MENU_DISABLED;
      usr = ao2_find(conf->usercontainer, &max_no, 0);
      if ((ast_channel_name(usr->chan) == ast_channel_name(chan)) || ast_test_flag64(&usr->userflags, CONFFLAG_ADMIN)) {
         if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      } else {
         usr->adminflags |= ADMINFLAG_KICKME;
      }
      ao2_ref(usr, -1);
      ast_stopstream(chan);
      break;
   }

   case '4':
      tweak_listen_volume(user, VOL_DOWN);
      break;

   case '5':
      /* Extend RT conference */
      if (rt_schedule) {
         if (!rt_extend_conf(conf->confno)) {
            if (!ast_streamfile(chan, "conf-extended", ast_channel_language(chan))) {
               ast_waitstream(chan, "");
            }
         } else {
            if (!ast_streamfile(chan, "conf-nonextended", ast_channel_language(chan))) {
               ast_waitstream(chan, "");
            }
         }
         ast_stopstream(chan);
      }
      *menu_mode = MENU_DISABLED;
      break;

   case '6':
      tweak_listen_volume(user, VOL_UP);
      break;

   case '7':
      tweak_talk_volume(user, VOL_DOWN);
      break;

   case '8':
      if (!ast_streamfile(chan, "conf-adminmenu-menu8", ast_channel_language(chan))) {
         /* If the user provides DTMF while playing the sound, we want to drop right into the extended menu function with new DTMF once we get out of here. */
         *dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
         ast_stopstream(chan);
      }
      *menu_mode = MENU_ADMIN_EXTENDED;
      break;

   case '9':
      tweak_talk_volume(user, VOL_UP);
      break;
   default:
      menu_mode = MENU_DISABLED;
      /* Play an error message! */
      if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
         ast_waitstream(chan, "");
      }
      break;
   }

}
static void meetme_menu_admin_extended ( enum menu_modes menu_mode,
int *  dtmf,
struct ast_conference conf,
struct ast_flags64 confflags,
struct ast_channel chan,
struct ast_conf_user user,
char *  recordingtmp,
int  recordingtmp_size,
struct dahdi_confinfo *  dahdic,
struct ast_format_cap cap_slin 
) [static]

Definition at line 2635 of file app_meetme.c.

References ao2_callback, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_channel_fd(), ast_channel_language(), ast_channel_lock, ast_channel_uniqueid(), ast_channel_unlock, AST_DIGIT_ANY, ast_fileexists(), AST_FORMAT_SLINEAR, ast_hangup(), ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_pthread_create_detached_background, AST_PTHREADT_NULL, ast_request(), ast_say_number(), ast_set_flag64, ast_set_read_format_by_id(), ast_set_write_format_by_id(), ast_stopstream(), ast_strdup, ast_streamfile(), ast_test_flag64, ast_verb, ast_waitstream(), CONFFLAG_RECORDCONF, ast_conference::confno, ast_conference::dahdiconf, ast_conference::gmuted, ast_conference::lchan, LOG_WARNING, MEETME_RECORD_ACTIVE, MENU_DISABLED, ast_conf_user::namerecloc, OBJ_NODATA, pbx_builtin_getvar_helper(), ast_conference::recording, ast_conference::recordingfilename, ast_conference::recordingformat, ast_conference::recordthread, ast_conference::recordthreadlock, user_set_kickme_cb(), user_set_muted_cb(), user_set_unmuted_cb(), ast_conference::usercontainer, ast_conference::users, and var.

Referenced by meetme_menu().

{
   int keepplaying;
   int playednamerec;
   int res;
   struct ao2_iterator user_iter;
   struct ast_conf_user *usr = NULL;

   switch(*dtmf) {
   case '1': /* *81 Roll call */
      keepplaying = 1;
      playednamerec = 0;
      if (conf->users == 1) {
         if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
            res = ast_waitstream(chan, AST_DIGIT_ANY);
            ast_stopstream(chan);
            if (res > 0) {
               keepplaying = 0;
            }
         }
      } else if (conf->users == 2) {
         if (keepplaying && !ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
            res = ast_waitstream(chan, AST_DIGIT_ANY);
            ast_stopstream(chan);
            if (res > 0) {
               keepplaying = 0;
            }
         }
      } else {
         if (keepplaying && !ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
            res = ast_waitstream(chan, AST_DIGIT_ANY);
            ast_stopstream(chan);
            if (res > 0) {
               keepplaying = 0;
            }
         }
         if (keepplaying) {
            res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
            ast_stopstream(chan);
            if (res > 0) {
               keepplaying = 0;
            }
         }
         if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
            res = ast_waitstream(chan, AST_DIGIT_ANY);
            ast_stopstream(chan);
            if (res > 0) {
               keepplaying = 0;
            }
         }
      }
      user_iter = ao2_iterator_init(conf->usercontainer, 0);
      while((usr = ao2_iterator_next(&user_iter))) {
         if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
            if (keepplaying && !ast_streamfile(chan, usr->namerecloc, ast_channel_language(chan))) {
               res = ast_waitstream(chan, AST_DIGIT_ANY);
               ast_stopstream(chan);
               if (res > 0) {
                  keepplaying = 0;
               }
            }
            playednamerec = 1;
         }
         ao2_ref(usr, -1);
      }
      ao2_iterator_destroy(&user_iter);
      if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", ast_channel_language(chan))) {
         res = ast_waitstream(chan, AST_DIGIT_ANY);
         ast_stopstream(chan);
         if (res > 0) {
            keepplaying = 0;
         }
      }

      *menu_mode = MENU_DISABLED;
      break;

   case '2': /* *82 Eject all non-admins */
      if (conf->users == 1) {
         if(!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      } else {
         ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
      }
      ast_stopstream(chan);
      *menu_mode = MENU_DISABLED;
      break;

   case '3': /* *83 (Admin) mute/unmute all non-admins */
      if(conf->gmuted) {
         conf->gmuted = 0;
         ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
         if (!ast_streamfile(chan, "conf-now-unmuted", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      } else {
         conf->gmuted = 1;
         ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
         if (!ast_streamfile(chan, "conf-now-muted", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      }
      ast_stopstream(chan);
      *menu_mode = MENU_DISABLED;
      break;

   case '4': /* *84 Record conference */
      if (conf->recording != MEETME_RECORD_ACTIVE) {
         ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
         if (!conf->recordingfilename) {
            const char *var;
            ast_channel_lock(chan);
            if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
               conf->recordingfilename = ast_strdup(var);
            }
            if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
               conf->recordingformat = ast_strdup(var);
            }
            ast_channel_unlock(chan);
            if (!conf->recordingfilename) {
               snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
               conf->recordingfilename = ast_strdup(recordingtmp);
            }
            if (!conf->recordingformat) {
               conf->recordingformat = ast_strdup("wav");
            }
            ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
            conf->confno, conf->recordingfilename, conf->recordingformat);
         }

         ast_mutex_lock(&conf->recordthreadlock);
         if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
            ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
            ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
            dahdic->chan = 0;
            dahdic->confno = conf->dahdiconf;
            dahdic->confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
            if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, dahdic)) {
               ast_log(LOG_WARNING, "Error starting listen channel\n");
               ast_hangup(conf->lchan);
               conf->lchan = NULL;
            } else {
               ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
            }
         }
         ast_mutex_unlock(&conf->recordthreadlock);
         if (!ast_streamfile(chan, "conf-now-recording", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      }

      ast_stopstream(chan);
      *menu_mode = MENU_DISABLED;
      break;

   case '8': /* *88 Exit the menu and return to the conference... without an error message */
      ast_stopstream(chan);
      *menu_mode = MENU_DISABLED;
      break;

   default:
      if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
         ast_waitstream(chan, "");
      }
      ast_stopstream(chan);
      *menu_mode = MENU_DISABLED;
      break;
   }
}
static void meetme_menu_normal ( enum menu_modes menu_mode,
int *  dtmf,
struct ast_conference conf,
struct ast_flags64 confflags,
struct ast_channel chan,
struct ast_conf_user user 
) [static]

Definition at line 2433 of file app_meetme.c.

References ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ADMINFLAG_T_REQUEST, ast_conf_user::adminflags, ast_channel_language(), ast_streamfile(), ast_test_flag64, ast_waitstream(), CONFFLAG_MONITOR, ast_conference::confno, MENU_DISABLED, rt_extend_conf(), tweak_listen_volume(), tweak_talk_volume(), VOL_DOWN, and VOL_UP.

Referenced by meetme_menu().

{
   switch (*dtmf) {
   case '1': /* Un/Mute */
      *menu_mode = MENU_DISABLED;

      /* user can only toggle the self-muted state */
      user->adminflags ^= ADMINFLAG_SELFMUTED;

      /* they can't override the admin mute state */
      if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
         if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      } else {
         if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      }
      break;

   case '2':
      *menu_mode = MENU_DISABLED;
      if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
         user->adminflags |= ADMINFLAG_T_REQUEST;
      }

      if (user->adminflags & ADMINFLAG_T_REQUEST) {
         if (!ast_streamfile(chan, "beep", ast_channel_language(chan))) {
            ast_waitstream(chan, "");
         }
      }
      break;

   case '4':
      tweak_listen_volume(user, VOL_DOWN);
      break;
   case '5':
      /* Extend RT conference */
      if (rt_schedule) {
         rt_extend_conf(conf->confno);
      }
      *menu_mode = MENU_DISABLED;
      break;

   case '6':
      tweak_listen_volume(user, VOL_UP);
      break;

   case '7':
      tweak_talk_volume(user, VOL_DOWN);
      break;

   case '8':
      *menu_mode = MENU_DISABLED;
      break;

   case '9':
      tweak_talk_volume(user, VOL_UP);
      break;

   default:
      *menu_mode = MENU_DISABLED;
      if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
         ast_waitstream(chan, "");
      }
      break;
   }
}
static char* meetme_mute_cmd ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1732 of file app_meetme.c.

References ast_cli_args::argc, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, ast_cli_entry::command, complete_meetmecmd_mute_kick(), ast_cli_args::line, meetme_cmd_helper(), ast_cli_args::n, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.

{
   switch (cmd) {
   case CLI_INIT:
      e->command = "meetme {mute|unmute}";
      e->usage =
         "Usage: meetme mute|unmute <confno> all|<userno>\n"
         "       Mute or unmute a conference or a user in a conference.\n";
      return NULL;
   case CLI_GENERATE:
      return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
   }

   if (a->argc != 4) {
      return CLI_SHOWUSAGE;
   }

   return meetme_cmd_helper(a);
}
static char* meetme_show_cmd ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1489 of file app_meetme.c.

References ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ADMINFLAG_T_REQUEST, ast_conf_user::adminflags, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_cli_args::argc, ast_cli_args::argv, ast_channel_caller(), ast_channel_name(), ast_cli(), ast_free, AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_str_buffer(), ast_str_create(), ast_str_set(), ast_test_flag64, ast_conf_user::chan, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_meetmecmd_list(), CONFFLAG_ADMIN, CONFFLAG_MONITOR, ast_conference::confno, ast_cli_args::fd, ast_party_caller::id, ast_conference::isdynamic, istalking(), ast_conf_user::jointime, ast_cli_args::line, ast_conference::locked, ast_conference::markedusers, MC_DATA_FORMAT, MC_HEADER_FORMAT, ast_cli_args::n, ast_party_id::name, ast_party_id::number, ast_cli_args::pos, S_COR, ast_conference::start, ast_party_name::str, ast_party_number::str, STR_CONCISE, ast_conf_user::talking, total, ast_cli_entry::usage, user, ast_conf_user::user_no, ast_conference::usercontainer, ast_conf_user::userflags, ast_conference::users, ast_party_name::valid, ast_party_number::valid, and ast_cli_args::word.

{
   /* Process the command */
   struct ast_conf_user *user;
   struct ast_conference *cnf;
   int hr, min, sec;
   int total = 0;
   time_t now;
#define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s  %-8s  %-6s\n"
#define MC_DATA_FORMAT "%-12.12s   %4.4d        %4.4s       %02d:%02d:%02d  %-8s  %-6s\n"

   switch (cmd) {
   case CLI_INIT:
      e->command = "meetme list";
      e->usage =
         "Usage: meetme list [<confno>] [" STR_CONCISE "]\n"
         "       List all conferences or a specific conference.\n";
      return NULL;
   case CLI_GENERATE:
      return complete_meetmecmd_list(a->line, a->word, a->pos, a->n);
   }

   if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], STR_CONCISE))) {
      /* List all the conferences */
      int concise = (a->argc == 3);
      struct ast_str *marked_users;

      if (!(marked_users = ast_str_create(30))) {
         return CLI_FAILURE;
      }

      now = time(NULL);
      AST_LIST_LOCK(&confs);
      if (AST_LIST_EMPTY(&confs)) {
         if (!concise) {
            ast_cli(a->fd, "No active MeetMe conferences.\n");
         }
         AST_LIST_UNLOCK(&confs);
         ast_free(marked_users);
         return CLI_SUCCESS;
      }
      if (!concise) {
         ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
      }
      AST_LIST_TRAVERSE(&confs, cnf, list) {
         hr = (now - cnf->start) / 3600;
         min = ((now - cnf->start) % 3600) / 60;
         sec = (now - cnf->start) % 60;
         if (!concise) {
            if (cnf->markedusers == 0) {
               ast_str_set(&marked_users, 0, "N/A ");
            } else {
               ast_str_set(&marked_users, 0, "%4.4d", cnf->markedusers);
            }
            ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users,
               ast_str_buffer(marked_users), hr, min, sec,
               cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
         } else {
            ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
               cnf->confno,
               cnf->users,
               cnf->markedusers,
               hr, min, sec,
               cnf->isdynamic,
               cnf->locked);
         }

         total += cnf->users;
      }
      AST_LIST_UNLOCK(&confs);
      if (!concise) {
         ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
      }
      ast_free(marked_users);
      return CLI_SUCCESS;
   }
   if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], STR_CONCISE))) {
      struct ao2_iterator user_iter;
      int concise = (a->argc == 4);

      /* List all the users in a conference */
      if (AST_LIST_EMPTY(&confs)) {
         if (!concise) {
            ast_cli(a->fd, "No active MeetMe conferences.\n");
         }
         return CLI_SUCCESS;
      }
      /* Find the right conference */
      AST_LIST_LOCK(&confs);
      AST_LIST_TRAVERSE(&confs, cnf, list) {
         if (strcmp(cnf->confno, a->argv[2]) == 0) {
            break;
         }
      }
      if (!cnf) {
         if (!concise)
            ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
         AST_LIST_UNLOCK(&confs);
         return CLI_SUCCESS;
      }
      /* Show all the users */
      time(&now);
      user_iter = ao2_iterator_init(cnf->usercontainer, 0);
      while((user = ao2_iterator_next(&user_iter))) {
         hr = (now - user->jointime) / 3600;
         min = ((now - user->jointime) % 3600) / 60;
         sec = (now - user->jointime) % 60;
         if (!concise) {
            ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
               user->user_no,
               S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
               S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
               ast_channel_name(user->chan),
               ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
               ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
               user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
               user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
               istalking(user->talking), hr, min, sec); 
         } else {
            ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
               user->user_no,
               S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""),
               S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""),
               ast_channel_name(user->chan),
               ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
               ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
               user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
               user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
               user->talking, hr, min, sec);
         }
         ao2_ref(user, -1);
      }
      ao2_iterator_destroy(&user_iter);
      if (!concise) {
         ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
      }
      AST_LIST_UNLOCK(&confs);
      return CLI_SUCCESS;
   }
   return CLI_SHOWUSAGE;
}
static int meetmemute ( struct mansession s,
const struct message m,
int  mute 
) [static]

Definition at line 5232 of file app_meetme.c.

References ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ADMINFLAG_T_REQUEST, ast_conf_user::adminflags, ao2_find, ao2_ref, ast_channel_name(), ast_channel_uniqueid(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), 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, user, ast_conf_user::user_no, and ast_conference::usercontainer.

Referenced by action_meetmemute(), and action_meetmeunmute().

{
   struct ast_conference *conf;
   struct ast_conf_user *user;
   const char *confid = astman_get_header(m, "Meetme");
   char *userid = ast_strdupa(astman_get_header(m, "Usernum"));
   int userno;

   if (ast_strlen_zero(confid)) {
      astman_send_error(s, m, "Meetme conference not specified");
      return 0;
   }

   if (ast_strlen_zero(userid)) {
      astman_send_error(s, m, "Meetme user number not specified");
      return 0;
   }

   userno = strtoul(userid, &userid, 10);

   if (*userid) {
      astman_send_error(s, m, "Invalid user number");
      return 0;
   }

   /* Look in the conference list */
   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, conf, list) {
      if (!strcmp(confid, conf->confno))
         break;
   }

   if (!conf) {
      AST_LIST_UNLOCK(&confs);
      astman_send_error(s, m, "Meetme conference does not exist");
      return 0;
   }

   user = ao2_find(conf->usercontainer, &userno, 0);

   if (!user) {
      AST_LIST_UNLOCK(&confs);
      astman_send_error(s, m, "User number not found");
      return 0;
   }

   if (mute)
      user->adminflags |= ADMINFLAG_MUTED;   /* request user muting */
   else
      user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST); /* request user unmuting */

   AST_LIST_UNLOCK(&confs);

   ast_log(LOG_NOTICE, "Requested to %smute conf %s user %d userchan %s uniqueid %s\n", mute ? "" : "un", conf->confno, user->user_no, ast_channel_name(user->chan), ast_channel_uniqueid(user->chan));

   ao2_ref(user, -1);
   astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
   return 0;
}
static enum ast_device_state meetmestate ( const char *  data) [static]

Callback for devicestate providers.

Definition at line 5538 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().

{
   struct ast_conference *conf;

   /* Find conference */
   AST_LIST_LOCK(&confs);
   AST_LIST_TRAVERSE(&confs, conf, list) {
      if (!strcmp(data, conf->confno))
         break;
   }
   AST_LIST_UNLOCK(&confs);
   if (!conf)
      return AST_DEVICE_INVALID;


   /* SKREP to fill */
   if (!conf->users)
      return AST_DEVICE_NOT_INUSE;

   return AST_DEVICE_INUSE;
}
static struct sla_ringing_trunk* queue_ringing_trunk ( struct sla_trunk trunk) [static, read]

Definition at line 6958 of file app_meetme.c.

References ALL_TRUNK_REFS, ao2_ref, 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().

{
   struct sla_ringing_trunk *ringing_trunk;

   if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk)))) {
      return NULL;
   }

   ao2_ref(trunk, 1);
   ringing_trunk->trunk = trunk;
   ringing_trunk->ring_begin = ast_tvnow();

   sla_change_trunk_state(trunk, SLA_TRUNK_STATE_RINGING, ALL_TRUNK_REFS, NULL);

   ast_mutex_lock(&sla.lock);
   AST_LIST_INSERT_HEAD(&sla.ringing_trunks, ringing_trunk, entry);
   ast_mutex_unlock(&sla.lock);

   sla_queue_event(SLA_EVENT_RINGING_TRUNK);

   return ringing_trunk;
}
static void * recordthread ( void *  args) [static]

Definition at line 5469 of file app_meetme.c.

References args, ast_closestream(), 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_strlen_zero(), ast_waitfor(), ast_writefile(), ast_writestream(), f, filename_parse(), 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::recording, ast_conference::recordingfilename, ast_conference::recordingformat, and ast_conference::transframe.

{
   struct ast_conference *cnf = args;
   struct ast_frame *f = NULL;
   int flags;
   struct ast_filestream *s = NULL;
   int res = 0;
   int x;
   const char *oldrecordingfilename = NULL;
   char filename_buffer[PATH_MAX];

   if (!cnf || !cnf->lchan) {
      pthread_exit(0);
   }

   filename_buffer[0] = '\0';
   filename_parse(cnf->recordingfilename, filename_buffer);

   ast_stopstream(cnf->lchan);
   flags = O_CREAT | O_TRUNC | O_WRONLY;


   cnf->recording = MEETME_RECORD_ACTIVE;
   while (ast_waitfor(cnf->lchan, -1) > -1) {
      if (cnf->recording == MEETME_RECORD_TERMINATE) {
         AST_LIST_LOCK(&confs);
         AST_LIST_UNLOCK(&confs);
         break;
      }
      if (!s && !(ast_strlen_zero(filename_buffer)) && (filename_buffer != oldrecordingfilename)) {
         s = ast_writefile(filename_buffer, cnf->recordingformat, NULL, flags, 0, AST_FILE_MODE);
         oldrecordingfilename = filename_buffer;
      }
      
      f = ast_read(cnf->lchan);
      if (!f) {
         res = -1;
         break;
      }
      if (f->frametype == AST_FRAME_VOICE) {
         ast_mutex_lock(&cnf->listenlock);
         for (x = 0; x < AST_FRAME_BITS; x++) {
            /* Free any translations that have occured */
            if (cnf->transframe[x]) {
               ast_frfree(cnf->transframe[x]);
               cnf->transframe[x] = NULL;
            }
         }
         if (cnf->origframe)
            ast_frfree(cnf->origframe);
         cnf->origframe = ast_frdup(f);
         ast_mutex_unlock(&cnf->listenlock);
         if (s)
            res = ast_writestream(s, f);
         if (res) {
            ast_frfree(f);
            break;
         }
      }
      ast_frfree(f);
   }
   cnf->recording = MEETME_RECORD_OFF;
   if (s)
      ast_closestream(s);
   
   pthread_exit(0);
}
static int reload ( void  ) [static]

Definition at line 8036 of file app_meetme.c.

References ast_unload_realtime(), and load_config().

{
   ast_unload_realtime("meetme");
   return load_config(1);
}
static void reset_volumes ( struct ast_conf_user user) [static]

Definition at line 1160 of file app_meetme.c.

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

Referenced by admin_exec(), conf_run(), and user_reset_vol_cb().

{
   signed char zero_volume = 0;

   ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
   ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
}
static int rt_extend_conf ( const char *  confno) [static]

Definition at line 2168 of file app_meetme.c.

References ast_copy_string(), ast_debug, ast_load_realtime(), ast_localtime(), ast_mktime(), ast_strftime(), ast_strptime(), ast_tvnow(), ast_update_realtime(), ast_variables_destroy(), DATE_FORMAT, extendby, ast_variable::name, ast_variable::next, ast_variable::value, and var.

Referenced by admin_exec(), meetme_menu_admin(), and meetme_menu_normal().

{
   char currenttime[32];
   char endtime[32];
   struct timeval now;
   struct ast_tm tm;
   struct ast_variable *var, *orig_var;
   char bookid[51];

   if (!extendby) {
      return 0;
   }

   now = ast_tvnow();

   ast_localtime(&now, &tm, NULL);
   ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);

   var = ast_load_realtime("meetme", "confno",
      confno, "startTime<= ", currenttime,
      "endtime>= ", currenttime, NULL);

   orig_var = var;

   /* Identify the specific RealTime conference */
   while (var) {
      if (!strcasecmp(var->name, "bookid")) {
         ast_copy_string(bookid, var->value, sizeof(bookid));
      }
      if (!strcasecmp(var->name, "endtime")) {
         ast_copy_string(endtime, var->value, sizeof(endtime));
      }

      var = var->next;
   }
   ast_variables_destroy(orig_var);

   ast_strptime(endtime, DATE_FORMAT, &tm);
   now = ast_mktime(&tm, NULL);

   now.tv_sec += extendby;

   ast_localtime(&now, &tm, NULL);
   ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
   strcat(currenttime, "0"); /* Seconds needs to be 00 */

   var = ast_load_realtime("meetme", "confno",
      confno, "startTime<= ", currenttime,
      "endtime>= ", currenttime, NULL);

   /* If there is no conflict with extending the conference, update the DB */
   if (!var) {
      ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
      ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
      return 0;

   }

   ast_variables_destroy(var);
   return -1;
}
static void* run_station ( void *  data) [static]

Definition at line 5844 of file app_meetme.c.

References sla_trunk::active_stations, admin_exec(), ALL_TRUNK_REFS, answer_trunk_chan(), ao2_cleanup, args, 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_flag64, ast_str_append(), ast_str_buffer(), 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(), 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, sla_trunk_ref::trunk, and run_station_args::trunk_ref.

Referenced by sla_handle_dial_state_event().

{
   RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
   RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
   struct ast_str *conf_name = ast_str_create(16);
   struct ast_flags64 conf_flags = { 0 };
   struct ast_conference *conf;

   {
      struct run_station_args *args = data;
      station = args->station;
      trunk_ref = args->trunk_ref;
      ast_mutex_lock(args->cond_lock);
      ast_cond_signal(args->cond);
      ast_mutex_unlock(args->cond_lock);
      /* args is no longer valid here. */
   }

   ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1);
   ast_str_set(&conf_name, 0, "SLA_%s", trunk_ref->trunk->name);
   ast_set_flag64(&conf_flags, 
      CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
   answer_trunk_chan(trunk_ref->chan);
   conf = build_conf(ast_str_buffer(conf_name), "", "", 0, 0, 1, trunk_ref->chan, NULL);
   if (conf) {
      conf_run(trunk_ref->chan, conf, &conf_flags, NULL);
      dispose_conf(conf);
      conf = NULL;
   }
   trunk_ref->chan = NULL;
   if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
      trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
      ast_str_append(&conf_name, 0, ",K");
      admin_exec(NULL, ast_str_buffer(conf_name));
      trunk_ref->trunk->hold_stations = 0;
      sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
   }

   ast_dial_join(station->dial);
   ast_dial_destroy(station->dial);
   station->dial = NULL;
   ast_free(conf_name);

   return NULL;
}
static void send_talking_event ( struct ast_channel chan,
struct ast_conference conf,
struct ast_conf_user user,
int  talking 
) [static]

Definition at line 2326 of file app_meetme.c.

References ast_channel_name(), ast_channel_uniqueid(), ast_manager_event, ast_conference::confno, EVENT_FLAG_CALL, and ast_conf_user::user_no.

Referenced by set_user_talking().

{
   /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when a MeetMe user begins or ends talking.</synopsis>
         <syntax>
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
            <parameter name="Status">
               <enumlist>
                  <enum name="on"/>
                  <enum name="off"/>
               </enumlist>
            </parameter>
         </syntax>
      </managerEventInstance>
   ***/
   ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
      "Channel: %s\r\n"
      "Uniqueid: %s\r\n"
      "Meetme: %s\r\n"
      "Usernum: %d\r\n"
      "Status: %s\r\n",
      ast_channel_name(chan), ast_channel_uniqueid(chan),
      conf->confno,
      user->user_no, talking ? "on" : "off");
}
static int set_listen_volume ( struct ast_conf_user user,
int  volume 
) [static]

Definition at line 1089 of file app_meetme.c.

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

Referenced by tweak_listen_volume().

{
   char gain_adjust;

   /* attempt to make the adjustment in the channel driver;
      if successful, don't adjust in the frame reading routine
   */
   gain_adjust = gain_map[volume + 5];

   return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
}
static int set_talk_volume ( struct ast_conf_user user,
int  volume 
) [static]

Definition at line 1077 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().

{
   char gain_adjust;

   /* attempt to make the adjustment in the channel driver;
      if successful, don't adjust in the frame reading routine
   */
   gain_adjust = gain_map[volume + 5];

   return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
}
static void set_user_talking ( struct ast_channel chan,
struct ast_conference conf,
struct ast_conf_user user,
int  talking,
int  monitor 
) [static]

Definition at line 2354 of file app_meetme.c.

References send_talking_event(), and ast_conf_user::talking.

Referenced by conf_run().

{
   int last_talking = user->talking;
   if (last_talking == talking)
      return;

   user->talking = talking;

   if (monitor) {
      /* Check if talking state changed. Take care of -1 which means unmonitored */
      int was_talking = (last_talking > 0);
      int now_talking = (talking > 0);
      if (was_talking != now_talking) {
         send_talking_event(chan, conf, user, now_talking);
      }
   }
}
static void sla_add_trunk_to_station ( struct sla_station station,
struct ast_variable var 
) [static]

Definition at line 7353 of file app_meetme.c.

References ao2_cleanup, ao2_lock, ao2_ref, ao2_unlock, ast_atomic_fetchadd_int(), AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), create_trunk_ref(), LOG_ERROR, LOG_WARNING, sla_station_ref::mark, sla_trunk_ref::mark, name, sla_trunk::name, sla_trunk::num_stations, sla_trunk_ref::ring_delay, sla_trunk_ref::ring_timeout, sla_create_station_ref(), sla_find_trunk(), SLA_TRUNK_STATE_IDLE, sla_trunk_ref::state, sla_station_ref::station, sla_trunk::stations, sla_trunk_ref::trunk, sla_station::trunks, value, and ast_variable::value.

Referenced by sla_build_station().

{
   RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
   struct sla_trunk_ref *trunk_ref = NULL;
   struct sla_station_ref *station_ref;
   char *trunk_name, *options, *cur;
   int existing_trunk_ref = 0;
   int existing_station_ref = 0;

   options = ast_strdupa(var->value);
   trunk_name = strsep(&options, ",");

   trunk = sla_find_trunk(trunk_name);
   if (!trunk) {
      ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
      return;
   }

   AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
      if (trunk_ref->trunk == trunk) {
         trunk_ref->mark = 0;
         existing_trunk_ref = 1;
         break;
      }
   }

   if (!trunk_ref && !(trunk_ref = create_trunk_ref(trunk))) {
      return;
   }

   trunk_ref->state = SLA_TRUNK_STATE_IDLE;

   while ((cur = strsep(&options, ","))) {
      char *name, *value = cur;
      name = strsep(&value, "=");
      if (!strcasecmp(name, "ringtimeout")) {
         if (sscanf(value, "%30u", &trunk_ref->ring_timeout) != 1) {
            ast_log(LOG_WARNING, "Invalid ringtimeout value '%s' for "
               "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
            trunk_ref->ring_timeout = 0;
         }
      } else if (!strcasecmp(name, "ringdelay")) {
         if (sscanf(value, "%30u", &trunk_ref->ring_delay) != 1) {
            ast_log(LOG_WARNING, "Invalid ringdelay value '%s' for "
               "trunk '%s' on station '%s'\n", value, trunk->name, station->name);
            trunk_ref->ring_delay = 0;
         }
      } else {
         ast_log(LOG_WARNING, "Invalid option '%s' for "
            "trunk '%s' on station '%s'\n", name, trunk->name, station->name);
      }
   }

   AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
      if (station_ref->station == station) {
         station_ref->mark = 0;
         existing_station_ref = 1;
         break;
      }
   }

   if (!station_ref && !(station_ref = sla_create_station_ref(station))) {
      if (!existing_trunk_ref) {
         ao2_ref(trunk_ref, -1);
      } else {
         trunk_ref->mark = 1;
      }
      return;
   }

   if (!existing_station_ref) {
      ao2_lock(trunk);
      AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
      ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
      ao2_unlock(trunk);
   }

   if (!existing_trunk_ref) {
      ao2_lock(station);
      AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
      ao2_unlock(station);
   }
}
static int sla_build_station ( struct ast_config cfg,
const char *  cat 
) [static]

Definition at line 7437 of file app_meetme.c.

References ao2_alloc, ao2_cleanup, ao2_link, ao2_lock, ao2_unlock, ast_add_extension2(), ast_context_find_or_create(), ast_free_ptr(), AST_LIST_TRAVERSE, ast_log(), AST_MAX_APP, AST_MAX_EXTENSION, ast_strdup, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), context, exten, sla_station::hold_access, ast_variable::lineno, LOG_ERROR, LOG_WARNING, sla_station::mark, ast_variable::name, sla_trunk::name, ast_variable::next, PRIORITY_HINT, sla_station::ring_delay, sla_station::ring_timeout, sla_add_trunk_to_station(), SLA_CONFIG_FILE, sla_find_station(), SLA_HOLD_OPEN, SLA_HOLD_PRIVATE, sla_registrar, sla_station_destructor(), sla_stations, sla_trunk_ref::trunk, sla_station::trunks, ast_variable::value, and var.

Referenced by sla_load_config().

{
   RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
   struct ast_variable *var;
   const char *dev;
   int existing_station = 0;

   if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
      ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
      return -1;
   }

   if ((station = sla_find_station(cat))) {
      station->mark = 0;
      existing_station = 1;
   } else if ((station = ao2_alloc(sizeof(*station), sla_station_destructor))) {
      if (ast_string_field_init(station, 32)) {
         return -1;
      }
      ast_string_field_set(station, name, cat);
   } else {
      return -1;
   }

   ao2_lock(station);

   ast_string_field_set(station, device, dev);

   for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
      if (!strcasecmp(var->name, "trunk")) {
         ao2_unlock(station);
         sla_add_trunk_to_station(station, var);
         ao2_lock(station);
      } else if (!strcasecmp(var->name, "autocontext")) {
         ast_string_field_set(station, autocontext, var->value);
      } else if (!strcasecmp(var->name, "ringtimeout")) {
         if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
            ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
               var->value, station->name);
            station->ring_timeout = 0;
         }
      } else if (!strcasecmp(var->name, "ringdelay")) {
         if (sscanf(var->value, "%30u", &station->ring_delay) != 1) {
            ast_log(LOG_WARNING, "Invalid ringdelay '%s' specified for station '%s'\n",
               var->value, station->name);
            station->ring_delay = 0;
         }
      } else if (!strcasecmp(var->name, "hold")) {
         if (!strcasecmp(var->value, "private"))
            station->hold_access = SLA_HOLD_PRIVATE;
         else if (!strcasecmp(var->value, "open"))
            station->hold_access = SLA_HOLD_OPEN;
         else {
            ast_log(LOG_WARNING, "Invalid value '%s' for hold on station %s\n",
               var->value, station->name);
         }

      } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
         ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
            var->name, var->lineno, SLA_CONFIG_FILE);
      }
   }

   ao2_unlock(station);

   if (!ast_strlen_zero(station->autocontext)) {
      struct ast_context *context;
      struct sla_trunk_ref *trunk_ref;
      context = ast_context_find_or_create(NULL, NULL, station->autocontext, sla_registrar);
      if (!context) {
         ast_log(LOG_ERROR, "Failed to automatically find or create "
            "context '%s' for SLA!\n", station->autocontext);
         return -1;
      }
      /* The extension for when the handset goes off-hook.
       * exten => station1,1,SLAStation(station1) */
      if (ast_add_extension2(context, 0 /* don't replace */, station->name, 1,
         NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
         ast_log(LOG_ERROR, "Failed to automatically create extension "
            "for trunk '%s'!\n", station->name);
         return -1;
      }
      AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
         char exten[AST_MAX_EXTENSION];
         char hint[AST_MAX_APP];
         snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
         snprintf(hint, sizeof(hint), "SLA:%s", exten);
         /* Extension for this line button 
          * exten => station1_line1,1,SLAStation(station1_line1) */
         if (ast_add_extension2(context, 0 /* don't replace */, exten, 1,
            NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
            ast_log(LOG_ERROR, "Failed to automatically create extension "
               "for trunk '%s'!\n", station->name);
            return -1;
         }
         /* Hint for this line button 
          * exten => station1_line1,hint,SLA:station1_line1 */
         if (ast_add_extension2(context, 0 /* don't replace */, exten, PRIORITY_HINT,
            NULL, NULL, hint, NULL, NULL, sla_registrar)) {
            ast_log(LOG_ERROR, "Failed to automatically create hint "
               "for trunk '%s'!\n", station->name);
            return -1;
         }
      }
   }

   if (!existing_station) {
      ao2_link(sla_stations, station);
   }

   return 0;
}
static int sla_build_trunk ( struct ast_config cfg,
const char *  cat 
) [static]

Definition at line 7264 of file app_meetme.c.

References ao2_alloc, ao2_cleanup, ao2_link, ao2_lock, ao2_unlock, ast_add_extension2(), ast_context_find_or_create(), ast_false(), ast_free_ptr(), ast_log(), 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, sla_trunk::hold_access, ast_variable::lineno, LOG_ERROR, LOG_WARNING, sla_trunk::mark, ast_variable::name, sla_trunk::name, ast_variable::next, sla_trunk::ring_timeout, sla_check_device(), SLA_CONFIG_FILE, sla_find_trunk(), SLA_HOLD_OPEN, SLA_HOLD_PRIVATE, sla_registrar, sla_trunk_destructor(), sla_trunks, ast_variable::value, and var.

Referenced by sla_load_config().

{
   RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
   struct ast_variable *var;
   const char *dev;
   int existing_trunk = 0;

   if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
      ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
      return -1;
   }

   if (sla_check_device(dev)) {
      ast_log(LOG_ERROR, "SLA Trunk '%s' defined with invalid device '%s'!\n",
         cat, dev);
      return -1;
   }

   if ((trunk = sla_find_trunk(cat))) {
      trunk->mark = 0;
      existing_trunk = 1;
   } else if ((trunk = ao2_alloc(sizeof(*trunk), sla_trunk_destructor))) {
      if (ast_string_field_init(trunk, 32)) {
         return -1;
      }
      ast_string_field_set(trunk, name, cat);
   } else {
      return -1;
   }

   ao2_lock(trunk);

   ast_string_field_set(trunk, device, dev);

   for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
      if (!strcasecmp(var->name, "autocontext"))
         ast_string_field_set(trunk, autocontext, var->value);
      else if (!strcasecmp(var->name, "ringtimeout")) {
         if (sscanf(var->value, "%30u", &trunk->ring_timeout) != 1) {
            ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for trunk '%s'\n",
               var->value, trunk->name);
            trunk->ring_timeout = 0;
         }
      } else if (!strcasecmp(var->name, "barge"))
         trunk->barge_disabled = ast_false(var->value);
      else if (!strcasecmp(var->name, "hold")) {
         if (!strcasecmp(var->value, "private"))
            trunk->hold_access = SLA_HOLD_PRIVATE;
         else if (!strcasecmp(var->value, "open"))
            trunk->hold_access = SLA_HOLD_OPEN;
         else {
            ast_log(LOG_WARNING, "Invalid value '%s' for hold on trunk %s\n",
               var->value, trunk->name);
         }
      } else if (strcasecmp(var->name, "type") && strcasecmp(var->name, "device")) {
         ast_log(LOG_ERROR, "Invalid option '%s' specified at line %d of %s!\n",
            var->name, var->lineno, SLA_CONFIG_FILE);
      }
   }

   ao2_unlock(trunk);

   if (!ast_strlen_zero(trunk->autocontext)) {
      struct ast_context *context;
      context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
      if (!context) {
         ast_log(LOG_ERROR, "Failed to automatically find or create "
            "context '%s' for SLA!\n", trunk->autocontext);
         return -1;
      }
      if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
         NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
         ast_log(LOG_ERROR, "Failed to automatically create extension "
            "for trunk '%s'!\n", trunk->name);
         return -1;
      }
   }

   if (!existing_trunk) {
      ao2_link(sla_trunks, trunk);
   }

   return 0;
}
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 6472 of file app_meetme.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, sla_check_inuse_station(), sla_check_ringing_station(), sla_check_station_delay(), sla_choose_ringing_trunk(), and sla_stations.

Referenced by sla_process_timers().

{
   struct sla_station *station;
   int res = 0;
   struct ao2_iterator i;

   i = ao2_iterator_init(sla_stations, 0);
   for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
      struct sla_ringing_trunk *ringing_trunk;
      int time_left;

      /* Ignore stations already ringing */
      if (sla_check_ringing_station(station))
         continue;

      /* Ignore stations already on a call */
      if (sla_check_inuse_station(station))
         continue;

      /* Ignore stations that don't have one of their trunks ringing */
      if (!(ringing_trunk = sla_choose_ringing_trunk(station, NULL, 0)))
         continue;

      if ((time_left = sla_check_station_delay(station, ringing_trunk)) == INT_MAX)
         continue;

      /* If there is no time left, then the station needs to start ringing.
       * Return non-zero so that an event will be queued up an event to 
       * make that happen. */
      if (time_left <= 0) {
         res = 1;
         continue;
      }

      if (time_left < *timeout)
         *timeout = time_left;
   }
   ao2_iterator_destroy(&i);

   return res;
}
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 6389 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_trunk::ring_begin, sla_ringing_station::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().

{
   struct sla_ringing_trunk *ringing_trunk;
   struct sla_ringing_station *ringing_station;
   int res = 0;

   AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
      unsigned int ring_timeout = 0;
      int time_elapsed, time_left = INT_MAX, final_trunk_time_left = INT_MIN;
      struct sla_trunk_ref *trunk_ref;

      /* If there are any ring timeouts specified for a specific trunk
       * on the station, then use the highest per-trunk ring timeout.
       * Otherwise, use the ring timeout set for the entire station. */
      AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
         struct sla_station_ref *station_ref;
         int trunk_time_elapsed, trunk_time_left;

         AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
            if (ringing_trunk->trunk == trunk_ref->trunk)
               break;
         }
         if (!ringing_trunk)
            continue;

         /* If there is a trunk that is ringing without a timeout, then the
          * only timeout that could matter is a global station ring timeout. */
         if (!trunk_ref->ring_timeout)
            break;

         /* This trunk on this station is ringing and has a timeout.
          * However, make sure this trunk isn't still ringing from a
          * previous timeout.  If so, don't consider it. */
         AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, station_ref, entry) {
            if (station_ref->station == ringing_station->station)
               break;
         }
         if (station_ref)
            continue;

         trunk_time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
         trunk_time_left = (trunk_ref->ring_timeout * 1000) - trunk_time_elapsed;
         if (trunk_time_left > final_trunk_time_left)
            final_trunk_time_left = trunk_time_left;
      }

      /* No timeout was found for ringing trunks, and no timeout for the entire station */
      if (final_trunk_time_left == INT_MIN && !ringing_station->station->ring_timeout)
         continue;

      /* Compute how much time is left for a global station timeout */
      if (ringing_station->station->ring_timeout) {
         ring_timeout = ringing_station->station->ring_timeout;
         time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_station->ring_begin);
         time_left = (ring_timeout * 1000) - time_elapsed;
      }

      /* If the time left based on the per-trunk timeouts is smaller than the
       * global station ring timeout, use that. */
      if (final_trunk_time_left > INT_MIN && final_trunk_time_left < time_left)
         time_left = final_trunk_time_left;

      /* If there is no time left, the station needs to stop ringing */
      if (time_left <= 0) {
         AST_LIST_REMOVE_CURRENT(entry);
         sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_TIMEOUT);
         res = 1;
         continue;
      }

      /* There is still some time left for this station to ring, so save that
       * timeout if it is the first event scheduled to occur */
      if (time_left < *timeout)
         *timeout = time_left;
   }
   AST_LIST_TRAVERSE_SAFE_END;

   return res;
}
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 6359 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().

{
   struct sla_ringing_trunk *ringing_trunk;
   int res = 0;

   AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
      int time_left, time_elapsed;
      if (!ringing_trunk->trunk->ring_timeout)
         continue;
      time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
      time_left = (ringing_trunk->trunk->ring_timeout * 1000) - time_elapsed;
      if (time_left <= 0) {
         pbx_builtin_setvar_helper(ringing_trunk->trunk->chan, "SLATRUNK_STATUS", "RINGTIMEOUT");
         AST_LIST_REMOVE_CURRENT(entry);
         sla_stop_ringing_trunk(ringing_trunk);
         res = 1;
         continue;
      }
      if (time_left < *timeout)
         *timeout = time_left;
   }
   AST_LIST_TRAVERSE_SAFE_END;

   return res;
}
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]
static int sla_check_device ( const char *  device) [static]

Definition at line 7236 of file app_meetme.c.

References ast_strlen_zero().

Referenced by sla_build_trunk().

{
   char *tech, *tech_data;

   tech_data = ast_strdupa(device);
   tech = strsep(&tech_data, "/");

   if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
      return -1;

   return 0;
}
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 6108 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_failed_station::last_try, sla, sla_failed_station_destroy(), and sla_failed_station::station.

Referenced by sla_ring_stations().

{
   struct sla_failed_station *failed_station;
   int res = 0;

   AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.failed_stations, failed_station, entry) {
      if (station != failed_station->station)
         continue;
      if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
         AST_LIST_REMOVE_CURRENT(entry);
         sla_failed_station_destroy(failed_station);
         break;
      }
      res = 1;
   }
   AST_LIST_TRAVERSE_SAFE_END

   return res;
}
static int sla_check_inuse_station ( const struct sla_station station) [static]

Check to see if a station is in use.

Definition at line 6191 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().

{
   struct sla_trunk_ref *trunk_ref;

   AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
      if (trunk_ref->chan)
         return 1;
   }

   return 0;
}
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 6093 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().

{
   struct sla_ringing_station *ringing_station;

   AST_LIST_TRAVERSE(&sla.ringing_stations, ringing_station, entry) {
      if (station == ringing_station->station)
         return 1;
   }

   return 0;
}
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:
stationthe station
ringing_trunkthe 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 6223 of file app_meetme.c.

References ao2_cleanup, ast_tvdiff_ms(), ast_tvnow(), sla_ringing_trunk::ring_begin, sla_station::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().

{
   RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
   unsigned int delay = UINT_MAX;
   int time_left, time_elapsed;

   if (!ringing_trunk)
      ringing_trunk = sla_choose_ringing_trunk(station, &trunk_ref, 0);
   else
      trunk_ref = sla_find_trunk_ref(station, ringing_trunk->trunk);

   if (!ringing_trunk || !trunk_ref)
      return delay;

   /* If this station has a ring delay specific to the highest priority
    * ringing trunk, use that.  Otherwise, use the ring delay specified
    * globally for the station. */
   delay = trunk_ref->ring_delay;
   if (!delay)
      delay = station->ring_delay;
   if (!delay)
      return INT_MAX;

   time_elapsed = ast_tvdiff_ms(ast_tvnow(), ringing_trunk->ring_begin);
   time_left = (delay * 1000) - time_elapsed;

   return time_left;
}
static int sla_check_station_hold_access ( const struct sla_trunk trunk,
const struct sla_station station 
) [static]

Definition at line 5656 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().

{
   struct sla_station_ref *station_ref;
   struct sla_trunk_ref *trunk_ref;

   /* For each station that has this call on hold, check for private hold. */
   AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
      AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
         if (trunk_ref->trunk != trunk || station_ref->station == station)
            continue;
         if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME &&
            station_ref->station->hold_access == SLA_HOLD_PRIVATE)
            return 1;
         return 0;
      }
   }

   return 0;
}
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 5951 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().

{
   struct sla_station_ref *timed_out_station;

   AST_LIST_TRAVERSE(&ringing_trunk->timed_out_stations, timed_out_station, entry) {
      if (station == timed_out_station->station)
         return 1;
   }

   return 0;
}
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.

Precondition:
sla_station is locked

Definition at line 6768 of file app_meetme.c.

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

Referenced by sla_station_exec().

{
   struct sla_trunk_ref *trunk_ref = NULL;

   AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
      if (trunk_ref->state == SLA_TRUNK_STATE_IDLE) {
         ao2_ref(trunk_ref, 1);
         break;
      }
   }

   return trunk_ref;
}
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:
stationthe station
rmremove the ringing trunk once selected
trunk_refa 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 5972 of file app_meetme.c.

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

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

{
   struct sla_trunk_ref *s_trunk_ref;
   struct sla_ringing_trunk *ringing_trunk = NULL;

   AST_LIST_TRAVERSE(&station->trunks, s_trunk_ref, entry) {
      AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
         /* Make sure this is the trunk we're looking for */
         if (s_trunk_ref->trunk != ringing_trunk->trunk)
            continue;

         /* This trunk on the station is ringing.  But, make sure this station
          * didn't already time out while this trunk was ringing. */
         if (sla_check_timed_out_station(ringing_trunk, station))
            continue;

         if (rm)
            AST_LIST_REMOVE_CURRENT(entry);

         if (trunk_ref) {
            ao2_ref(s_trunk_ref, 1);
            *trunk_ref = s_trunk_ref;
         }

         break;
      }
      AST_LIST_TRAVERSE_SAFE_END;
   
      if (ringing_trunk)
         break;
   }

   return ringing_trunk;
}
static struct sla_failed_station* sla_create_failed_station ( struct sla_station station) [static, read]

Definition at line 5763 of file app_meetme.c.

References ao2_ref, ast_calloc, ast_tvnow(), sla_failed_station::last_try, and sla_failed_station::station.

Referenced by sla_ring_station().

{
   struct sla_failed_station *failed_station;

   if (!(failed_station = ast_calloc(1, sizeof(*failed_station)))) {
      return NULL;
   }

   ao2_ref(station, 1);
   failed_station->station = station;
   failed_station->last_try = ast_tvnow();

   return failed_station;
}
static struct sla_ringing_station* sla_create_ringing_station ( struct sla_station station) [static, read]

Definition at line 5739 of file app_meetme.c.

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

Referenced by sla_ring_station().

{
   struct sla_ringing_station *ringing_station;

   if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
      return NULL;

   ao2_ref(station, 1);
   ringing_station->station = station;
   ringing_station->ring_begin = ast_tvnow();

   return ringing_station;
}
static struct sla_station_ref* sla_create_station_ref ( struct sla_station station) [static, read]

Definition at line 5725 of file app_meetme.c.

References ao2_alloc, ao2_ref, sla_station_ref_destructor(), and sla_station_ref::station.

Referenced by sla_add_trunk_to_station(), and sla_stop_ringing_station().

{
   struct sla_station_ref *station_ref;

   if (!(station_ref = ao2_alloc(sizeof(*station_ref), sla_station_ref_destructor))) {
      return NULL;
   }

   ao2_ref(station, 1);
   station_ref->station = station;

   return station_ref;
}
static void sla_dial_state_callback ( struct ast_dial dial) [static]

Definition at line 5943 of file app_meetme.c.

References SLA_EVENT_DIAL_STATE, and sla_queue_event().

Referenced by sla_ring_station().

static void sla_event_destroy ( struct sla_event event) [static]

Definition at line 6551 of file app_meetme.c.

References ao2_ref, ast_free, sla_event::station, and sla_event::trunk_ref.

Referenced by sla_thread().

{
   if (event->trunk_ref) {
      ao2_ref(event->trunk_ref, -1);
      event->trunk_ref = NULL;
   }

   if (event->station) {
      ao2_ref(event->station, -1);
      event->station = NULL;
   }

   ast_free(event);
}
static void sla_failed_station_destroy ( struct sla_failed_station failed_station) [static]

Definition at line 5778 of file app_meetme.c.

References ao2_ref, ast_free, and sla_failed_station::station.

Referenced by sla_check_failed_station(), and sla_thread().

{
   if (failed_station->station) {
      ao2_ref(failed_station->station, -1);
      failed_station->station = NULL;
   }

   ast_free(failed_station);
}
static struct sla_station* sla_find_station ( const char *  name) [static, read]

Definition at line 5647 of file app_meetme.c.

References ao2_find, name, OBJ_POINTER, and sla_stations.

Referenced by sla_build_station(), sla_state(), and sla_station_exec().

{
   struct sla_station tmp_station = {
      .name = name,
   };

   return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
}
static struct sla_trunk* sla_find_trunk ( const char *  name) [static, read]

Definition at line 5634 of file app_meetme.c.

References ao2_find, name, sla_trunk::name, OBJ_POINTER, and sla_trunks.

Referenced by sla_add_trunk_to_station(), sla_build_trunk(), and sla_trunk_exec().

{
   struct sla_trunk tmp_trunk = {
      .name = name,
   };

   return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
}
static struct sla_trunk_ref* sla_find_trunk_ref ( const struct sla_station station,
const struct sla_trunk trunk 
) [static, read]

Definition at line 6203 of file app_meetme.c.

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

Referenced by sla_check_station_delay().

{
   struct sla_trunk_ref *trunk_ref = NULL;

   AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
      if (trunk_ref->trunk == trunk)
         break;
   }

   ao2_ref(trunk_ref, 1);

   return trunk_ref;
}
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:
stationthe station
namethe trunk's name
Precondition:
sla_station is locked
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 5686 of file app_meetme.c.

References ao2_ref, 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().

{
   struct sla_trunk_ref *trunk_ref = NULL;

   AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
      if (strcasecmp(trunk_ref->trunk->name, name))
         continue;

      if ( (trunk_ref->trunk->barge_disabled 
         && trunk_ref->state == SLA_TRUNK_STATE_UP) ||
         (trunk_ref->trunk->hold_stations 
         && trunk_ref->trunk->hold_access == SLA_HOLD_PRIVATE
         && trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) ||
         sla_check_station_hold_access(trunk_ref->trunk, station) ) 
      {
         trunk_ref = NULL;
      }

      break;
   }

   if (trunk_ref) {
      ao2_ref(trunk_ref, 1);
   }

   return trunk_ref;
}
static void sla_handle_dial_state_event ( void  ) [static]

Definition at line 6008 of file app_meetme.c.

References ALL_TRUNK_REFS, answer_trunk_chan(), ao2_cleanup, ao2_ref, ast_cond_destroy, ast_cond_init, ast_cond_wait, ast_debug, ast_dial_answered(), 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_state(), 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, cond, run_station_args::cond, run_station_args::cond_lock, sla_station::dial, run_station(), sla, sla_change_trunk_state(), sla_choose_ringing_trunk(), SLA_EVENT_DIAL_STATE, SLA_EVENT_RINGING_TRUNK, sla_queue_event(), sla_ringing_station_destroy(), sla_ringing_trunk_destroy(), SLA_STATION_HANGUP_NORMAL, sla_stop_ringing_station(), SLA_TRUNK_STATE_UP, sla_ringing_station::station, run_station_args::station, sla_ringing_trunk::trunk, and run_station_args::trunk_ref.

Referenced by sla_thread().

{
   struct sla_ringing_station *ringing_station;

   AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
      RAII_VAR(struct sla_trunk_ref *, s_trunk_ref, NULL, ao2_cleanup);
      struct sla_ringing_trunk *ringing_trunk = NULL;
      struct run_station_args args;
      enum ast_dial_result dial_res;
      pthread_t dont_care;
      ast_mutex_t cond_lock;
      ast_cond_t cond;

      switch ((dial_res = ast_dial_state(ringing_station->station->dial))) {
      case AST_DIAL_RESULT_HANGUP:
      case AST_DIAL_RESULT_INVALID:
      case AST_DIAL_RESULT_FAILED:
      case AST_DIAL_RESULT_TIMEOUT:
      case AST_DIAL_RESULT_UNANSWERED:
         AST_LIST_REMOVE_CURRENT(entry);
         sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL);
         break;
      case AST_DIAL_RESULT_ANSWERED:
         AST_LIST_REMOVE_CURRENT(entry);
         /* Find the appropriate trunk to answer. */
         ast_mutex_lock(&sla.lock);
         ringing_trunk = sla_choose_ringing_trunk(ringing_station->station, &s_trunk_ref, 1);
         ast_mutex_unlock(&sla.lock);
         if (!ringing_trunk) {
            /* This case happens in a bit of a race condition.  If two stations answer
             * the outbound call at the same time, the first one will get connected to
             * the trunk.  When the second one gets here, it will not see any trunks
             * ringing so we have no idea what to conect it to.  So, we just hang up
             * on it. */
            ast_debug(1, "Found no ringing trunk for station '%s' to answer!\n", ringing_station->station->name);
            ast_dial_join(ringing_station->station->dial);
            ast_dial_destroy(ringing_station->station->dial);
            ringing_station->station->dial = NULL;
            sla_ringing_station_destroy(ringing_station);
            break;
         }
         /* Track the channel that answered this trunk */
         s_trunk_ref->chan = ast_dial_answered(ringing_station->station->dial);
         /* Actually answer the trunk */
         answer_trunk_chan(ringing_trunk->trunk->chan);
         sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
         /* Now, start a thread that will connect this station to the trunk.  The rest of
          * the code here sets up the thread and ensures that it is able to save the arguments
          * before they are no longer valid since they are allocated on the stack. */
         ao2_ref(s_trunk_ref, 1);
         args.trunk_ref = s_trunk_ref;
         ao2_ref(ringing_station->station, 1);
         args.station = ringing_station->station;
         args.cond = &cond;
         args.cond_lock = &cond_lock;
         sla_ringing_trunk_destroy(ringing_trunk);
         sla_ringing_station_destroy(ringing_station);
         ast_mutex_init(&cond_lock);
         ast_cond_init(&cond, NULL);
         ast_mutex_lock(&cond_lock);
         ast_pthread_create_detached_background(&dont_care, NULL, run_station, &args);
         ast_cond_wait(&cond, &cond_lock);
         ast_mutex_unlock(&cond_lock);
         ast_mutex_destroy(&cond_lock);
         ast_cond_destroy(&cond);
         break;
      case AST_DIAL_RESULT_TRYING:
      case AST_DIAL_RESULT_RINGING:
      case AST_DIAL_RESULT_PROGRESS:
      case AST_DIAL_RESULT_PROCEEDING:
         break;
      }
      if (dial_res == AST_DIAL_RESULT_ANSWERED) {
         /* Queue up reprocessing ringing trunks, and then ringing stations again */
         sla_queue_event(SLA_EVENT_RINGING_TRUNK);
         sla_queue_event(SLA_EVENT_DIAL_STATE);
         break;
      }
   }
   AST_LIST_TRAVERSE_SAFE_END;
}
static void sla_handle_ringing_trunk_event ( void  ) [static]

Definition at line 6325 of file app_meetme.c.

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

Referenced by sla_thread().

{
   ast_mutex_lock(&sla.lock);
   sla_ring_stations();
   ast_mutex_unlock(&sla.lock);

   /* Find stations that shouldn't be ringing anymore. */
   sla_hangup_stations();
}
static void sla_hangup_stations ( void  ) [static]

Definition at line 6297 of file app_meetme.c.

References ast_dial_destroy(), ast_dial_join(), 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_destroy(), sla_ringing_station::station, sla_trunk_ref::trunk, sla_ringing_trunk::trunk, and sla_station::trunks.

Referenced by sla_handle_ringing_trunk_event().

{
   struct sla_trunk_ref *trunk_ref;
   struct sla_ringing_station *ringing_station;

   AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
      AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
         struct sla_ringing_trunk *ringing_trunk;
         ast_mutex_lock(&sla.lock);
         AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
            if (trunk_ref->trunk == ringing_trunk->trunk)
               break;
         }
         ast_mutex_unlock(&sla.lock);
         if (ringing_trunk)
            break;
      }
      if (!trunk_ref) {
         AST_LIST_REMOVE_CURRENT(entry);
         ast_dial_join(ringing_station->station->dial);
         ast_dial_destroy(ringing_station->station->dial);
         ringing_station->station->dial = NULL;
         sla_ringing_station_destroy(ringing_station);
      }
   }
   AST_LIST_TRAVERSE_SAFE_END
}
static const char* sla_hold_str ( unsigned int  hold_access) [static]

Definition at line 1752 of file app_meetme.c.

References SLA_HOLD_OPEN, and SLA_HOLD_PRIVATE.

Referenced by sla_show_stations(), and sla_show_trunks().

{
   const char *hold = "Unknown";

   switch (hold_access) {
   case SLA_HOLD_OPEN:
      hold = "Open";
      break;
   case SLA_HOLD_PRIVATE:
      hold = "Private";
   default:
      break;
   }

   return hold;
}
static int sla_in_use ( void  ) [static]
static int sla_load_config ( int  reload) [static]

Definition at line 7647 of file app_meetme.c.

References ao2_callback, ao2_container_alloc, ast_category_browse(), ast_cond_init, ast_config_destroy(), ast_config_load, ast_log(), ast_mutex_init, ast_pthread_create, AST_PTHREADT_NULL, ast_true(), ast_variable_retrieve(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, LOG_ERROR, LOG_WARNING, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, sla, sla_build_station(), sla_build_trunk(), SLA_CONFIG_FILE, sla_in_use(), sla_station_cmp(), sla_station_hash(), sla_station_is_marked(), sla_station_mark(), sla_stations, sla_thread(), sla_trunk_cmp(), sla_trunk_hash(), sla_trunk_is_marked(), sla_trunk_mark(), sla_trunks, and type.

Referenced by load_config().

{
   struct ast_config *cfg;
   struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
   const char *cat = NULL;
   int res = 0;
   const char *val;

   if (!reload) {
      ast_mutex_init(&sla.lock);
      ast_cond_init(&sla.cond, NULL);
      sla_trunks = ao2_container_alloc(1, sla_trunk_hash, sla_trunk_cmp);
      sla_stations = ao2_container_alloc(1, sla_station_hash, sla_station_cmp);
   }

   if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
      return 0; /* Treat no config as normal */
   } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
      return 0;
   } else if (cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_ERROR, "Config file " SLA_CONFIG_FILE " is in an invalid format.  Aborting.\n");
      return 0;
   }

   if (reload) {
      ao2_callback(sla_trunks, 0, sla_trunk_mark, NULL);
      ao2_callback(sla_stations, 0, sla_station_mark, NULL);
   }

   if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
      sla.attempt_callerid = ast_true(val);

   while ((cat = ast_category_browse(cfg, cat)) && !res) {
      const char *type;
      if (!strcasecmp(cat, "general"))
         continue;
      if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
         ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
            SLA_CONFIG_FILE);
         continue;
      }
      if (!strcasecmp(type, "trunk"))
         res = sla_build_trunk(cfg, cat);
      else if (!strcasecmp(type, "station"))
         res = sla_build_station(cfg, cat);
      else {
         ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'!\n",
            SLA_CONFIG_FILE, type);
      }
   }

   ast_config_destroy(cfg);

   if (reload) {
      ao2_callback(sla_trunks, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_trunk_is_marked, NULL);
      ao2_callback(sla_stations, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_station_is_marked, NULL);
   }

   /* Start SLA event processing thread once SLA has been configured. */
   if (sla.thread == AST_PTHREADT_NULL && sla_in_use()) {
      ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
   }

   return res;
}
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 6516 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().

{
   unsigned int timeout = UINT_MAX;
   struct timeval wait;
   unsigned int change_made = 0;

   /* Check for ring timeouts on ringing trunks */
   if (sla_calc_trunk_timeouts(&timeout))
      change_made = 1;

   /* Check for ring timeouts on ringing stations */
   if (sla_calc_station_timeouts(&timeout))
      change_made = 1;

   /* Check for station ring delays */
   if (sla_calc_station_delays(&timeout))
      change_made = 1;

   /* queue reprocessing of ringing trunks */
   if (change_made)
      sla_queue_event_nolock(SLA_EVENT_RINGING_TRUNK);

   /* No timeout */
   if (timeout == UINT_MAX)
      return 0;

   if (ts) {
      wait = ast_tvadd(ast_tvnow(), ast_samp2tv(timeout, 1000));
      ts->tv_sec = wait.tv_sec;
      ts->tv_nsec = wait.tv_usec * 1000;
   }

   return 1;
}
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 2107 of file app_meetme.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_debug, AST_LIST_TRAVERSE, ast_log(), ast_strlen_zero(), sla_trunk_ref::chan, ast_conference::confno, LOG_ERROR, sla_trunk::name, sla_queue_event_full(), sla_stations, sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by conf_run().

{
   struct sla_station *station;
   struct sla_trunk_ref *trunk_ref = NULL;
   char *trunk_name;
   struct ao2_iterator i;

   trunk_name = ast_strdupa(conf->confno);
   strsep(&trunk_name, "_");
   if (ast_strlen_zero(trunk_name)) {
      ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
      return;
   }

   i = ao2_iterator_init(sla_stations, 0);
   while ((station = ao2_iterator_next(&i))) {
      ao2_lock(station);
      AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
         if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name)) {
            ao2_ref(trunk_ref, 1);
            break;
         }
      }
      ao2_unlock(station);
      if (trunk_ref) {
         /* station reference given to sla_queue_event_full() */
         break;
      }
      ao2_ref(station, -1);
   }
   ao2_iterator_destroy(&i);

   if (!trunk_ref) {
      ast_debug(1, "Trunk not found for event!\n");
      return;
   }

   sla_queue_event_full(type, trunk_ref, station, 1);
}
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 2064 of file app_meetme.c.

References ao2_ref, ast_calloc, ast_cond_signal, AST_LIST_INSERT_TAIL, ast_mutex_lock, ast_mutex_unlock, AST_PTHREADT_NULL, sla, sla_event::station, sla_event::trunk_ref, and type.

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

{
   struct sla_event *event;

   if (sla.thread == AST_PTHREADT_NULL) {
      ao2_ref(station, -1);
      ao2_ref(trunk_ref, -1);
      return;
   }

   if (!(event = ast_calloc(1, sizeof(*event)))) {
      ao2_ref(station, -1);
      ao2_ref(trunk_ref, -1);
      return;
   }

   event->type = type;
   event->trunk_ref = trunk_ref;
   event->station = station;

   if (!lock) {
      AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
      return;
   }

   ast_mutex_lock(&sla.lock);
   AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
   ast_cond_signal(&sla.cond);
   ast_mutex_unlock(&sla.lock);
}
static void sla_queue_event_nolock ( enum sla_event_type  type) [static]

Definition at line 2096 of file app_meetme.c.

References sla_queue_event_full().

Referenced by sla_process_timers().

{
   sla_queue_event_full(type, NULL, NULL, 0);
}
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 6131 of file app_meetme.c.

References ast_channel_caller(), ast_channel_caller_set(), 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_LIST_INSERT_HEAD, ast_party_caller_free(), ast_party_caller_init(), sla_trunk::chan, sla_station::dial, sla, sla_create_failed_station(), sla_create_ringing_station(), sla_dial_state_callback(), and sla_ringing_trunk::trunk.

Referenced by sla_ring_stations().

{
   char *tech, *tech_data;
   struct ast_dial *dial;
   struct sla_ringing_station *ringing_station;
   enum ast_dial_result res;
   int caller_is_saved;
   struct ast_party_caller caller;

   if (!(dial = ast_dial_create()))
      return -1;

   ast_dial_set_state_callback(dial, sla_dial_state_callback);
   tech_data = ast_strdupa(station->device);
   tech = strsep(&tech_data, "/");

   if (ast_dial_append(dial, tech, tech_data) == -1) {
      ast_dial_destroy(dial);
      return -1;
   }

   /* Do we need to save off the caller ID data? */
   caller_is_saved = 0;
   if (!sla.attempt_callerid) {
      caller_is_saved = 1;
      caller = *ast_channel_caller(ringing_trunk->trunk->chan);
      ast_party_caller_init(ast_channel_caller(ringing_trunk->trunk->chan));
   }

   res = ast_dial_run(dial, ringing_trunk->trunk->chan, 1);
   
   /* Restore saved caller ID */
   if (caller_is_saved) {
      ast_party_caller_free(ast_channel_caller(ringing_trunk->trunk->chan));
      ast_channel_caller_set(ringing_trunk->trunk->chan, &caller);
   }
   
   if (res != AST_DIAL_RESULT_TRYING) {
      struct sla_failed_station *failed_station;
      ast_dial_destroy(dial);
      if ((failed_station = sla_create_failed_station(station))) {
         AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
      }
      return -1;
   }
   if (!(ringing_station = sla_create_ringing_station(station))) {
      ast_dial_join(dial);
      ast_dial_destroy(dial);
      return -1;
   }

   station->dial = dial;

   AST_LIST_INSERT_HEAD(&sla.ringing_stations, ringing_station, entry);

   return 0;
}
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 6256 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().

{
   struct sla_station_ref *station_ref;
   struct sla_ringing_trunk *ringing_trunk;

   /* Make sure that every station that uses at least one of the ringing
    * trunks, is ringing. */
   AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
      AST_LIST_TRAVERSE(&ringing_trunk->trunk->stations, station_ref, entry) {
         int time_left;

         /* Is this station already ringing? */
         if (sla_check_ringing_station(station_ref->station))
            continue;

         /* Is this station already in a call? */
         if (sla_check_inuse_station(station_ref->station))
            continue;

         /* Did we fail to dial this station earlier?  If so, has it been
          * a minute since we tried? */
         if (sla_check_failed_station(station_ref->station))
            continue;

         /* If this station already timed out while this trunk was ringing,
          * do not dial it again for this ringing trunk. */
         if (sla_check_timed_out_station(ringing_trunk, station_ref->station))
            continue;

         /* Check for a ring delay in progress */
         time_left = sla_check_station_delay(station_ref->station, ringing_trunk);
         if (time_left != INT_MAX && time_left > 0)
            continue;

         /* It is time to make this station begin to ring.  Do it! */
         sla_ring_station(ringing_trunk, station_ref->station);
      }
   }
   /* Now, all of the stations that should be ringing, are ringing. */
}
static void sla_ringing_station_destroy ( struct sla_ringing_station ringing_station) [static]

Definition at line 5753 of file app_meetme.c.

References ao2_ref, ast_free, and sla_ringing_station::station.

Referenced by sla_handle_dial_state_event(), sla_hangup_stations(), sla_stop_ringing_station(), and sla_thread().

{
   if (ringing_station->station) {
      ao2_ref(ringing_station->station, -1);
      ringing_station->station = NULL;
   }

   ast_free(ringing_station);
}
static void sla_ringing_trunk_destroy ( struct sla_ringing_trunk ringing_trunk) [static]

Definition at line 6981 of file app_meetme.c.

References ao2_ref, ast_free, and sla_ringing_trunk::trunk.

Referenced by sla_handle_dial_state_event(), sla_station_exec(), sla_stop_ringing_trunk(), and sla_trunk_exec().

{
   if (ringing_trunk->trunk) {
      ao2_ref(ringing_trunk->trunk, -1);
      ringing_trunk->trunk = NULL;
   }

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

Definition at line 1843 of file app_meetme.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_cli(), AST_LIST_TRAVERSE, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, sla_station::hold_access, sla_trunk::name, sla_station::ring_delay, sla_trunk_ref::ring_delay, sla_station::ring_timeout, sla_trunk_ref::ring_timeout, S_OR, sla_hold_str(), sla_stations, sla_trunk_ref::state, sla_trunk_ref::trunk, sla_station::trunks, trunkstate2str(), and ast_cli_entry::usage.

{
   struct ao2_iterator i;
   struct sla_station *station;

   switch (cmd) {
   case CLI_INIT:
      e->command = "sla show stations";
      e->usage =
         "Usage: sla show stations\n"
         "       This will list all stations defined in sla.conf\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   ast_cli(a->fd, "\n" 
               "=============================================================\n"
               "=== Configured SLA Stations =================================\n"
               "=============================================================\n"
               "===\n");
   i = ao2_iterator_init(sla_stations, 0);
   for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
      struct sla_trunk_ref *trunk_ref;
      char ring_timeout[16] = "(none)";
      char ring_delay[16] = "(none)";

      ao2_lock(station);

      if (station->ring_timeout) {
         snprintf(ring_timeout, sizeof(ring_timeout), 
            "%u", station->ring_timeout);
      }
      if (station->ring_delay) {
         snprintf(ring_delay, sizeof(ring_delay), 
            "%u", station->ring_delay);
      }
      ast_cli(a->fd, "=== ---------------------------------------------------------\n"
                  "=== Station Name:    %s\n"
                  "=== ==> Device:      %s\n"
                  "=== ==> AutoContext: %s\n"
                  "=== ==> RingTimeout: %s\n"
                  "=== ==> RingDelay:   %s\n"
                  "=== ==> HoldAccess:  %s\n"
                  "=== ==> Trunks ...\n",
                  station->name, station->device,
                  S_OR(station->autocontext, "(none)"), 
                  ring_timeout, ring_delay,
                  sla_hold_str(station->hold_access));
      AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
         if (trunk_ref->ring_timeout) {
            snprintf(ring_timeout, sizeof(ring_timeout),
               "%u", trunk_ref->ring_timeout);
         } else
            strcpy(ring_timeout, "(none)");
         if (trunk_ref->ring_delay) {
            snprintf(ring_delay, sizeof(ring_delay),
               "%u", trunk_ref->ring_delay);
         } else
            strcpy(ring_delay, "(none)");
            ast_cli(a->fd, "===    ==> Trunk Name: %s\n"
                     "===       ==> State:       %s\n"
                     "===       ==> RingTimeout: %s\n"
                     "===       ==> RingDelay:   %s\n",
                     trunk_ref->trunk->name,
                     trunkstate2str(trunk_ref->state),
                     ring_timeout, ring_delay);
      }
      ast_cli(a->fd, "=== ---------------------------------------------------------\n"
                  "===\n");

      ao2_unlock(station);
   }
   ao2_iterator_destroy(&i);
   ast_cli(a->fd, "============================================================\n"
               "\n");

   return CLI_SUCCESS;
}
static char* sla_show_trunks ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1769 of file app_meetme.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_cli(), AST_LIST_TRAVERSE, sla_trunk::autocontext, sla_trunk::barge_disabled, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, sla_trunk::device, ast_cli_args::fd, sla_trunk::hold_access, sla_trunk::name, sla_trunk::ring_timeout, S_OR, sla_hold_str(), sla_trunks, sla_station_ref::station, sla_trunk::stations, and ast_cli_entry::usage.

{
   struct ao2_iterator i;
   struct sla_trunk *trunk;

   switch (cmd) {
   case CLI_INIT:
      e->command = "sla show trunks";
      e->usage =
         "Usage: sla show trunks\n"
         "       This will list all trunks defined in sla.conf\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   ast_cli(a->fd, "\n"
               "=============================================================\n"
               "=== Configured SLA Trunks ===================================\n"
               "=============================================================\n"
               "===\n");
   i = ao2_iterator_init(sla_trunks, 0);
   for (; (trunk = ao2_iterator_next(&i)); ao2_ref(trunk, -1)) {
      struct sla_station_ref *station_ref;
      char ring_timeout[16] = "(none)";

      ao2_lock(trunk);

      if (trunk->ring_timeout) {
         snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
      }

      ast_cli(a->fd, "=== ---------------------------------------------------------\n"
                  "=== Trunk Name:       %s\n"
                  "=== ==> Device:       %s\n"
                  "=== ==> AutoContext:  %s\n"
                  "=== ==> RingTimeout:  %s\n"
                  "=== ==> BargeAllowed: %s\n"
                  "=== ==> HoldAccess:   %s\n"
                  "=== ==> Stations ...\n",
                  trunk->name, trunk->device, 
                  S_OR(trunk->autocontext, "(none)"), 
                  ring_timeout,
                  trunk->barge_disabled ? "No" : "Yes",
                  sla_hold_str(trunk->hold_access));

      AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
         ast_cli(a->fd, "===    ==> Station name: %s\n", station_ref->station->name);
      }

      ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");

      ao2_unlock(trunk);
   }
   ao2_iterator_destroy(&i);
   ast_cli(a->fd, "=============================================================\n\n");

   return CLI_SUCCESS;
}
static enum ast_device_state sla_state ( const char *  data) [static]

Definition at line 7102 of file app_meetme.c.

References ao2_cleanup, ao2_lock, ao2_unlock, AST_DEVICE_INVALID, AST_LIST_TRAVERSE, ast_log(), LOG_ERROR, sla_trunk::name, sla_find_station(), sla_state_to_devstate(), sla_trunk_ref::state, sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by load_module().

{
   char *buf, *station_name, *trunk_name;
   RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
   struct sla_trunk_ref *trunk_ref;
   enum ast_device_state res = AST_DEVICE_INVALID;

   trunk_name = buf = ast_strdupa(data);
   station_name = strsep(&trunk_name, "_");

   station = sla_find_station(station_name);
   if (station) {
      ao2_lock(station);
      AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
         if (!strcasecmp(trunk_name, trunk_ref->trunk->name)) {
            res = sla_state_to_devstate(trunk_ref->state);
            break;
         }
      }
      ao2_unlock(station);
   }

   if (res == AST_DEVICE_INVALID) {
      ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
         trunk_name, station_name);
   }

   return res;
}
static int sla_station_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7203 of file app_meetme.c.

References CMP_MATCH, and CMP_STOP.

Referenced by sla_load_config().

{
   struct sla_station *station = obj, *station2 = arg;

   return !strcasecmp(station->name, station2->name) ? CMP_MATCH | CMP_STOP : 0;
}
static void sla_station_destructor ( void *  obj) [static]

Definition at line 7156 of file app_meetme.c.

References ast_context_remove_extension(), ast_debug, AST_LIST_TRAVERSE, AST_MAX_APP, AST_MAX_EXTENSION, ast_string_field_free_memory, ast_strlen_zero(), exten, sla_trunk::name, PRIORITY_HINT, sla_registrar, sla_station_release_refs(), sla_trunk_ref::trunk, and sla_station::trunks.

Referenced by sla_build_station().

{
   struct sla_station *station = obj;

   ast_debug(1, "sla_station destructor for '%s'\n", station->name);

   if (!ast_strlen_zero(station->autocontext)) {
      struct sla_trunk_ref *trunk_ref;

      AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
         char exten[AST_MAX_EXTENSION];
         char hint[AST_MAX_APP];
         snprintf(exten, sizeof(exten), "%s_%s", station->name, trunk_ref->trunk->name);
         snprintf(hint, sizeof(hint), "SLA:%s", exten);
         ast_context_remove_extension(station->autocontext, exten, 
            1, sla_registrar);
         ast_context_remove_extension(station->autocontext, hint, 
            PRIORITY_HINT, sla_registrar);
      }
   }

   sla_station_release_refs(station, NULL, 0);

   ast_string_field_free_memory(station);
}
static int sla_station_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 6782 of file app_meetme.c.

References sla_trunk::active_stations, admin_exec(), ALL_TRUNK_REFS, answer_trunk_chan(), ao2_cleanup, ao2_lock, ao2_ref, ao2_unlock, ast_answer(), ast_atomic_dec_and_test(), ast_atomic_fetchadd_int(), ast_autoservice_start(), ast_autoservice_stop(), ast_cond_destroy, ast_cond_init, ast_cond_wait, AST_CONTROL_UNHOLD, ast_debug, AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), ast_indicate(), AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log(), ast_mutex_destroy, ast_mutex_init, ast_mutex_lock, ast_mutex_unlock, ast_pthread_create_detached_background, ast_set_flag64, ast_strlen_zero(), build_conf(), sla_trunk::chan, sla_trunk_ref::chan, cond, dial_trunk_args::cond_lock, conf_run(), CONFFLAG_MARKEDEXIT, CONFFLAG_PASS_DTMF, CONFFLAG_QUIET, CONFFLAG_SLA_STATION, dial_trunk(), dispose_conf(), sla_trunk::hold_stations, LOG_NOTICE, LOG_WARNING, MAX_CONFNUM, sla_trunk::name, sla_trunk::on_hold, pbx_builtin_setvar_helper(), sla, sla_change_trunk_state(), sla_choose_idle_trunk(), SLA_EVENT_DIAL_STATE, SLA_EVENT_RINGING_TRUNK, sla_find_station(), sla_find_trunk_ref_byname(), sla_queue_event(), sla_ringing_trunk_destroy(), SLA_TRUNK_STATE_IDLE, SLA_TRUNK_STATE_ONHOLD_BYME, SLA_TRUNK_STATE_RINGING, SLA_TRUNK_STATE_UP, sla_trunk_ref::state, dial_trunk_args::station, sla_trunk_ref::trunk, sla_ringing_trunk::trunk, and dial_trunk_args::trunk_ref.

Referenced by load_module().

{
   char *station_name, *trunk_name;
   RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
   RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
   char conf_name[MAX_CONFNUM];
   struct ast_flags64 conf_flags = { 0 };
   struct ast_conference *conf;

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
      pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
      return 0;
   }

   trunk_name = ast_strdupa(data);
   station_name = strsep(&trunk_name, "_");

   if (ast_strlen_zero(station_name)) {
      ast_log(LOG_WARNING, "Invalid Arguments to SLAStation!\n");
      pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
      return 0;
   }

   station = sla_find_station(station_name);

   if (!station) {
      ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
      pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
      return 0;
   }

   ao2_lock(station);
   if (!ast_strlen_zero(trunk_name)) {
      trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
   } else {
      trunk_ref = sla_choose_idle_trunk(station);
   }
   ao2_unlock(station);

   if (!trunk_ref) {
      if (ast_strlen_zero(trunk_name))
         ast_log(LOG_NOTICE, "No trunks available for call.\n");
      else {
         ast_log(LOG_NOTICE, "Can't join existing call on trunk "
            "'%s' due to access controls.\n", trunk_name);
      }
      pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
      return 0;
   }

   if (trunk_ref->state == SLA_TRUNK_STATE_ONHOLD_BYME) {
      if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->hold_stations) == 1)
         sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
      else {
         trunk_ref->state = SLA_TRUNK_STATE_UP;
         ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE,
                    "SLA:%s_%s", station->name, trunk_ref->trunk->name);
      }
   } else if (trunk_ref->state == SLA_TRUNK_STATE_RINGING) {
      struct sla_ringing_trunk *ringing_trunk;

      ast_mutex_lock(&sla.lock);
      AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
         if (ringing_trunk->trunk == trunk_ref->trunk) {
            AST_LIST_REMOVE_CURRENT(entry);
            break;
         }
      }
      AST_LIST_TRAVERSE_SAFE_END
      ast_mutex_unlock(&sla.lock);

      if (ringing_trunk) {
         answer_trunk_chan(ringing_trunk->trunk->chan);
         sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);

         sla_ringing_trunk_destroy(ringing_trunk);

         /* Queue up reprocessing ringing trunks, and then ringing stations again */
         sla_queue_event(SLA_EVENT_RINGING_TRUNK);
         sla_queue_event(SLA_EVENT_DIAL_STATE);
      }
   }

   trunk_ref->chan = chan;

   if (!trunk_ref->trunk->chan) {
      ast_mutex_t cond_lock;
      ast_cond_t cond;
      pthread_t dont_care;
      struct dial_trunk_args args = {
         .trunk_ref = trunk_ref,
         .station = station,
         .cond_lock = &cond_lock,
         .cond = &cond,
      };
      ao2_ref(trunk_ref, 1);
      ao2_ref(station, 1);
      sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
      /* Create a thread to dial the trunk and dump it into the conference.
       * However, we want to wait until the trunk has been dialed and the
       * conference is created before continuing on here. */
      ast_autoservice_start(chan);
      ast_mutex_init(&cond_lock);
      ast_cond_init(&cond, NULL);
      ast_mutex_lock(&cond_lock);
      ast_pthread_create_detached_background(&dont_care, NULL, dial_trunk, &args);
      ast_cond_wait(&cond, &cond_lock);
      ast_mutex_unlock(&cond_lock);
      ast_mutex_destroy(&cond_lock);
      ast_cond_destroy(&cond);
      ast_autoservice_stop(chan);
      if (!trunk_ref->trunk->chan) {
         ast_debug(1, "Trunk didn't get created. chan: %lx\n", (long) trunk_ref->trunk->chan);
         pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
         sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
         trunk_ref->chan = NULL;
         return 0;
      }
   }

   if (ast_atomic_fetchadd_int((int *) &trunk_ref->trunk->active_stations, 1) == 0 &&
      trunk_ref->trunk->on_hold) {
      trunk_ref->trunk->on_hold = 0;
      ast_indicate(trunk_ref->trunk->chan, AST_CONTROL_UNHOLD);
      sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
   }

   snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
   ast_set_flag64(&conf_flags,
      CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_PASS_DTMF | CONFFLAG_SLA_STATION);
   ast_answer(chan);
   conf = build_conf(conf_name, "", "", 0, 0, 1, chan, NULL);
   if (conf) {
      conf_run(chan, conf, &conf_flags, NULL);
      dispose_conf(conf);
      conf = NULL;
   }
   trunk_ref->chan = NULL;
   if (ast_atomic_dec_and_test((int *) &trunk_ref->trunk->active_stations) &&
      trunk_ref->state != SLA_TRUNK_STATE_ONHOLD_BYME) {
      strncat(conf_name, ",K", sizeof(conf_name) - strlen(conf_name) - 1);
      admin_exec(NULL, conf_name);
      trunk_ref->trunk->hold_stations = 0;
      sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
   }
   
   pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");

   return 0;
}
static int sla_station_hash ( const void *  obj,
const int  flags 
) [static]

Definition at line 7196 of file app_meetme.c.

References ast_str_case_hash().

Referenced by sla_load_config().

{
   const struct sla_station *station = obj;

   return ast_str_case_hash(station->name);
}
static int sla_station_is_marked ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7614 of file app_meetme.c.

References ao2_lock, ao2_ref, ao2_unlock, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, CMP_MATCH, sla_station::mark, sla_trunk_ref::mark, sla_station_release_refs(), and sla_station::trunks.

Referenced by sla_load_config().

{
   struct sla_station *station = obj;

   ao2_lock(station);

   if (station->mark) {
      /* Only remove all of the trunk references if the station itself is going away */
      sla_station_release_refs(station, NULL, 0);
   } else {
      struct sla_trunk_ref *trunk_ref;

      /* Otherwise only remove references to trunks no longer in the config */
      AST_LIST_TRAVERSE_SAFE_BEGIN(&station->trunks, trunk_ref, entry) {
         if (!trunk_ref->mark) {
            continue;
         }
         AST_LIST_REMOVE_CURRENT(entry);
         ao2_ref(trunk_ref, -1);
      }
      AST_LIST_TRAVERSE_SAFE_END
   }

   ao2_unlock(station);

   return station->mark ? CMP_MATCH : 0;
}
static int sla_station_mark ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7568 of file app_meetme.c.

References ao2_lock, ao2_unlock, AST_LIST_TRAVERSE, sla_station::mark, sla_trunk_ref::mark, and sla_station::trunks.

Referenced by sla_load_config().

{
   struct sla_station *station = obj;
   struct sla_trunk_ref *trunk_ref;

   ao2_lock(station);

   station->mark = 1;

   AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
      trunk_ref->mark = 1;
   }

   ao2_unlock(station);

   return 0;
}
static void sla_station_ref_destructor ( void *  obj) [static]

Definition at line 5715 of file app_meetme.c.

References ao2_ref, and sla_station_ref::station.

Referenced by sla_create_station_ref().

{
   struct sla_station_ref *station_ref = obj;

   if (station_ref->station) {
      ao2_ref(station_ref->station, -1);
      station_ref->station = NULL;
   }
}
static int sla_station_release_refs ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7144 of file app_meetme.c.

References ao2_ref, AST_LIST_REMOVE_HEAD, and sla_station::trunks.

Referenced by sla_destroy(), sla_station_destructor(), and sla_station_is_marked().

{
   struct sla_station *station = obj;
   struct sla_trunk_ref *trunk_ref;

   while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry))) {
      ao2_ref(trunk_ref, -1);
   }

   return 0;
}
static void sla_stop_ringing_station ( struct sla_ringing_station ringing_station,
enum sla_station_hangup  hangup 
) [static]

Definition at line 5908 of file app_meetme.c.

References ast_dial_destroy(), ast_dial_join(), AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, sla_station::dial, sla, sla_create_station_ref(), sla_ringing_station_destroy(), SLA_STATION_HANGUP_NORMAL, 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_calc_station_timeouts(), and sla_handle_dial_state_event().

{
   struct sla_ringing_trunk *ringing_trunk;
   struct sla_trunk_ref *trunk_ref;
   struct sla_station_ref *station_ref;

   ast_dial_join(ringing_station->station->dial);
   ast_dial_destroy(ringing_station->station->dial);
   ringing_station->station->dial = NULL;

   if (hangup == SLA_STATION_HANGUP_NORMAL)
      goto done;

   /* If the station is being hung up because of a timeout, then add it to the
    * list of timed out stations on each of the ringing trunks.  This is so
    * that when doing further processing to figure out which stations should be
    * ringing, which trunk to answer, determining timeouts, etc., we know which
    * ringing trunks we should ignore. */
   AST_LIST_TRAVERSE(&sla.ringing_trunks, ringing_trunk, entry) {
      AST_LIST_TRAVERSE(&ringing_station->station->trunks, trunk_ref, entry) {
         if (ringing_trunk->trunk == trunk_ref->trunk)
            break;
      }
      if (!trunk_ref)
         continue;
      if (!(station_ref = sla_create_station_ref(ringing_station->station)))
         continue;
      AST_LIST_INSERT_TAIL(&ringing_trunk->timed_out_stations, station_ref, entry);
   }

done:
   sla_ringing_station_destroy(ringing_station);
}
static void sla_stop_ringing_trunk ( struct sla_ringing_trunk ringing_trunk) [static]

Definition at line 5892 of file app_meetme.c.

References admin_exec(), ALL_TRUNK_REFS, ao2_ref, AST_LIST_REMOVE_HEAD, sla_trunk::name, sla_change_trunk_state(), sla_ringing_trunk_destroy(), SLA_TRUNK_STATE_IDLE, sla_ringing_trunk::timed_out_stations, and sla_ringing_trunk::trunk.

Referenced by sla_calc_trunk_timeouts().

{
   char buf[80];
   struct sla_station_ref *station_ref;

   snprintf(buf, sizeof(buf), "SLA_%s,K", ringing_trunk->trunk->name);
   admin_exec(NULL, buf);
   sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);

   while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
      ao2_ref(station_ref, -1);
   }

   sla_ringing_trunk_destroy(ringing_trunk);
}
static void* sla_thread ( void *  data) [static]

Definition at line 6566 of file app_meetme.c.

References ast_cond_timedwait, ast_cond_wait, AST_LIST_EMPTY, AST_LIST_REMOVE_HEAD, ast_mutex_lock, ast_mutex_unlock, sla, sla_event_destroy(), SLA_EVENT_DIAL_STATE, SLA_EVENT_HOLD, SLA_EVENT_RINGING_TRUNK, sla_failed_station_destroy(), sla_handle_dial_state_event(), sla_handle_hold_event(), sla_handle_ringing_trunk_event(), sla_process_timers(), sla_ringing_station_destroy(), and sla_event::type.

Referenced by sla_load_config().

{
   struct sla_failed_station *failed_station;
   struct sla_ringing_station *ringing_station;

   ast_mutex_lock(&sla.lock);

   while (!sla.stop) {
      struct sla_event *event;
      struct timespec ts = { 0, };
      unsigned int have_timeout = 0;

      if (AST_LIST_EMPTY(&sla.event_q)) {
         if ((have_timeout = sla_process_timers(&ts)))
            ast_cond_timedwait(&sla.cond, &sla.lock, &ts);
         else
            ast_cond_wait(&sla.cond, &sla.lock);
         if (sla.stop)
            break;
      }

      if (have_timeout)
         sla_process_timers(NULL);

      while ((event = AST_LIST_REMOVE_HEAD(&sla.event_q, entry))) {
         ast_mutex_unlock(&sla.lock);
         switch (event->type) {
         case SLA_EVENT_HOLD:
            sla_handle_hold_event(event);
            break;
         case SLA_EVENT_DIAL_STATE:
            sla_handle_dial_state_event();
            break;
         case SLA_EVENT_RINGING_TRUNK:
            sla_handle_ringing_trunk_event();
            break;
         }
         sla_event_destroy(event);
         ast_mutex_lock(&sla.lock);
      }
   }

   ast_mutex_unlock(&sla.lock);

   while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry))) {
      sla_ringing_station_destroy(ringing_station);
   }

   while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) {
      sla_failed_station_destroy(failed_station);
   }

   return NULL;
}
static int sla_trunk_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7189 of file app_meetme.c.

References CMP_MATCH, CMP_STOP, and sla_trunk::name.

Referenced by sla_load_config().

{
   struct sla_trunk *trunk = obj, *trunk2 = arg;

   return !strcasecmp(trunk->name, trunk2->name) ? CMP_MATCH | CMP_STOP : 0;
}
static void sla_trunk_destructor ( void *  obj) [static]
static int sla_trunk_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 7004 of file app_meetme.c.

References ALL_TRUNK_REFS, ao2_cleanup, args, AST_APP_ARG, ast_app_parse_options(), AST_CONTROL_RINGING, AST_DECLARE_APP_ARGS, ast_indicate(), AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_set_flag64, AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_test_flag, build_conf(), sla_trunk::chan, conf_run(), CONFFLAG_MARKEDEXIT, CONFFLAG_MARKEDUSER, CONFFLAG_MOH, CONFFLAG_NO_AUDIO_UNTIL_UP, CONFFLAG_PASS_DTMF, CONFFLAG_QUIET, dispose_conf(), LOG_ERROR, MAX_CONFNUM, sla_trunk::on_hold, parse(), pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), queue_ringing_trunk(), sla, sla_change_trunk_state(), SLA_EVENT_RINGING_TRUNK, sla_find_trunk(), sla_queue_event(), sla_ringing_trunk_destroy(), SLA_TRUNK_OPT_ARG_ARRAY_SIZE, SLA_TRUNK_OPT_MOH, sla_trunk_opts, SLA_TRUNK_STATE_IDLE, and sla_ringing_trunk::trunk.

Referenced by load_module().

{
   char conf_name[MAX_CONFNUM];
   struct ast_conference *conf;
   struct ast_flags64 conf_flags = { 0 };
   RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
   struct sla_ringing_trunk *ringing_trunk;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(trunk_name);
      AST_APP_ARG(options);
   );
   char *opts[SLA_TRUNK_OPT_ARG_ARRAY_SIZE] = { NULL, };
   struct ast_flags opt_flags = { 0 };
   char *parse;

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "The SLATrunk application requires an argument, the trunk name\n");
      return -1;
   }

   parse = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, parse);
   if (args.argc == 2) {
      if (ast_app_parse_options(sla_trunk_opts, &opt_flags, opts, args.options)) {
         ast_log(LOG_ERROR, "Error parsing options for SLATrunk\n");
         return -1;
      }
   }

   trunk = sla_find_trunk(args.trunk_name);

   if (!trunk) {
      ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
      pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
      return 0;
   }

   if (trunk->chan) {
      ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
         args.trunk_name);
      pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
      return 0;
   }

   trunk->chan = chan;

   if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
      pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
      return 0;
   }

   snprintf(conf_name, sizeof(conf_name), "SLA_%s", args.trunk_name);
   conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
   if (!conf) {
      pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
      return 0;
   }
   ast_set_flag64(&conf_flags, 
      CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP);

   if (ast_test_flag(&opt_flags, SLA_TRUNK_OPT_MOH)) {
      ast_indicate(chan, -1);
      ast_set_flag64(&conf_flags, CONFFLAG_MOH);
   } else
      ast_indicate(chan, AST_CONTROL_RINGING);

   conf_run(chan, conf, &conf_flags, opts);
   dispose_conf(conf);
   conf = NULL;
   trunk->chan = NULL;
   trunk->on_hold = 0;

   sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);

   if (!pbx_builtin_getvar_helper(chan, "SLATRUNK_STATUS"))
      pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "SUCCESS");

   /* Remove the entry from the list of ringing trunks if it is still there. */
   ast_mutex_lock(&sla.lock);
   AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_trunks, ringing_trunk, entry) {
      if (ringing_trunk->trunk == trunk) {
         AST_LIST_REMOVE_CURRENT(entry);
         break;
      }
   }
   AST_LIST_TRAVERSE_SAFE_END;
   ast_mutex_unlock(&sla.lock);
   if (ringing_trunk) {
      sla_ringing_trunk_destroy(ringing_trunk);
      pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
      /* Queue reprocessing of ringing trunks to make stations stop ringing
       * that shouldn't be ringing after this trunk stopped. */
      sla_queue_event(SLA_EVENT_RINGING_TRUNK);
   }

   return 0;
}
static int sla_trunk_hash ( const void *  obj,
const int  flags 
) [static]

Definition at line 7182 of file app_meetme.c.

References ast_str_case_hash(), and sla_trunk::name.

Referenced by sla_load_config().

{
   const struct sla_trunk *trunk = obj;

   return ast_str_case_hash(trunk->name);
}
static int sla_trunk_is_marked ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7586 of file app_meetme.c.

References ao2_lock, ao2_ref, ao2_unlock, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, CMP_MATCH, sla_station_ref::mark, sla_trunk::mark, sla_trunk_release_refs(), and sla_trunk::stations.

Referenced by sla_load_config().

{
   struct sla_trunk *trunk = obj;

   ao2_lock(trunk);

   if (trunk->mark) {
      /* Only remove all of the station references if the trunk itself is going away */
      sla_trunk_release_refs(trunk, NULL, 0);
   } else {
      struct sla_station_ref *station_ref;

      /* Otherwise only remove references to stations no longer in the config */
      AST_LIST_TRAVERSE_SAFE_BEGIN(&trunk->stations, station_ref, entry) {
         if (!station_ref->mark) {
            continue;
         }
         AST_LIST_REMOVE_CURRENT(entry);
         ao2_ref(station_ref, -1);
      }
      AST_LIST_TRAVERSE_SAFE_END
   }

   ao2_unlock(trunk);

   return trunk->mark ? CMP_MATCH : 0;
}
static int sla_trunk_mark ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7550 of file app_meetme.c.

References ao2_lock, ao2_unlock, AST_LIST_TRAVERSE, sla_station_ref::mark, sla_trunk::mark, and sla_trunk::stations.

Referenced by sla_load_config().

{
   struct sla_trunk *trunk = obj;
   struct sla_station_ref *station_ref;

   ao2_lock(trunk);

   trunk->mark = 1;

   AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
      station_ref->mark = 1;
   }

   ao2_unlock(trunk);

   return 0;
}
static void sla_trunk_ref_destructor ( void *  obj) [static]

Definition at line 6934 of file app_meetme.c.

References ao2_ref, and sla_trunk_ref::trunk.

Referenced by create_trunk_ref().

{
   struct sla_trunk_ref *trunk_ref = obj;

   if (trunk_ref->trunk) {
      ao2_ref(trunk_ref->trunk, -1);
      trunk_ref->trunk = NULL;
   }
}
static int sla_trunk_release_refs ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7132 of file app_meetme.c.

References ao2_ref, AST_LIST_REMOVE_HEAD, and sla_trunk::stations.

Referenced by sla_destroy(), sla_trunk_destructor(), and sla_trunk_is_marked().

{
   struct sla_trunk *trunk = obj;
   struct sla_station_ref *station_ref;

   while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry))) {
      ao2_ref(station_ref, -1);
   }

   return 0;
}
static const char* trunkstate2str ( enum sla_trunk_state  state) [static]

Definition at line 1829 of file app_meetme.c.

References S, SLA_TRUNK_STATE_IDLE, SLA_TRUNK_STATE_ONHOLD, SLA_TRUNK_STATE_ONHOLD_BYME, SLA_TRUNK_STATE_RINGING, and SLA_TRUNK_STATE_UP.

Referenced by sla_show_stations().

{
#define S(e) case e: return # e;
   switch (state) {
   S(SLA_TRUNK_STATE_IDLE)
   S(SLA_TRUNK_STATE_RINGING)
   S(SLA_TRUNK_STATE_UP)
   S(SLA_TRUNK_STATE_ONHOLD)
   S(SLA_TRUNK_STATE_ONHOLD_BYME)
   }
   return "Uknown State";
#undef S
}
static void tweak_listen_volume ( struct ast_conf_user user,
enum volume_action  action 
) [static]

Definition at line 1148 of file app_meetme.c.

References volume::actual, volume::desired, ast_conf_user::listen, set_listen_volume(), and tweak_volume().

Referenced by admin_exec(), meetme_menu_admin(), meetme_menu_normal(), user_listen_voldown_cb(), and user_listen_volup_cb().

{
   tweak_volume(&user->listen, action);
   /* attempt to make the adjustment in the channel driver;
      if successful, don't adjust in the frame reading routine
   */
   if (!set_listen_volume(user, user->listen.desired))
      user->listen.actual = 0;
   else
      user->listen.actual = user->listen.desired;
}
static void tweak_talk_volume ( struct ast_conf_user user,
enum volume_action  action 
) [static]

Definition at line 1136 of file app_meetme.c.

References volume::actual, volume::desired, set_talk_volume(), ast_conf_user::talk, and tweak_volume().

Referenced by admin_exec(), meetme_menu_admin(), meetme_menu_normal(), user_talk_voldown_cb(), and user_talk_volup_cb().

{
   tweak_volume(&user->talk, action);
   /* attempt to make the adjustment in the channel driver;
      if successful, don't adjust in the frame reading routine
   */
   if (!set_talk_volume(user, user->talk.desired))
      user->talk.actual = 0;
   else
      user->talk.actual = user->talk.desired;
}
static void tweak_volume ( struct volume vol,
enum volume_action  action 
) [static]

Definition at line 1101 of file app_meetme.c.

References volume::desired, VOL_DOWN, and VOL_UP.

Referenced by tweak_listen_volume(), and tweak_talk_volume().

{
   switch (action) {
   case VOL_UP:
      switch (vol->desired) { 
      case 5:
         break;
      case 0:
         vol->desired = 2;
         break;
      case -2:
         vol->desired = 0;
         break;
      default:
         vol->desired++;
         break;
      }
      break;
   case VOL_DOWN:
      switch (vol->desired) {
      case -5:
         break;
      case 2:
         vol->desired = 0;
         break;
      case 0:
         vol->desired = -2;
         break;
      default:
         vol->desired--;
         break;
      }
   }
}
static int user_add_provider_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7823 of file app_meetme.c.

References volume::actual, ast_channel_data_add_structure(), ast_data_add_int(), ast_data_add_node(), ast_data_add_structure, ast_conf_user::chan, volume::desired, ast_conf_user::listen, and ast_conf_user::talk.

Referenced by meetme_data_provider_get().

{
   struct ast_data *data_meetme_user;
   struct ast_data *data_meetme_user_channel;
   struct ast_data *data_meetme_user_volume;

   struct ast_conf_user *user = obj;
   struct ast_data *data_meetme_users = arg;

   data_meetme_user = ast_data_add_node(data_meetme_users, "user");
   if (!data_meetme_user) {
      return 0;
   }
   /* user structure */
   ast_data_add_structure(ast_conf_user, data_meetme_user, user);

   /* user's channel */
   data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel");
   if (!data_meetme_user_channel) {
      return 0;
   }

   ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1);

   /* volume structure */
   data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume");
   if (!data_meetme_user_volume) {
      return 0;
   }
   ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired);
   ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual);

   data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume");
   if (!data_meetme_user_volume) {
      return 0;
   }
   ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired);
   ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual);

   return 0;
}
static int user_chan_cb ( void *  obj,
void *  args,
int  flags 
) [static]

Definition at line 4991 of file app_meetme.c.

References args, ast_channel_name(), ast_conf_user::chan, CMP_MATCH, and CMP_STOP.

Referenced by channel_admin_exec().

{
   struct ast_conf_user *user = obj;
   const char *channel = args;

   if (!strcmp(ast_channel_name(user->chan), channel)) {
      return (CMP_MATCH | CMP_STOP);
   }

   return 0;
}
static int user_listen_voldown_cb ( void *  obj,
void *  unused,
int  flags 
) [static]

Definition at line 4963 of file app_meetme.c.

References tweak_listen_volume(), and VOL_DOWN.

Referenced by admin_exec().

{
   struct ast_conf_user *user = obj;
   tweak_listen_volume(user, VOL_DOWN);
   return 0;
}
static int user_listen_volup_cb ( void *  obj,
void *  unused,
int  flags 
) [static]

Definition at line 4956 of file app_meetme.c.

References tweak_listen_volume(), and VOL_UP.

Referenced by admin_exec().

{
   struct ast_conf_user *user = obj;
   tweak_listen_volume(user, VOL_UP);
   return 0;
}
static int user_max_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1221 of file app_meetme.c.

References ast_conf_user::user_no.

Referenced by admin_exec(), conf_run(), and meetme_menu_admin().

{
   struct ast_conf_user *user = obj;
   int *max_no = arg;

   if (user->user_no > *max_no) {
      *max_no = user->user_no;
   }

   return 0;
}
static int user_no_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1209 of file app_meetme.c.

References CMP_MATCH, CMP_STOP, and ast_conf_user::user_no.

Referenced by build_conf().

{
   struct ast_conf_user *user = obj;
   int *user_no = arg;

   if (user->user_no == *user_no) {
      return (CMP_MATCH | CMP_STOP);
   }

   return 0;
}
static int user_reset_vol_cb ( void *  obj,
void *  unused,
int  flags 
) [static]

Definition at line 4984 of file app_meetme.c.

References reset_volumes().

Referenced by admin_exec().

{
   struct ast_conf_user *user = obj;
   reset_volumes(user);
   return 0;
}
static int user_set_hangup_cb ( void *  obj,
void *  check_admin_arg,
int  flags 
) [static]

Definition at line 2372 of file app_meetme.c.

References ADMINFLAG_HANGUP, ast_conf_user::adminflags, ast_test_flag64, CONFFLAG_ADMIN, and ast_conf_user::userflags.

Referenced by conf_run().

{
   struct ast_conf_user *user = obj;
   /* actual pointer contents of check_admin_arg is irrelevant */

   if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
      user->adminflags |= ADMINFLAG_HANGUP;
   }
   return 0;
}
static int user_set_kickme_cb ( void *  obj,
void *  check_admin_arg,
int  flags 
) [static]

Definition at line 2383 of file app_meetme.c.

References ADMINFLAG_KICKME, ast_conf_user::adminflags, ast_test_flag64, CONFFLAG_ADMIN, and ast_conf_user::userflags.

Referenced by admin_exec(), and meetme_menu_admin_extended().

{
   struct ast_conf_user *user = obj;
   /* actual pointer contents of check_admin_arg is irrelevant */

   if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
      user->adminflags |= ADMINFLAG_KICKME;
   }
   return 0;
}
static int user_set_muted_cb ( void *  obj,
void *  check_admin_arg,
int  flags 
) [static]

Definition at line 2405 of file app_meetme.c.

References ADMINFLAG_MUTED, ast_conf_user::adminflags, ast_test_flag64, CONFFLAG_ADMIN, and ast_conf_user::userflags.

Referenced by admin_exec(), and meetme_menu_admin_extended().

{
   struct ast_conf_user *user = obj;
   /* actual pointer contents of check_admin_arg is irrelevant */

   if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
      user->adminflags |= ADMINFLAG_MUTED;
   }
   return 0;
}
static int user_set_unmuted_cb ( void *  obj,
void *  check_admin_arg,
int  flags 
) [static]

Definition at line 2394 of file app_meetme.c.

References ADMINFLAG_MUTED, ADMINFLAG_SELFMUTED, ADMINFLAG_T_REQUEST, ast_conf_user::adminflags, ast_test_flag64, CONFFLAG_ADMIN, and ast_conf_user::userflags.

Referenced by admin_exec(), and meetme_menu_admin_extended().

{
   struct ast_conf_user *user = obj;
   /* actual pointer contents of check_admin_arg is irrelevant */

   if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
      user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
   }
   return 0;
}
static int user_talk_voldown_cb ( void *  obj,
void *  unused,
int  flags 
) [static]

Definition at line 4977 of file app_meetme.c.

References tweak_talk_volume(), and VOL_DOWN.

Referenced by admin_exec().

{
   struct ast_conf_user *user = obj;
   tweak_talk_volume(user, VOL_DOWN);
   return 0;
}
static int user_talk_volup_cb ( void *  obj,
void *  unused,
int  flags 
) [static]

Definition at line 4970 of file app_meetme.c.

References tweak_talk_volume(), and VOL_UP.

Referenced by admin_exec().

{
   struct ast_conf_user *user = obj;
   tweak_talk_volume(user, VOL_UP);
   return 0;
}

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .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, .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, } [static]

Definition at line 8047 of file app_meetme.c.

const char* const app = "MeetMe" [static]

Definition at line 709 of file app_meetme.c.

const char* const app2 = "MeetMeCount" [static]

Definition at line 710 of file app_meetme.c.

const char* const app3 = "MeetMeAdmin" [static]

Definition at line 711 of file app_meetme.c.

const char* const app4 = "MeetMeChannelAdmin" [static]

Definition at line 712 of file app_meetme.c.

Definition at line 8047 of file app_meetme.c.

unsigned int attempt_callerid

Attempt to handle CallerID, even though it is known not to work properly in some situations.

Definition at line 1006 of file app_meetme.c.

int audio_buffers [static]

The number of audio buffers to be allocated on pseudo channels when in a conference.

Definition at line 1013 of file app_meetme.c.

Referenced by conf_run().

struct ast_cli_entry cli_meetme[] [static]

Definition at line 1923 of file app_meetme.c.

unsigned int conf_map[1024] = {0, } [static]

Definition at line 795 of file app_meetme.c.

Referenced by build_conf(), conf_exec(), and dispose_conf().

struct confs confs [static]
int earlyalert [static]

Definition at line 719 of file app_meetme.c.

Referenced by find_conf_realtime().

int endalert [static]

Definition at line 720 of file app_meetme.c.

Referenced by find_conf_realtime().

struct { ... } event_q
int extendby [static]

Definition at line 721 of file app_meetme.c.

Referenced by rt_extend_conf().

struct { ... } failed_stations
int fuzzystart [static]

Definition at line 718 of file app_meetme.c.

Referenced by find_conf_realtime().

const char gain_map[] [static]

Map 'volume' levels from -5 through +5 into decibel (dB) settings for channel drivers.

Note:
these are not a straight linear-to-dB conversion... the numbers have been modified to give the user a better level of adjustability.

Definition at line 1022 of file app_meetme.c.

Initial value:

Definition at line 7903 of file app_meetme.c.

Initial value:
 {
   AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider),
}

Definition at line 7908 of file app_meetme.c.

Initial value:
 {
   .name = "MEETME_INFO",
   .read = acf_meetme_info,
}

Definition at line 7782 of file app_meetme.c.

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 }, [ 'G' ] = { .flag = (1ULL << 32) , .arg_index = OPT_ARG_INTROMSG + 1 }, [ 'v' ] = { .flag = (1ULL << 33) , .arg_index = OPT_ARG_INTROUSER_VMREC + 1 }, [ 'i' ] = { .flag = CONFFLAG_INTROUSER }, [ 'I' ] = { .flag = CONFFLAG_INTROUSERNOREVIEW }, [ 'k' ] = { .flag = (1ULL << 34) }, [ 'M' ] = { .flag = CONFFLAG_MOH , .arg_index = OPT_ARG_MOH_CLASS + 1 }, [ 'm' ] = { .flag = CONFFLAG_STARTMUTED }, [ 'n' ] = { .flag = (1ULL << 35) }, [ '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]

Definition at line 707 of file app_meetme.c.

Referenced by conf_exec(), and find_conf_realtime().

struct { ... } ringing_stations
struct { ... } ringing_trunks
int rt_log_members [static]

Log participant count to the RealTime backend

Definition at line 724 of file app_meetme.c.

int rt_schedule [static]

Definition at line 717 of file app_meetme.c.

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]

Definition at line 7002 of file app_meetme.c.

Referenced by sla_trunk_exec().

const char* const slastation_app = "SLAStation" [static]

Definition at line 713 of file app_meetme.c.

const char* const slatrunk_app = "SLATrunk" [static]

Definition at line 714 of file app_meetme.c.