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 "asterisk/stasis.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/stasis_message_router.h"
#include "asterisk/json.h"
#include "asterisk/format_compatibility.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_conf_user, MEETME_USER_DATA_EXPORT)
 AST_DATA_STRUCTURE (ast_conference, MEETME_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 (int reload)
static int load_module (void)
 Load the module.
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 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 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 void meetme_set_defaults (void)
static char * meetme_show_cmd (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void meetme_stasis_cb (void *data, struct stasis_subscription *sub, struct stasis_message *message)
static void meetme_stasis_cleanup (void)
static void meetme_stasis_generate_msg (struct ast_conference *meetme_conference, struct ast_channel *chan, struct ast_conf_user *user, struct stasis_message_type *message_type, struct ast_json *extras)
static int meetme_stasis_init (void)
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)
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (meetme_talk_request_type)
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (meetme_talking_type)
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (meetme_mute_type)
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (meetme_end_type)
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (meetme_leave_type)
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (meetme_join_type)
static struct ast_jsonstatus_to_json (int on)
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, .support_level = AST_MODULE_SUPPORT_EXTENDED, .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 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
stasis_message_router
meetme_event_message_router
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 667 of file app_meetme.c.

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

#define CONF_SIZE   320

Definition at line 686 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 756 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 751 of file app_meetme.c.

Referenced by conf_run().

#define CONFFLAG_INTROUSER_VMREC   (1ULL << 33)

Definition at line 752 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 754 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 750 of file app_meetme.c.

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

#define CONFIG_FILE_NAME   "meetme.conf"

Definition at line 645 of file app_meetme.c.

Referenced by _dsp_init(), 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 653 of file app_meetme.c.

#define DEFAULT_AUDIO_BUFFERS   32

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

Definition at line 650 of file app_meetme.c.

Referenced by load_config_meetme().

#define MAX_CONFNUM   80

#define MAX_PIN   80

Definition at line 823 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 827 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 8009 of file app_meetme.c.

#define MEETME_DELAYDETECTENDTALK   1000

Definition at line 665 of file app_meetme.c.

Referenced by conf_run().

#define MEETME_DELAYDETECTTALK   300

Definition at line 664 of file app_meetme.c.

Referenced by conf_run().

#define MEETME_USER_DATA_EXPORT ( MEMBER   ) 

Definition at line 8026 of file app_meetme.c.

#define OPTIONS_LEN   100

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

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

#define STR_CONCISE   "concise"

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

00655      {
00656    ADMINFLAG_MUTED =     (1 << 1), /*!< User is muted */
00657    ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
00658    ADMINFLAG_KICKME =    (1 << 3),  /*!< User has been kicked */
00659    /*! User has requested to speak */
00660    ADMINFLAG_T_REQUEST = (1 << 4),
00661    ADMINFLAG_HANGUP = (1 << 5),  /*!< User will be leaving the conference */
00662 };

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

00688      {
00689    /*! user has admin access on the conference */
00690    CONFFLAG_ADMIN = (1 << 0),
00691    /*! If set the user can only receive audio from the conference */
00692    CONFFLAG_MONITOR = (1 << 1),
00693    /*! If set asterisk will exit conference when key defined in p() option is pressed */
00694    CONFFLAG_KEYEXIT = (1 << 2),
00695    /*! If set asterisk will provide a menu to the user when '*' is pressed */
00696    CONFFLAG_STARMENU = (1 << 3),
00697    /*! If set the use can only send audio to the conference */
00698    CONFFLAG_TALKER = (1 << 4),
00699    /*! If set there will be no enter or leave sounds */
00700    CONFFLAG_QUIET = (1 << 5),
00701    /*! If set, when user joins the conference, they will be told the number 
00702     *  of users that are already in */
00703    CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
00704    /*! Set to run AGI Script in Background */
00705    CONFFLAG_AGI = (1 << 7),
00706    /*! Set to have music on hold when user is alone in conference */
00707    CONFFLAG_MOH = (1 << 8),
00708    /*! If set, the channel will leave the conference if all marked users leave */
00709    CONFFLAG_MARKEDEXIT = (1 << 9),
00710    /*! If set, the MeetMe will wait until a marked user enters */
00711    CONFFLAG_WAITMARKED = (1 << 10),
00712    /*! If set, the MeetMe will exit to the specified context */
00713    CONFFLAG_EXIT_CONTEXT = (1 << 11),
00714    /*! If set, the user will be marked */
00715    CONFFLAG_MARKEDUSER = (1 << 12),
00716    /*! If set, user will be ask record name on entry of conference */
00717    CONFFLAG_INTROUSER = (1 << 13),
00718    /*! If set, the MeetMe will be recorded */
00719    CONFFLAG_RECORDCONF = (1<< 14),
00720    /*! If set, the user will be monitored if the user is talking or not */
00721    CONFFLAG_MONITORTALKER = (1 << 15),
00722    CONFFLAG_DYNAMIC = (1 << 16),
00723    CONFFLAG_DYNAMICPIN = (1 << 17),
00724    CONFFLAG_EMPTY = (1 << 18),
00725    CONFFLAG_EMPTYNOPIN = (1 << 19),
00726    CONFFLAG_ALWAYSPROMPT = (1 << 20),
00727    /*! If set, treat talking users as muted users */
00728    CONFFLAG_OPTIMIZETALKER = (1 << 21),
00729    /*! If set, won't speak the extra prompt when the first person 
00730     *  enters the conference */
00731    CONFFLAG_NOONLYPERSON = (1 << 22),
00732    /*! If set, user will be asked to record name on entry of conference 
00733     *  without review */
00734    CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
00735    /*! If set, the user will be initially self-muted */
00736    CONFFLAG_STARTMUTED = (1 << 24),
00737    /*! Pass DTMF through the conference */
00738    CONFFLAG_PASS_DTMF = (1 << 25),
00739    CONFFLAG_SLA_STATION = (1 << 26),
00740    CONFFLAG_SLA_TRUNK = (1 << 27),
00741    /*! If set, the user should continue in the dialplan if kicked out */
00742    CONFFLAG_KICK_CONTINUE = (1 << 28),
00743    CONFFLAG_DURATION_STOP = (1 << 29),
00744    CONFFLAG_DURATION_LIMIT = (1 << 30),
00745 };

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

00758      {
00759    OPT_ARG_WAITMARKED = 0,
00760    OPT_ARG_EXITKEYS   = 1,
00761    OPT_ARG_DURATION_STOP = 2,
00762    OPT_ARG_DURATION_LIMIT = 3,
00763    OPT_ARG_MOH_CLASS = 4,
00764    OPT_ARG_INTROMSG = 5,
00765    OPT_ARG_INTROUSER_VMREC = 6,
00766    OPT_ARG_ARRAY_SIZE = 7,
00767 };

anonymous enum

Enumerator:
SLA_TRUNK_OPT_MOH 

Definition at line 7206 of file app_meetme.c.

07206      {
07207    SLA_TRUNK_OPT_MOH = (1 << 0),
07208 };

anonymous enum

Enumerator:
SLA_TRUNK_OPT_ARG_MOH_CLASS 
SLA_TRUNK_OPT_ARG_ARRAY_SIZE 

Definition at line 7210 of file app_meetme.c.

07210      {
07211    SLA_TRUNK_OPT_ARG_MOH_CLASS = 0,
07212    SLA_TRUNK_OPT_ARG_ARRAY_SIZE = 1,
07213 };

Enumerator:
CONF_HASJOIN 
CONF_HASLEFT 

Definition at line 829 of file app_meetme.c.

00829                    {
00830    CONF_HASJOIN,
00831    CONF_HASLEFT
00832 };

Enumerator:
ENTER 
LEAVE 

Definition at line 674 of file app_meetme.c.

00674                     {
00675    ENTER,
00676    LEAVE
00677 };

enum menu_modes

Enumerator:
MENU_DISABLED 
MENU_NORMAL 
MENU_ADMIN 
MENU_ADMIN_EXTENDED 

Definition at line 2743 of file app_meetme.c.

02743                 {
02744    MENU_DISABLED = 0,
02745    MENU_NORMAL,
02746    MENU_ADMIN,
02747    MENU_ADMIN_EXTENDED,
02748 };

Enumerator:
MEETME_RECORD_OFF 
MEETME_RECORD_STARTED 
MEETME_RECORD_ACTIVE 
MEETME_RECORD_TERMINATE 

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

01041                     {
01042    /*! A station has put the call on hold */
01043    SLA_EVENT_HOLD,
01044    /*! The state of a dial has changed */
01045    SLA_EVENT_DIAL_STATE,
01046    /*! The state of a ringing trunk has changed */
01047    SLA_EVENT_RINGING_TRUNK,
01048 };

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

00934                      {
00935    /*! This means that any station can put it on hold, and any station
00936     * can retrieve the call from hold. */
00937    SLA_HOLD_OPEN,
00938    /*! This means that only the station that put the call on hold may
00939     * retrieve it from hold. */
00940    SLA_HOLD_PRIVATE,
00941 };

Enumerator:
SLA_STATION_HANGUP_NORMAL 
SLA_STATION_HANGUP_TIMEOUT 

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

Enumerator:
ALL_TRUNK_REFS 
INACTIVE_TRUNK_REFS 

Definition at line 921 of file app_meetme.c.

00921                           {
00922    ALL_TRUNK_REFS,
00923    INACTIVE_TRUNK_REFS,
00924 };

Enumerator:
VOL_UP 
VOL_DOWN 

Definition at line 669 of file app_meetme.c.

00669                    {
00670    VOL_UP,
00671    VOL_DOWN
00672 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 8279 of file app_meetme.c.

static void __unreg_module ( void   )  [static]

Definition at line 8279 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 7946 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_strdupa, ast_strlen_zero, ast_conference::confno, ast_conference::list, LOG_ERROR, LOG_NOTICE, parse(), and result.

07947 {
07948    struct ast_conference *conf;
07949    char *parse;
07950    int result = -2; /* only non-negative numbers valid, -1 is used elsewhere */
07951    AST_DECLARE_APP_ARGS(args,
07952       AST_APP_ARG(keyword);
07953       AST_APP_ARG(confno);
07954    );
07955 
07956    if (ast_strlen_zero(data)) {
07957       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires two arguments\n");
07958       return -1;
07959    }
07960 
07961    parse = ast_strdupa(data);
07962    AST_STANDARD_APP_ARGS(args, parse);
07963 
07964    if (ast_strlen_zero(args.keyword)) {
07965       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a keyword\n");
07966       return -1;
07967    }
07968 
07969    if (ast_strlen_zero(args.confno)) {
07970       ast_log(LOG_ERROR, "Syntax: MEETME_INFO() requires a conference number\n");
07971       return -1;
07972    }
07973 
07974    AST_LIST_LOCK(&confs);
07975    AST_LIST_TRAVERSE(&confs, conf, list) {
07976       if (!strcmp(args.confno, conf->confno)) {
07977          result = acf_meetme_info_eval(args.keyword, conf);
07978          break;
07979       }
07980    }
07981    AST_LIST_UNLOCK(&confs);
07982 
07983    if (result > -1) {
07984       snprintf(buf, len, "%d", result);
07985    } else if (result == -1) {
07986       ast_log(LOG_NOTICE, "Error: invalid keyword: '%s'\n", args.keyword);
07987       snprintf(buf, len, "0");
07988    } else if (result == -2) {
07989       ast_log(LOG_NOTICE, "Error: conference (%s) not found\n", args.confno); 
07990       snprintf(buf, len, "0");
07991    }
07992 
07993    return 0;
07994 }

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

Definition at line 7928 of file app_meetme.c.

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

Referenced by acf_meetme_info().

07929 {
07930    if (!strcasecmp("lock", keyword)) {
07931       return conf->locked;
07932    } else if (!strcasecmp("parties", keyword)) {
07933       return conf->users;
07934    } else if (!strcasecmp("activity", keyword)) {
07935       time_t now;
07936       now = time(NULL);
07937       return (now - conf->start);
07938    } else if (!strcasecmp("dynamic", keyword)) {
07939       return conf->isdynamic;
07940    } else {
07941       return -1;
07942    }
07943 
07944 }

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

Definition at line 5515 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_list_complete_end(), astman_send_list_complete_start(), astman_send_listack(), ast_conf_user::chan, CONFFLAG_ADMIN, CONFFLAG_MARKEDUSER, CONFFLAG_MONITOR, CONFFLAG_TALKER, ast_conference::confno, ast_party_connected_line::id, ast_party_caller::id, ast_party_id::name, ast_party_id::number, S_COR, ast_party_name::str, ast_party_number::str, ast_conf_user::talking, total, 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().

05516 {
05517    const char *actionid = astman_get_header(m, "ActionID");
05518    const char *conference = astman_get_header(m, "Conference");
05519    char idText[80] = "";
05520    struct ast_conference *cnf;
05521    struct ast_conf_user *user;
05522    struct ao2_iterator user_iter;
05523    int total = 0;
05524 
05525    if (!ast_strlen_zero(actionid))
05526       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05527 
05528    if (AST_LIST_EMPTY(&confs)) {
05529       astman_send_error(s, m, "No active conferences.");
05530       return 0;
05531    }
05532 
05533    astman_send_listack(s, m, "Meetme user list will follow", "start");
05534 
05535    /* Find the right conference */
05536    AST_LIST_LOCK(&confs);
05537    AST_LIST_TRAVERSE(&confs, cnf, list) {
05538       /* If we ask for one particular, and this isn't it, skip it */
05539       if (!ast_strlen_zero(conference) && strcmp(cnf->confno, conference))
05540          continue;
05541 
05542       /* Show all the users */
05543       user_iter = ao2_iterator_init(cnf->usercontainer, 0);
05544       while ((user = ao2_iterator_next(&user_iter))) {
05545          total++;
05546          astman_append(s,
05547             "Event: MeetmeList\r\n"
05548             "%s"
05549             "Conference: %s\r\n"
05550             "UserNumber: %d\r\n"
05551             "CallerIDNum: %s\r\n"
05552             "CallerIDName: %s\r\n"
05553             "ConnectedLineNum: %s\r\n"
05554             "ConnectedLineName: %s\r\n"
05555             "Channel: %s\r\n"
05556             "Admin: %s\r\n"
05557             "Role: %s\r\n"
05558             "MarkedUser: %s\r\n"
05559             "Muted: %s\r\n"
05560             "Talking: %s\r\n"
05561             "\r\n",
05562             idText,
05563             cnf->confno,
05564             user->user_no,
05565             S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
05566             S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
05567             S_COR(ast_channel_connected(user->chan)->id.number.valid, ast_channel_connected(user->chan)->id.number.str, "<unknown>"),
05568             S_COR(ast_channel_connected(user->chan)->id.name.valid, ast_channel_connected(user->chan)->id.name.str, "<no name>"),
05569             ast_channel_name(user->chan),
05570             ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "Yes" : "No",
05571             ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "Listen only" : ast_test_flag64(&user->userflags, CONFFLAG_TALKER) ? "Talk only" : "Talk and listen",
05572             ast_test_flag64(&user->userflags, CONFFLAG_MARKEDUSER) ? "Yes" : "No",
05573             user->adminflags & ADMINFLAG_MUTED ? "By admin" : user->adminflags & ADMINFLAG_SELFMUTED ? "By self" : "No",
05574             user->talking > 0 ? "Yes" : user->talking == 0 ? "No" : "Not monitored");
05575          ao2_ref(user, -1);
05576       }
05577       ao2_iterator_destroy(&user_iter);
05578    }
05579    AST_LIST_UNLOCK(&confs);
05580 
05581    /* Send final confirmation */
05582    astman_send_list_complete_start(s, m, "MeetmeListComplete", total);
05583    astman_send_list_complete_end(s);
05584    return 0;
05585 }

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

Definition at line 5587 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_list_complete_end(), astman_send_list_complete_start(), astman_send_listack(), ast_conference::confno, ast_conference::isdynamic, ast_conference::list, ast_conference::locked, ast_conference::markedusers, min, NULL, ast_conference::start, and ast_conference::users.

Referenced by load_module().

05588 {
05589    const char *actionid = astman_get_header(m, "ActionID");
05590    char idText[80] = "";
05591    struct ast_conference *cnf;
05592    int totalitems = 0;
05593    int hr, min, sec;
05594    time_t now;
05595    char markedusers[5];
05596 
05597    if (!ast_strlen_zero(actionid)) {
05598       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
05599    }
05600 
05601    if (AST_LIST_EMPTY(&confs)) {
05602       astman_send_error(s, m, "No active conferences.");
05603       return 0;
05604    }
05605 
05606    astman_send_listack(s, m, "Meetme conferences will follow", "start");
05607 
05608    now = time(NULL);
05609 
05610    /* Traverse the conference list */
05611    AST_LIST_LOCK(&confs);
05612    AST_LIST_TRAVERSE(&confs, cnf, list) {
05613       totalitems++;
05614 
05615       if (cnf->markedusers == 0) {
05616          strcpy(markedusers, "N/A");
05617       } else {
05618          sprintf(markedusers, "%.4d", cnf->markedusers);
05619       }
05620       hr = (now - cnf->start) / 3600;
05621       min = ((now - cnf->start) % 3600) / 60;
05622       sec = (now - cnf->start) % 60;
05623 
05624       astman_append(s,
05625       "Event: MeetmeListRooms\r\n"
05626       "%s"
05627       "Conference: %s\r\n"
05628       "Parties: %d\r\n"
05629       "Marked: %s\r\n"
05630       "Activity: %2.2d:%2.2d:%2.2d\r\n"
05631       "Creation: %s\r\n"
05632       "Locked: %s\r\n"
05633       "\r\n",
05634       idText,
05635       cnf->confno,
05636       cnf->users,
05637       markedusers,
05638       hr,  min, sec,
05639       cnf->isdynamic ? "Dynamic" : "Static",
05640       cnf->locked ? "Yes" : "No"); 
05641    }
05642    AST_LIST_UNLOCK(&confs);
05643 
05644    /* Send final confirmation */
05645    astman_send_list_complete_start(s, m, "MeetmeListRoomsComplete", totalitems);
05646    astman_send_list_complete_end(s);
05647    return 0;
05648 }

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

Definition at line 5505 of file app_meetme.c.

References meetmemute().

Referenced by load_module().

05506 {
05507    return meetmemute(s, m, 1);
05508 }

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

Definition at line 5510 of file app_meetme.c.

References meetmemute().

Referenced by load_module().

05511 {
05512    return meetmemute(s, m, 0);
05513 }

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

The MeetMeadmin application.

MeetMeAdmin(confno, command, caller)

Definition at line 5219 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_strdupa, 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, NULL, OBJ_NODATA, pbx_builtin_setvar_helper(), RAII_VAR, 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().

05219                                                                   {
05220    char *params;
05221    struct ast_conference *cnf;
05222    struct ast_conf_user *user = NULL;
05223    AST_DECLARE_APP_ARGS(args,
05224       AST_APP_ARG(confno);
05225       AST_APP_ARG(command);
05226       AST_APP_ARG(user);
05227    );
05228    int res = 0;
05229 
05230    if (ast_strlen_zero(data)) {
05231       ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
05232       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
05233       return -1;
05234    }
05235 
05236    params = ast_strdupa(data);
05237    AST_STANDARD_APP_ARGS(args, params);
05238 
05239    if (!args.command) {
05240       ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
05241       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
05242       return -1;
05243    }
05244 
05245    AST_LIST_LOCK(&confs);
05246    AST_LIST_TRAVERSE(&confs, cnf, list) {
05247       if (!strcmp(cnf->confno, args.confno))
05248          break;
05249    }
05250 
05251    if (!cnf) {
05252       ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
05253       AST_LIST_UNLOCK(&confs);
05254       pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
05255       return 0;
05256    }
05257 
05258    ast_atomic_fetchadd_int(&cnf->refcount, 1);
05259 
05260    if (args.user) {
05261       user = find_user(cnf, args.user);
05262       if (!user) {
05263          ast_log(LOG_NOTICE, "Specified User not found!\n");
05264          res = -2;
05265          goto usernotfound;
05266       }
05267    } else {
05268       /* fail for commands that require a user */
05269       switch (*args.command) {
05270       case 'm': /* Unmute */
05271       case 'M': /* Mute */
05272       case 't': /* Lower user's talk volume */
05273       case 'T': /* Raise user's talk volume */
05274       case 'u': /* Lower user's listen volume */
05275       case 'U': /* Raise user's listen volume */
05276       case 'r': /* Reset user's volume level */
05277       case 'k': /* Kick user */
05278          res = -2;
05279          ast_log(LOG_NOTICE, "No user specified!\n");
05280          goto usernotfound;
05281       default:
05282          break;
05283       }
05284    }
05285 
05286    switch (*args.command) {
05287    case 76: /* L: Lock */ 
05288       cnf->locked = 1;
05289       break;
05290    case 108: /* l: Unlock */ 
05291       cnf->locked = 0;
05292       break;
05293    case 75: /* K: kick all users */
05294       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_kickme_cb, NULL);
05295       break;
05296    case 101: /* e: Eject last user*/
05297    {
05298       int max_no = 0;
05299       RAII_VAR(struct ast_conf_user *, eject_user, NULL, ao2_cleanup);
05300 
05301       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
05302       eject_user = ao2_find(cnf->usercontainer, &max_no, 0);
05303       if (!eject_user) {
05304          res = -1;
05305          ast_log(LOG_NOTICE, "No last user to kick!\n");
05306          break;
05307       }
05308 
05309       if (!ast_test_flag64(&eject_user->userflags, CONFFLAG_ADMIN)) {
05310          eject_user->adminflags |= ADMINFLAG_KICKME;
05311       } else {
05312          res = -1;
05313          ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
05314       }
05315       break;
05316    }
05317    case 77: /* M: Mute */ 
05318       user->adminflags |= ADMINFLAG_MUTED;
05319       break;
05320    case 78: /* N: Mute all (non-admin) users */
05321       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_muted_cb, &cnf);
05322       break;               
05323    case 109: /* m: Unmute */ 
05324       user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
05325       break;
05326    case 110: /* n: Unmute all users */
05327       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, NULL);
05328       break;
05329    case 107: /* k: Kick user */ 
05330       user->adminflags |= ADMINFLAG_KICKME;
05331       break;
05332    case 118: /* v: Lower all users listen volume */
05333       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_voldown_cb, NULL);
05334       break;
05335    case 86: /* V: Raise all users listen volume */
05336       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_listen_volup_cb, NULL);
05337       break;
05338    case 115: /* s: Lower all users speaking volume */
05339       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_voldown_cb, NULL);
05340       break;
05341    case 83: /* S: Raise all users speaking volume */
05342       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_talk_volup_cb, NULL);
05343       break;
05344    case 82: /* R: Reset all volume levels */
05345       ao2_callback(cnf->usercontainer, OBJ_NODATA, user_reset_vol_cb, NULL);
05346       break;
05347    case 114: /* r: Reset user's volume level */
05348       reset_volumes(user);
05349       break;
05350    case 85: /* U: Raise user's listen volume */
05351       tweak_listen_volume(user, VOL_UP);
05352       break;
05353    case 117: /* u: Lower user's listen volume */
05354       tweak_listen_volume(user, VOL_DOWN);
05355       break;
05356    case 84: /* T: Raise user's talk volume */
05357       tweak_talk_volume(user, VOL_UP);
05358       break;
05359    case 116: /* t: Lower user's talk volume */
05360       tweak_talk_volume(user, VOL_DOWN);
05361       break;
05362    case 'E': /* E: Extend conference */
05363       if (rt_extend_conf(args.confno)) {
05364          res = -1;
05365       }
05366       break;
05367    }
05368 
05369    if (args.user) {
05370       /* decrement reference from find_user */
05371       ao2_ref(user, -1);
05372    }
05373 usernotfound:
05374    AST_LIST_UNLOCK(&confs);
05375 
05376    dispose_conf(cnf);
05377    pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
05378 
05379    return 0;
05380 }

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

