Sat Nov 1 06:30:28 2008

Asterisk developer's documentation


app_meetme.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Meet me conference bridge
00022  * 
00023  * \ingroup applications
00024  */
00025 
00026 #include <stdlib.h>
00027 #include <stdio.h>
00028 #include <string.h>
00029 #include <unistd.h>
00030 #include <errno.h>
00031 #include <sys/ioctl.h>
00032 #ifdef __linux__
00033 #include <linux/zaptel.h>
00034 #else
00035 #include <zaptel.h>
00036 #endif /* __linux__ */
00037 
00038 #include "asterisk.h"
00039 
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 69894 $")
00041 
00042 #include "asterisk/lock.h"
00043 #include "asterisk/file.h"
00044 #include "asterisk/logger.h"
00045 #include "asterisk/channel.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/app.h"
00050 #include "asterisk/dsp.h"
00051 #include "asterisk/musiconhold.h"
00052 #include "asterisk/manager.h"
00053 #include "asterisk/options.h"
00054 #include "asterisk/cli.h"
00055 #include "asterisk/say.h"
00056 #include "asterisk/utils.h"
00057 #include "asterisk/linkedlists.h"
00058 
00059 static const char *tdesc = "MeetMe conference bridge";
00060 
00061 static const char *app = "MeetMe";
00062 static const char *app2 = "MeetMeCount";
00063 static const char *app3 = "MeetMeAdmin";
00064 
00065 static const char *synopsis = "MeetMe conference bridge";
00066 static const char *synopsis2 = "MeetMe participant count";
00067 static const char *synopsis3 = "MeetMe conference Administration";
00068 
00069 static const char *descrip =
00070 "  MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
00071 "If the conference number is omitted, the user will be prompted to enter\n"
00072 "one. \n"
00073 "User can exit the conference by hangup, or if the 'p' option is specified, by pressing '#'.\n"
00074 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
00075 "             must be present for conferencing to operate properly. In addition, the chan_zap\n"
00076 "             channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
00077 "The option string may contain zero or more of the following characters:\n"
00078 "      'a' -- set admin mode\n"
00079 "      'A' -- set marked mode\n"
00080 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
00081 "             Default: conf-background.agi\n"
00082 "             (Note: This does not work with non-Zap channels in the same conference)\n"
00083 "      'c' -- announce user(s) count on joining a conference\n"
00084 "      'd' -- dynamically add conference\n"
00085 "      'D' -- dynamically add conference, prompting for a PIN\n"
00086 "      'e' -- select an empty conference\n"
00087 "      'E' -- select an empty pinless conference\n"
00088 "      'i' -- announce user join/leave\n"
00089 "      'm' -- set monitor only mode (Listen only, no talking)\n"
00090 "      'M' -- enable music on hold when the conference has a single caller\n"
00091 "      'p' -- allow user to exit the conference by pressing '#'\n"
00092 "      'P' -- always prompt for the pin even if it is specified\n"
00093 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
00094 "      'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
00095 "             using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
00096 "             meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is wav.\n"
00097 "      's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
00098 "      't' -- set talk only mode. (Talk only, no listening)\n"
00099 "      'T' -- set talker detection (sent to manager interface and meetme list)\n"
00100 "      'w[(<secs>)]'\n"
00101 "          -- wait until the marked user enters the conference\n"
00102 "      'x' -- close the conference when last marked user exits\n"
00103 "      'X' -- allow user to exit the conference by entering a valid single\n"
00104 "             digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
00105 "             if that variable is not defined.\n";
00106 
00107 static const char *descrip2 =
00108 "  MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
00109 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
00110 "will be returned in the variable. Upon app completion, MeetMeCount will hangup the\n"
00111 "channel, unless priority n+1 exists, in which case priority progress will continue.\n"
00112 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
00113 
00114 static const char *descrip3 = 
00115 "  MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
00116 "      'e' -- Eject last user that joined\n"
00117 "      'k' -- Kick one user out of conference\n"
00118 "      'K' -- Kick all users out of conference\n"
00119 "      'l' -- Unlock conference\n"
00120 "      'L' -- Lock conference\n"
00121 "      'm' -- Unmute conference\n"
00122 "      'M' -- Mute conference\n"
00123 "      'n' -- Unmute entire conference (except admin)\n"
00124 "      'N' -- Mute entire conference (except admin)\n"
00125 "";
00126 
00127 #define CONFIG_FILE_NAME "meetme.conf"
00128 
00129 STANDARD_LOCAL_USER;
00130 
00131 LOCAL_USER_DECL;
00132 
00133 struct ast_conference {
00134    char confno[AST_MAX_EXTENSION];     /* Conference */
00135    struct ast_channel *chan;     /* Announcements channel */
00136    int fd;              /* Announcements fd */
00137    int zapconf;            /* Zaptel Conf # */
00138    int users;           /* Number of active users */
00139    int markedusers;        /* Number of marked users */
00140    AST_LIST_HEAD_NOLOCK(, ast_conf_user) userlist;
00141    time_t start;           /* Start time (s) */
00142    int refcount;
00143    int recording;          /* recording status */
00144    int isdynamic;          /* Created on the fly? */
00145    int locked;          /* Is the conference locked? */
00146    pthread_t recordthread;       /* thread for recording */
00147    pthread_attr_t attr;       /* thread attribute */
00148    char *recordingfilename;      /* Filename to record the Conference into */
00149    char *recordingformat;        /* Format to record the Conference in */
00150    char pin[AST_MAX_EXTENSION];     /* If protected by a PIN */
00151    char pinadmin[AST_MAX_EXTENSION];   /* If protected by a admin PIN */
00152    AST_LIST_ENTRY(ast_conference) list;
00153 };
00154 
00155 static AST_LIST_HEAD_STATIC(confs, ast_conference);
00156 
00157 static unsigned int conf_map[1024] = {0, };
00158 
00159 struct volume {
00160    int desired;            /* Desired volume adjustment */
00161    int actual;          /* Actual volume adjustment (for channels that can't adjust) */
00162 };
00163 
00164 struct ast_conf_user {
00165    int user_no;            /* User Number */
00166    AST_LIST_ENTRY(ast_conf_user) list;
00167    int userflags;          /* Flags as set in the conference */
00168    int adminflags;            /* Flags set by the Admin */
00169    struct ast_channel *chan;     /* Connected channel */
00170    int talking;            /* Is user talking */
00171    int zapchannel;            /* Is a Zaptel channel */
00172    char usrvalue[50];         /* Custom User Value */
00173    char namerecloc[PATH_MAX]; /* Name Recorded file Location */
00174    time_t jointime;        /* Time the user joined the conference */
00175    struct volume talk;
00176    struct volume listen;
00177 };
00178 
00179 static int audio_buffers;        /* The number of audio buffers to be allocated on pseudo channels
00180                      when in a conference
00181                   */
00182 
00183 #define DEFAULT_AUDIO_BUFFERS 32    /* each buffer is 20ms, so this is 640ms total */
00184 
00185 #define ADMINFLAG_MUTED (1 << 1)    /* User is muted */
00186 #define ADMINFLAG_KICKME (1 << 2)      /* User is kicked */
00187 #define MEETME_DELAYDETECTTALK      300
00188 #define MEETME_DELAYDETECTENDTALK   1000
00189 
00190 enum volume_action {
00191    VOL_UP,
00192    VOL_DOWN,
00193 };
00194 
00195 static int admin_exec(struct ast_channel *chan, void *data);
00196 
00197 static void *recordthread(void *args);
00198 
00199 #include "enter.h"
00200 #include "leave.h"
00201 
00202 #define ENTER  0
00203 #define LEAVE  1
00204 
00205 #define MEETME_RECORD_OFF  0
00206 #define MEETME_RECORD_ACTIVE  1
00207 #define MEETME_RECORD_TERMINATE  2
00208 
00209 #define CONF_SIZE 320
00210 
00211 #define CONFFLAG_ADMIN  (1 << 1)    /* If set the user has admin access on the conference */
00212 #define CONFFLAG_MONITOR (1 << 2)      /* If set the user can only receive audio from the conference */
00213 #define CONFFLAG_POUNDEXIT (1 << 3)    /* If set asterisk will exit conference when '#' is pressed */
00214 #define CONFFLAG_STARMENU (1 << 4)     /* If set asterisk will provide a menu to the user when '*' is pressed */
00215 #define CONFFLAG_TALKER (1 << 5)    /* If set the use can only send audio to the conference */
00216 #define CONFFLAG_QUIET (1 << 6)        /* If set there will be no enter or leave sounds */
00217 #define CONFFLAG_ANNOUNCEUSERCOUNT (1 << 7)  /* If set, when user joins the conference, they will be told the number of users that are already in */
00218 #define CONFFLAG_AGI (1 << 8)       /* Set to run AGI Script in Background */
00219 #define CONFFLAG_MOH (1 << 9)       /* Set to have music on hold when user is alone in conference */
00220 #define CONFFLAG_MARKEDEXIT (1 << 10)     /* If set the MeetMe will return if all marked with this flag left */
00221 #define CONFFLAG_WAITMARKED (1 << 11)     /* If set, the MeetMe will wait until a marked user enters */
00222 #define CONFFLAG_EXIT_CONTEXT (1 << 12)      /* If set, the MeetMe will exit to the specified context */
00223 #define CONFFLAG_MARKEDUSER (1 << 13)     /* If set, the user will be marked */
00224 #define CONFFLAG_INTROUSER (1 << 14)      /* If set, user will be ask record name on entry of conference */
00225 #define CONFFLAG_RECORDCONF (1<< 15)      /* If set, the MeetMe will be recorded */
00226 #define CONFFLAG_MONITORTALKER (1 << 16)  /* If set, the user will be monitored if the user is talking or not */
00227 #define CONFFLAG_DYNAMIC (1 << 17)
00228 #define CONFFLAG_DYNAMICPIN (1 << 18)
00229 #define CONFFLAG_EMPTY (1 << 19)
00230 #define CONFFLAG_EMPTYNOPIN (1 << 20)
00231 #define CONFFLAG_ALWAYSPROMPT (1 << 21)
00232 
00233 enum {
00234    OPT_ARG_WAITMARKED = 0,
00235    OPT_ARG_ARRAY_SIZE = 1,
00236 } meetme_option_args;
00237 
00238 AST_APP_OPTIONS(meetme_opts, {
00239    AST_APP_OPTION('a', CONFFLAG_ADMIN ),
00240    AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
00241    AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
00242    AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
00243    AST_APP_OPTION('m', CONFFLAG_MONITOR ),
00244    AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
00245    AST_APP_OPTION('s', CONFFLAG_STARMENU ),
00246    AST_APP_OPTION('t', CONFFLAG_TALKER ),
00247    AST_APP_OPTION('q', CONFFLAG_QUIET ),
00248    AST_APP_OPTION('M', CONFFLAG_MOH ),
00249    AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
00250    AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
00251    AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
00252    AST_APP_OPTION('b', CONFFLAG_AGI ),
00253    AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
00254    AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
00255    AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
00256    AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
00257    AST_APP_OPTION('e', CONFFLAG_EMPTY ),
00258    AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
00259    AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
00260 });
00261 
00262 static char *istalking(int x)
00263 {
00264    if (x > 0)
00265       return "(talking)";
00266    else if (x < 0)
00267       return "(unmonitored)";
00268    else 
00269       return "(not talking)";
00270 }
00271 
00272 static int careful_write(int fd, unsigned char *data, int len, int block)
00273 {
00274    int res;
00275    int x;
00276 
00277    while (len) {
00278       if (block) {
00279          x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
00280          res = ioctl(fd, ZT_IOMUX, &x);
00281       } else
00282          res = 0;
00283       if (res >= 0)
00284          res = write(fd, data, len);
00285       if (res < 1) {
00286          if (errno != EAGAIN) {
00287             ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
00288             return -1;
00289          } else
00290             return 0;
00291       }
00292       len -= res;
00293       data += res;
00294    }
00295 
00296    return 0;
00297 }
00298 
00299 /* Map 'volume' levels from -5 through +5 into
00300    decibel (dB) settings for channel drivers
00301    Note: these are not a straight linear-to-dB
00302    conversion... the numbers have been modified
00303    to give the user a better level of adjustability
00304 */
00305 static signed char gain_map[] = {
00306    -15,
00307    -13,
00308    -10,
00309    -6,
00310    0,
00311    0,
00312    0,
00313    6,
00314    10,
00315    13,
00316    15,
00317 };
00318 
00319 static int set_talk_volume(struct ast_conf_user *user, int volume)
00320 {
00321    signed char gain_adjust;
00322 
00323    /* attempt to make the adjustment in the channel driver;
00324       if successful, don't adjust in the frame reading routine
00325    */
00326    gain_adjust = gain_map[volume + 5];
00327 
00328    return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
00329 }
00330 
00331 static int set_listen_volume(struct ast_conf_user *user, int volume)
00332 {
00333    signed char gain_adjust;
00334 
00335    /* attempt to make the adjustment in the channel driver;
00336       if successful, don't adjust in the frame reading routine
00337    */
00338    gain_adjust = gain_map[volume + 5];
00339 
00340    return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
00341 }
00342 
00343 static void tweak_volume(struct volume *vol, enum volume_action action)
00344 {
00345    switch (action) {
00346    case VOL_UP:
00347       switch (vol->desired) {
00348       case 5:
00349          break;
00350       case 0:
00351          vol->desired = 2;
00352          break;
00353       case -2:
00354          vol->desired = 0;
00355          break;
00356       default:
00357          vol->desired++;
00358          break;
00359       }
00360       break;
00361    case VOL_DOWN:
00362       switch (vol->desired) {
00363       case -5:
00364          break;
00365       case 2:
00366          vol->desired = 0;
00367          break;
00368       case 0:
00369          vol->desired = -2;
00370          break;
00371       default:
00372          vol->desired--;
00373          break;
00374       }
00375    }
00376 }
00377 
00378 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
00379 {
00380    tweak_volume(&user->talk, action);
00381    /* attempt to make the adjustment in the channel driver;
00382       if successful, don't adjust in the frame reading routine
00383    */
00384    if (!set_talk_volume(user, user->talk.desired))
00385       user->talk.actual = 0;
00386    else
00387       user->talk.actual = user->talk.desired;
00388 }
00389 
00390 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
00391 {
00392    tweak_volume(&user->listen, action);
00393    /* attempt to make the adjustment in the channel driver;
00394       if successful, don't adjust in the frame reading routine
00395    */
00396    if (!set_listen_volume(user, user->listen.desired))
00397       user->listen.actual = 0;
00398    else
00399       user->listen.actual = user->listen.desired;
00400 }
00401 
00402 static void reset_volumes(struct ast_conf_user *user)
00403 {
00404    signed char zero_volume = 0;
00405 
00406    ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00407    ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
00408 }
00409 
00410 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int sound)
00411 {
00412    unsigned char *data;
00413    int len;
00414    int res = -1;
00415 
00416    if (!chan->_softhangup)
00417       res = ast_autoservice_start(chan);
00418 
00419    AST_LIST_LOCK(&confs);
00420 
00421    switch(sound) {
00422    case ENTER:
00423       data = enter;
00424       len = sizeof(enter);
00425       break;
00426    case LEAVE:
00427       data = leave;
00428       len = sizeof(leave);
00429       break;
00430    default:
00431       data = NULL;
00432       len = 0;
00433    }
00434    if (data) 
00435       careful_write(conf->fd, data, len, 1);
00436 
00437    AST_LIST_UNLOCK(&confs);
00438 
00439    if (!res) 
00440       ast_autoservice_stop(chan);
00441 }
00442 
00443 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic, int refcount)
00444 {
00445    struct ast_conference *cnf;
00446    struct zt_confinfo ztc;
00447    int confno_int = 0;
00448 
00449    AST_LIST_LOCK(&confs);
00450    AST_LIST_TRAVERSE(&confs, cnf, list) {
00451       if (!strcmp(confno, cnf->confno)) 
00452          break;
00453    }
00454 
00455    if (!cnf && (make || dynamic)) {
00456       /* Make a new one */
00457       cnf = calloc(1, sizeof(*cnf));
00458       if (cnf) {
00459          ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
00460          ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
00461          ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
00462          cnf->markedusers = 0;
00463          cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL);
00464          if (cnf->chan) {
00465             cnf->fd = cnf->chan->fds[0];  /* for use by conf_play() */
00466          } else {
00467             ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
00468             cnf->fd = open("/dev/zap/pseudo", O_RDWR);
00469             if (cnf->fd < 0) {
00470                ast_log(LOG_WARNING, "Unable to open pseudo device\n");
00471                free(cnf);
00472                cnf = NULL;
00473                goto cnfout;
00474             }
00475          }
00476          memset(&ztc, 0, sizeof(ztc));
00477          /* Setup a new zap conference */
00478          ztc.chan = 0;
00479          ztc.confno = -1;
00480          ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
00481          if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
00482             ast_log(LOG_WARNING, "Error setting conference\n");
00483             if (cnf->chan)
00484                ast_hangup(cnf->chan);
00485             else
00486                close(cnf->fd);
00487             free(cnf);
00488             cnf = NULL;
00489             goto cnfout;
00490          }
00491          /* Fill the conference struct */
00492          cnf->start = time(NULL);
00493          cnf->zapconf = ztc.confno;
00494          cnf->isdynamic = dynamic;
00495          cnf->locked = 0;
00496          if (option_verbose > 2)
00497             ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
00498          AST_LIST_INSERT_HEAD(&confs, cnf, list);
00499          /* Reserve conference number in map */
00500          if ((sscanf(cnf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
00501             conf_map[confno_int] = 1;
00502       } else   
00503          ast_log(LOG_WARNING, "Out of memory\n");
00504    }
00505  cnfout:
00506    if (cnf)
00507       ast_atomic_fetchadd_int(&cnf->refcount, refcount);
00508    AST_LIST_UNLOCK(&confs);
00509    return cnf;
00510 }
00511 
00512 static int confs_show(int fd, int argc, char **argv)
00513 {
00514    ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
00515 
00516    return RESULT_SUCCESS;
00517 }
00518 
00519 static char show_confs_usage[] =
00520 "Deprecated! Please use 'meetme' instead.\n";
00521 
00522 static struct ast_cli_entry cli_show_confs = {
00523    { "show", "conferences", NULL }, confs_show,
00524    "Show status of conferences", show_confs_usage, NULL };
00525    
00526 static int conf_cmd(int fd, int argc, char **argv) {
00527    /* Process the command */
00528    struct ast_conference *cnf;
00529    struct ast_conf_user *user;
00530    int hr, min, sec;
00531    int i = 0, total = 0;
00532    time_t now;
00533    char *header_format = "%-14s %-14s %-10s %-8s  %-8s\n";
00534    char *data_format = "%-12.12s   %4.4d        %4.4s       %02d:%02d:%02d  %-8s\n";
00535    char cmdline[1024] = "";
00536 
00537    if (argc > 8)
00538       ast_cli(fd, "Invalid Arguments.\n");
00539    /* Check for length so no buffer will overflow... */
00540    for (i = 0; i < argc; i++) {
00541       if (strlen(argv[i]) > 100)
00542          ast_cli(fd, "Invalid Arguments.\n");
00543    }
00544    if (argc == 1) {
00545       /* 'MeetMe': List all the conferences */  
00546       now = time(NULL);
00547       AST_LIST_LOCK(&confs);
00548       if (!AST_LIST_FIRST(&confs)) {
00549          ast_cli(fd, "No active MeetMe conferences.\n");
00550          AST_LIST_UNLOCK(&confs);
00551          return RESULT_SUCCESS;
00552       }
00553       ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
00554       AST_LIST_TRAVERSE(&confs, cnf, list) {
00555          if (cnf->markedusers == 0)
00556             strcpy(cmdline, "N/A ");
00557          else 
00558             snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
00559          hr = (now - cnf->start) / 3600;
00560          min = ((now - cnf->start) % 3600) / 60;
00561          sec = (now - cnf->start) % 60;
00562 
00563          ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
00564 
00565          total += cnf->users;    
00566       }
00567       ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
00568       AST_LIST_UNLOCK(&confs);
00569       return RESULT_SUCCESS;
00570    }
00571    if (argc < 3)
00572       return RESULT_SHOWUSAGE;
00573    ast_copy_string(cmdline, argv[2], sizeof(cmdline));   /* Argv 2: conference number */
00574    if (strstr(argv[1], "lock")) {   
00575       if (strcmp(argv[1], "lock") == 0) {
00576          /* Lock */
00577          strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
00578       } else {
00579          /* Unlock */
00580          strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
00581       }
00582    } else if (strstr(argv[1], "mute")) { 
00583       if (argc < 4)
00584          return RESULT_SHOWUSAGE;
00585       if (strcmp(argv[1], "mute") == 0) {
00586          /* Mute */
00587          if (strcmp(argv[3], "all") == 0) {
00588             strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
00589          } else {
00590             strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);   
00591             strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
00592          }
00593       } else {
00594          /* Unmute */
00595          if (strcmp(argv[3], "all") == 0) {
00596             strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
00597          } else {
00598             strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
00599             strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
00600          }
00601       }
00602    } else if (strcmp(argv[1], "kick") == 0) {
00603       if (argc < 4)
00604          return RESULT_SHOWUSAGE;
00605       if (strcmp(argv[3], "all") == 0) {
00606          /* Kick all */
00607          strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
00608       } else {
00609          /* Kick a single user */
00610          strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
00611          strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
00612       }  
00613    } else if(strcmp(argv[1], "list") == 0) {
00614       /* List all the users in a conference */
00615       if (!AST_LIST_FIRST(&confs)) {
00616          ast_cli(fd, "No active conferences.\n");
00617          return RESULT_SUCCESS;  
00618       }
00619       AST_LIST_LOCK(&confs);
00620       AST_LIST_TRAVERSE(&confs, cnf, list) {
00621          if (strcmp(cnf->confno, argv[2]) == 0)
00622             break;
00623       }
00624       if (!cnf) {
00625          ast_cli(fd, "No such conference: %s.\n", argv[2]);
00626          AST_LIST_UNLOCK(&confs);
00627          return RESULT_SUCCESS;
00628       }
00629       /* Show all the users */
00630       AST_LIST_TRAVERSE(&cnf->userlist, user, list) {
00631          ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s\n",
00632             user->user_no,
00633             user->chan->cid.cid_num ? user->chan->cid.cid_num : "<unknown>",
00634             user->chan->cid.cid_name ? user->chan->cid.cid_name : "<no name>",
00635             user->chan->name,
00636             user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
00637             user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
00638             user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : "",
00639             istalking(user->talking));
00640       }
00641       ast_cli(fd,"%d users in that conference.\n",cnf->users);
00642       AST_LIST_UNLOCK(&confs);
00643 
00644       return RESULT_SUCCESS;
00645    } else 
00646       return RESULT_SHOWUSAGE;
00647    ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
00648    admin_exec(NULL, cmdline);
00649 
00650    return 0;
00651 }
00652 
00653 static char *complete_confcmd(char *line, char *word, int pos, int state) {
00654 #define CONF_COMMANDS 6
00655    int which = 0, x = 0;
00656    struct ast_conference *cnf = NULL;
00657    struct ast_conf_user *usr = NULL;
00658    char *confno = NULL;
00659    char usrno[50] = "";
00660    char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
00661    char *myline;
00662    char *res = NULL;
00663    
00664    if (pos == 1) {
00665       /* Command */
00666       for (x = 0;x < CONF_COMMANDS; x++) {
00667          if (!strncasecmp(cmds[x], word, strlen(word))) {
00668             if (++which > state) {
00669                return strdup(cmds[x]);
00670             }
00671          }
00672       }
00673    } else if (pos == 2) {
00674       /* Conference Number */
00675       AST_LIST_LOCK(&confs);
00676       AST_LIST_TRAVERSE(&confs, cnf, list) {
00677          if (!strncasecmp(word, cnf->confno, strlen(word))) {
00678             if (++which > state)
00679                break;
00680          }
00681       }
00682       res = cnf ? strdup(cnf->confno) : NULL;
00683       AST_LIST_UNLOCK(&confs);
00684       return res;
00685    } else if (pos == 3) {
00686       /* User Number || Conf Command option*/
00687       if (strstr(line, "mute") || strstr(line, "kick")) {
00688          if ((state == 0) && (strstr(line, "kick") || strstr(line,"mute")) && !(strncasecmp(word, "all", strlen(word)))) {
00689             return strdup("all");
00690          }
00691          which++;
00692          AST_LIST_LOCK(&confs);
00693 
00694          /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
00695          myline = ast_strdupa(line);
00696          if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
00697             while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
00698                ;
00699          }
00700 
00701          AST_LIST_TRAVERSE(&confs, cnf, list) {
00702             if (!strcmp(confno, cnf->confno))
00703                 break;
00704          }
00705 
00706          if (cnf) {
00707             /* Search for the user */
00708             AST_LIST_TRAVERSE(&cnf->userlist, usr, list) {
00709                snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
00710                if (!strncasecmp(word, usrno, strlen(word))) {
00711                   if (++which > state)
00712                      break;
00713                }
00714             }
00715          }
00716          AST_LIST_UNLOCK(&confs);
00717          return usr ? strdup(usrno) : NULL;
00718       }
00719    }
00720 
00721    return NULL;
00722 }
00723    
00724 static char conf_usage[] =
00725 "Usage: meetme  (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
00726 "       Executes a command for the conference or on a conferee\n";
00727 
00728 static struct ast_cli_entry cli_conf = {
00729    {"meetme", NULL, NULL }, conf_cmd,
00730    "Execute a command on a conference or conferee", conf_usage, complete_confcmd};
00731 
00732 static void conf_flush(int fd, struct ast_channel *chan)
00733 {
00734    int x;
00735 
00736    /* read any frames that may be waiting on the channel
00737       and throw them away
00738    */
00739    if (chan) {
00740       struct ast_frame *f;
00741 
00742       /* when no frames are available, this will wait
00743          for 1 millisecond maximum
00744       */
00745       while (ast_waitfor(chan, 1)) {
00746          f = ast_read(chan);
00747          if (f)
00748             ast_frfree(f);
00749          else /* channel was hung up or something else happened */
00750             break;
00751       }
00752    }
00753 
00754    /* flush any data sitting in the pseudo channel */
00755    x = ZT_FLUSH_ALL;
00756    if (ioctl(fd, ZT_FLUSH, &x))
00757       ast_log(LOG_WARNING, "Error flushing channel\n");
00758 
00759 }
00760 
00761 /* Remove the conference from the list and free it.
00762    XXX We assume that this was called while holding the confs list lock. */
00763 static int conf_free(struct ast_conference *conf)
00764 {
00765    struct ast_conference *cur;
00766 
00767    AST_LIST_TRAVERSE_SAFE_BEGIN(&confs, cur, list) {
00768       if (cur == conf) {
00769          AST_LIST_REMOVE_CURRENT(&confs, list);
00770          break;
00771       }
00772    }
00773    AST_LIST_TRAVERSE_SAFE_END;
00774 
00775    if (!cur)
00776       ast_log(LOG_WARNING, "Conference not found\n");
00777 
00778    if (conf->recording == MEETME_RECORD_ACTIVE) {
00779       conf->recording = MEETME_RECORD_TERMINATE;
00780       AST_LIST_UNLOCK(&confs);
00781       while (1) {
00782          usleep(1);
00783          AST_LIST_LOCK(&confs);
00784          if (conf->recording == MEETME_RECORD_OFF)
00785             break;
00786          AST_LIST_UNLOCK(&confs);
00787       }
00788    }
00789 
00790    if (conf->chan)
00791       ast_hangup(conf->chan);
00792    else
00793       close(conf->fd);
00794    
00795    free(conf);
00796 
00797    return 0;
00798 }
00799 
00800 /* Decrement reference counts, as incremented by find_conf() */
00801 static int dispose_conf(struct ast_conference *conf)
00802 {
00803    int res = 0;
00804    int confno_int = 0;
00805 
00806    AST_LIST_LOCK(&confs);
00807    if (ast_atomic_dec_and_test(&conf->refcount)) {
00808       /* Take the conference room number out of an inuse state */
00809       if ((sscanf(conf->confno, "%d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
00810          conf_map[confno_int] = 0;
00811       conf_free(conf);
00812       res = 1;
00813    }
00814    AST_LIST_UNLOCK(&confs);
00815 
00816    return res;
00817 }
00818 
00819 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags, char *optargs[])
00820 {
00821    struct ast_conf_user *user = calloc(1, sizeof(*user));
00822    struct ast_conf_user *usr = NULL;
00823    int fd;
00824    struct zt_confinfo ztc, ztc_empty;
00825    struct ast_frame *f;
00826    struct ast_channel *c;
00827    struct ast_frame fr;
00828    int outfd;
00829    int ms;
00830    int nfds;
00831    int res;
00832    int flags;
00833    int retryzap;
00834    int origfd;
00835    int musiconhold = 0;
00836    int firstpass = 0;
00837    int lastmarked = 0;
00838    int currentmarked = 0;
00839    int ret = -1;
00840    int x;
00841    int menu_active = 0;
00842    int using_pseudo = 0;
00843    int duration=20;
00844    struct ast_dsp *dsp=NULL;
00845    struct ast_app *app;
00846    char *agifile;
00847    char *agifiledefault = "conf-background.agi";
00848    char meetmesecs[30] = "";
00849    char exitcontext[AST_MAX_CONTEXT] = "";
00850    char recordingtmp[AST_MAX_EXTENSION] = "";
00851    int dtmf, opt_waitmarked_timeout = 0;
00852    time_t timeout = 0;
00853    ZT_BUFFERINFO bi;
00854    char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
00855    char *buf = __buf + AST_FRIENDLY_OFFSET;
00856    
00857    if (!user) {
00858       ast_log(LOG_ERROR, "Out of memory\n");
00859       return ret;
00860    }
00861 
00862    /* Possible timeout waiting for marked user */
00863    if ((confflags & CONFFLAG_WAITMARKED) &&
00864       !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
00865       (sscanf(optargs[OPT_ARG_WAITMARKED], "%d", &opt_waitmarked_timeout) == 1) &&
00866       (opt_waitmarked_timeout > 0)) {
00867       timeout = time(NULL) + opt_waitmarked_timeout;
00868    }
00869 
00870    AST_LIST_LOCK(&confs);
00871 
00872    if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
00873       conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
00874       if (!conf->recordingfilename) {
00875          snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
00876          conf->recordingfilename = ast_strdupa(recordingtmp);
00877       }
00878       conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
00879       if (!conf->recordingformat) {
00880          snprintf(recordingtmp, sizeof(recordingtmp), "wav");
00881          conf->recordingformat = ast_strdupa(recordingtmp);
00882       }
00883       pthread_attr_init(&conf->attr);
00884       pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
00885       ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
00886              conf->confno, conf->recordingfilename, conf->recordingformat);
00887       ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
00888       pthread_attr_destroy(&conf->attr);
00889    }
00890 
00891    time(&user->jointime);
00892 
00893    if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
00894       /* Sorry, but this confernce is locked! */   
00895       if (!ast_streamfile(chan, "conf-locked", chan->language))
00896          ast_waitstream(chan, "");
00897       AST_LIST_UNLOCK(&confs);
00898       goto outrun;
00899    }
00900 
00901    if (confflags & CONFFLAG_MARKEDUSER)
00902       conf->markedusers++;
00903 
00904    if (AST_LIST_LAST(&conf->userlist))
00905       user->user_no = AST_LIST_LAST(&conf->userlist)->user_no + 1;
00906    else
00907       user->user_no = 1;
00908 
00909    user->chan = chan;
00910    user->userflags = confflags;
00911    user->adminflags = 0;
00912    user->talking = -1;
00913    conf->users++;
00914 
00915    AST_LIST_INSERT_TAIL(&conf->userlist, user, list);
00916 
00917    /* Since we control a user in the userlist, our conference should never go away now. */
00918    AST_LIST_UNLOCK(&confs);
00919 
00920    if (confflags & CONFFLAG_EXIT_CONTEXT) {
00921       if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) 
00922          ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
00923       else if (!ast_strlen_zero(chan->macrocontext)) 
00924          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00925       else
00926          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00927    }
00928 
00929    if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
00930       snprintf(user->namerecloc, sizeof(user->namerecloc),
00931           "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
00932           conf->confno, user->user_no);
00933       res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
00934       if (res == -1)
00935          goto outrun;
00936    }
00937 
00938    if (!(confflags & CONFFLAG_QUIET)) {
00939       if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
00940          if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
00941             ast_waitstream(chan, "");
00942       if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
00943          if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
00944             ast_waitstream(chan, "");
00945    }
00946 
00947    if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
00948       int keepplaying = 1;
00949 
00950       if (conf->users == 2) { 
00951          if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
00952             res = ast_waitstream(chan, AST_DIGIT_ANY);
00953             ast_stopstream(chan);
00954             if (res > 0)
00955                keepplaying=0;
00956             else if (res == -1)
00957