Definition at line 2609 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, NULL, and announce_listitem::vmrec.

Referenced by conf_run().

02610 {
02611    struct announce_listitem *current;
02612    struct ast_conference *conf = data;
02613    int res;
02614    char filename[PATH_MAX] = "";
02615    AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
02616    AST_LIST_HEAD_INIT_NOLOCK(&local_list);
02617 
02618    while (!conf->announcethread_stop) {
02619       ast_mutex_lock(&conf->announcelistlock);
02620       if (conf->announcethread_stop) {
02621          ast_mutex_unlock(&conf->announcelistlock);
02622          break;
02623       }
02624       if (AST_LIST_EMPTY(&conf->announcelist))
02625          ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
02626 
02627       AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
02628       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
02629 
02630       ast_mutex_unlock(&conf->announcelistlock);
02631       if (conf->announcethread_stop) {
02632          break;
02633       }
02634 
02635       for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
02636          ast_debug(1, "About to play %s\n", current->namerecloc);
02637          if (!ast_fileexists(current->namerecloc, NULL, NULL))
02638             continue;
02639          if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
02640             if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
02641                res = ast_waitstream(current->confchan, "");
02642             if (!res) {
02643                ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
02644                if (!ast_streamfile(current->confchan, filename, current->language))
02645                   ast_waitstream(current->confchan, "");
02646             }
02647          }
02648          if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
02649             /* only remove it if it isn't a VM recording file */
02650             ast_filedelete(current->namerecloc, NULL);
02651          }
02652       }
02653    }
02654 
02655    /* thread marked to stop, clean up */
02656    while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
02657       /* only delete if it's a vm rec */
02658       if (!current->vmrec) {
02659          ast_filedelete(current->namerecloc, NULL);
02660       }
02661       ao2_ref(current, -1);
02662    }
02663    return NULL;
02664 }

static void answer_trunk_chan ( struct ast_channel chan  )  [static]

Definition at line 6053 of file app_meetme.c.

References ast_answer(), and ast_indicate().

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

06054 {
06055    ast_answer(chan);
06056    ast_indicate(chan, -1);
06057 }

AST_DATA_STRUCTURE ( ast_conf_user  ,
MEETME_USER_DATA_EXPORT   
)

AST_DATA_STRUCTURE ( ast_conference  ,
MEETME_DATA_EXPORT   
)

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

Definition at line 1609 of file app_meetme.c.

References ast_conference::announcethread, ast_conference::announcethreadlock, ao2_cleanup, ao2_container_alloc, ao2_ref, ast_atomic_fetchadd_int(), ast_calloc, ast_channel_fd(), ast_channel_uniqueid(), ast_copy_string(), ast_format_cap_alloc, ast_format_cap_append, AST_FORMAT_CAP_FLAG_DEFAULT, ast_format_slin, 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(), ast_set_write_format(), 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, NULL, 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().

01612 {
01613    struct ast_conference *cnf;
01614    struct dahdi_confinfo dahdic = { 0, };
01615    int confno_int = 0;
01616    struct ast_format_cap *cap_slin = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
01617 
01618    AST_LIST_LOCK(&confs);
01619 
01620    AST_LIST_TRAVERSE(&confs, cnf, list) {
01621       if (!strcmp(confno, cnf->confno)) 
01622          break;
01623    }
01624 
01625    if (cnf || (!make && !dynamic) || !cap_slin)
01626       goto cnfout;
01627 
01628    ast_format_cap_append(cap_slin, ast_format_slin, 0);
01629    /* Make a new one */
01630    if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
01631       !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
01632       goto cnfout;
01633    }
01634 
01635    ast_mutex_init(&cnf->playlock);
01636    ast_mutex_init(&cnf->listenlock);
01637    cnf->recordthread = AST_PTHREADT_NULL;
01638    ast_mutex_init(&cnf->recordthreadlock);
01639    cnf->announcethread = AST_PTHREADT_NULL;
01640    ast_mutex_init(&cnf->announcethreadlock);
01641    ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
01642    ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
01643    ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
01644    ast_copy_string(cnf->uniqueid, ast_channel_uniqueid(chan), sizeof(cnf->uniqueid));
01645 
01646    /* Setup a new dahdi conference */
01647    dahdic.confno = -1;
01648    dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01649    cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
01650    if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
01651       if (test) {
01652          /* if we are creating a conference for a unit test, it is not neccesary
01653           * to open a pseudo channel, so, if we fail continue creating
01654           * the conference. */
01655          ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
01656       } else {
01657          ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
01658          if (cnf->fd >= 0)
01659             close(cnf->fd);
01660          ao2_ref(cnf->usercontainer, -1);
01661          ast_mutex_destroy(&cnf->playlock);
01662          ast_mutex_destroy(&cnf->listenlock);
01663          ast_mutex_destroy(&cnf->recordthreadlock);
01664          ast_mutex_destroy(&cnf->announcethreadlock);
01665          ast_free(cnf);
01666          cnf = NULL;
01667          goto cnfout;
01668       }
01669    }
01670 
01671    cnf->dahdiconf = dahdic.confno;
01672 
01673    /* Setup a new channel for playback of audio files */
01674    cnf->chan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL);
01675    if (cnf->chan) {
01676       ast_set_read_format(cnf->chan, ast_format_slin);
01677       ast_set_write_format(cnf->chan, ast_format_slin);
01678       dahdic.chan = 0;
01679       dahdic.confno = cnf->dahdiconf;
01680       dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
01681       if (ioctl(ast_channel_fd(cnf->chan, 0), DAHDI_SETCONF, &dahdic)) {
01682          if (test) {
01683             ast_test_status_update(test, "Error setting conference on pseudo channel\n");
01684          }
01685          ast_log(LOG_WARNING, "Error setting conference\n");
01686          if (cnf->chan)
01687             ast_hangup(cnf->chan);
01688          else
01689             close(cnf->fd);
01690          ao2_ref(cnf->usercontainer, -1);
01691          ast_mutex_destroy(&cnf->playlock);
01692          ast_mutex_destroy(&cnf->listenlock);
01693          ast_mutex_destroy(&cnf->recordthreadlock);
01694          ast_mutex_destroy(&cnf->announcethreadlock);
01695          ast_free(cnf);
01696          cnf = NULL;
01697          goto cnfout;
01698       }
01699    }
01700 
01701    /* Fill the conference struct */
01702    cnf->start = time(NULL);
01703    cnf->maxusers = 0x7fffffff;
01704    cnf->isdynamic = dynamic ? 1 : 0;
01705    ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
01706    AST_LIST_INSERT_HEAD(&confs, cnf, list);
01707 
01708    /* Reserve conference number in map */
01709    if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
01710       conf_map[confno_int] = 1;
01711    
01712 cnfout:
01713    ao2_cleanup(cap_slin);
01714    if (cnf)
01715       ast_atomic_fetchadd_int(&cnf->refcount, refcount);
01716 
01717    AST_LIST_UNLOCK(&confs);
01718 
01719    return cnf;
01720 }

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

Definition at line 2666 of file app_meetme.c.

References AST_STATE_UP, ast_test_flag64, and CONFFLAG_NO_AUDIO_UNTIL_UP.

Referenced by conf_run().

02667 {
02668    if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
02669       return 1;
02670    }
02671 
02672    return (ast_channel_state(chan) == AST_STATE_UP);
02673 }

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

Definition at line 1411 of file app_meetme.c.

References ast_log, errno, and LOG_WARNING.

Referenced by conf_play(), and conf_run().

01412 {
01413    int res;
01414    int x;
01415 
01416    while (len) {
01417       if (block) {
01418          x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
01419          res = ioctl(fd, DAHDI_IOMUX, &x);
01420       } else
01421          res = 0;
01422       if (res >= 0)
01423          res = write(fd, data, len);
01424       if (res < 1) {
01425          if (errno != EAGAIN) {
01426             ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
01427             return -1;
01428          } else
01429             return 0;
01430       }
01431       len -= res;
01432       data += res;
01433    }
01434 
01435    return 0;
01436 }

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

The MeetMeChannelAdmin application MeetMeChannelAdmin(channel, command).

Definition at line 5384 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_strdupa, ast_strlen_zero, ast_conf_user::list, LOG_NOTICE, LOG_WARNING, NULL, user_chan_cb(), and ast_conference::usercontainer.

Referenced by load_module().

05384                                                                           {
05385    char *params;
05386    struct ast_conference *conf = NULL;
05387    struct ast_conf_user *user = NULL;
05388    AST_DECLARE_APP_ARGS(args,
05389       AST_APP_ARG(channel);
05390       AST_APP_ARG(command);
05391    );
05392 
05393    if (ast_strlen_zero(data)) {
05394       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires two arguments!\n");
05395       return -1;
05396    }
05397    
05398    params = ast_strdupa(data);
05399    AST_STANDARD_APP_ARGS(args, params);
05400 
05401    if (!args.channel) {
05402       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a channel name!\n");
05403       return -1;
05404    }
05405 
05406    if (!args.command) {
05407       ast_log(LOG_WARNING, "MeetMeChannelAdmin requires a command!\n");
05408       return -1;
05409    }
05410 
05411    AST_LIST_LOCK(&confs);
05412    AST_LIST_TRAVERSE(&confs, conf, list) {
05413       if ((user = ao2_callback(conf->usercontainer, 0, user_chan_cb, args.channel))) {
05414          break;
05415       }
05416    }
05417    
05418    if (!user) {
05419       ast_log(LOG_NOTICE, "Specified user (%s) not found\n", args.channel);
05420       AST_LIST_UNLOCK(&confs);
05421       return 0;
05422    }
05423    
05424    /* perform the specified action */
05425    switch (*args.command) {
05426       case 77: /* M: Mute */ 
05427          user->adminflags |= ADMINFLAG_MUTED;
05428          break;
05429       case 109: /* m: Unmute */ 
05430          user->adminflags &= ~ADMINFLAG_MUTED;
05431          break;
05432       case 107: /* k: Kick user */ 
05433          user->adminflags |= ADMINFLAG_KICKME;
05434          break;
05435       default: /* unknown command */
05436          ast_log(LOG_WARNING, "Unknown MeetMeChannelAdmin command '%s'\n", args.command);
05437          break;
05438    }
05439    ao2_ref(user, -1);
05440    AST_LIST_UNLOCK(&confs);
05441    
05442    return 0;
05443 }

static char* complete_confno ( const char *  word,
int  state 
) [static]

Definition at line 1722 of file app_meetme.c.

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

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

01723 {
01724    struct ast_conference *cnf;
01725    char *ret = NULL;
01726    int which = 0;
01727    int len = strlen(word);
01728 
01729    AST_LIST_LOCK(&confs);
01730    AST_LIST_TRAVERSE(&confs, cnf, list) {
01731       if (!strncmp(word, cnf->confno, len) && ++which > state) {
01732          /* dup before releasing the lock */
01733          ret = ast_strdup(cnf->confno);
01734          break;
01735       }
01736    }
01737    AST_LIST_UNLOCK(&confs);
01738    return ret;
01739 }

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

Definition at line 1811 of file app_meetme.c.

References ast_strdup, ast_strdupa, complete_confno(), len(), NULL, and STR_CONCISE.

Referenced by meetme_show_cmd().

01812 {
01813    int len;
01814 
01815    if (pos == 2) {
01816       len = strlen(word);
01817       if (!strncasecmp(word, STR_CONCISE, len)) {
01818          if (state == 0) {
01819             return ast_strdup(STR_CONCISE);
01820          }
01821          --state;
01822       }
01823 
01824       return complete_confno(word, state);
01825    }
01826    if (pos == 3 && state == 0) {
01827       char *saved = NULL;
01828       char *myline;
01829       char *confno;
01830 
01831       /* Extract the confno from the command line. */
01832       myline = ast_strdupa(line);
01833       strtok_r(myline, " ", &saved);
01834       strtok_r(NULL, " ", &saved);
01835       confno = strtok_r(NULL, " ", &saved);
01836 
01837       if (!strcasecmp(confno, STR_CONCISE)) {
01838          /* There is nothing valid in this position now. */
01839          return NULL;
01840       }
01841 
01842       len = strlen(word);
01843       if (!strncasecmp(word, STR_CONCISE, len)) {
01844          return ast_strdup(STR_CONCISE);
01845       }
01846    }
01847    return NULL;
01848 }

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

Definition at line 1803 of file app_meetme.c.

References complete_confno(), and NULL.

Referenced by meetme_lock_cmd().

01804 {
01805    if (pos == 2) {
01806       return complete_confno(word, state);
01807    }
01808    return NULL;
01809 }

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

Definition at line 1763 of file app_meetme.c.

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

Referenced by meetme_kick_cmd(), and meetme_mute_cmd().

01764 {
01765    if (pos == 2) {
01766       return complete_confno(word, state);
01767    }
01768    if (pos == 3) {
01769       int len = strlen(word);
01770       char *ret = NULL;
01771       char *saved = NULL;
01772       char *myline;
01773       char *confno;
01774       struct ast_conference *cnf;
01775 
01776       if (!strncasecmp(word, "all", len)) {
01777          if (state == 0) {
01778             return ast_strdup("all");
01779          }
01780          --state;
01781       }
01782 
01783       /* Extract the confno from the command line. */
01784       myline = ast_strdupa(line);
01785       strtok_r(myline, " ", &saved);
01786       strtok_r(NULL, " ", &saved);
01787       confno = strtok_r(NULL, " ", &saved);
01788 
01789       AST_LIST_LOCK(&confs);
01790       AST_LIST_TRAVERSE(&confs, cnf, list) {
01791          if (!strcmp(confno, cnf->confno)) {
01792             ret = complete_userno(cnf, word, state);
01793             break;
01794          }
01795       }
01796       AST_LIST_UNLOCK(&confs);
01797 
01798       return ret;
01799    }
01800    return NULL;
01801 }

static char* complete_userno ( struct ast_conference cnf,
const char *  word,
int  state 
) [static]

Definition at line 1741 of file app_meetme.c.

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

Referenced by complete_meetmecmd_mute_kick().

01742 {
01743    char usrno[50];
01744    struct ao2_iterator iter;
01745    struct ast_conf_user *usr;
01746    char *ret = NULL;
01747    int which = 0;
01748    int len = strlen(word);
01749 
01750    iter = ao2_iterator_init(cnf->usercontainer, 0);
01751    for (; (usr = ao2_iterator_next(&iter)); ao2_ref(usr, -1)) {
01752       snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
01753       if (!strncmp(word, usrno, len) && ++which > state) {
01754          ao2_ref(usr, -1);
01755          ret = ast_strdup(usrno);
01756          break;
01757       }
01758    }
01759    ao2_iterator_destroy(&iter);
01760    return ret;
01761 }

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

The meetme() application.

Definition at line 4831 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_strdupa, 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(), empty, 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, NULL, OPT_ARG_ARRAY_SIZE, parse(), ast_conference::pin, ast_conference::pinadmin, ast_conference::recordingfilename, ast_conference::recordingformat, SENTINEL, strsep(), ast_conference::useropts, ast_conference::users, ast_variable::value, and var.

Referenced by load_module().

04832 {
04833    int res = -1;
04834    char confno[MAX_CONFNUM] = "";
04835    int allowretry = 0;
04836    int retrycnt = 0;
04837    struct ast_conference *cnf = NULL;
04838    struct ast_flags64 confflags = {0};
04839    struct ast_flags config_flags = { 0 };
04840    int dynamic = 0;
04841    int empty = 0, empty_no_pin = 0;
04842    int always_prompt = 0;
04843    const char *notdata;
04844    char *info, the_pin[MAX_PIN] = "";
04845    AST_DECLARE_APP_ARGS(args,
04846       AST_APP_ARG(confno);
04847       AST_APP_ARG(options);
04848       AST_APP_ARG(pin);
04849    );
04850    char *optargs[OPT_ARG_ARRAY_SIZE] = { NULL, };
04851 
04852    if (ast_strlen_zero(data)) {
04853       allowretry = 1;
04854       notdata = "";
04855    } else {
04856       notdata = data;
04857    }
04858    
04859    if (ast_channel_state(chan) != AST_STATE_UP)
04860       ast_answer(chan);
04861 
04862    info = ast_strdupa(notdata);
04863 
04864    AST_STANDARD_APP_ARGS(args, info);  
04865 
04866    if (args.confno) {
04867       ast_copy_string(confno, args.confno, sizeof(confno));
04868       if (ast_strlen_zero(confno)) {
04869          allowretry = 1;
04870       }
04871    }
04872    
04873    if (args.pin)
04874       ast_copy_string(the_pin, args.pin, sizeof(the_pin));
04875 
04876    if (args.options) {
04877       ast_app_parse_options64(meetme_opts, &confflags, optargs, args.options);
04878       dynamic = ast_test_flag64(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
04879       if (ast_test_flag64(&confflags, CONFFLAG_DYNAMICPIN) && ast_strlen_zero(args.pin))
04880          strcpy(the_pin, "q");
04881 
04882       empty = ast_test_flag64(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
04883       empty_no_pin = ast_test_flag64(&confflags, CONFFLAG_EMPTYNOPIN);
04884       always_prompt = ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT | CONFFLAG_DYNAMICPIN);
04885    }
04886 
04887    do {
04888       if (retrycnt > 3)
04889          allowretry = 0;
04890       if (empty) {
04891          int i;
04892          struct ast_config *cfg;
04893          struct ast_variable *var;
04894          int confno_int;
04895 
04896          /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
04897          if ((empty_no_pin) || (!dynamic)) {
04898             cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04899             if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
04900                var = ast_variable_browse(cfg, "rooms");
04901                while (var) {
04902                   char parse[MAX_SETTINGS], *stringp = parse, *confno_tmp;
04903                   if (!strcasecmp(var->name, "conf")) {
04904                      int found = 0;
04905                      ast_copy_string(parse, var->value, sizeof(parse));
04906                      confno_tmp = strsep(&stringp, "|,");
04907                      if (!dynamic) {
04908                         /* For static:  run through the list and see if this conference is empty */
04909                         AST_LIST_LOCK(&confs);
04910                         AST_LIST_TRAVERSE(&confs, cnf, list) {
04911                            if (!strcmp(confno_tmp, cnf->confno)) {
04912                               /* The conference exists, therefore it's not empty */
04913                               found = 1;
04914                               break;
04915                            }
04916                         }
04917                         AST_LIST_UNLOCK(&confs);
04918                         cnf = NULL;
04919                         if (!found) {
04920                            /* At this point, we have a confno_tmp (static conference) that is empty */
04921                            if ((empty_no_pin && ast_strlen_zero(stringp)) || (!empty_no_pin)) {
04922                               /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
04923                                * Case 2:  empty_no_pin and pin is blank (but not NULL)
04924                                * Case 3:  not empty_no_pin
04925                                */
04926                               ast_copy_string(confno, confno_tmp, sizeof(confno));
04927                               break;
04928                            }
04929                         }
04930                      }
04931                   }
04932                   var = var->next;
04933                }
04934                ast_config_destroy(cfg);
04935             }
04936 
04937             if (ast_strlen_zero(confno) && (cfg = ast_load_realtime_multientry("meetme", "confno LIKE", "%", SENTINEL))) {
04938                const char *catg;
04939                for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
04940                   const char *confno_tmp = ast_variable_retrieve(cfg, catg, "confno");
04941                   const char *pin_tmp = ast_variable_retrieve(cfg, catg, "pin");
04942                   if (ast_strlen_zero(confno_tmp)) {
04943                      continue;
04944                   }
04945                   if (!dynamic) {
04946                      int found = 0;
04947                      /* For static:  run through the list and see if this conference is empty */
04948                      AST_LIST_LOCK(&confs);
04949                      AST_LIST_TRAVERSE(&confs, cnf, list) {
04950                         if (!strcmp(confno_tmp, cnf->confno)) {
04951                            /* The conference exists, therefore it's not empty */
04952                            found = 1;
04953                            break;
04954                         }
04955                      }
04956                      AST_LIST_UNLOCK(&confs);
04957                      if (!found) {
04958                         /* At this point, we have a confno_tmp (realtime conference) that is empty */
04959                         if ((empty_no_pin && ast_strlen_zero(pin_tmp)) || (!empty_no_pin)) {
04960                            /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
04961                             * Case 2:  empty_no_pin and pin is blank (but not NULL)
04962                             * Case 3:  not empty_no_pin
04963                             */
04964                            ast_copy_string(confno, confno_tmp, sizeof(confno));
04965                            break;
04966                         }
04967                      }
04968                   }
04969                }
04970                ast_config_destroy(cfg);
04971             }
04972          }
04973 
04974          /* Select first conference number not in use */
04975          if (ast_strlen_zero(confno) && dynamic) {
04976             AST_LIST_LOCK(&confs);
04977             for (i = 0; i < ARRAY_LEN(conf_map); i++) {
04978                if (!conf_map[i]) {
04979                   snprintf(confno, sizeof(confno), "%d", i);
04980                   conf_map[i] = 1;
04981                   break;
04982                }
04983             }
04984             AST_LIST_UNLOCK(&confs);
04985          }
04986 
04987          /* Not found? */
04988          if (ast_strlen_zero(confno)) {
04989             res = ast_streamfile(chan, "conf-noempty", ast_channel_language(chan));
04990             ast_test_suite_event_notify("PLAYBACK", "Message: conf-noempty");
04991             if (!res)
04992                ast_waitstream(chan, "");
04993          } else {
04994             if (sscanf(confno, "%30d", &confno_int) == 1) {
04995                if (!ast_test_flag64(&confflags, CONFFLAG_QUIET)) {
04996                   res = ast_streamfile(chan, "conf-enteringno", ast_channel_language(chan));
04997                   if (!res) {
04998                      ast_waitstream(chan, "");
04999                      res = ast_say_digits(chan, confno_int, "", ast_channel_language(chan));
05000                   }
05001                }
05002             } else {
05003                ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
05004             }
05005          }
05006       }
05007 
05008       while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
05009          /* Prompt user for conference number */
05010          res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
05011          if (res < 0) {
05012             /* Don't try to validate when we catch an error */
05013             confno[0] = '\0';
05014             allowretry = 0;
05015             break;
05016          }
05017       }
05018       if (!ast_strlen_zero(confno)) {
05019          /* Check the validity of the conference */
05020          cnf = find_conf(chan, confno, 1, dynamic, the_pin, 
05021             sizeof(the_pin), 1, &confflags);
05022          if (!cnf) {
05023             int too_early = 0;
05024 
05025             cnf = find_conf_realtime(chan, confno, 1, dynamic, 
05026                the_pin, sizeof(the_pin), 1, &confflags, &too_early, optargs);
05027             if (rt_schedule && too_early)
05028                allowretry = 0;
05029          }
05030 
05031          if (!cnf) {
05032             if (allowretry) {
05033                confno[0] = '\0';
05034                res = ast_streamfile(chan, "conf-invalid", ast_channel_language(chan));
05035                if (!res)
05036                   ast_waitstream(chan, "");
05037                res = -1;
05038             }
05039          } else {
05040             /* Conference requires a pin for specified access level */
05041             int req_pin = !ast_strlen_zero(cnf->pin) ||
05042                (!ast_strlen_zero(cnf->pinadmin) &&
05043                   ast_test_flag64(&confflags, CONFFLAG_ADMIN));
05044             /* The following logic was derived from a
05045              * 4 variable truth table and defines which
05046              * circumstances are not exempt from pin
05047              * checking.
05048              * If this needs to be modified, write the
05049              * truth table back out from the boolean
05050              * expression AB+A'D+C', change the erroneous
05051              * result, and rederive the expression.
05052              * Variables:
05053              *  A: pin provided?
05054              *  B: always prompt?
05055              *  C: dynamic?
05056              *  D: has users? */
05057             int not_exempt = !cnf->isdynamic;
05058             not_exempt = not_exempt || (!ast_strlen_zero(args.pin) && ast_test_flag64(&confflags, CONFFLAG_ALWAYSPROMPT));
05059             not_exempt = not_exempt || (ast_strlen_zero(args.pin) && cnf->users);
05060             if (req_pin && not_exempt) {
05061                char pin[MAX_PIN] = "";
05062                int j;
05063 
05064                /* Allow the pin to be retried up to 3 times */
05065                for (j = 0; j < 3; j++) {
05066                   if (*the_pin && (always_prompt == 0)) {
05067                      ast_copy_string(pin, the_pin, sizeof(pin));
05068                      res = 0;
05069                   } else {
05070                      /* Prompt user for pin if pin is required */
05071                      ast_test_suite_event_notify("PLAYBACK", "Message: conf-getpin\r\n"
05072                         "Channel: %s",
05073                         ast_channel_name(chan));
05074                      res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
05075                   }
05076                   if (res >= 0) {
05077                      if ((!strcasecmp(pin, cnf->pin) &&
05078                           (ast_strlen_zero(cnf->pinadmin) ||
05079                            !ast_test_flag64(&confflags, CONFFLAG_ADMIN))) ||
05080                           (!ast_strlen_zero(cnf->pinadmin) &&
05081                            !strcasecmp(pin, cnf->pinadmin))) {
05082                         /* Pin correct */
05083                         allowretry = 0;
05084                         if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) {
05085                            if (!ast_strlen_zero(cnf->adminopts)) {
05086                               char *opts = ast_strdupa(cnf->adminopts);
05087                               ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
05088                            }
05089                         } else {
05090                            if (!ast_strlen_zero(cnf->useropts)) {
05091                               char *opts = ast_strdupa(cnf->useropts);
05092                               ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
05093                            }
05094                         }
05095                         /* Run the conference */
05096                         ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
05097                         res = conf_run(chan, cnf, &confflags, optargs);
05098                         break;
05099                      } else {
05100                         /* Pin invalid */
05101                         if (!ast_streamfile(chan, "conf-invalidpin", ast_channel_language(chan))) {
05102                            res = ast_waitstream(chan, AST_DIGIT_ANY);
05103                            ast_stopstream(chan);
05104                         } else {
05105                            ast_log(LOG_WARNING, "Couldn't play invalid pin msg!\n");
05106                            break;
05107                         }
05108                         if (res < 0)
05109                            break;
05110                         pin[0] = res;
05111                         pin[1] = '\0';
05112                         res = -1;
05113                         if (allowretry)
05114                            confno[0] = '\0';
05115                      }
05116                   } else {
05117                      /* failed when getting the pin */
05118                      res = -1;
05119                      allowretry = 0;
05120                      /* see if we need to get rid of the conference */
05121                      break;
05122                   }
05123 
05124                   /* Don't retry pin with a static pin */
05125                   if (*the_pin && (always_prompt == 0)) {
05126                      break;
05127                   }
05128                }
05129             } else {
05130                /* No pin required */
05131                allowretry = 0;
05132 
05133                /* For RealTime conferences without a pin 
05134                 * should still support loading options
05135                 */
05136                if (!ast_strlen_zero(cnf->useropts)) {
05137                   char *opts = ast_strdupa(cnf->useropts);
05138                   ast_app_parse_options64(meetme_opts, &confflags, optargs, opts);
05139                }
05140 
05141                /* Run the conference */
05142                res = conf_run(chan, cnf, &confflags, optargs);
05143             }
05144             dispose_conf(cnf);
05145             cnf = NULL;
05146          }
05147       }
05148    } while (allowretry);
05149 
05150    if (cnf)
05151       dispose_conf(cnf);
05152    
05153    return res;
05154 }

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

Definition at line 2293 of file app_meetme.c.

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

Referenced by conf_run().

02294 {
02295    int x;
02296 
02297    /* read any frames that may be waiting on the channel
02298       and throw them away
02299    */
02300    if (chan) {
02301       struct ast_frame *f;
02302 
02303       /* when no frames are available, this will wait
02304          for 1 millisecond maximum
02305       */
02306       while (ast_waitfor(chan, 1) > 0) {
02307          f = ast_read(chan);
02308          if (f)
02309             ast_frfree(f);
02310          else /* channel was hung up or something else happened */
02311             break;
02312       }
02313    }
02314 
02315    /* flush any data sitting in the pseudo channel */
02316    x = DAHDI_FLUSH_ALL;
02317    if (ioctl(fd, DAHDI_FLUSH, &x))
02318       ast_log(LOG_WARNING, "Error flushing channel\n");
02319 
02320 }

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 2325 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::fd, item, ast_conference::lchan, ast_conference::listenlock, MEETME_RECORD_ACTIVE, MEETME_RECORD_OFF, MEETME_RECORD_TERMINATE, meetme_stasis_generate_msg(), announce_listitem::namerecloc, NULL, ast_conference::origframe, ast_conference::playlock, 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().

02326 {
02327    int x;
02328    struct announce_listitem *item;
02329 
02330    AST_LIST_REMOVE(&confs, conf, list);
02331 
02332    meetme_stasis_generate_msg(conf, NULL, NULL, meetme_end_type(), NULL);
02333 
02334    if (conf->recording == MEETME_RECORD_ACTIVE) {
02335       conf->recording = MEETME_RECORD_TERMINATE;
02336       AST_LIST_UNLOCK(&confs);
02337       while (1) {
02338          usleep(1);
02339          AST_LIST_LOCK(&confs);
02340          if (conf->recording == MEETME_RECORD_OFF)
02341             break;
02342          AST_LIST_UNLOCK(&confs);
02343       }
02344    }
02345 
02346    for (x = 0; x < AST_FRAME_BITS; x++) {
02347       if (conf->transframe[x])
02348          ast_frfree(conf->transframe[x]);
02349       if (conf->transpath[x])
02350          ast_translator_free_path(conf->transpath[x]);
02351    }
02352    if (conf->announcethread != AST_PTHREADT_NULL) {
02353       ast_mutex_lock(&conf->announcelistlock);
02354       conf->announcethread_stop = 1;
02355       ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
02356       ast_cond_signal(&conf->announcelist_addition);
02357       ast_mutex_unlock(&conf->announcelistlock);
02358       pthread_join(conf->announcethread, NULL);
02359    
02360       while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
02361          /* If it's a voicemail greeting file we don't want to remove it */
02362          if (!item->vmrec){
02363             ast_filedelete(item->namerecloc, NULL);
02364          }
02365          ao2_ref(item, -1);
02366       }
02367       ast_mutex_destroy(&conf->announcelistlock);
02368    }
02369 
02370    if (conf->origframe)
02371       ast_frfree(conf->origframe);
02372    ast_hangup(conf->lchan);
02373    ast_hangup(conf->chan);
02374    if (conf->fd >= 0)
02375       close(conf->fd);
02376    if (conf->recordingfilename) {
02377       ast_free(conf->recordingfilename);
02378    }
02379    if (conf->usercontainer) {
02380       ao2_ref(conf->usercontainer, -1);
02381    }
02382    if (conf->recordingformat) {
02383       ast_free(conf->recordingformat);
02384    }
02385    ast_mutex_destroy(&conf->playlock);
02386    ast_mutex_destroy(&conf->listenlock);
02387    ast_mutex_destroy(&conf->recordthreadlock);
02388    ast_mutex_destroy(&conf->announcethreadlock);
02389    ast_free(conf);
02390 
02391    return 0;
02392 }

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

Definition at line 1529 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(), ast_conference::markedusers, and NULL.

Referenced by conf_run().

01530 {
01531    unsigned char *data;
01532    int len;
01533    int res = -1;
01534 
01535    ast_test_suite_event_notify("CONFPLAY", "Channel: %s\r\n"
01536       "Conference: %s\r\n"
01537       "Marked: %d",
01538       ast_channel_name(chan),
01539       conf->confno,
01540       conf->markedusers);
01541 
01542    if (!ast_check_hangup(chan))
01543       res = ast_autoservice_start(chan);
01544 
01545    AST_LIST_LOCK(&confs);
01546 
01547    switch(sound) {
01548    case ENTER:
01549       data = enter;
01550       len = sizeof(enter);
01551       break;
01552    case LEAVE:
01553       data = leave;
01554       len = sizeof(leave);
01555       break;
01556    default:
01557       data = NULL;
01558       len = 0;
01559    }
01560    if (data) {
01561       careful_write(conf->fd, data, len, 1);
01562    }
01563 
01564    AST_LIST_UNLOCK(&confs);
01565 
01566    if (!res) 
01567       ast_autoservice_stop(chan);
01568 }

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

Definition at line 2394 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, and ast_conference::usercontainer.

Referenced by conf_run().

02396 {
02397    struct ast_conf_user *user;
02398    struct ao2_iterator user_iter;
02399 
02400    user_iter = ao2_iterator_init(conf->usercontainer, 0);
02401    while ((user = ao2_iterator_next(&user_iter))) {
02402       if (user == sender) {
02403          ao2_ref(user, -1);
02404          continue;
02405       }
02406       if (ast_write(user->chan, f) < 0)
02407          ast_log(LOG_WARNING, "Error writing frame to channel %s\n", ast_channel_name(user->chan));
02408       ao2_ref(user, -1);
02409    }
02410    ao2_iterator_destroy(&user_iter);
02411 }

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

Definition at line 3171 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_cleanup, ao2_link, ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, ast_channel_audiohooks(), 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_alloc, ast_format_cap_append, AST_FORMAT_CAP_FLAG_DEFAULT, ast_format_cmp(), AST_FORMAT_CMP_EQUAL, ast_format_compatibility_format2bitfield(), ast_format_slin, 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_json_unref(), 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_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(), ast_set_write_format(), ast_stopstream(), ast_strdup, ast_strdupa, 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(), ast_conference::bookid, buf, c, 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, exitcontext, f, ast_frame_subclass::format, ast_frame::frametype, ast_conference::gmuted, ast_frame_subclass::integer, ast_conference::isdynamic, item, 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(), meetme_stasis_generate_msg(), MENU_ADMIN, MENU_DISABLED, MENU_NORMAL, ast_variable::name, announce_listitem::namerecloc, ast_conf_user::namerecloc, ast_variable::next, NULL, 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, RAII_VAR, ast_conference::recordingfilename, ast_conference::recordingformat, ast_conference::recordthread, ast_conference::recordthreadlock, reset_volumes(), RQ_UINTEGER1, RQ_UINTEGER2, RQ_UINTEGER3, RQ_UINTEGER4, ast_frame::samples, set_talk_volume(), set_user_talking(), SLA_EVENT_HOLD, sla_queue_event_conf(), ast_conf_user::start_time, status_to_json(), strsep(), ast_frame::subclass, ast_conf_user::talk, ast_conf_user::talking, THRESHOLD_SILENCE, ast_conf_user::timelimit, timeout, 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_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().

03172 {
03173    struct ast_conf_user *user = NULL;
03174    int fd;
03175    struct dahdi_confinfo dahdic, dahdic_empty;
03176    struct ast_frame *f;
03177    struct ast_channel *c;
03178    struct ast_frame fr;
03179    int outfd;
03180    int ms;
03181    int nfds;
03182    int res;
03183    int retrydahdi;
03184    int origfd;
03185    int musiconhold = 0, mohtempstopped = 0;
03186    int firstpass = 0;
03187    int lastmarked = 0;
03188    int currentmarked = 0;
03189    int ret = -1;
03190    int x;
03191    enum menu_modes menu_mode = MENU_DISABLED;
03192    int talkreq_manager = 0;
03193    int using_pseudo = 0;
03194    int duration = 20;
03195    int sent_event = 0;
03196    int checked = 0;
03197    int announcement_played = 0;
03198    struct timeval now;
03199    struct ast_dsp *dsp = NULL;
03200    struct ast_app *agi_app;
03201    char *agifile, *mod_speex;
03202    const char *agifiledefault = "conf-background.agi", *tmpvar;
03203    char meetmesecs[30] = "";
03204    char exitcontext[AST_MAX_CONTEXT] = "";
03205    char recordingtmp[AST_MAX_EXTENSION] = "";
03206    char members[10] = "";
03207    int dtmf = 0, opt_waitmarked_timeout = 0;
03208    time_t timeout = 0;
03209    struct dahdi_bufferinfo bi;
03210    char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
03211    char *buf = __buf + AST_FRIENDLY_OFFSET;
03212    char *exitkeys = NULL;
03213    unsigned int calldurationlimit = 0;
03214    long timelimit = 0;
03215    long play_warning = 0;
03216    long warning_freq = 0;
03217    const char *warning_sound = NULL;
03218    const char *end_sound = NULL;
03219    char *parse;
03220    long time_left_ms = 0;
03221    struct timeval nexteventts = { 0, };
03222    int to;
03223    int setusercount = 0;
03224    int confsilence = 0, totalsilence = 0;
03225    char *mailbox, *context;
03226    struct ast_format_cap *cap_slin = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
03227 
03228    if (!cap_slin) {
03229       goto conf_run_cleanup;
03230    }
03231    ast_format_cap_append(cap_slin, ast_format_slin, 0);
03232 
03233    if (!(user = ao2_alloc(sizeof(*user), NULL))) {
03234       goto conf_run_cleanup;
03235    }
03236 
03237    /* Possible timeout waiting for marked user */
03238    if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
03239       !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
03240       (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
03241       (opt_waitmarked_timeout > 0)) {
03242       timeout = time(NULL) + opt_waitmarked_timeout;
03243    }
03244 
03245    if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
03246       calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
03247       ast_verb(3, "Setting call duration limit to %u seconds.\n", calldurationlimit);
03248    }
03249 
03250    if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
03251       char *limit_str, *warning_str, *warnfreq_str;
03252       const char *var;
03253 
03254       parse = optargs[OPT_ARG_DURATION_LIMIT];
03255       limit_str = strsep(&parse, ":");
03256       warning_str = strsep(&parse, ":");
03257       warnfreq_str = parse;
03258 
03259       timelimit = atol(limit_str);
03260       if (warning_str)
03261          play_warning = atol(warning_str);
03262       if (warnfreq_str)
03263          warning_freq = atol(warnfreq_str);
03264 
03265       if (!timelimit) {
03266          timelimit = play_warning = warning_freq = 0;
03267          warning_sound = NULL;
03268       } else if (play_warning > timelimit) {
03269          if (!warning_freq) {
03270             play_warning = 0;
03271          } else {
03272             while (play_warning > timelimit)
03273                play_warning -= warning_freq;
03274             if (play_warning < 1)
03275                play_warning = warning_freq = 0;
03276          }
03277       }
03278 
03279       ast_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
03280       if (play_warning) {
03281          ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
03282       }
03283       if (warning_freq) {
03284          ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
03285       }
03286 
03287       ast_channel_lock(chan);
03288       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
03289          var = ast_strdupa(var);
03290       }
03291       ast_channel_unlock(chan);
03292 
03293       warning_sound = var ? var : "timeleft";
03294 
03295       ast_channel_lock(chan);
03296       if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
03297          var = ast_strdupa(var);
03298       }
03299       ast_channel_unlock(chan);
03300 
03301       end_sound = var ? var : NULL;
03302 
03303       /* undo effect of S(x) in case they are both used */
03304       calldurationlimit = 0;
03305       /* more efficient do it like S(x) does since no advanced opts */
03306       if (!play_warning && !end_sound && timelimit) {
03307          calldurationlimit = timelimit / 1000;
03308          timelimit = play_warning = warning_freq = 0;
03309       } else {
03310          ast_debug(2, "Limit Data for this call:\n");
03311          ast_debug(2, "- timelimit     = %ld\n", timelimit);
03312          ast_debug(2, "- play_warning  = %ld\n", play_warning);
03313          ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
03314          ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
03315          ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
03316       }
03317    }
03318 
03319    /* Get exit keys */
03320    if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
03321       if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
03322          exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
03323       else
03324          exitkeys = ast_strdupa("#"); /* Default */
03325    }
03326    
03327    if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
03328       if (!conf->recordingfilename) {
03329          const char *var;
03330          ast_channel_lock(chan);
03331          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03332             conf->recordingfilename = ast_strdup(var);
03333          }
03334          if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03335             conf->recordingformat = ast_strdup(var);
03336          }
03337          ast_channel_unlock(chan);
03338          if (!conf->recordingfilename) {
03339             snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
03340             conf->recordingfilename = ast_strdup(recordingtmp);
03341          }
03342          if (!conf->recordingformat) {
03343             conf->recordingformat = ast_strdup("wav");
03344          }
03345          ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
03346                 conf->confno, conf->recordingfilename, conf->recordingformat);
03347       }
03348    }
03349 
03350    ast_mutex_lock(&conf->recordthreadlock);
03351    if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
03352       ((conf->lchan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL)))) {
03353       ast_set_read_format(conf->lchan, ast_format_slin);
03354       ast_set_write_format(conf->lchan, ast_format_slin);
03355       dahdic.chan = 0;
03356       dahdic.confno = conf->dahdiconf;
03357       dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
03358       if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
03359          ast_log(LOG_WARNING, "Error starting listen channel\n");
03360          ast_hangup(conf->lchan);
03361          conf->lchan = NULL;
03362       } else {
03363          ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
03364       }
03365    }
03366    ast_mutex_unlock(&conf->recordthreadlock);
03367 
03368    ast_mutex_lock(&conf->announcethreadlock);
03369    if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03370       ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
03371       ast_mutex_init(&conf->announcelistlock);
03372       AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
03373       ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
03374    }
03375    ast_mutex_unlock(&conf->announcethreadlock);
03376 
03377    time(&user->jointime);
03378    
03379    user->timelimit = timelimit;
03380    user->play_warning = play_warning;
03381    user->warning_freq = warning_freq;
03382    user->warning_sound = warning_sound;
03383    user->end_sound = end_sound;  
03384    
03385    if (calldurationlimit > 0) {
03386       time(&user->kicktime);
03387       user->kicktime = user->kicktime + calldurationlimit;
03388    }
03389    
03390    if (ast_tvzero(user->start_time))
03391       user->start_time = ast_tvnow();
03392    time_left_ms = user->timelimit;
03393    
03394    if (user->timelimit) {
03395       nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03396       nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
03397    }
03398 
03399    if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
03400       /* Sorry, but this conference is locked! */  
03401       if (!ast_streamfile(chan, "conf-locked", ast_channel_language(chan)))
03402          ast_waitstream(chan, "");
03403       goto outrun;
03404    }
03405 
03406       ast_mutex_lock(&conf->playlock);
03407 
03408    if (rt_schedule && conf->maxusers) {
03409       if (conf->users >= conf->maxusers) {
03410          /* Sorry, but this confernce has reached the participant limit! */   
03411          ast_mutex_unlock(&conf->playlock);
03412          if (!ast_streamfile(chan, "conf-full", ast_channel_language(chan)))
03413             ast_waitstream(chan, "");
03414          goto outrun;
03415       }
03416    }
03417 
03418    ao2_lock(conf->usercontainer);
03419    ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
03420    user->user_no++;
03421    ao2_link(conf->usercontainer, user);
03422    ao2_unlock(conf->usercontainer);
03423 
03424    user->chan = chan;
03425    user->userflags = *confflags;
03426    user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
03427    user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
03428    user->talking = -1;
03429 
03430    ast_mutex_unlock(&conf->playlock);
03431 
03432    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
03433       char destdir[PATH_MAX];
03434 
03435       snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
03436 
03437       if (ast_mkdir(destdir, 0777) != 0) {
03438          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
03439          goto outrun;
03440       }
03441 
03442       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
03443          context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
03444          mailbox = strsep(&context, "@");
03445 
03446          if (ast_strlen_zero(mailbox)) {
03447             /* invalid input, clear the v flag*/
03448             ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
03449             ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
03450          } else {
03451             if (ast_strlen_zero(context)) {
03452                 context = "default";
03453             }
03454             /* if there is no mailbox we don't need to do this logic  */
03455             snprintf(user->namerecloc, sizeof(user->namerecloc),
03456                 "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
03457 
03458             /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
03459             if (!ast_fileexists(user->namerecloc, NULL, NULL)){
03460                snprintf(user->namerecloc, sizeof(user->namerecloc),
03461                    "%s/meetme-username-%s-%d", destdir,
03462                    conf->confno, user->user_no);
03463                ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
03464             }
03465          }
03466       } else {
03467          snprintf(user->namerecloc, sizeof(user->namerecloc),
03468              "%s/meetme-username-%s-%d", destdir,
03469              conf->confno, user->user_no);
03470       }
03471 
03472       res = 0;
03473       if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
03474          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);
03475       else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
03476          res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
03477       if (res == -1)
03478          goto outrun;
03479 
03480    }
03481 
03482    ast_mutex_lock(&conf->playlock);
03483 
03484    if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
03485       conf->markedusers++;
03486    conf->users++;
03487    if (rt_log_members) {
03488       /* Update table */
03489       snprintf(members, sizeof(members), "%d", conf->users);
03490       ast_realtime_require_field("meetme",
03491          "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
03492          "members", RQ_UINTEGER1, strlen(members),
03493          NULL);
03494       ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
03495    }
03496    setusercount = 1;
03497 
03498    /* This device changed state now - if this is the first user */
03499    if (conf->users == 1)
03500       ast_devstate_changed(AST_DEVICE_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
03501 
03502    ast_mutex_unlock(&conf->playlock);
03503 
03504    /* return the unique ID of the conference */
03505    pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
03506 
03507    if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
03508       ast_channel_lock(chan);
03509       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
03510          ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
03511       } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
03512          ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
03513       } else {
03514          ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
03515       }
03516       ast_channel_unlock(chan);
03517    }
03518 
03519    /* Play an arbitrary intro message */
03520    if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
03521          !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
03522       if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], ast_channel_language(chan))) {
03523          ast_waitstream(chan, "");
03524       }
03525    }
03526 
03527    if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
03528       if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
03529          if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan)))
03530             ast_waitstream(chan, "");
03531       if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
03532          if (!ast_streamfile(chan, "conf-waitforleader", ast_channel_language(chan)))
03533             ast_waitstream(chan, "");
03534    }
03535 
03536    if (ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
03537       int keepplaying = 1;
03538 
03539       if (conf->users == 2) { 
03540          if (!ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
03541             res = ast_waitstream(chan, AST_DIGIT_ANY);
03542             ast_stopstream(chan);
03543             if (res > 0)
03544                keepplaying = 0;
03545             else if (res == -1)
03546                goto outrun;
03547          }
03548       } else { 
03549          if (!ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
03550             res = ast_waitstream(chan, AST_DIGIT_ANY);
03551             ast_stopstream(chan);
03552             if (res > 0)
03553                keepplaying = 0;
03554             else if (res == -1)
03555                goto outrun;
03556          }
03557          if (keepplaying) {
03558             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03559             if (res > 0)
03560                keepplaying = 0;
03561             else if (res == -1)
03562                goto outrun;
03563          }
03564          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
03565             res = ast_waitstream(chan, AST_DIGIT_ANY);
03566             ast_stopstream(chan);
03567             if (res > 0)
03568                keepplaying = 0;
03569             else if (res == -1) 
03570                goto outrun;
03571          }
03572       }
03573    }
03574 
03575    if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
03576       /* We're leaving this alone until the state gets changed to up */
03577       ast_indicate(chan, -1);
03578    }
03579 
03580    if (ast_set_write_format(chan, ast_format_slin) < 0) {
03581       ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", ast_channel_name(chan));
03582       goto outrun;
03583    }
03584 
03585    if (ast_set_read_format(chan, ast_format_slin) < 0) {
03586       ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", ast_channel_name(chan));
03587       goto outrun;
03588    }
03589 
03590    /* Reduce background noise from each participant */
03591    if (!ast_test_flag64(confflags, CONFFLAG_DONT_DENOISE) &&
03592       (mod_speex = ast_module_helper("", "func_speex", 0, 0, 0, 0))) {
03593       ast_free(mod_speex);
03594       ast_func_write(chan, "DENOISE(rx)", "on");
03595    }
03596 
03597    retrydahdi = (strcasecmp(ast_channel_tech(chan)->type, "DAHDI") || (ast_channel_audiohooks(chan) || ast_channel_monitor(chan)) ? 1 : 0);
03598    user->dahdichannel = !retrydahdi;
03599 
03600  dahdiretry:
03601    origfd = ast_channel_fd(chan, 0);
03602    if (retrydahdi) {
03603       /* open pseudo in non-blocking mode */
03604       fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
03605       if (fd < 0) {
03606          ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
03607          goto outrun;
03608       }
03609       using_pseudo = 1;
03610       /* Setup buffering information */
03611       memset(&bi, 0, sizeof(bi));
03612       bi.bufsize = CONF_SIZE / 2;
03613       bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
03614       bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
03615       bi.numbufs = audio_buffers;
03616       if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
03617          ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
03618          close(fd);
03619          goto outrun;
03620       }
03621       x = 1;
03622       if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
03623          ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
03624          close(fd);
03625          goto outrun;
03626       }
03627       nfds = 1;
03628    } else {
03629       /* XXX Make sure we're not running on a pseudo channel XXX */
03630       fd = ast_channel_fd(chan, 0);
03631       nfds = 0;
03632    }
03633    memset(&dahdic, 0, sizeof(dahdic));
03634    memset(&dahdic_empty, 0, sizeof(dahdic_empty));
03635    /* Check to see if we're in a conference... */
03636    dahdic.chan = 0;
03637    if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
03638       ast_log(LOG_WARNING, "Error getting conference\n");
03639       close(fd);
03640       goto outrun;
03641    }
03642    if (dahdic.confmode) {
03643       /* Whoa, already in a conference...  Retry... */
03644       if (!retrydahdi) {
03645          ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
03646          retrydahdi = 1;
03647          goto dahdiretry;
03648       }
03649    }
03650    memset(&dahdic, 0, sizeof(dahdic));
03651    /* Add us to the conference */
03652    dahdic.chan = 0;
03653    dahdic.confno = conf->dahdiconf;
03654 
03655    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
03656          ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
03657       struct announce_listitem *item;
03658       if (!(item = ao2_alloc(sizeof(*item), NULL)))
03659          goto outrun;
03660       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
03661       ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
03662       item->confchan = conf->chan;
03663       item->confusers = conf->users;
03664       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
03665          item->vmrec = 1;
03666       }
03667       item->announcetype = CONF_HASJOIN;
03668       ast_mutex_lock(&conf->announcelistlock);
03669       ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
03670       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
03671       ast_cond_signal(&conf->announcelist_addition);
03672       ast_mutex_unlock(&conf->announcelistlock);
03673 
03674       while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
03675          ;
03676       }
03677       ao2_ref(item, -1);
03678    }
03679 
03680    if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
03681       dahdic.confmode = DAHDI_CONF_CONF;
03682    else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
03683       dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03684    else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
03685       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03686    else
03687       dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03688 
03689    if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03690       ast_log(LOG_WARNING, "Error setting conference\n");
03691       close(fd);
03692       goto outrun;
03693    }
03694    ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf);
03695 
03696    if (!sent_event) {
03697       meetme_stasis_generate_msg(conf, chan, user, meetme_join_type(), NULL);
03698       sent_event = 1;
03699    }
03700 
03701    if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
03702       !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
03703       firstpass = 1;
03704       if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
03705          if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03706             (conf->markedusers >= 1))) {
03707             conf_play(chan, conf, ENTER);
03708          }
03709    }
03710 
03711    conf_flush(fd, chan);
03712 
03713    if (dsp)
03714       ast_dsp_free(dsp);
03715 
03716    if (!(dsp = ast_dsp_new())) {
03717       ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
03718       res = -1;
03719    }
03720 
03721    if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
03722       /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
03723          or use default filename of conf-background.agi */
03724 
03725       ast_channel_lock(chan);
03726       if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
03727          agifile = ast_strdupa(tmpvar);
03728       } else {
03729          agifile = ast_strdupa(agifiledefault);
03730       }
03731       ast_channel_unlock(chan);
03732       
03733       if (user->dahdichannel) {
03734          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
03735          x = 1;
03736          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03737       }
03738       /* Find a pointer to the agi app and execute the script */
03739       agi_app = pbx_findapp("agi");
03740       if (agi_app) {
03741          ret = pbx_exec(chan, agi_app, agifile);
03742       } else {
03743          ast_log(LOG_WARNING, "Could not find application (agi)\n");
03744          ret = -2;
03745       }
03746       if (user->dahdichannel) {
03747          /*  Remove CONFMUTE mode on DAHDI channel */
03748          x = 0;
03749          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03750       }
03751    } else {
03752       int lastusers = conf->users;
03753       if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
03754          /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
03755          x = 1;
03756          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
03757       }
03758 
03759       for (;;) {
03760          int menu_was_active = 0;
03761 
03762          outfd = -1;
03763          ms = -1;
03764          now = ast_tvnow();
03765 
03766          if (rt_schedule && conf->endtime) {
03767             char currenttime[32];
03768             long localendtime = 0;
03769             int extended = 0;
03770             struct ast_tm tm;
03771             struct ast_variable *var, *origvar;
03772             struct timeval tmp;
03773 
03774             if (now.tv_sec % 60 == 0) {
03775                if (!checked) {
03776                   ast_localtime(&now, &tm, NULL);
03777                   ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
03778                   var = origvar = ast_load_realtime("meetme", "confno",
03779                      conf->confno, "starttime <=", currenttime,
03780                       "endtime >=", currenttime, NULL);
03781 
03782                   for ( ; var; var = var->next) {
03783                      if (!strcasecmp(var->name, "endtime")) {
03784                         struct ast_tm endtime_tm;
03785                         ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
03786                         tmp = ast_mktime(&endtime_tm, NULL);
03787                         localendtime = tmp.tv_sec;
03788                      }
03789                   }
03790                   ast_variables_destroy(origvar);
03791 
03792                   /* A conference can be extended from the
03793                      Admin/User menu or by an external source */
03794                   if (localendtime > conf->endtime){
03795                      conf->endtime = localendtime;
03796                      extended = 1;
03797                   }
03798 
03799                   if (conf->endtime && (now.tv_sec >= conf->endtime)) {
03800                      ast_verbose("Quitting time...\n");
03801                      goto outrun;
03802                   }
03803 
03804                   if (!announcement_played && conf->endalert) {
03805                      if (now.tv_sec + conf->endalert >= conf->endtime) {
03806                         if (!ast_streamfile(chan, "conf-will-end-in", ast_channel_language(chan)))
03807                            ast_waitstream(chan, "");
03808                         ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", ast_channel_language(chan));
03809                         if (!ast_streamfile(chan, "minutes", ast_channel_language(chan)))
03810                            ast_waitstream(chan, "");
03811                         if (musiconhold) {
03812                            conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03813                         }
03814                         announcement_played = 1;
03815                      }
03816                   }
03817 
03818                   if (extended) {
03819                      announcement_played = 0;
03820                   }
03821 
03822                   checked = 1;
03823                }
03824             } else {
03825                checked = 0;
03826             }
03827          }
03828 
03829          if (user->kicktime && (user->kicktime <= now.tv_sec)) {
03830             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03831                ret = 0;
03832             } else {
03833                ret = -1;
03834             }
03835             break;
03836          }
03837   
03838          to = -1;
03839          if (user->timelimit) {
03840             int minutes = 0, seconds = 0, remain = 0;
03841  
03842             to = ast_tvdiff_ms(nexteventts, now);
03843             if (to < 0) {
03844                to = 0;
03845             }
03846             time_left_ms = user->timelimit - ast_tvdiff_ms(now, user->start_time);
03847             if (time_left_ms < to) {
03848                to = time_left_ms;
03849             }
03850    
03851             if (time_left_ms <= 0) {
03852                if (user->end_sound) {                 
03853                   res = ast_streamfile(chan, user->end_sound, ast_channel_language(chan));
03854                   res = ast_waitstream(chan, "");
03855                }
03856                if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03857                   ret = 0;
03858                } else {
03859                   ret = -1;
03860                }
03861                break;
03862             }
03863             
03864             if (!to) {
03865                if (time_left_ms >= 5000) {                  
03866                   
03867                   remain = (time_left_ms + 500) / 1000;
03868                   if (remain / 60 >= 1) {
03869                      minutes = remain / 60;
03870                      seconds = remain % 60;
03871                   } else {
03872                      seconds = remain;
03873                   }
03874                   
03875                   /* force the time left to round up if appropriate */
03876                   if (user->warning_sound && user->play_warning) {
03877                      if (!strcmp(user->warning_sound, "timeleft")) {
03878                         
03879                         res = ast_streamfile(chan, "vm-youhave", ast_channel_language(chan));
03880                         res = ast_waitstream(chan, "");
03881                         if (minutes) {
03882                            res = ast_say_number(chan, minutes, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03883                            res = ast_streamfile(chan, "queue-minutes", ast_channel_language(chan));
03884                            res = ast_waitstream(chan, "");
03885                         }
03886                         if (seconds) {
03887                            res = ast_say_number(chan, seconds, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03888                            res = ast_streamfile(chan, "queue-seconds", ast_channel_language(chan));
03889                            res = ast_waitstream(chan, "");
03890                         }
03891                      } else {
03892                         res = ast_streamfile(chan, user->warning_sound, ast_channel_language(chan));
03893                         res = ast_waitstream(chan, "");
03894                      }
03895                      if (musiconhold) {
03896                         conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03897                      }
03898                   }
03899                }
03900                if (user->warning_freq) {
03901                   nexteventts = ast_tvadd(nexteventts, ast_samp2tv(user->warning_freq, 1000));
03902                } else {
03903                   nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
03904                }
03905             }
03906          }
03907 
03908          now = ast_tvnow();
03909          if (timeout && now.tv_sec >= timeout) {
03910             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03911                ret = 0;
03912             } else {
03913                ret = -1;
03914             }
03915             break;
03916          }
03917 
03918          /* if we have just exited from the menu, and the user had a channel-driver
03919             volume adjustment, restore it
03920          */
03921          if (!menu_mode && menu_was_active && user->listen.desired && !user->listen.actual) {
03922             set_talk_volume(user, user->listen.desired);
03923          }
03924 
03925          menu_was_active = menu_mode;
03926 
03927          currentmarked = conf->markedusers;
03928          if (!ast_test_flag64(confflags, CONFFLAG_QUIET) &&
03929              ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
03930              ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
03931              lastmarked == 0) {
03932             if (currentmarked == 1 && conf->users > 1) {
03933                ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03934                if (conf->users - 1 == 1) {
03935                   if (!ast_streamfile(chan, "conf-userwilljoin", ast_channel_language(chan))) {
03936                      ast_waitstream(chan, "");
03937                   }
03938                } else {
03939                   if (!ast_streamfile(chan, "conf-userswilljoin", ast_channel_language(chan))) {
03940                      ast_waitstream(chan, "");
03941                   }
03942                }
03943             }
03944             if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
03945                if (!ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
03946                   ast_waitstream(chan, "");
03947                }
03948             }
03949          }
03950 
03951          /* Update the struct with the actual confflags */
03952          user->userflags = *confflags;
03953 
03954          if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
03955             if (currentmarked == 0) {
03956                if (lastmarked != 0) {
03957                   if (!ast_test_flag64(confflags, CONFFLAG_QUIET)) {
03958                      if (!ast_streamfile(chan, "conf-leaderhasleft", ast_channel_language(chan))) {
03959                         ast_waitstream(chan, "");
03960                      }
03961                   }
03962                   if (ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
03963                      if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
03964                         ret = 0;
03965                      }
03966                      break;
03967                   } else {
03968                      dahdic.confmode = DAHDI_CONF_CONF;
03969                      if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03970                         ast_log(LOG_WARNING, "Error setting conference\n");
03971                         close(fd);
03972                         goto outrun;
03973                      }
03974                   }
03975                }
03976                if (!musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03977                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
03978                   musiconhold = 1;
03979                }
03980             } else if (currentmarked >= 1 && lastmarked == 0) {
03981                /* Marked user entered, so cancel timeout */
03982                timeout = 0;
03983                if (ast_test_flag64(confflags, CONFFLAG_MONITOR)) {
03984                   dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
03985                } else if (ast_test_flag64(confflags, CONFFLAG_TALKER)) {
03986                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
03987                } else {
03988                   dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
03989                }
03990                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
03991                   ast_log(LOG_WARNING, "Error setting conference\n");
03992                   close(fd);
03993                   goto outrun;
03994                }
03995                if (musiconhold && (ast_test_flag64(confflags, CONFFLAG_MOH))) {
03996                   ast_moh_stop(chan);
03997                   musiconhold = 0;
03998                }
03999                if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
04000                   !ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
04001                   if (!ast_streamfile(chan, "conf-placeintoconf", ast_channel_language(chan))) {
04002                      ast_waitstream(chan, "");
04003                   }
04004                   conf_play(chan, conf, ENTER);
04005                }
04006             }
04007          }
04008 
04009          /* trying to add moh for single person conf */
04010          if (ast_test_flag64(confflags, CONFFLAG_MOH) && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)) {
04011             if (conf->users == 1) {
04012                if (!musiconhold) {
04013                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04014                   musiconhold = 1;
04015                } 
04016             } else {
04017                if (musiconhold) {
04018                   ast_moh_stop(chan);
04019                   musiconhold = 0;
04020                }
04021             }
04022          }
04023          
04024          /* Leave if the last marked user left */
04025          if (currentmarked == 0 && lastmarked != 0 && ast_test_flag64(confflags, CONFFLAG_MARKEDEXIT)) {
04026             if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
04027                ret = 0;
04028             } else {
04029                ret = -1;
04030             }
04031             break;
04032          }
04033 
04034          /* Throw a TestEvent if a user exit did not cause this user to leave the conference */
04035          if (conf->users != lastusers) {
04036             if (conf->users < lastusers) {
04037                ast_test_suite_event_notify("NOEXIT", "Message: CONFFLAG_MARKEDEXIT\r\nLastUsers: %d\r\nUsers: %d", lastusers, conf->users);
04038             }
04039             lastusers = conf->users;
04040          }
04041 
04042          /* Check if my modes have changed */
04043 
04044          /* If I should be muted but am still talker, mute me */
04045          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && (dahdic.confmode & DAHDI_CONF_TALKER)) {
04046             RAII_VAR(struct ast_json *, status_blob, status_to_json(1), ast_json_unref);
04047             dahdic.confmode ^= DAHDI_CONF_TALKER;
04048             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04049                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
04050                ret = -1;
04051                break;
04052             }
04053 
04054             /* Indicate user is not talking anymore - change him to unmonitored state */
04055             if (ast_test_flag64(confflags,  (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
04056                set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
04057             }
04058             meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob);
04059          }
04060 
04061          /* If I should be un-muted but am not talker, un-mute me */
04062          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) && !(dahdic.confmode & DAHDI_CONF_TALKER)) {
04063             RAII_VAR(struct ast_json *, status_blob, status_to_json(0), ast_json_unref);
04064             dahdic.confmode |= DAHDI_CONF_TALKER;
04065             if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04066                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
04067                ret = -1;
04068                break;
04069             }
04070             meetme_stasis_generate_msg(conf, chan, user, meetme_mute_type(), status_blob);
04071          }
04072 
04073          if ((user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
04074             (user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
04075 
04076             RAII_VAR(struct ast_json *, status_blob, status_to_json(1), ast_json_unref);
04077             talkreq_manager = 1;
04078             meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob);
04079          }
04080 
04081          if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) && 
04082             !(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
04083             RAII_VAR(struct ast_json *, status_blob, status_to_json(0), ast_json_unref);
04084             talkreq_manager = 0;
04085             meetme_stasis_generate_msg(conf, chan, user, meetme_talk_request_type(), status_blob);
04086          }
04087 
04088          /* If user have been hung up, exit the conference */
04089          if (user->adminflags & ADMINFLAG_HANGUP) {
04090             ret = 0;
04091             break;
04092          }
04093 
04094          /* If I have been kicked, exit the conference */
04095          if (user->adminflags & ADMINFLAG_KICKME) {
04096             /* You have been kicked. */
04097             if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && 
04098                !ast_streamfile(chan, "conf-kicked", ast_channel_language(chan))) {
04099                ast_waitstream(chan, "");
04100             }
04101             ret = 0;
04102             break;
04103          }
04104 
04105          /* Perform a hangup check here since ast_waitfor_nandfds will not always be able to get a channel after a hangup has occurred */
04106          if (ast_check_hangup(chan)) {
04107             break;
04108          }
04109 
04110          c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
04111 
04112          if (c) {
04113             char dtmfstr[2] = "";
04114 
04115             if (ast_channel_fd(c, 0) != origfd || (user->dahdichannel && (ast_channel_audiohooks(c) || ast_channel_monitor(c)))) {
04116                if (using_pseudo) {
04117                   /* Kill old pseudo */
04118                   close(fd);
04119                   using_pseudo = 0;
04120                }
04121                ast_debug(1, "Ooh, something swapped out under us, starting over\n");
04122                retrydahdi = (strcasecmp(ast_channel_tech(c)->type, "DAHDI") || (ast_channel_audiohooks(c) || ast_channel_monitor(c)) ? 1 : 0);
04123                user->dahdichannel = !retrydahdi;
04124                goto dahdiretry;
04125             }
04126             if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
04127                f = ast_read_noaudio(c);
04128             } else {
04129                f = ast_read(c);
04130             }
04131             if (!f) {
04132                break;
04133             }
04134             if (f->frametype == AST_FRAME_DTMF) {
04135                dtmfstr[0] = f->subclass.integer;
04136                dtmfstr[1] = '\0';
04137             }
04138 
04139             if ((f->frametype == AST_FRAME_VOICE) && (ast_format_cmp(f->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL)) {
04140                if (user->talk.actual) {
04141                   ast_frame_adjust_volume(f, user->talk.actual);
04142                }
04143 
04144                if (ast_test_flag64(confflags, (CONFFLAG_OPTIMIZETALKER | CONFFLAG_MONITORTALKER))) {
04145                   if (user->talking == -1) {
04146                      user->talking = 0;
04147                   }
04148 
04149                   res = ast_dsp_silence(dsp, f, &totalsilence);
04150                   if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
04151                      set_user_talking(chan, conf, user, 1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
04152                   }
04153 
04154                   if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
04155                      set_user_talking(chan, conf, user, 0, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
04156                   }
04157                }
04158                if (using_pseudo) {
04159                   /* Absolutely do _not_ use careful_write here...
04160                      it is important that we read data from the channel
04161                      as fast as it arrives, and feed it into the conference.
04162                      The buffering in the pseudo channel will take care of any
04163                      timing differences, unless they are so drastic as to lose
04164                      audio frames (in which case carefully writing would only
04165                      have delayed the audio even further).
04166                   */
04167                   /* As it turns out, we do want to use careful write.  We just
04168                      don't want to block, but we do want to at least *try*
04169                      to write out all the samples.
04170                    */
04171                   if (user->talking || !ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER)) {
04172                      careful_write(fd, f->data.ptr, f->datalen, 0);
04173                   }
04174                }
04175             } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '*') && ast_test_flag64(confflags, CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_mode)) {
04176                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04177                   conf_queue_dtmf(conf, user, f);
04178                }
04179                /* Take out of conference */
04180                if (ioctl(fd, DAHDI_SETCONF, &dahdic_empty)) {
04181                   ast_log(LOG_WARNING, "Error setting conference\n");
04182                   close(fd);
04183                   ast_frfree(f);
04184                   goto outrun;
04185                }
04186 
04187                /* if we are entering the menu, and the user has a channel-driver
04188                   volume adjustment, clear it
04189                */
04190                if (!menu_mode && user->talk.desired && !user->talk.actual) {
04191                   set_talk_volume(user, 0);
04192                }
04193 
04194                if (musiconhold) {
04195                   ast_moh_stop(chan);
04196                } else if (!menu_mode) {
04197                   char *menu_to_play;
04198                   if (ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
04199                      menu_mode = MENU_ADMIN;
04200                      menu_to_play = "conf-adminmenu-18";
04201                   } else {
04202                      menu_mode = MENU_NORMAL;
04203                      menu_to_play = "conf-usermenu-162";
04204                   }
04205 
04206                   if (!ast_streamfile(chan, menu_to_play, ast_channel_language(chan))) {
04207                      dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
04208                      ast_stopstream(chan);
04209                   } else {
04210                      dtmf = 0;
04211                   }
04212                } else {
04213                   dtmf = f->subclass.integer;
04214                }
04215 
04216                if (dtmf > 0) {
04217                   meetme_menu(&menu_mode, &dtmf, conf, confflags,
04218                      chan, user, recordingtmp, sizeof(recordingtmp), cap_slin);
04219                }
04220 
04221                if (musiconhold && !menu_mode) {
04222                   conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04223                }
04224 
04225                /* Put back into conference */
04226                if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04227                   ast_log(LOG_WARNING, "Error setting conference\n");
04228                   close(fd);
04229                   ast_frfree(f);
04230                   goto outrun;
04231                }
04232 
04233                conf_flush(fd, chan);
04234             /*
04235              * Since options using DTMF could absorb DTMF meant for the
04236              * conference menu, we have to check them after the menu.
04237              */
04238             } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) {
04239                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04240                   conf_queue_dtmf(conf, user, f);
04241                }
04242 
04243                if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) {
04244                   ast_debug(1, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext);
04245                   ret = 0;
04246                   ast_frfree(f);
04247                   break;
04248                } else {
04249                   ast_debug(2, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", dtmfstr, exitcontext);
04250                }
04251             } else if ((f->frametype == AST_FRAME_DTMF) && ast_test_flag64(confflags, CONFFLAG_KEYEXIT) &&
04252                (strchr(exitkeys, f->subclass.integer))) {
04253                pbx_builtin_setvar_helper(chan, "MEETME_EXIT_KEY", dtmfstr);
04254 
04255                if (ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04256                   conf_queue_dtmf(conf, user, f);
04257                }
04258                ret = 0;
04259                ast_frfree(f);
04260                break;
04261             } else if ((f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END)
04262                && ast_test_flag64(confflags, CONFFLAG_PASS_DTMF)) {
04263                conf_queue_dtmf(conf, user, f);
04264             } else if (ast_test_flag64(confflags, CONFFLAG_SLA_STATION) && f->frametype == AST_FRAME_CONTROL) {
04265                switch (f->subclass.integer) {
04266                case AST_CONTROL_HOLD:
04267                   sla_queue_event_conf(SLA_EVENT_HOLD, chan, conf);
04268                   break;
04269                default:
04270                   break;
04271                }
04272             } else if (f->frametype == AST_FRAME_NULL) {
04273                /* Ignore NULL frames. It is perfectly normal to get these if the person is muted. */
04274             } else if (f->frametype == AST_FRAME_CONTROL) {
04275                switch (f->subclass.integer) {
04276                case AST_CONTROL_BUSY:
04277                case AST_CONTROL_CONGESTION:
04278                   ast_frfree(f);
04279                   goto outrun;
04280                   break;
04281                default:
04282                   ast_debug(1,
04283                      "Got ignored control frame on channel %s, f->frametype=%u,f->subclass=%d\n",
04284                      ast_channel_name(chan), f->frametype, f->subclass.integer);
04285                }
04286             } else {
04287                ast_debug(1,
04288                   "Got unrecognized frame on channel %s, f->frametype=%u,f->subclass=%d\n",
04289                   ast_channel_name(chan), f->frametype, f->subclass.integer);
04290             }
04291             ast_frfree(f);
04292          } else if (outfd > -1) {
04293             res = read(outfd, buf, CONF_SIZE);
04294             if (res > 0) {
04295                memset(&fr, 0, sizeof(fr));
04296                fr.frametype = AST_FRAME_VOICE;
04297                fr.subclass.format = ast_format_slin;
04298                fr.datalen = res;
04299                fr.samples = res / 2;
04300                fr.data.ptr = buf;
04301                fr.offset = AST_FRIENDLY_OFFSET;
04302                if (!user->listen.actual &&
04303                   (ast_test_flag64(confflags, CONFFLAG_MONITOR) ||
04304                    (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) ||
04305                    (!user->talking && ast_test_flag64(confflags, CONFFLAG_OPTIMIZETALKER))
04306                    )) {
04307                   int idx;
04308                   for (idx = 0; idx < AST_FRAME_BITS; idx++) {
04309                      if (ast_format_compatibility_format2bitfield(ast_channel_rawwriteformat(chan)) & (1 << idx)) {
04310                         break;
04311                      }
04312                   }
04313                   if (idx >= AST_FRAME_BITS) {
04314                      goto bailoutandtrynormal;
04315                   }
04316                   ast_mutex_lock(&conf->listenlock);
04317                   if (!conf->transframe[idx]) {
04318                      if (conf->origframe) {
04319                         if (musiconhold
04320                            && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
04321                            && !ast_dsp_silence(dsp, conf->origframe, &confsilence)
04322                            && confsilence < MEETME_DELAYDETECTTALK) {
04323                            ast_moh_stop(chan);
04324                            mohtempstopped = 1;
04325                         }
04326                         if (!conf->transpath[idx]) {
04327                            conf->transpath[idx] = ast_translator_build_path(ast_channel_rawwriteformat(chan), ast_format_slin);
04328                         }
04329                         if (conf->transpath[idx]) {
04330                            conf->transframe[idx] = ast_translate(conf->transpath[idx], conf->origframe, 0);
04331                            if (!conf->transframe[idx]) {
04332                               conf->transframe[idx] = &ast_null_frame;
04333                            }
04334                         }
04335                      }
04336                   }
04337                   if (conf->transframe[idx]) {
04338                      if ((conf->transframe[idx]->frametype != AST_FRAME_NULL) &&
04339                          can_write(chan, confflags)) {
04340                         struct ast_frame *cur;
04341                         /* the translator may have returned a list of frames, so
04342                            write each one onto the channel
04343                         */
04344                         for (cur = conf->transframe[idx]; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
04345                            if (ast_write(chan, cur)) {
04346                               ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
04347                               break;
04348                            }
04349                         }
04350                         if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
04351                            mohtempstopped = 0;
04352                            conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04353                         }
04354                      }
04355                   } else {
04356                      ast_mutex_unlock(&conf->listenlock);
04357                      goto bailoutandtrynormal;
04358                   }
04359                   ast_mutex_unlock(&conf->listenlock);
04360                } else {
04361 bailoutandtrynormal:
04362                   if (musiconhold
04363                      && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED)
04364                      && !ast_dsp_silence(dsp, &fr, &confsilence)
04365                      && confsilence < MEETME_DELAYDETECTTALK) {
04366                      ast_moh_stop(chan);
04367                      mohtempstopped = 1;
04368                   }
04369                   if (user->listen.actual) {
04370                      ast_frame_adjust_volume(&fr, user->listen.actual);
04371                   }
04372                   if (can_write(chan, confflags) && ast_write(chan, &fr) < 0) {
04373                      ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", ast_channel_name(chan));
04374                   }
04375                   if (musiconhold && mohtempstopped && confsilence > MEETME_DELAYDETECTENDTALK) {
04376                      mohtempstopped = 0;
04377                      conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
04378                   }
04379                }
04380             } else {
04381                ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
04382             }
04383          }
04384          lastmarked = currentmarked;
04385       }
04386    }
04387 
04388    if (musiconhold) {
04389       ast_moh_stop(chan);
04390    }
04391    
04392    if (using_pseudo) {
04393       close(fd);
04394    } else {
04395       /* Take out of conference */
04396       dahdic.chan = 0;  
04397       dahdic.confno = 0;
04398       dahdic.confmode = 0;
04399       if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
04400          ast_log(LOG_WARNING, "Error setting conference\n");
04401       }
04402    }
04403 
04404    reset_volumes(user);
04405 
04406    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
04407       !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
04408       conf_play(chan, conf, LEAVE);
04409    }
04410 
04411    if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_INTROUSER |CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC) && conf->users > 1) {
04412       struct announce_listitem *item;
04413       if (!(item = ao2_alloc(sizeof(*item), NULL)))
04414          goto outrun;
04415       ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
04416       ast_copy_string(item->language, ast_channel_language(chan), sizeof(item->language));
04417       item->confchan = conf->chan;
04418       item->confusers = conf->users;
04419       item->announcetype = CONF_HASLEFT;
04420       if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
04421          item->vmrec = 1;
04422       }
04423       ast_mutex_lock(&conf->announcelistlock);
04424       AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
04425       ast_cond_signal(&conf->announcelist_addition);
04426       ast_mutex_unlock(&conf->announcelistlock);
04427    } 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) {
04428       /* Last person is leaving, so no reason to try and announce, but should delete the name recording */
04429       ast_filedelete(user->namerecloc, NULL);
04430    }
04431 
04432  outrun:
04433    AST_LIST_LOCK(&confs);
04434 
04435    if (dsp) {
04436       ast_dsp_free(dsp);
04437    }
04438    
04439    if (user->user_no) {
04440       /* Only cleanup users who really joined! */
04441       now = ast_tvnow();
04442 
04443       if (sent_event) {
04444          meetme_stasis_generate_msg(conf, chan, user, meetme_leave_type(), NULL);
04445       }
04446 
04447       if (setusercount) {
04448          conf->users--;
04449          if (rt_log_members) {
04450             /* Update table */
04451             snprintf(members, sizeof(members), "%d", conf->users);
04452             ast_realtime_require_field("meetme",
04453                "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
04454                "members", RQ_UINTEGER1, strlen(members),
04455                NULL);
04456             ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
04457          }
04458          if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER)) {
04459             conf->markedusers--;
04460          }
04461       }
04462       /* Remove ourselves from the container */
04463       ao2_unlink(conf->usercontainer, user); 
04464 
04465       /* Change any states */
04466       if (!conf->users) {
04467          ast_devstate_changed(AST_DEVICE_NOT_INUSE, (conf->isdynamic ? AST_DEVSTATE_NOT_CACHABLE : AST_DEVSTATE_CACHABLE), "meetme:%s", conf->confno);
04468       }
04469 
04470       /* This flag is meant to kill a conference with only one participant remaining.  */
04471       if (conf->users == 1 && ast_test_flag64(confflags, CONFFLAG_KILL_LAST_MAN_STANDING)) {
04472          ao2_callback(conf->usercontainer, 0, user_set_hangup_cb, NULL);
04473       }
04474 
04475       /* Return the number of seconds the user was in the conf */
04476       snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
04477       pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
04478 
04479       /* Return the RealTime bookid for CDR linking */
04480       if (rt_schedule) {
04481          pbx_builtin_setvar_helper(chan, "MEETMEBOOKID", conf->bookid);
04482       }
04483    }
04484    ao2_ref(user, -1);
04485    AST_LIST_UNLOCK(&confs);
04486 
04487 
04488 conf_run_cleanup:
04489    ao2_cleanup(cap_slin);
04490 
04491    return ret;
04492 }

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

Definition at line 2579 of file app_meetme.c.

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

Referenced by conf_run().

02580 {
02581    char *original_moh;
02582 
02583    ast_channel_lock(chan);
02584    original_moh = ast_strdupa(ast_channel_musicclass(chan));
02585    ast_channel_musicclass_set(chan, musicclass);
02586    ast_channel_unlock(chan);
02587 
02588    ast_moh_start(chan, original_moh, NULL);
02589 
02590    ast_channel_lock(chan);
02591    ast_channel_musicclass_set(chan, original_moh);
02592    ast_channel_unlock(chan);
02593 }

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

The MeetmeCount application.

Definition at line 4786 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_strdupa, ast_strlen_zero, dispose_conf(), find_conf(), LOG_WARNING, NULL, pbx_builtin_setvar_helper(), and ast_conference::users.

Referenced by load_module().

04787 {
04788    int res = 0;
04789    struct ast_conference *conf;
04790    int count;
04791    char *localdata;
04792    char val[80] = "0"; 
04793    AST_DECLARE_APP_ARGS(args,
04794       AST_APP_ARG(confno);
04795       AST_APP_ARG(varname);
04796    );
04797 
04798    if (ast_strlen_zero(data)) {
04799       ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
04800       return -1;
04801    }
04802    
04803    localdata = ast_strdupa(data);
04804 
04805    AST_STANDARD_APP_ARGS(args, localdata);
04806    
04807    conf = find_conf(chan, args.confno, 0, 0, NULL, 0, 1, NULL);
04808 
04809    if (conf) {
04810       count = conf->users;
04811       dispose_conf(conf);
04812       conf = NULL;
04813    } else
04814       count = 0;
04815 
04816    if (!ast_strlen_zero(args.varname)) {
04817       /* have var so load it and exit */
04818       snprintf(val, sizeof(val), "%d", count);
04819       pbx_builtin_setvar_helper(chan, args.varname, val);
04820    } else {
04821       if (ast_channel_state(chan) != AST_STATE_UP) {
04822          ast_answer(chan);
04823       }
04824       res = ast_say_number(chan, count, "", ast_channel_language(chan), (char *) NULL); /* Needs gender */
04825    }
04826 
04827    return res;
04828 }

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

Definition at line 7159 of file app_meetme.c.

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

Referenced by sla_add_trunk_to_station().

07160 {
07161    struct sla_trunk_ref *trunk_ref;
07162 
07163    if (!(trunk_ref = ao2_alloc(sizeof(*trunk_ref), sla_trunk_ref_destructor))) {
07164       return NULL;
07165    }
07166 
07167    ao2_ref(trunk, 1);
07168    trunk_ref->trunk = trunk;
07169 
07170    return trunk_ref;
07171 }

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

Definition at line 6843 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, ast_strdupa, 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, sla_station::device, dispose_conf(), MAX_CONFNUM, NULL, RAII_VAR, sla, sla_change_trunk_state(), SLA_TRUNK_STATE_IDLE, dial_trunk_args::station, strsep(), and dial_trunk_args::trunk_ref.

Referenced by sla_station_exec().

06844 {
06845    struct dial_trunk_args *args = data;
06846    struct ast_dial *dial;
06847    char *tech, *tech_data;
06848    enum ast_dial_result dial_res;
06849    char conf_name[MAX_CONFNUM];
06850    struct ast_conference *conf;
06851    struct ast_flags64 conf_flags = { 0 };
06852    RAII_VAR(struct sla_trunk_ref *, trunk_ref, args->trunk_ref, ao2_cleanup);
06853    RAII_VAR(struct sla_station *, station, args->station, ao2_cleanup);
06854    int caller_is_saved;
06855    struct ast_party_caller caller;
06856    int last_state = 0;
06857    int current_state = 0;
06858 
06859    if (!(dial = ast_dial_create())) {
06860       ast_mutex_lock(args->cond_lock);
06861       ast_cond_signal(args->cond);
06862       ast_mutex_unlock(args->cond_lock);
06863       return NULL;
06864    }
06865 
06866    tech_data = ast_strdupa(trunk_ref->trunk->device);
06867    tech = strsep(&tech_data, "/");
06868    if (ast_dial_append(dial, tech, tech_data, NULL) == -1) {
06869       ast_mutex_lock(args->cond_lock);
06870       ast_cond_signal(args->cond);
06871       ast_mutex_unlock(args->cond_lock);
06872       ast_dial_destroy(dial);
06873       return NULL;
06874    }
06875 
06876    /* Do we need to save of the caller ID data? */
06877    caller_is_saved = 0;
06878    if (!sla.attempt_callerid) {
06879       caller_is_saved = 1;
06880       caller = *ast_channel_caller(trunk_ref->chan);
06881       ast_party_caller_init(ast_channel_caller(trunk_ref->chan));
06882    }
06883 
06884    dial_res = ast_dial_run(dial, trunk_ref->chan, 1);
06885 
06886    /* Restore saved caller ID */
06887    if (caller_is_saved) {
06888       ast_party_caller_free(ast_channel_caller(trunk_ref->chan));
06889       ast_channel_caller_set(trunk_ref->chan, &caller);
06890    }
06891 
06892    if (dial_res != AST_DIAL_RESULT_TRYING) {
06893       ast_mutex_lock(args->cond_lock);
06894       ast_cond_signal(args->cond);
06895       ast_mutex_unlock(args->cond_lock);
06896       ast_dial_destroy(dial);
06897       return NULL;
06898    }
06899 
06900    for (;;) {
06901       unsigned int done = 0;
06902       switch ((dial_res = ast_dial_state(dial))) {
06903       case AST_DIAL_RESULT_ANSWERED:
06904          trunk_ref->trunk->chan = ast_dial_answered(dial);
06905       case AST_DIAL_RESULT_HANGUP:
06906       case AST_DIAL_RESULT_INVALID:
06907       case AST_DIAL_RESULT_FAILED:
06908       case AST_DIAL_RESULT_TIMEOUT:
06909       case AST_DIAL_RESULT_UNANSWERED:
06910          done = 1;
06911          break;
06912       case AST_DIAL_RESULT_TRYING:
06913          current_state = AST_CONTROL_PROGRESS;
06914          break;
06915       case AST_DIAL_RESULT_RINGING:
06916       case AST_DIAL_RESULT_PROGRESS:
06917       case AST_DIAL_RESULT_PROCEEDING:
06918          current_state = AST_CONTROL_RINGING;
06919          break;
06920       }
06921       if (done)
06922          break;
06923 
06924       /* check that SLA station that originated trunk call is still alive */
06925       if (station && ast_device_state(station->device) == AST_DEVICE_NOT_INUSE) {
06926          ast_debug(3, "Originating station device %s no longer active\n", station->device);
06927          trunk_ref->trunk->chan = NULL;
06928          break;
06929       }
06930 
06931       /* If trunk line state changed, send indication back to originating SLA Station channel */
06932       if (current_state != last_state) {
06933          ast_debug(3, "Indicating State Change %d to channel %s\n", current_state, ast_channel_name(trunk_ref->chan));
06934          ast_indicate(trunk_ref->chan, current_state);
06935          last_state = current_state;
06936       }
06937 
06938       /* avoid tight loop... sleep for 1/10th second */
06939       ast_safe_sleep(trunk_ref->chan, 100);
06940    }
06941 
06942    if (!trunk_ref->trunk->chan) {
06943       ast_mutex_lock(args->cond_lock);
06944       ast_cond_signal(args->cond);
06945       ast_mutex_unlock(args->cond_lock);
06946       ast_dial_join(dial);
06947       ast_dial_destroy(dial);
06948       return NULL;
06949    }
06950 
06951    snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_ref->trunk->name);
06952    ast_set_flag64(&conf_flags, 
06953       CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | 
06954       CONFFLAG_PASS_DTMF | CONFFLAG_SLA_TRUNK);
06955    conf = build_conf(conf_name, "", "", 1, 1, 1, trunk_ref->trunk->chan, NULL);
06956 
06957    ast_mutex_lock(args->cond_lock);
06958    ast_cond_signal(args->cond);
06959    ast_mutex_unlock(args->cond_lock);
06960 
06961    if (conf) {
06962       conf_run(trunk_ref->trunk->chan, conf, &conf_flags, NULL);
06963       dispose_conf(conf);
06964       conf = NULL;
06965    }
06966 
06967    /* If the trunk is going away, it is definitely now IDLE. */
06968    sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
06969 
06970    trunk_ref->trunk->chan = NULL;
06971    trunk_ref->trunk->on_hold = 0;
06972 
06973    ast_dial_join(dial);
06974    ast_dial_destroy(dial);
06975 
06976    return NULL;
06977 }

static int dispose_conf ( struct ast_conference conf  )  [static]

Decrement reference counts, as incremented by find_conf().

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

02499 {
02500    int res = 0;
02501    int confno_int = 0;
02502 
02503    AST_LIST_LOCK(&confs);
02504    if (ast_atomic_dec_and_test(&conf->refcount)) {
02505       /* Take the conference room number out of an inuse state */
02506       if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
02507          conf_map[confno_int] = 0;
02508       }
02509       conf_free(conf);
02510       res = 1;
02511    }
02512    AST_LIST_UNLOCK(&confs);
02513 
02514    return res;
02515 }

static void filename_parse ( char *  filename,
char *  buffer 
) [static]

Definition at line 5656 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 mixmonitor_exec(), and recordthread().

05657 {
05658    char *slash;
05659    if (ast_strlen_zero(filename)) {
05660       ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
05661    } else if (filename[0] != '/') {
05662       snprintf(buffer, PATH_MAX, "%s/meetme/%s", ast_config_AST_SPOOL_DIR, filename);
05663    } else {
05664       ast_copy_string(buffer, filename, PATH_MAX);
05665    }
05666 
05667    slash = buffer;
05668    if ((slash = strrchr(slash, '/'))) {
05669       *slash = '\0';
05670       ast_mkdir(buffer, 0777);
05671       *slash = '/';
05672    }
05673 }

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 4682 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, NULL, parse(), ast_conference::refcount, S_OR, ast_variable::value, and var.

Referenced by conf_exec(), and count_exec().

04684 {
04685    struct ast_config *cfg;
04686    struct ast_variable *var;
04687    struct ast_flags config_flags = { 0 };
04688    struct ast_conference *cnf;
04689 
04690    AST_DECLARE_APP_ARGS(args,
04691       AST_APP_ARG(confno);
04692       AST_APP_ARG(pin);
04693       AST_APP_ARG(pinadmin);
04694    );
04695 
04696    /* Check first in the conference list */
04697    ast_debug(1, "The requested confno is '%s'?\n", confno);
04698    AST_LIST_LOCK(&confs);
04699    AST_LIST_TRAVERSE(&confs, cnf, list) {
04700       ast_debug(3, "Does conf %s match %s?\n", confno, cnf->confno);
04701       if (!strcmp(confno, cnf->confno))
04702          break;
04703    }
04704    if (cnf) {
04705       cnf->refcount += refcount;
04706    }
04707    AST_LIST_UNLOCK(&confs);
04708 
04709    if (!cnf) {
04710       if (dynamic) {
04711          /* No need to parse meetme.conf */
04712          ast_debug(1, "Building dynamic conference '%s'\n", confno);
04713          if (dynamic_pin) {
04714             if (dynamic_pin[0] == 'q') {
04715                /* Query the user to enter a PIN */
04716                if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, pin_buf_len - 1, 0) < 0)
04717                   return NULL;
04718             }
04719             cnf = build_conf(confno, dynamic_pin, "", make, dynamic, refcount, chan, NULL);
04720          } else {
04721             cnf = build_conf(confno, "", "", make, dynamic, refcount, chan, NULL);
04722          }
04723       } else {
04724          /* Check the config */
04725          cfg = ast_config_load(CONFIG_FILE_NAME, config_flags);
04726          if (!cfg) {
04727             ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
04728             return NULL;
04729          } else if (cfg == CONFIG_STATUS_FILEINVALID) {
04730             ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
04731             return NULL;
04732          }
04733 
04734          for (var = ast_variable_browse(cfg, "rooms"); var; var = var->next) {
04735             char parse[MAX_SETTINGS];
04736 
04737             if (strcasecmp(var->name, "conf"))
04738                continue;
04739 
04740             ast_copy_string(parse, var->value, sizeof(parse));
04741 
04742             AST_STANDARD_APP_ARGS(args, parse);
04743             ast_debug(3, "Will conf %s match %s?\n", confno, args.confno);
04744             if (!strcasecmp(args.confno, confno)) {
04745                /* Bingo it's a valid conference */
04746                cnf = build_conf(args.confno,
04747                      S_OR(args.pin, ""),
04748                      S_OR(args.pinadmin, ""),
04749                      make, dynamic, refcount, chan, NULL);
04750                break;
04751             }
04752          }
04753          if (!var) {
04754             ast_debug(1, "%s isn't a valid conference\n", confno);
04755          }
04756          ast_config_destroy(cfg);
04757       }
04758    } else if (dynamic_pin) {
04759       /* Correct for the user selecting 'D' instead of 'd' to have
04760          someone join into a conference that has already been created
04761          with a pin. */
04762       if (dynamic_pin[0] == 'q') {
04763          dynamic_pin[0] = '\0';
04764       }
04765    }
04766 
04767    if (cnf) {
04768       if (confflags && !cnf->chan &&
04769           !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04770           ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW  | CONFFLAG_INTROUSER_VMREC)) {
04771          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04772          ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
04773       }
04774       
04775       if (confflags && !cnf->chan &&
04776           ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04777          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04778          ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04779       }
04780    }
04781 
04782    return cnf;
04783 }

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 4494 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_strdupa, 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, ast_conference::endalert, ast_conference::endtime, ast_flags64::flags, ast_conference::list, LOG_WARNING, ast_conference::maxusers, meetme_opts, ast_variable::name, ast_variable::next, NULL, 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().

04496 {
04497    struct ast_variable *var, *origvar;
04498    struct ast_conference *cnf;
04499 
04500    *too_early = 0;
04501 
04502    /* Check first in the conference list */
04503    AST_LIST_LOCK(&confs);
04504    AST_LIST_TRAVERSE(&confs, cnf, list) {
04505       if (!strcmp(confno, cnf->confno)) {
04506          break;
04507       }
04508    }
04509    if (cnf) {
04510       cnf->refcount += refcount;
04511    }
04512    AST_LIST_UNLOCK(&confs);
04513 
04514    if (!cnf) {
04515       char *pin = NULL, *pinadmin = NULL; /* For temp use */
04516       int maxusers = 0;
04517       struct timeval now;
04518       char recordingfilename[256] = "";
04519       char recordingformat[11] = "";
04520       char currenttime[32] = "";
04521       char eatime[32] = "";
04522       char bookid[51] = "";
04523       char recordingtmp[AST_MAX_EXTENSION] = "";
04524       char useropts[OPTIONS_LEN + 1] = ""; /* Used for RealTime conferences */
04525       char adminopts[OPTIONS_LEN + 1] = "";
04526       struct ast_tm tm, etm;
04527       struct timeval endtime = { .tv_sec = 0 };
04528       const char *var2;
04529 
04530       if (rt_schedule) {
04531          now = ast_tvnow();
04532 
04533          ast_localtime(&now, &tm, NULL);
04534          ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04535 
04536          ast_debug(1, "Looking for conference %s that starts after %s\n", confno, currenttime);
04537 
04538          var = ast_load_realtime("meetme", "confno",
04539             confno, "starttime <= ", currenttime, "endtime >= ",
04540             currenttime, NULL);
04541 
04542          if (!var && fuzzystart) {
04543             now = ast_tvnow();
04544             now.tv_sec += fuzzystart;
04545 
04546             ast_localtime(&now, &tm, NULL);
04547             ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
04548             var = ast_load_realtime("meetme", "confno",
04549                confno, "starttime <= ", currenttime, "endtime >= ",
04550                currenttime, NULL);
04551          }
04552 
04553          if (!var && earlyalert) {
04554             now = ast_tvnow();
04555             now.tv_sec += earlyalert;
04556             ast_localtime(&now, &etm, NULL);
04557             ast_strftime(eatime, sizeof(eatime), DATE_FORMAT, &etm);
04558             var = ast_load_realtime("meetme", "confno",
04559                confno, "starttime <= ", eatime, "endtime >= ",
04560                currenttime, NULL);
04561             if (var) {
04562                *too_early = 1;
04563             }
04564          }
04565 
04566       } else {
04567           var = ast_load_realtime("meetme", "confno", confno, NULL);
04568       }
04569 
04570       if (!var) {
04571          return NULL;
04572       }
04573 
04574       if (rt_schedule && *too_early) {
04575          /* Announce that the caller is early and exit */
04576          if (!ast_streamfile(chan, "conf-has-not-started", ast_channel_language(chan))) {
04577             ast_waitstream(chan, "");
04578          }
04579          ast_variables_destroy(var);
04580          return NULL;
04581       }
04582 
04583       for (origvar = var; var; var = var->next) {
04584          if (!strcasecmp(var->name, "pin")) {
04585             pin = ast_strdupa(var->value);
04586          } else if (!strcasecmp(var->name, "adminpin")) {
04587             pinadmin = ast_strdupa(var->value);
04588          } else if (!strcasecmp(var->name, "bookId")) {
04589             ast_copy_string(bookid, var->value, sizeof(bookid));
04590          } else if (!strcasecmp(var->name, "opts")) {
04591             ast_copy_string(useropts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04592          } else if (!strcasecmp(var->name, "maxusers")) {
04593             maxusers = atoi(var->value);
04594          } else if (!strcasecmp(var->name, "adminopts")) {
04595             ast_copy_string(adminopts, var->value, sizeof(char[OPTIONS_LEN + 1]));
04596          } else if (!strcasecmp(var->name, "recordingfilename")) {
04597             ast_copy_string(recordingfilename, var->value, sizeof(recordingfilename));
04598          } else if (!strcasecmp(var->name, "recordingformat")) {
04599             ast_copy_string(recordingformat, var->value, sizeof(recordingformat));
04600          } else if (!strcasecmp(var->name, "endtime")) {
04601             struct ast_tm endtime_tm;
04602             ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
04603             endtime = ast_mktime(&endtime_tm, NULL);
04604          }
04605       }
04606 
04607       ast_variables_destroy(origvar);
04608 
04609       cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount, chan, NULL);
04610 
04611       if (cnf) {
04612          struct ast_flags64 tmp_flags;
04613 
04614          cnf->maxusers = maxusers;
04615          cnf->endalert = endalert;
04616          cnf->endtime = endtime.tv_sec;
04617          cnf->useropts = ast_strdup(useropts);
04618          cnf->adminopts = ast_strdup(adminopts);
04619          cnf->bookid = ast_strdup(bookid);
04620          if (!ast_strlen_zero(recordingfilename)) {
04621             cnf->recordingfilename = ast_strdup(recordingfilename);
04622          }
04623          if (!ast_strlen_zero(recordingformat)) {
04624             cnf->recordingformat = ast_strdup(recordingformat);
04625          }
04626 
04627          /* Parse the other options into confflags -- need to do this in two
04628           * steps, because the parse_options routine zeroes the buffer. */
04629          ast_app_parse_options64(meetme_opts, &tmp_flags, optargs, useropts);
04630          ast_copy_flags64(confflags, &tmp_flags, tmp_flags.flags);
04631 
04632          if (strchr(cnf->useropts, 'r')) {
04633             if (ast_strlen_zero(recordingfilename)) { /* If the recordingfilename in the database is empty, use the channel definition or use the default. */
04634                ast_channel_lock(chan);
04635                if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
04636                   ast_free(cnf->recordingfilename);
04637                   cnf->recordingfilename = ast_strdup(var2);
04638                }
04639                ast_channel_unlock(chan);
04640                if (ast_strlen_zero(cnf->recordingfilename)) {
04641                   snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", cnf->confno, ast_channel_uniqueid(chan));
04642                   ast_free(cnf->recordingfilename);
04643                   cnf->recordingfilename = ast_strdup(recordingtmp);
04644                }
04645             }
04646             if (ast_strlen_zero(cnf->recordingformat)) {/* If the recording format is empty, use the wav as default */
04647                ast_channel_lock(chan);
04648                if ((var2 = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
04649                   ast_free(cnf->recordingformat);
04650                   cnf->recordingformat = ast_strdup(var2);
04651                }
04652                ast_channel_unlock(chan);
04653                if (ast_strlen_zero(cnf->recordingformat)) {
04654                   ast_free(cnf->recordingformat);
04655                   cnf->recordingformat = ast_strdup("wav");
04656                }
04657             }
04658             ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n", cnf->confno, cnf->recordingfilename, cnf->recordingformat);
04659          }
04660       }
04661    }
04662 
04663    if (cnf) {
04664       if (confflags->flags && !cnf->chan &&
04665           !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
04666           ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW) | CONFFLAG_INTROUSER_VMREC) {
04667          ast_log(LOG_WARNING, "No DAHDI channel available for conference, user introduction disabled (is chan_dahdi loaded?)\n");
04668          ast_clear_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC);
04669       }
04670 
04671       if (confflags && !cnf->chan &&
04672           ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
04673          ast_log(LOG_WARNING, "No DAHDI channel available for conference, conference recording disabled (is chan_dahdi loaded?)\n");
04674          ast_clear_flag64(confflags, CONFFLAG_RECORDCONF);
04675       }
04676    }
04677 
04678    return cnf;
04679 }

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

Definition at line 5156 of file app_meetme.c.

References ao2_find, NULL, and ast_conference::usercontainer.

05157 {
05158    struct ast_conf_user *user = NULL;
05159    int cid;
05160 
05161    if (conf && callerident && sscanf(callerident, "%30d", &cid) == 1) {
05162       user = ao2_find(conf->usercontainer, &cid, 0);
05163       /* reference decremented later in admin_exec */
05164       return user;
05165    }
05166    return NULL;
05167 }

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

Definition at line 2595 of file app_meetme.c.

References CONF_HASJOIN, and CONF_HASLEFT.

Referenced by announce_thread().

02596 {
02597    switch (type) {
02598    case CONF_HASLEFT:
02599       return "conf-hasleft";
02600       break;
02601    case CONF_HASJOIN:
02602       return "conf-hasjoin";
02603       break;
02604    default:
02605       return "";
02606    }
02607 }

static const char* istalking ( int  x  )  [static]

Definition at line 1401 of file app_meetme.c.

Referenced by meetme_show_cmd().

01402 {
01403    if (x > 0)
01404       return "(talking)";
01405    else if (x < 0)
01406       return "(unmonitored)";
01407    else 
01408       return "(not talking)";
01409 }

static int load_config ( int  reload  )  [static]

Definition at line 8003 of file app_meetme.c.

References load_config_meetme(), and sla_load_config().

08004 {
08005    load_config_meetme(reload);
08006    return sla_load_config(reload);
08007 }

static void load_config_meetme ( int  reload  )  [static]

Definition at line 5779 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, LOG_WARNING, and meetme_set_defaults().

Referenced by load_config().

05780 {
05781    struct ast_config *cfg;
05782    struct ast_flags config_flags = { 0 };
05783    const char *val;
05784 
05785    if (!reload) {
05786       meetme_set_defaults();
05787    }
05788 
05789    if (!(cfg = ast_config_load(CONFIG_FILE_NAME, config_flags))) {
05790       return;
05791    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
05792       ast_log(LOG_ERROR, "Config file " CONFIG_FILE_NAME " is in an invalid format.  Aborting.\n");
05793       return;
05794    }
05795 
05796    if (reload) {
05797       meetme_set_defaults();
05798    }
05799 
05800    if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
05801       if ((sscanf(val, "%30d", &audio_buffers) != 1)) {
05802          ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
05803          audio_buffers = DEFAULT_AUDIO_BUFFERS;
05804       } else if ((audio_buffers < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) {
05805          ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
05806             DAHDI_DEFAULT_NUM_BUFS, DAHDI_MAX_NUM_BUFS);
05807          audio_buffers = DEFAULT_AUDIO_BUFFERS;
05808       }
05809       if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
05810          ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
05811    }
05812 
05813    if ((val = ast_variable_retrieve(cfg, "general", "schedule")))
05814       rt_schedule = ast_true(val);
05815    if ((val = ast_variable_retrieve(cfg, "general", "logmembercount")))
05816       rt_log_members = ast_true(val);
05817    if ((val = ast_variable_retrieve(cfg, "general", "fuzzystart"))) {
05818       if ((sscanf(val, "%30d", &fuzzystart) != 1)) {
05819          ast_log(LOG_WARNING, "fuzzystart must be a number, not '%s'\n", val);
05820          fuzzystart = 0;
05821       } 
05822    }
05823    if ((val = ast_variable_retrieve(cfg, "general", "earlyalert"))) {
05824       if ((sscanf(val, "%30d", &earlyalert) != 1)) {
05825          ast_log(LOG_WARNING, "earlyalert must be a number, not '%s'\n", val);
05826          earlyalert = 0;
05827       } 
05828    }
05829    if ((val = ast_variable_retrieve(cfg, "general", "endalert"))) {
05830       if ((sscanf(val, "%30d", &endalert) != 1)) {
05831          ast_log(LOG_WARNING, "endalert must be a number, not '%s'\n", val);
05832          endalert = 0;
05833       } 
05834    }
05835    if ((val = ast_variable_retrieve(cfg, "general", "extendby"))) {
05836       if ((sscanf(val, "%30d", &extendby) != 1)) {
05837          ast_log(LOG_WARNING, "extendby must be a number, not '%s'\n", val);
05838          extendby = 0;
05839       } 
05840    }
05841 
05842    ast_config_destroy(cfg);
05843 }

static int load_module ( void   )  [static]

Load the module.

Module loading including tests for configuration or dependencies. This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails tests return AST_MODULE_LOAD_FAILURE. If the module can not load the configuration file or other non-critical problem return AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.

Definition at line 8233 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(), meetme_stasis_init(), meetmestate(), NULL, RQ_UINTEGER1, RQ_UINTEGER2, sla_state(), sla_station_exec(), and sla_trunk_exec().

08234 {
08235    int res = 0;
08236 
08237    res |= load_config(0);
08238 
08239    res |= meetme_stasis_init();
08240 
08241    ast_cli_register_multiple(cli_meetme, ARRAY_LEN(cli_meetme));
08242    res |= ast_manager_register_xml("MeetmeMute", EVENT_FLAG_CALL, action_meetmemute);
08243    res |= ast_manager_register_xml("MeetmeUnmute", EVENT_FLAG_CALL, action_meetmeunmute);
08244    res |= ast_manager_register_xml("MeetmeList", EVENT_FLAG_REPORTING, action_meetmelist);
08245    res |= ast_manager_register_xml("MeetmeListRooms", EVENT_FLAG_REPORTING, action_meetmelistrooms);
08246    res |= ast_register_application_xml(app4, channel_admin_exec);
08247    res |= ast_register_application_xml(app3, admin_exec);
08248    res |= ast_register_application_xml(app2, count_exec);
08249    res |= ast_register_application_xml(app, conf_exec);
08250    res |= ast_register_application_xml(slastation_app, sla_station_exec);
08251    res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec);
08252 
08253 #ifdef TEST_FRAMEWORK
08254    AST_TEST_REGISTER(test_meetme_data_provider);
08255 #endif
08256    ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers));
08257 
08258    res |= ast_devstate_prov_add("Meetme", meetmestate);
08259    res |= ast_devstate_prov_add("SLA", sla_state);
08260 
08261    res |= ast_custom_function_register(&meetme_info_acf);
08262    ast_realtime_require_field("meetme", "confno", RQ_UINTEGER2, 3, "members", RQ_UINTEGER1, 3, NULL);
08263 
08264    return res;
08265 }

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

Definition at line 1993 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, MAX_CONFNUM, NULL, and strcasestr().

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

01994 {
01995    /* Process the command */
01996    struct ast_str *cmdline;
01997 
01998    /* Max confno length */
01999    if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
02000       return CLI_FAILURE;
02001    }
02002 
02003    ast_str_set(&cmdline, 0, "%s", a->argv[2]);  /* Argv 2: conference number */
02004    if (strcasestr(a->argv[1], "lock")) {
02005       if (strcasecmp(a->argv[1], "lock") == 0) {
02006          /* Lock */
02007          ast_str_append(&cmdline, 0, ",L");
02008       } else {
02009          /* Unlock */
02010          ast_str_append(&cmdline, 0, ",l");
02011       }
02012    } else if (strcasestr(a->argv[1], "mute")) { 
02013       if (strcasecmp(a->argv[1], "mute") == 0) {
02014          /* Mute */
02015          if (strcasecmp(a->argv[3], "all") == 0) {
02016             ast_str_append(&cmdline, 0, ",N");
02017          } else {
02018             ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);  
02019          }
02020       } else {
02021          /* Unmute */
02022          if (strcasecmp(a->argv[3], "all") == 0) {
02023             ast_str_append(&cmdline, 0, ",n");
02024          } else {
02025             ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
02026          }
02027       }
02028    } else if (strcasecmp(a->argv[1], "kick") == 0) {
02029       if (strcasecmp(a->argv[3], "all") == 0) {
02030          /* Kick all */
02031          ast_str_append(&cmdline, 0, ",K");
02032       } else {
02033          /* Kick a single user */
02034          ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
02035       }
02036    } else {
02037       /*
02038        * Should never get here because it is already filtered by the
02039        * callers.
02040        */
02041       ast_free(cmdline);
02042       return CLI_SHOWUSAGE;
02043    }
02044 
02045    ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
02046 
02047    admin_exec(NULL, ast_str_buffer(cmdline));
02048    ast_free(cmdline);
02049 
02050    return CLI_SUCCESS;
02051 }

static int meetme_data_provider_get ( const struct ast_data_search search,
struct ast_data data_root 
) [static]

Definition at line 8084 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.

08086 {
08087    struct ast_conference *cnf;
08088    struct ast_data *data_meetme, *data_meetme_users;
08089 
08090    AST_LIST_LOCK(&confs);
08091    AST_LIST_TRAVERSE(&confs, cnf, list) {
08092       data_meetme = ast_data_add_node(data_root, "meetme");
08093       if (!data_meetme) {
08094          continue;
08095       }
08096 
08097       ast_data_add_structure(ast_conference, data_meetme, cnf);
08098 
08099       if (ao2_container_count(cnf->usercontainer)) {
08100          data_meetme_users = ast_data_add_node(data_meetme, "users");
08101          if (!data_meetme_users) {
08102             ast_data_remove_node(data_root, data_meetme);
08103             continue;
08104          }
08105 
08106          ao2_callback(cnf->usercontainer, OBJ_NODATA, user_add_provider_cb, data_meetme_users); 
08107       }
08108 
08109       if (!ast_data_search_match(search, data_meetme)) {
08110          ast_data_remove_node(data_root, data_meetme);
08111       }
08112    }
08113    AST_LIST_UNLOCK(&confs);
08114 
08115    return 0;
08116 }

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

Definition at line 2073 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, NULL, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.

02074 {
02075    switch (cmd) {
02076    case CLI_INIT:
02077       e->command = "meetme kick";
02078       e->usage =
02079          "Usage: meetme kick <confno> all|<userno>\n"
02080          "       Kick a conference or a user in a conference.\n";
02081       return NULL;
02082    case CLI_GENERATE:
02083       return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
02084    }
02085 
02086    if (a->argc != 4) {
02087       return CLI_SHOWUSAGE;
02088    }
02089 
02090    return meetme_cmd_helper(a);
02091 }

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

Definition at line 2053 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, NULL, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.

02054 {
02055    switch (cmd) {
02056    case CLI_INIT:
02057       e->command = "meetme {lock|unlock}";
02058       e->usage =
02059          "Usage: meetme lock|unlock <confno>\n"
02060          "       Lock or unlock a conference to new users.\n";
02061       return NULL;
02062    case CLI_GENERATE:
02063       return complete_meetmecmd_lock(a->word, a->pos, a->n);
02064    }
02065 
02066    if (a->argc != 3) {
02067       return CLI_SHOWUSAGE;
02068    }
02069 
02070    return meetme_cmd_helper(a);
02071 }

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 ast_format_cap cap_slin 
) [static]

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

03151 {
03152    switch (*menu_mode) {
03153    case MENU_DISABLED:
03154       break;
03155    case MENU_NORMAL:
03156       meetme_menu_normal(menu_mode, dtmf, conf, confflags, chan, user);
03157       break;
03158    case MENU_ADMIN:
03159       meetme_menu_admin(menu_mode, dtmf, conf, confflags, chan, user);
03160       /* Admin Menu is capable of branching into another menu, in which case it will reset dtmf and change the menu mode. */
03161       if (*menu_mode != MENU_ADMIN_EXTENDED || (*dtmf <= 0)) {
03162          break;
03163       }
03164    case MENU_ADMIN_EXTENDED:
03165       meetme_menu_admin_extended(menu_mode, dtmf, conf, confflags, chan, user,
03166          recordingtmp, recordingtmp_size, cap_slin);
03167       break;
03168    }
03169 }

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 2840 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, NULL, 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().

02841 {
02842    switch(*dtmf) {
02843    case '1': /* Un/Mute */
02844       *menu_mode = MENU_DISABLED;
02845       /* for admin, change both admin and use flags */
02846       if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
02847          user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02848       } else {
02849          user->adminflags |= (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED);
02850       }
02851 
02852       if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02853          if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
02854             ast_waitstream(chan, "");
02855          }
02856       } else {
02857          if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
02858             ast_waitstream(chan, "");
02859          }
02860       }
02861       break;
02862 
02863    case '2': /* Un/Lock the Conference */
02864       *menu_mode = MENU_DISABLED;
02865       if (conf->locked) {
02866          conf->locked = 0;
02867          if (!ast_streamfile(chan, "conf-unlockednow", ast_channel_language(chan))) {
02868             ast_waitstream(chan, "");
02869          }
02870       } else {
02871          conf->locked = 1;
02872          if (!ast_streamfile(chan, "conf-lockednow", ast_channel_language(chan))) {
02873             ast_waitstream(chan, "");
02874          }
02875       }
02876       break;
02877 
02878    case '3': /* Eject last user */
02879    {
02880       struct ast_conf_user *usr = NULL;
02881       int max_no = 0;
02882       ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &max_no);
02883       *menu_mode = MENU_DISABLED;
02884       usr = ao2_find(conf->usercontainer, &max_no, 0);
02885       if ((ast_channel_name(usr->chan) == ast_channel_name(chan)) || ast_test_flag64(&usr->userflags, CONFFLAG_ADMIN)) {
02886          if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02887             ast_waitstream(chan, "");
02888          }
02889       } else {
02890          usr->adminflags |= ADMINFLAG_KICKME;
02891       }
02892       ao2_ref(usr, -1);
02893       ast_stopstream(chan);
02894       break;
02895    }
02896 
02897    case '4':
02898       tweak_listen_volume(user, VOL_DOWN);
02899       break;
02900 
02901    case '5':
02902       /* Extend RT conference */
02903       if (rt_schedule) {
02904          if (!rt_extend_conf(conf->confno)) {
02905             if (!ast_streamfile(chan, "conf-extended", ast_channel_language(chan))) {
02906                ast_waitstream(chan, "");
02907             }
02908          } else {
02909             if (!ast_streamfile(chan, "conf-nonextended", ast_channel_language(chan))) {
02910                ast_waitstream(chan, "");
02911             }
02912          }
02913          ast_stopstream(chan);
02914       }
02915       *menu_mode = MENU_DISABLED;
02916       break;
02917 
02918    case '6':
02919       tweak_listen_volume(user, VOL_UP);
02920       break;
02921 
02922    case '7':
02923       tweak_talk_volume(user, VOL_DOWN);
02924       break;
02925 
02926    case '8':
02927       if (!ast_streamfile(chan, "conf-adminmenu-menu8", ast_channel_language(chan))) {
02928          /* 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. */
02929          *dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
02930          ast_stopstream(chan);
02931       }
02932       *menu_mode = MENU_ADMIN_EXTENDED;
02933       break;
02934 
02935    case '9':
02936       tweak_talk_volume(user, VOL_UP);
02937       break;
02938    default:
02939       menu_mode = MENU_DISABLED;
02940       /* Play an error message! */
02941       if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02942          ast_waitstream(chan, "");
02943       }
02944       break;
02945    }
02946 
02947 }

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 ast_format_cap cap_slin 
) [static]

Definition at line 2960 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_slin, 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(), ast_set_write_format(), 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, NULL, OBJ_NODATA, pbx_builtin_getvar_helper(), 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().

02964 {
02965    int keepplaying;
02966    int playednamerec;
02967    int res;
02968    struct ao2_iterator user_iter;
02969    struct ast_conf_user *usr = NULL;
02970 
02971    switch(*dtmf) {
02972    case '1': /* *81 Roll call */
02973       keepplaying = 1;
02974       playednamerec = 0;
02975       if (conf->users == 1) {
02976          if (keepplaying && !ast_streamfile(chan, "conf-onlyperson", ast_channel_language(chan))) {
02977             res = ast_waitstream(chan, AST_DIGIT_ANY);
02978             ast_stopstream(chan);
02979             if (res > 0) {
02980                keepplaying = 0;
02981             }
02982          }
02983       } else if (conf->users == 2) {
02984          if (keepplaying && !ast_streamfile(chan, "conf-onlyone", ast_channel_language(chan))) {
02985             res = ast_waitstream(chan, AST_DIGIT_ANY);
02986             ast_stopstream(chan);
02987             if (res > 0) {
02988                keepplaying = 0;
02989             }
02990          }
02991       } else {
02992          if (keepplaying && !ast_streamfile(chan, "conf-thereare", ast_channel_language(chan))) {
02993             res = ast_waitstream(chan, AST_DIGIT_ANY);
02994             ast_stopstream(chan);
02995             if (res > 0) {
02996                keepplaying = 0;
02997             }
02998          }
02999          if (keepplaying) {
03000             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL);
03001             ast_stopstream(chan);
03002             if (res > 0) {
03003                keepplaying = 0;
03004             }
03005          }
03006          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", ast_channel_language(chan))) {
03007             res = ast_waitstream(chan, AST_DIGIT_ANY);
03008             ast_stopstream(chan);
03009             if (res > 0) {
03010                keepplaying = 0;
03011             }
03012          }
03013       }
03014       user_iter = ao2_iterator_init(conf->usercontainer, 0);
03015       while((usr = ao2_iterator_next(&user_iter))) {
03016          if (ast_fileexists(usr->namerecloc, NULL, NULL)) {
03017             if (keepplaying && !ast_streamfile(chan, usr->namerecloc, ast_channel_language(chan))) {
03018                res = ast_waitstream(chan, AST_DIGIT_ANY);
03019                ast_stopstream(chan);
03020                if (res > 0) {
03021                   keepplaying = 0;
03022                }
03023             }
03024             playednamerec = 1;
03025          }
03026          ao2_ref(usr, -1);
03027       }
03028       ao2_iterator_destroy(&user_iter);
03029       if (keepplaying && playednamerec && !ast_streamfile(chan, "conf-roll-callcomplete", ast_channel_language(chan))) {
03030          res = ast_waitstream(chan, AST_DIGIT_ANY);
03031          ast_stopstream(chan);
03032          if (res > 0) {
03033             keepplaying = 0;
03034          }
03035       }
03036 
03037       *menu_mode = MENU_DISABLED;
03038       break;
03039 
03040    case '2': /* *82 Eject all non-admins */
03041       if (conf->users == 1) {
03042          if(!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
03043             ast_waitstream(chan, "");
03044          }
03045       } else {
03046          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_kickme_cb, &conf);
03047       }
03048       ast_stopstream(chan);
03049       *menu_mode = MENU_DISABLED;
03050       break;
03051 
03052    case '3': /* *83 (Admin) mute/unmute all non-admins */
03053       if(conf->gmuted) {
03054          conf->gmuted = 0;
03055          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_unmuted_cb, &conf);
03056          if (!ast_streamfile(chan, "conf-now-unmuted", ast_channel_language(chan))) {
03057             ast_waitstream(chan, "");
03058          }
03059       } else {
03060          conf->gmuted = 1;
03061          ao2_callback(conf->usercontainer, OBJ_NODATA, user_set_muted_cb, &conf);
03062          if (!ast_streamfile(chan, "conf-now-muted", ast_channel_language(chan))) {
03063             ast_waitstream(chan, "");
03064          }
03065       }
03066       ast_stopstream(chan);
03067       *menu_mode = MENU_DISABLED;
03068       break;
03069 
03070    case '4': /* *84 Record conference */
03071       if (conf->recording != MEETME_RECORD_ACTIVE) {
03072          ast_set_flag64(confflags, CONFFLAG_RECORDCONF);
03073          if (!conf->recordingfilename) {
03074             const char *var;
03075             ast_channel_lock(chan);
03076             if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
03077                conf->recordingfilename = ast_strdup(var);
03078             }
03079             if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
03080                conf->recordingformat = ast_strdup(var);
03081             }
03082             ast_channel_unlock(chan);
03083             if (!conf->recordingfilename) {
03084                snprintf(recordingtmp, recordingtmp_size, "meetme-conf-rec-%s-%s", conf->confno, ast_channel_uniqueid(chan));
03085                conf->recordingfilename = ast_strdup(recordingtmp);
03086             }
03087             if (!conf->recordingformat) {
03088                conf->recordingformat = ast_strdup("wav");
03089             }
03090             ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
03091             conf->confno, conf->recordingfilename, conf->recordingformat);
03092          }
03093 
03094          ast_mutex_lock(&conf->recordthreadlock);
03095          if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) && ((conf->lchan = ast_request("DAHDI", cap_slin, NULL, chan, "pseudo", NULL)))) {
03096             struct dahdi_confinfo dahdic;
03097 
03098             ast_set_read_format(conf->lchan, ast_format_slin);
03099             ast_set_write_format(conf->lchan, ast_format_slin);
03100             dahdic.chan = 0;
03101             dahdic.confno = conf->dahdiconf;
03102             dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
03103             if (ioctl(ast_channel_fd(conf->lchan, 0), DAHDI_SETCONF, &dahdic)) {
03104                ast_log(LOG_WARNING, "Error starting listen channel\n");
03105                ast_hangup(conf->lchan);
03106                conf->lchan = NULL;
03107             } else {
03108                ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
03109             }
03110          }
03111          ast_mutex_unlock(&conf->recordthreadlock);
03112          if (!ast_streamfile(chan, "conf-now-recording", ast_channel_language(chan))) {
03113             ast_waitstream(chan, "");
03114          }
03115       }
03116 
03117       ast_stopstream(chan);
03118       *menu_mode = MENU_DISABLED;
03119       break;
03120 
03121    case '8': /* *88 Exit the menu and return to the conference... without an error message */
03122       ast_stopstream(chan);
03123       *menu_mode = MENU_DISABLED;
03124       break;
03125 
03126    default:
03127       if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
03128          ast_waitstream(chan, "");
03129       }
03130       ast_stopstream(chan);
03131       *menu_mode = MENU_DISABLED;
03132       break;
03133    }
03134 }

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 2760 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().

02761 {
02762    switch (*dtmf) {
02763    case '1': /* Un/Mute */
02764       *menu_mode = MENU_DISABLED;
02765 
02766       /* user can only toggle the self-muted state */
02767       user->adminflags ^= ADMINFLAG_SELFMUTED;
02768 
02769       /* they can't override the admin mute state */
02770       if (ast_test_flag64(confflags, CONFFLAG_MONITOR) || (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED))) {
02771          if (!ast_streamfile(chan, "conf-muted", ast_channel_language(chan))) {
02772             ast_waitstream(chan, "");
02773          }
02774       } else {
02775          if (!ast_streamfile(chan, "conf-unmuted", ast_channel_language(chan))) {
02776             ast_waitstream(chan, "");
02777          }
02778       }
02779       break;
02780 
02781    case '2':
02782       *menu_mode = MENU_DISABLED;
02783       if (user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) {
02784          user->adminflags |= ADMINFLAG_T_REQUEST;
02785       }
02786 
02787       if (user->adminflags & ADMINFLAG_T_REQUEST) {
02788          if (!ast_streamfile(chan, "beep", ast_channel_language(chan))) {
02789             ast_waitstream(chan, "");
02790          }
02791       }
02792       break;
02793 
02794    case '4':
02795       tweak_listen_volume(user, VOL_DOWN);
02796       break;
02797    case '5':
02798       /* Extend RT conference */
02799       if (rt_schedule) {
02800          rt_extend_conf(conf->confno);
02801       }
02802       *menu_mode = MENU_DISABLED;
02803       break;
02804 
02805    case '6':
02806       tweak_listen_volume(user, VOL_UP);
02807       break;
02808 
02809    case '7':
02810       tweak_talk_volume(user, VOL_DOWN);
02811       break;
02812 
02813    case '8':
02814       *menu_mode = MENU_DISABLED;
02815       break;
02816 
02817    case '9':
02818       tweak_talk_volume(user, VOL_UP);
02819       break;
02820 
02821    default:
02822       *menu_mode = MENU_DISABLED;
02823       if (!ast_streamfile(chan, "conf-errormenu", ast_channel_language(chan))) {
02824          ast_waitstream(chan, "");
02825       }
02826       break;
02827    }
02828 }

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

Definition at line 2093 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, NULL, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.

02094 {
02095    switch (cmd) {
02096    case CLI_INIT:
02097       e->command = "meetme {mute|unmute}";
02098       e->usage =
02099          "Usage: meetme mute|unmute <confno> all|<userno>\n"
02100          "       Mute or unmute a conference or a user in a conference.\n";
02101       return NULL;
02102    case CLI_GENERATE:
02103       return complete_meetmecmd_mute_kick(a->line, a->word, a->pos, a->n);
02104    }
02105 
02106    if (a->argc != 4) {
02107       return CLI_SHOWUSAGE;
02108    }
02109 
02110    return meetme_cmd_helper(a);
02111 }

static void meetme_set_defaults ( void   )  [static]

Definition at line 5766 of file app_meetme.c.

Referenced by load_config_meetme().

05767 {
05768    /*  Scheduling support is off by default */
05769    rt_schedule = 0;
05770    fuzzystart = 0;
05771    earlyalert = 0;
05772    endalert = 0;
05773    extendby = 0;
05774 
05775    /*  Logging of participants defaults to ON for compatibility reasons */
05776    rt_log_members = 1;
05777 }

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

Definition at line 1850 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, min, ast_cli_args::n, ast_party_id::name, NULL, 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, 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.

01851 {
01852    /* Process the command */
01853    struct ast_conf_user *user;
01854    struct ast_conference *cnf;
01855    int hr, min, sec;
01856    int total = 0;
01857    time_t now;
01858 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s  %-8s  %-6s\n"
01859 #define MC_DATA_FORMAT "%-12.12s   %4.4d        %4.4s       %02d:%02d:%02d  %-8s  %-6s\n"
01860 
01861    switch (cmd) {
01862    case CLI_INIT:
01863       e->command = "meetme list";
01864       e->usage =
01865          "Usage: meetme list [<confno>] [" STR_CONCISE "]\n"
01866          "       List all conferences or a specific conference.\n";
01867       return NULL;
01868    case CLI_GENERATE:
01869       return complete_meetmecmd_list(a->line, a->word, a->pos, a->n);
01870    }
01871 
01872    if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], STR_CONCISE))) {
01873       /* List all the conferences */
01874       int concise = (a->argc == 3);
01875       struct ast_str *marked_users;
01876 
01877       if (!(marked_users = ast_str_create(30))) {
01878          return CLI_FAILURE;
01879       }
01880 
01881       now = time(NULL);
01882       AST_LIST_LOCK(&confs);
01883       if (AST_LIST_EMPTY(&confs)) {
01884          if (!concise) {
01885             ast_cli(a->fd, "No active MeetMe conferences.\n");
01886          }
01887          AST_LIST_UNLOCK(&confs);
01888          ast_free(marked_users);
01889          return CLI_SUCCESS;
01890       }
01891       if (!concise) {
01892          ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
01893       }
01894       AST_LIST_TRAVERSE(&confs, cnf, list) {
01895          hr = (now - cnf->start) / 3600;
01896          min = ((now - cnf->start) % 3600) / 60;
01897          sec = (now - cnf->start) % 60;
01898          if (!concise) {
01899             if (cnf->markedusers == 0) {
01900                ast_str_set(&marked_users, 0, "N/A ");
01901             } else {
01902                ast_str_set(&marked_users, 0, "%4.4d", cnf->markedusers);
01903             }
01904             ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users,
01905                ast_str_buffer(marked_users), hr, min, sec,
01906                cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
01907          } else {
01908             ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
01909                cnf->confno,
01910                cnf->users,
01911                cnf->markedusers,
01912                hr, min, sec,
01913                cnf->isdynamic,
01914                cnf->locked);
01915          }
01916 
01917          total += cnf->users;
01918       }
01919       AST_LIST_UNLOCK(&confs);
01920       if (!concise) {
01921          ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
01922       }
01923       ast_free(marked_users);
01924       return CLI_SUCCESS;
01925    }
01926    if (a->argc == 3 || (a->argc == 4 && !strcasecmp(a->argv[3], STR_CONCISE))) {
01927       struct ao2_iterator user_iter;
01928       int concise = (a->argc == 4);
01929 
01930       /* List all the users in a conference */
01931       if (AST_LIST_EMPTY(&confs)) {
01932          if (!concise) {
01933             ast_cli(a->fd, "No active MeetMe conferences.\n");
01934          }
01935          return CLI_SUCCESS;
01936       }
01937       /* Find the right conference */
01938       AST_LIST_LOCK(&confs);
01939       AST_LIST_TRAVERSE(&confs, cnf, list) {
01940          if (strcmp(cnf->confno, a->argv[2]) == 0) {
01941             break;
01942          }
01943       }
01944       if (!cnf) {
01945          if (!concise)
01946             ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
01947          AST_LIST_UNLOCK(&confs);
01948          return CLI_SUCCESS;
01949       }
01950       /* Show all the users */
01951       time(&now);
01952       user_iter = ao2_iterator_init(cnf->usercontainer, 0);
01953       while((user = ao2_iterator_next(&user_iter))) {
01954          hr = (now - user->jointime) / 3600;
01955          min = ((now - user->jointime) % 3600) / 60;
01956          sec = (now - user->jointime) % 60;
01957          if (!concise) {
01958             ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
01959                user->user_no,
01960                S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
01961                S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
01962                ast_channel_name(user->chan),
01963                ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
01964                ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
01965                user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
01966                user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
01967                istalking(user->talking), hr, min, sec); 
01968          } else {
01969             ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
01970                user->user_no,
01971                S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, ""),
01972                S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, ""),
01973                ast_channel_name(user->chan),
01974                ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
01975                ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
01976                user->adminflags & (ADMINFLAG_MUTED | ADM