Fri May 26 01:45:27 2006

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: 18089 $")
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 
00058 static const char *tdesc = "MeetMe conference bridge";
00059 
00060 static const char *app = "MeetMe";
00061 static const char *app2 = "MeetMeCount";
00062 static const char *app3 = "MeetMeAdmin";
00063 
00064 static const char *synopsis = "MeetMe conference bridge";
00065 static const char *synopsis2 = "MeetMe participant count";
00066 static const char *synopsis3 = "MeetMe conference Administration";
00067 
00068 static const char *descrip =
00069 "  MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
00070 "If the conference number is omitted, the user will be prompted to enter\n"
00071 "one. \n"
00072 "User can exit the conference by hangup, or if the 'p' option is specified, by pressing '#'.\n"
00073 "Please note: The Zaptel kernel modules and at least one hardware driver (or ztdummy)\n"
00074 "             must be present for conferencing to operate properly. In addition, the chan_zap\n"
00075 "             channel driver must be loaded for the 'i' and 'r' options to operate at all.\n\n"
00076 "The option string may contain zero or more of the following characters:\n"
00077 "      'a' -- set admin mode\n"
00078 "      'A' -- set marked mode\n"
00079 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
00080 "             Default: conf-background.agi\n"
00081 "             (Note: This does not work with non-Zap channels in the same conference)\n"
00082 "      'c' -- announce user(s) count on joining a conference\n"
00083 "      'd' -- dynamically add conference\n"
00084 "      'D' -- dynamically add conference, prompting for a PIN\n"
00085 "      'e' -- select an empty conference\n"
00086 "      'E' -- select an empty pinless conference\n"
00087 "      'i' -- announce user join/leave\n"
00088 "      'm' -- set monitor only mode (Listen only, no talking)\n"
00089 "      'M' -- enable music on hold when the conference has a single caller\n"
00090 "      'p' -- allow user to exit the conference by pressing '#'\n"
00091 "      'P' -- always prompt for the pin even if it is specified\n"
00092 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
00093 "      'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
00094 "             using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
00095 "             meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is wav.\n"
00096 "      's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
00097 "      't' -- set talk only mode. (Talk only, no listening)\n"
00098 "      'T' -- set talker detection (sent to manager interface and meetme list)\n"
00099 "      'v' -- video mode\n"
00100 "      'w' -- wait until the marked user enters the conference\n"
00101 "      'x' -- close the conference when last marked user exits\n"
00102 "      'X' -- allow user to exit the conference by entering a valid single\n"
00103 "             digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
00104 "             if that variable is not defined.\n";
00105 
00106 static const char *descrip2 =
00107 "  MeetMeCount(confno[|var]): Plays back the number of users in the specified\n"
00108 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
00109 "will be returned in the variable. Upon app completion, MeetMeCount will hangup the\n"
00110 "channel, unless priority n+1 exists, in which case priority progress will continue.\n"
00111 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
00112 
00113 static const char *descrip3 = 
00114 "  MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
00115 "      'e' -- Eject last user that joined\n"
00116 "      'k' -- Kick one user out of conference\n"
00117 "      'K' -- Kick all users out of conference\n"
00118 "      'l' -- Unlock conference\n"
00119 "      'L' -- Lock conference\n"
00120 "      'm' -- Unmute conference\n"
00121 "      'M' -- Mute conference\n"
00122 "      'n' -- Unmute entire conference (except admin)\n"
00123 "      'N' -- Mute entire conference (except admin)\n"
00124 "";
00125 
00126 #define CONFIG_FILE_NAME "meetme.conf"
00127 
00128 STANDARD_LOCAL_USER;
00129 
00130 LOCAL_USER_DECL;
00131 
00132 static struct ast_conference {
00133    char confno[AST_MAX_EXTENSION];     /* Conference */
00134    struct ast_channel *chan;     /* Announcements channel */
00135    int fd;              /* Announcements fd */
00136    int zapconf;            /* Zaptel Conf # */
00137    int users;           /* Number of active users */
00138    int markedusers;        /* Number of marked users */
00139    struct ast_conf_user *firstuser; /* Pointer to the first user struct */
00140    struct ast_conf_user *lastuser;     /* Pointer to the last user struct */
00141    time_t start;           /* Start time (s) */
00142    int recording;          /* recording status */
00143    int isdynamic;          /* Created on the fly? */
00144    int locked;          /* Is the conference locked? */
00145    pthread_t recordthread;       /* thread for recording */
00146    pthread_attr_t attr;       /* thread attribute */
00147    char *recordingfilename;      /* Filename to record the Conference into */
00148    char *recordingformat;        /* Format to record the Conference in */
00149    char pin[AST_MAX_EXTENSION];     /* If protected by a PIN */
00150    char pinadmin[AST_MAX_EXTENSION];   /* If protected by a admin PIN */
00151    struct ast_conference *next;
00152 } *confs;
00153 
00154 struct volume {
00155    int desired;            /* Desired volume adjustment */
00156    int actual;          /* Actual volume adjustment (for channels that can't adjust) */
00157 };
00158 
00159 struct ast_conf_user {
00160    int user_no;            /* User Number */
00161    struct ast_conf_user *prevuser;     /* Pointer to the previous user */
00162    struct ast_conf_user *nextuser;     /* Pointer to the next user */
00163    int userflags;          /* Flags as set in the conference */
00164    int adminflags;            /* Flags set by the Admin */
00165    struct ast_channel *chan;     /* Connected channel */
00166    int talking;            /* Is user talking */
00167    int zapchannel;            /* Is a Zaptel channel */
00168    char usrvalue[50];         /* Custom User Value */
00169    char namerecloc[AST_MAX_EXTENSION]; /* Name Recorded file Location */
00170    time_t jointime;        /* Time the user joined the conference */
00171    struct volume talk;
00172    struct volume listen;
00173 };
00174 
00175 static int audio_buffers;        /* The number of audio buffers to be allocated on pseudo channels
00176                      when in a conference
00177                   */
00178 
00179 #define DEFAULT_AUDIO_BUFFERS 32    /* each buffer is 20ms, so this is 640ms total */
00180 
00181 #define ADMINFLAG_MUTED (1 << 1)    /* User is muted */
00182 #define ADMINFLAG_KICKME (1 << 2)      /* User is kicked */
00183 #define MEETME_DELAYDETECTTALK      300
00184 #define MEETME_DELAYDETECTENDTALK   1000
00185 
00186 enum volume_action {
00187    VOL_UP,
00188    VOL_DOWN,
00189 };
00190 
00191 AST_MUTEX_DEFINE_STATIC(conflock);
00192 
00193 static int admin_exec(struct ast_channel *chan, void *data);
00194 
00195 static void *recordthread(void *args);
00196 
00197 #include "enter.h"
00198 #include "leave.h"
00199 
00200 #define ENTER  0
00201 #define LEAVE  1
00202 
00203 #define MEETME_RECORD_OFF  0
00204 #define MEETME_RECORD_ACTIVE  1
00205 #define MEETME_RECORD_TERMINATE  2
00206 
00207 #define CONF_SIZE 320
00208 
00209 #define CONFFLAG_ADMIN  (1 << 1)    /* If set the user has admin access on the conference */
00210 #define CONFFLAG_MONITOR (1 << 2)      /* If set the user can only receive audio from the conference */
00211 #define CONFFLAG_POUNDEXIT (1 << 3)    /* If set asterisk will exit conference when '#' is pressed */
00212 #define CONFFLAG_STARMENU (1 << 4)     /* If set asterisk will provide a menu to the user when '*' is pressed */
00213 #define CONFFLAG_TALKER (1 << 5)    /* If set the use can only send audio to the conference */
00214 #define CONFFLAG_QUIET (1 << 6)        /* If set there will be no enter or leave sounds */
00215 #define CONFFLAG_VIDEO (1 << 7)        /* Set to enable video mode */
00216 #define CONFFLAG_AGI (1 << 8)       /* Set to run AGI Script in Background */
00217 #define CONFFLAG_MOH (1 << 9)       /* Set to have music on hold when user is alone in conference */
00218 #define CONFFLAG_MARKEDEXIT (1 << 10)     /* If set the MeetMe will return if all marked with this flag left */
00219 #define CONFFLAG_WAITMARKED (1 << 11)     /* If set, the MeetMe will wait until a marked user enters */
00220 #define CONFFLAG_EXIT_CONTEXT (1 << 12)      /* If set, the MeetMe will exit to the specified context */
00221 #define CONFFLAG_MARKEDUSER (1 << 13)     /* If set, the user will be marked */
00222 #define CONFFLAG_INTROUSER (1 << 14)      /* If set, user will be ask record name on entry of conference */
00223 #define CONFFLAG_RECORDCONF (1<< 15)      /* If set, the MeetMe will be recorded */
00224 #define CONFFLAG_MONITORTALKER (1 << 16)  /* If set, the user will be monitored if the user is talking or not */
00225 #define CONFFLAG_DYNAMIC (1 << 17)
00226 #define CONFFLAG_DYNAMICPIN (1 << 18)
00227 #define CONFFLAG_EMPTY (1 << 19)
00228 #define CONFFLAG_EMPTYNOPIN (1 << 20)
00229 #define CONFFLAG_ALWAYSPROMPT (1 << 21)
00230 #define CONFFLAG_ANNOUNCEUSERCOUNT (1 << 22) /* If set, when user joins the conference, they will be told the number of users that are already in */
00231 
00232 
00233 AST_APP_OPTIONS(meetme_opts, {
00234    AST_APP_OPTION('a', CONFFLAG_ADMIN ),
00235    AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
00236    AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
00237    AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
00238    AST_APP_OPTION('m', CONFFLAG_MONITOR ),
00239    AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ),
00240    AST_APP_OPTION('s', CONFFLAG_STARMENU ),
00241    AST_APP_OPTION('t', CONFFLAG_TALKER ),
00242    AST_APP_OPTION('q', CONFFLAG_QUIET ),
00243    AST_APP_OPTION('M', CONFFLAG_MOH ),
00244    AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
00245    AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
00246    AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
00247    AST_APP_OPTION('b', CONFFLAG_AGI ),
00248    AST_APP_OPTION('w', CONFFLAG_WAITMARKED ),
00249    AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
00250    AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
00251    AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
00252    AST_APP_OPTION('e', CONFFLAG_EMPTY ),
00253    AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
00254    AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
00255 });
00256 
00257 static char *istalking(int x)
00258 {
00259    if (x > 0)
00260       return "(talking)";
00261    else if (x < 0)
00262       return "(unmonitored)";
00263    else 
00264       return "(not talking)";
00265 }
00266 
00267 static int careful_write(int fd, unsigned char *data, int len, int block)
00268 {
00269    int res;
00270    int x;
00271 
00272    while (len) {
00273       if (block) {
00274          x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
00275          res = ioctl(fd, ZT_IOMUX, &x);
00276       } else
00277          res = 0;
00278       if (res >= 0)
00279          res = write(fd, data, len);
00280       if (res < 1) {
00281          if (errno != EAGAIN) {
00282             ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
00283             return -1;
00284          } else
00285             return 0;
00286       }
00287       len -= res;
00288       data += res;
00289    }
00290 
00291    return 0;
00292 }
00293 
00294 /* Map 'volume' levels from -5 through +5 into
00295    decibel (dB) settings for channel drivers
00296    Note: these are not a straight linear-to-dB
00297    conversion... the numbers have been modified
00298    to give the user a better level of adjustability
00299 */
00300 static signed char gain_map[] = {
00301    -15,
00302    -13,
00303    -10,
00304    -6,
00305    0,
00306    0,
00307    0,
00308    6,
00309    10,
00310    13,
00311    15,
00312 };
00313 
00314 static int set_talk_volume(struct ast_conf_user *user, int volume)
00315 {
00316    signed char gain_adjust;
00317 
00318    /* attempt to make the adjustment in the channel driver;
00319       if successful, don't adjust in the frame reading routine
00320    */
00321    gain_adjust = gain_map[volume + 5];
00322 
00323    return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
00324 }
00325 
00326 static int set_listen_volume(struct ast_conf_user *user, int volume)
00327 {
00328    signed char gain_adjust;
00329 
00330    /* attempt to make the adjustment in the channel driver;
00331       if successful, don't adjust in the frame reading routine
00332    */
00333    gain_adjust = gain_map[volume + 5];
00334 
00335    return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
00336 }
00337 
00338 static void tweak_volume(struct volume *vol, enum volume_action action)
00339 {
00340    switch (action) {
00341    case VOL_UP:
00342       switch (vol->desired) {
00343       case 5:
00344          break;
00345       case 0:
00346          vol->desired = 2;
00347          break;
00348       case -2:
00349          vol->desired = 0;
00350          break;
00351       default:
00352          vol->desired++;
00353          break;
00354       }
00355       break;
00356    case VOL_DOWN:
00357       switch (vol->desired) {
00358       case -5:
00359          break;
00360       case 2:
00361          vol->desired = 0;
00362          break;
00363       case 0:
00364          vol->desired = -2;
00365          break;
00366       default:
00367          vol->desired--;
00368          break;
00369       }
00370    }
00371 }
00372 
00373 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
00374 {
00375    tweak_volume(&user->talk, action);
00376    /* attempt to make the adjustment in the channel driver;
00377       if successful, don't adjust in the frame reading routine
00378    */
00379    if (!set_talk_volume(user, user->talk.desired))
00380       user->talk.actual = 0;
00381    else
00382       user->talk.actual = user->talk.desired;
00383 }
00384 
00385 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
00386 {
00387    tweak_volume(&user->listen, action);
00388    /* attempt to make the adjustment in the channel driver;
00389       if successful, don't adjust in the frame reading routine
00390    */
00391    if (!set_listen_volume(user, user->listen.desired))
00392       user->listen.actual = 0;
00393    else
00394       user->listen.actual = user->listen.desired;
00395 }
00396 
00397 static void reset_volumes(struct ast_conf_user *user)
00398 {
00399    signed char zero_volume = 0;
00400 
00401    ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00402    ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
00403 }
00404 
00405 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int sound)
00406 {
00407    unsigned char *data;
00408    int len;
00409    int res = -1;
00410 
00411    if (!chan->_softhangup)
00412       res = ast_autoservice_start(chan);
00413 
00414    ast_mutex_lock(&conflock);
00415 
00416    switch(sound) {
00417    case ENTER:
00418       data = enter;
00419       len = sizeof(enter);
00420       break;
00421    case LEAVE:
00422       data = leave;
00423       len = sizeof(leave);
00424       break;
00425    default:
00426       data = NULL;
00427       len = 0;
00428    }
00429    if (data) 
00430       careful_write(conf->fd, data, len, 1);
00431 
00432    ast_mutex_unlock(&conflock);
00433 
00434    if (!res) 
00435       ast_autoservice_stop(chan);
00436 }
00437 
00438 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic)
00439 {
00440    struct ast_conference *cnf;
00441    struct zt_confinfo ztc;
00442 
00443    ast_mutex_lock(&conflock);
00444 
00445    for (cnf = confs; cnf; cnf = cnf->next) {
00446       if (!strcmp(confno, cnf->confno)) 
00447          break;
00448    }
00449 
00450    if (!cnf && (make || dynamic)) {
00451       /* Make a new one */
00452       cnf = calloc(1, sizeof(*cnf));
00453       if (cnf) {
00454          ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
00455          ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
00456          ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
00457          cnf->markedusers = 0;
00458          cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL);
00459          if (cnf->chan) {
00460             cnf->fd = cnf->chan->fds[0];  /* for use by conf_play() */
00461          } else {
00462             ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
00463             cnf->fd = open("/dev/zap/pseudo", O_RDWR);
00464             if (cnf->fd < 0) {
00465                ast_log(LOG_WARNING, "Unable to open pseudo device\n");
00466                free(cnf);
00467                cnf = NULL;
00468                goto cnfout;
00469             }
00470          }
00471          memset(&ztc, 0, sizeof(ztc));
00472          /* Setup a new zap conference */
00473          ztc.chan = 0;
00474          ztc.confno = -1;
00475          ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
00476          if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
00477             ast_log(LOG_WARNING, "Error setting conference\n");
00478             if (cnf->chan)
00479                ast_hangup(cnf->chan);
00480             else
00481                close(cnf->fd);
00482             free(cnf);
00483             cnf = NULL;
00484             goto cnfout;
00485          }
00486          /* Fill the conference struct */
00487          cnf->start = time(NULL);
00488          cnf->zapconf = ztc.confno;
00489          cnf->isdynamic = dynamic;
00490          cnf->firstuser = NULL;
00491          cnf->lastuser = NULL;
00492          cnf->locked = 0;
00493          if (option_verbose > 2)
00494             ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
00495          cnf->next = confs;
00496          confs = cnf;
00497       } else   
00498          ast_log(LOG_WARNING, "Out of memory\n");
00499    }
00500  cnfout:
00501    ast_mutex_unlock(&conflock);
00502    return cnf;
00503 }
00504 
00505 static int confs_show(int fd, int argc, char **argv)
00506 {
00507    ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
00508 
00509    return RESULT_SUCCESS;
00510 }
00511 
00512 static char show_confs_usage[] =
00513 "Deprecated! Please use 'meetme' instead.\n";
00514 
00515 static struct ast_cli_entry cli_show_confs = {
00516    { "show", "conferences", NULL }, confs_show,
00517    "Show status of conferences", show_confs_usage, NULL };
00518    
00519 static int conf_cmd(int fd, int argc, char **argv) {
00520    /* Process the command */
00521    struct ast_conference *cnf;
00522    struct ast_conf_user *user;
00523    int hr, min, sec;
00524    int i = 0, total = 0;
00525    time_t now;
00526    char *header_format = "%-14s %-14s %-10s %-8s  %-8s\n";
00527    char *data_format = "%-12.12s   %4.4d        %4.4s       %02d:%02d:%02d  %-8s\n";
00528    char cmdline[1024] = "";
00529 
00530    if (argc > 8)
00531       ast_cli(fd, "Invalid Arguments.\n");
00532    /* Check for length so no buffer will overflow... */
00533    for (i = 0; i < argc; i++) {
00534       if (strlen(argv[i]) > 100)
00535          ast_cli(fd, "Invalid Arguments.\n");
00536    }
00537    if (argc == 1) {
00538       /* 'MeetMe': List all the conferences */  
00539       now = time(NULL);
00540       cnf = confs;
00541       if (!cnf) {
00542          ast_cli(fd, "No active MeetMe conferences.\n");
00543          return RESULT_SUCCESS;
00544       }
00545       ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
00546       while(cnf) {
00547          if (cnf->markedusers == 0)
00548             strcpy(cmdline, "N/A ");
00549          else 
00550             snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
00551          hr = (now - cnf->start) / 3600;
00552          min = ((now - cnf->start) % 3600) / 60;
00553          sec = (now - cnf->start) % 60;
00554 
00555          ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
00556 
00557          total += cnf->users;    
00558          cnf = cnf->next;
00559       }
00560       ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
00561       return RESULT_SUCCESS;
00562    }
00563    if (argc < 3)
00564       return RESULT_SHOWUSAGE;
00565    ast_copy_string(cmdline, argv[2], sizeof(cmdline));   /* Argv 2: conference number */
00566    if (strstr(argv[1], "lock")) {   
00567       if (strcmp(argv[1], "lock") == 0) {
00568          /* Lock */
00569          strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
00570       } else {
00571          /* Unlock */
00572          strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
00573       }
00574    } else if (strstr(argv[1], "mute")) { 
00575       if (argc < 4)
00576          return RESULT_SHOWUSAGE;
00577       if (strcmp(argv[1], "mute") == 0) {
00578          /* Mute */
00579          if (strcmp(argv[3], "all") == 0) {
00580             strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
00581          } else {
00582             strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);   
00583             strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
00584          }
00585       } else {
00586          /* Unmute */
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       }
00594    } else if (strcmp(argv[1], "kick") == 0) {
00595       if (argc < 4)
00596          return RESULT_SHOWUSAGE;
00597       if (strcmp(argv[3], "all") == 0) {
00598          /* Kick all */
00599          strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
00600       } else {
00601          /* Kick a single user */
00602          strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
00603          strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
00604       }  
00605    } else if(strcmp(argv[1], "list") == 0) {
00606       /* List all the users in a conference */
00607       if (!confs) {
00608          ast_cli(fd, "No active conferences.\n");
00609          return RESULT_SUCCESS;  
00610       }
00611       cnf = confs;
00612       /* Find the right conference */
00613       while(cnf) {
00614          if (strcmp(cnf->confno, argv[2]) == 0)
00615             break;
00616          if (cnf->next) {
00617             cnf = cnf->next;  
00618          } else {
00619             ast_cli(fd, "No such conference: %s.\n",argv[2]);
00620             return RESULT_SUCCESS;
00621          }
00622       }
00623       /* Show all the users */
00624       for (user = cnf->firstuser; user; user = user->nextuser)
00625          ast_cli(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s\n",
00626             user->user_no,
00627             user->chan->cid.cid_num ? user->chan->cid.cid_num : "<unknown>",
00628             user->chan->cid.cid_name ? user->chan->cid.cid_name : "<no name>",
00629             user->chan->name,
00630             user->userflags & CONFFLAG_ADMIN ? "(Admin)" : "",
00631             user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "",
00632             user->adminflags & ADMINFLAG_MUTED ? "(Admn Muted)" : "",
00633             istalking(user->talking));
00634       ast_cli(fd,"%d users in that conference.\n",cnf->users);
00635 
00636       return RESULT_SUCCESS;
00637    } else 
00638       return RESULT_SHOWUSAGE;
00639    ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
00640    admin_exec(NULL, cmdline);
00641 
00642    return 0;
00643 }
00644 
00645 static char *complete_confcmd(char *line, char *word, int pos, int state) {
00646 #define CONF_COMMANDS 6
00647    int which = 0, x = 0;
00648    struct ast_conference *cnf = NULL;
00649    struct ast_conf_user *usr = NULL;
00650    char *confno = NULL;
00651    char usrno[50] = "";
00652    char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
00653    char *myline;
00654    
00655    if (pos == 1) {
00656       /* Command */
00657       for (x = 0;x < CONF_COMMANDS; x++) {
00658          if (!strncasecmp(cmds[x], word, strlen(word))) {
00659             if (++which > state) {
00660                return strdup(cmds[x]);
00661             }
00662          }
00663       }
00664    } else if (pos == 2) {
00665       /* Conference Number */
00666       ast_mutex_lock(&conflock);
00667       cnf = confs;
00668       while(cnf) {
00669          if (!strncasecmp(word, cnf->confno, strlen(word))) {
00670             if (++which > state)
00671                break;
00672          }
00673          cnf = cnf->next;
00674       }
00675       ast_mutex_unlock(&conflock);
00676       return cnf ? strdup(cnf->confno) : NULL;
00677    } else if (pos == 3) {
00678       /* User Number || Conf Command option*/
00679       if (strstr(line, "mute") || strstr(line, "kick")) {
00680          if ((state == 0) && (strstr(line, "kick") || strstr(line,"mute")) && !(strncasecmp(word, "all", strlen(word)))) {
00681             return strdup("all");
00682          }
00683          which++;
00684          ast_mutex_lock(&conflock);
00685 
00686          /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
00687          myline = ast_strdupa(line);
00688          if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
00689             while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
00690                ;
00691          }
00692          
00693          for (cnf = confs; cnf; cnf = cnf->next) {
00694             if (!strcmp(confno, cnf->confno))
00695                 break;
00696          }
00697 
00698          if (cnf) {
00699             /* Search for the user */
00700             for (usr = cnf->firstuser; usr; usr = usr->nextuser) {
00701                snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
00702                if (!strncasecmp(word, usrno, strlen(word))) {
00703                   if (++which > state)
00704                      break;
00705                }
00706             }
00707          }
00708          ast_mutex_unlock(&conflock);
00709          return usr ? strdup(usrno) : NULL;
00710       }
00711    }
00712 
00713    return NULL;
00714 }
00715    
00716 static char conf_usage[] =
00717 "Usage: meetme  (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
00718 "       Executes a command for the conference or on a conferee\n";
00719 
00720 static struct ast_cli_entry cli_conf = {
00721    {"meetme", NULL, NULL }, conf_cmd,
00722    "Execute a command on a conference or conferee", conf_usage, complete_confcmd};
00723 
00724 static void conf_flush(int fd, struct ast_channel *chan)
00725 {
00726    int x;
00727 
00728    /* read any frames that may be waiting on the channel
00729       and throw them away
00730    */
00731    if (chan) {
00732       struct ast_frame *f;
00733 
00734       /* when no frames are available, this will wait
00735          for 1 millisecond maximum
00736       */
00737       while (ast_waitfor(chan, 1)) {
00738          f = ast_read(chan);
00739          if (f)
00740             ast_frfree(f);
00741          else /* channel was hung up or something else happened */
00742             break;
00743       }
00744    }
00745 
00746    /* flush any data sitting in the pseudo channel */
00747    x = ZT_FLUSH_ALL;
00748    if (ioctl(fd, ZT_FLUSH, &x))
00749       ast_log(LOG_WARNING, "Error flushing channel\n");
00750 
00751 }
00752 
00753 /* Remove the conference from the list and free it.
00754    We assume that this was called while holding conflock. */
00755 static int conf_free(struct ast_conference *conf)
00756 {
00757    struct ast_conference *prev = NULL, *cur = confs;
00758 
00759    while (cur) {
00760       if (cur == conf) {
00761          if (prev)
00762             prev->next = conf->next;
00763          else
00764             confs = conf->next;
00765          break;
00766       }
00767       prev = cur;
00768       cur = cur->next;
00769    }
00770 
00771    if (!cur)
00772       ast_log(LOG_WARNING, "Conference not found\n");
00773 
00774    if (conf->recording == MEETME_RECORD_ACTIVE) {
00775       conf->recording = MEETME_RECORD_TERMINATE;
00776       ast_mutex_unlock(&conflock);
00777       while (1) {
00778          ast_mutex_lock(&conflock);
00779          if (conf->recording == MEETME_RECORD_OFF)
00780             break;
00781          ast_mutex_unlock(&conflock);
00782       }
00783    }
00784 
00785    if (conf->chan)
00786       ast_hangup(conf->chan);
00787    else
00788       close(conf->fd);
00789    
00790    free(conf);
00791 
00792    return 0;
00793 }
00794 
00795 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
00796 {
00797    struct ast_conf_user *user = calloc(1, sizeof(*user));
00798    struct ast_conf_user *usr = NULL;
00799    int fd;
00800    struct zt_confinfo ztc, ztc_empty;
00801    struct ast_frame *f;
00802    struct ast_channel *c;
00803    struct ast_frame fr;
00804    int outfd;
00805    int ms;
00806    int nfds;
00807    int res;
00808    int flags;
00809    int retryzap;
00810    int origfd;
00811    int musiconhold = 0;
00812    int firstpass = 0;
00813    int lastmarked = 0;
00814    int currentmarked = 0;
00815    int ret = -1;
00816    int x;
00817    int menu_active = 0;
00818    int using_pseudo = 0;
00819    int duration=20;
00820    struct ast_dsp *dsp=NULL;
00821    struct ast_app *app;
00822    char *agifile;
00823    char *agifiledefault = "conf-background.agi";
00824    char meetmesecs[30] = "";
00825    char exitcontext[AST_MAX_CONTEXT] = "";
00826    char recordingtmp[AST_MAX_EXTENSION] = "";
00827    int dtmf;
00828    ZT_BUFFERINFO bi;
00829    char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
00830    char *buf = __buf + AST_FRIENDLY_OFFSET;
00831    
00832    if (!user) {
00833       ast_log(LOG_ERROR, "Out of memory\n");
00834       return ret;
00835    }
00836 
00837    if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
00838       conf->recordingfilename = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE");
00839       if (!conf->recordingfilename) {
00840          snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
00841          conf->recordingfilename = ast_strdupa(recordingtmp);
00842       }
00843       conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
00844       if (!conf->recordingformat) {
00845          snprintf(recordingtmp, sizeof(recordingtmp), "wav");
00846          conf->recordingformat = ast_strdupa(recordingtmp);
00847       }
00848       pthread_attr_init(&conf->attr);
00849       pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
00850       ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n",
00851              conf->confno, conf->recordingfilename, conf->recordingformat);
00852       ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
00853    }
00854 
00855    time(&user->jointime);
00856 
00857    if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) {
00858       /* Sorry, but this confernce is locked! */   
00859       if (!ast_streamfile(chan, "conf-locked", chan->language))
00860          ast_waitstream(chan, "");
00861       goto outrun;
00862    }
00863 
00864    if (confflags & CONFFLAG_MARKEDUSER)
00865       conf->markedusers++;
00866       
00867       ast_mutex_lock(&conflock);
00868    if (!conf->firstuser) {
00869       /* Fill the first new User struct */
00870       user->user_no = 1;
00871       conf->firstuser = user;
00872       conf->lastuser = user;
00873    } else {
00874       /* Fill the new user struct */   
00875       user->user_no = conf->lastuser->user_no + 1; 
00876       user->prevuser = conf->lastuser;
00877       if (conf->lastuser->nextuser) {
00878          ast_log(LOG_WARNING, "Error in User Management!\n");
00879          ast_mutex_unlock(&conflock);
00880          goto outrun;
00881       } else {
00882          conf->lastuser->nextuser = user;
00883          conf->lastuser = user;
00884       }
00885    }
00886 
00887    user->chan = chan;
00888    user->userflags = confflags;
00889    user->adminflags = 0;
00890    user->talking = -1;
00891    conf->users++;
00892    ast_mutex_unlock(&conflock);
00893 
00894    if (confflags & CONFFLAG_EXIT_CONTEXT) {
00895       if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) 
00896          ast_copy_string(exitcontext, agifile, sizeof(exitcontext));
00897       else if (!ast_strlen_zero(chan->macrocontext)) 
00898          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00899       else
00900          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00901    }
00902 
00903    if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
00904       snprintf(user->namerecloc, sizeof(user->namerecloc),
00905           "%s/meetme/meetme-username-%s-%d", ast_config_AST_SPOOL_DIR,
00906           conf->confno, user->user_no);
00907       res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
00908       if (res == -1)
00909          goto outrun;
00910    }
00911 
00912    if (!(confflags & CONFFLAG_QUIET)) {
00913       if (conf->users == 1 && !(confflags & CONFFLAG_WAITMARKED))
00914          if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
00915             ast_waitstream(chan, "");
00916       if ((confflags & CONFFLAG_WAITMARKED) && conf->markedusers == 0)
00917          if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
00918             ast_waitstream(chan, "");
00919    }
00920 
00921    if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_ANNOUNCEUSERCOUNT) && conf->users > 1) {
00922       int keepplaying = 1;
00923 
00924       if (conf->users == 2) { 
00925          if (!ast_streamfile(chan,"conf-onlyone",chan->language)) {
00926             res = ast_waitstream(chan, AST_DIGIT_ANY);
00927             if (res > 0)
00928                keepplaying=0;
00929             else if (res == -1)
00930                goto outrun;
00931          }
00932       } else { 
00933          if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
00934             res = ast_waitstream(chan, AST_DIGIT_ANY);
00935             if (res > 0)
00936                keepplaying=0;
00937             else if (res == -1)
00938                goto outrun;
00939          }
00940          if (keepplaying) {
00941             res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
00942             if (res > 0)
00943                keepplaying=0;
00944             else if (res == -1)
00945                goto outrun;
00946          }
00947          if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
00948             res = ast_waitstream(chan, AST_DIGIT_ANY);
00949             if (res > 0)
00950                keepplaying=0;
00951             else if (res == -1) 
00952                goto outrun;
00953          }
00954       }
00955    }
00956 
00957    ast_indicate(chan, -1);
00958 
00959    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00960       ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
00961       goto outrun;
00962    }
00963 
00964    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
00965       ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
00966       goto outrun;
00967    }
00968 
00969    retryzap = strcasecmp(chan->type, "Zap");
00970    user->zapchannel = !retryzap;
00971 
00972  zapretry:
00973    origfd = chan->fds[0];
00974    if (retryzap) {
00975       fd = open("/dev/zap/pseudo", O_RDWR);
00976       if (fd < 0) {
00977          ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
00978          goto outrun;
00979       }
00980       using_pseudo = 1;
00981       /* Make non-blocking */
00982       flags = fcntl(fd, F_GETFL);
00983       if (flags < 0) {
00984          ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
00985          close(fd);
00986          goto outrun;
00987       }
00988       if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
00989          ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
00990          close(fd);
00991          goto outrun;
00992       }
00993       /* Setup buffering information */
00994       memset(&bi, 0, sizeof(bi));
00995       bi.bufsize = CONF_SIZE/2;
00996       bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
00997       bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
00998       bi.numbufs = audio_buffers;
00999       if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
01000          ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
01001          close(fd);
01002          goto outrun;
01003       }
01004       x = 1;
01005       if (ioctl(fd, ZT_SETLINEAR, &x)) {
01006          ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
01007          close(fd);
01008          goto outrun;
01009       }
01010       nfds = 1;
01011    } else {
01012       /* XXX Make sure we're not running on a pseudo channel XXX */
01013       fd = chan->fds[0];
01014       nfds = 0;
01015    }
01016    memset(&ztc, 0, sizeof(ztc));
01017    memset(&ztc_empty, 0, sizeof(ztc_empty));
01018    /* Check to see if we're in a conference... */
01019    ztc.chan = 0;  
01020    if (ioctl(fd, ZT_GETCONF, &ztc)) {
01021       ast_log(LOG_WARNING, "Error getting conference\n");
01022       close(fd);
01023       goto outrun;
01024    }
01025    if (ztc.confmode) {
01026       /* Whoa, already in a conference...  Retry... */
01027       if (!retryzap) {
01028          ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
01029          retryzap = 1;
01030          goto zapretry;
01031       }
01032    }
01033    memset(&ztc, 0, sizeof(ztc));
01034    /* Add us to the conference */
01035    ztc.chan = 0;  
01036    ztc.confno = conf->zapconf;
01037 
01038    ast_mutex_lock(&conflock);
01039 
01040    if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
01041       if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
01042          if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
01043             ast_waitstream(conf->chan, "");
01044          if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
01045             ast_waitstream(conf->chan, "");
01046       }
01047    }
01048 
01049    if (confflags & CONFFLAG_MONITOR)
01050       ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
01051    else if (confflags & CONFFLAG_TALKER)
01052       ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
01053    else 
01054       ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
01055 
01056    if (ioctl(fd, ZT_SETCONF, &ztc)) {
01057       ast_log(LOG_WARNING, "Error setting conference\n");
01058       close(fd);
01059       ast_mutex_unlock(&conflock);
01060       goto outrun;
01061    }
01062    ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
01063 
01064    manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
01065             "Channel: %s\r\n"
01066             "Uniqueid: %s\r\n"
01067             "Meetme: %s\r\n"
01068             "Usernum: %d\r\n",
01069             chan->name, chan->uniqueid, conf->confno, user->user_no);
01070 
01071    if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
01072       firstpass = 1;
01073       if (!(confflags & CONFFLAG_QUIET))
01074          if (!(confflags & CONFFLAG_WAITMARKED) || (conf->markedusers >= 1))
01075             conf_play(chan, conf, ENTER);
01076    }
01077 
01078    ast_mutex_unlock(&conflock);
01079 
01080    conf_flush(fd, chan);
01081 
01082    if (confflags & CONFFLAG_AGI) {
01083       /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
01084          or use default filename of conf-background.agi */
01085 
01086       agifile = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND");
01087       if (!agifile)
01088          agifile = agifiledefault;
01089 
01090       if (user->zapchannel) {
01091          /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
01092          x = 1;
01093          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
01094       }
01095       /* Find a pointer to the agi app and execute the script */
01096       app = pbx_findapp("agi");
01097       if (app) {
01098          ret = pbx_exec(chan, app, agifile, 1);
01099       } else {
01100          ast_log(LOG_WARNING, "Could not find application (agi)\n");
01101          ret = -2;
01102       }
01103       if (user->zapchannel) {
01104          /*  Remove CONFMUTE mode on Zap channel */
01105          x = 0;
01106          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
01107       }
01108    } else {
01109       if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
01110          /*  Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
01111          x = 1;
01112          ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
01113       }  
01114       if (confflags & CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
01115          ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
01116          res = -1;
01117       }
01118       for(;;) {
01119          int menu_was_active = 0;
01120 
01121          outfd = -1;
01122          ms = -1;
01123          
01124          /* if we have just exited from the menu, and the user had a channel-driver
01125             volume adjustment, restore it
01126          */
01127          if (!menu_active && menu_was_active && user->listen.desired && !user->listen.actual)
01128             set_talk_volume(user, user->listen.desired);
01129 
01130          menu_was_active = menu_active;
01131 
01132          currentmarked = conf->markedusers;
01133          if (!(confflags & CONFFLAG_QUIET) &&
01134              (confflags & CONFFLAG_MARKEDUSER) &&
01135              (confflags & CONFFLAG_WAITMARKED) &&
01136              lastmarked == 0) {
01137             if (currentmarked == 1 && conf->users > 1) {
01138                ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
01139                if (conf->users - 1 == 1) {
01140                   if (!ast_streamfile(chan, "conf-userwilljoin", chan->language))
01141                      ast_waitstream(chan, "");
01142                } else {
01143                   if (!ast_streamfile(chan, "conf-userswilljoin", chan->language))
01144                      ast_waitstream(chan, "");
01145                }
01146             }
01147             if (conf->users == 1 && ! (confflags & CONFFLAG_MARKEDUSER))
01148                if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
01149                   ast_waitstream(chan, "");
01150          }
01151 
01152          c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
01153          
01154          /* Update the struct with the actual confflags */
01155          user->userflags = confflags;
01156          
01157          if (confflags & CONFFLAG_WAITMARKED) {
01158             if(currentmarked == 0) {
01159                if (lastmarked != 0) {
01160                   if (!(confflags & CONFFLAG_QUIET))
01161                      if (!ast_streamfile(chan, "conf-leaderhasleft", chan->language))
01162                         ast_waitstream(chan, "");
01163                   if(confflags & CONFFLAG_MARKEDEXIT)
01164                      break;
01165                   else {
01166                      ztc.confmode = ZT_CONF_CONF;
01167                      if (ioctl(fd, ZT_SETCONF, &ztc)) {
01168                         ast_log(LOG_WARNING, "Error setting conference\n");
01169                         close(fd);
01170                         goto outrun;
01171                      }
01172                   }
01173                }
01174                if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) {
01175                   ast_moh_start(chan, NULL);
01176                   musiconhold = 1;
01177                } else {
01178                   ztc.confmode = ZT_CONF_CONF;
01179                   if (ioctl(fd, ZT_SETCONF, &ztc)) {
01180                      ast_log(LOG_WARNING, "Error setting conference\n");
01181                      close(fd);
01182                      goto outrun;
01183                   }
01184                }
01185             } else if(currentmarked >= 1 && lastmarked == 0) {
01186                if (confflags & CONFFLAG_MONITOR)
01187                   ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
01188                else if (confflags & CONFFLAG_TALKER)
01189                   ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
01190                else
01191                   ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
01192                if (ioctl(fd, ZT_SETCONF, &ztc)) {
01193                   ast_log(LOG_WARNING, "Error setting conference\n");
01194                   close(fd);
01195                   goto outrun;
01196                }
01197                if (musiconhold && (confflags & CONFFLAG_MOH)) {
01198                   ast_moh_stop(chan);
01199                   musiconhold = 0;
01200                }
01201                if ( !(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MARKEDUSER)) {
01202                   if (!ast_streamfile(chan, "conf-placeintoconf", chan->language))
01203                      ast_waitstream(chan, "");
01204                   conf_play(chan, conf, ENTER);
01205                }
01206             }
01207          }
01208 
01209          /* trying to add moh for single person conf */
01210          if ((confflags & CONFFLAG_MOH) && !(confflags & CONFFLAG_WAITMARKED)) {
01211             if (conf->users == 1) {
01212                if (musiconhold == 0) {
01213                   ast_moh_start(chan, NULL);
01214                   musiconhold = 1;
01215                } 
01216             } else {
01217                if (musiconhold) {
01218                   ast_moh_stop(chan);
01219                   musiconhold = 0;
01220                }
01221             }
01222          }
01223          
01224          /* Leave if the last marked user left */
01225          if (currentmarked == 0 && lastmarked != 0 && (confflags & CONFFLAG_MARKEDEXIT)) {
01226             ret = -1;
01227             break;
01228          }
01229    
01230          /* Check if the admin changed my modes */
01231          if (user->adminflags) {       
01232             /* Set the new modes */
01233             if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
01234                ztc.confmode ^= ZT_CONF_TALKER;
01235                if (ioctl(fd, ZT_SETCONF, &ztc)) {
01236                   ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
01237                   ret = -1;
01238                   break;
01239                }
01240             }
01241             if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
01242                ztc.confmode |= ZT_CONF_TALKER;
01243                if (ioctl(fd, ZT_SETCONF, &ztc)) {
01244                   ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
01245                   ret = -1;
01246                   break;
01247                }
01248             }
01249             if (user->adminflags & ADMINFLAG_KICKME) {
01250                /* You have been kicked. */
01251                if (!ast_streamfile(chan, "conf-kicked", chan->language))
01252                   ast_waitstream(chan, "");
01253                ret = 0;
01254                break;
01255             }
01256          } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
01257             ztc.confmode |= ZT_CONF_TALKER;
01258             if (ioctl(fd, ZT_SETCONF, &ztc)) {
01259                ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
01260                ret = -1;
01261                break;
01262             }
01263          }
01264 
01265          if (c) {
01266             if (c->fds[0] != origfd) {
01267                if (using_pseudo) {
01268                   /* Kill old pseudo */
01269                   close(fd);
01270                   using_pseudo = 0;
01271                }
01272                ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
01273                retryzap = strcasecmp(c->type, "Zap");
01274                user->zapchannel = !retryzap;
01275                goto zapretry;
01276             }
01277             f = ast_read(c);
01278             if (!f)
01279                break;
01280             if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
01281                if (user->talk.actual)
01282                   ast_frame_adjust_volume(f, user->talk.actual);
01283 
01284                if (confflags &  CONFFLAG_MONITORTALKER) {
01285                   int totalsilence;
01286 
01287                   if (user->talking == -1)
01288                      user->talking = 0;
01289 
01290                   res = ast_dsp_silence(dsp, f, &totalsilence);
01291                   if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
01292                      user->talking = 1;
01293                      manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
01294                               "Channel: %s\r\n"
01295                               "Uniqueid: %s\r\n"
01296                               "Meetme: %s\r\n"
01297                               "Usernum: %d\r\n",
01298                               chan->name, chan->uniqueid, conf->confno, user->user_no);
01299                   }
01300                   if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
01301                      user->talking = 0;
01302                      manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
01303                               "Channel: %s\r\n"
01304                               "Uniqueid: %s\r\n"
01305                               "Meetme: %s\r\n"
01306                               "Usernum: %d\r\n",
01307                               chan->name, chan->uniqueid, conf->confno, user->user_no);
01308                   }
01309                }
01310                if (using_pseudo) {
01311                   /* Absolutely do _not_ use careful_write here...
01312                      it is important that we read data from the channel
01313                      as fast as it arrives, and feed it into the conference.
01314                      The buffering in the pseudo channel will take care of any
01315                      timing differences, unless they are so drastic as to lose
01316                      audio frames (in which case carefully writing would only
01317                      have delayed the audio even further).
01318                   */
01319                   /* As it turns out, we do want to use careful write.  We just
01320                      don't want to block, but we do want to at least *try*
01321                      to write out all the samples.
01322                    */
01323                   careful_write(fd, f->data, f->datalen, 0);
01324                }
01325             } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
01326                char tmp[2];
01327 
01328                tmp[0] = f->subclass;
01329                tmp[1] = '\0';
01330                if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
01331                   ret = 0;
01332                   break;
01333                } else if (option_debug > 1)
01334                   ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension %s does not exist in context %s\n", tmp, exitcontext);
01335             } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
01336                ret = 0;
01337                break;
01338             } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
01339                if (ioctl(fd, ZT_SETCONF, &ztc_empty)) {
01340                   ast_log(LOG_WARNING, "Error setting conference\n");
01341                   close(fd);
01342                   goto outrun;
01343                }
01344 
01345                /* if we are entering the menu, and the user has a channel-driver
01346                   volume adjustment, clear it
01347                */
01348                if (!menu_active && user->talk.desired && !user->talk.actual)
01349                   set_talk_volume(user, 0);
01350 
01351                if (musiconhold) {
01352                      ast_moh_stop(chan);
01353                }
01354                if ((confflags & CONFFLAG_ADMIN)) {
01355                   /* Admin menu */
01356                   if (!menu_active) {
01357                      menu_active = 1;
01358                      /* Record this sound! */
01359                      if (!ast_streamfile(chan, "conf-adminmenu", chan->language)) {
01360                         dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
01361                         ast_stopstream(chan);
01362                      } else 
01363                         dtmf = 0;
01364                   } else 
01365                      dtmf = f->subclass;
01366                   if (dtmf) {
01367                      switch(dtmf) {
01368                      case '1': /* Un/Mute */
01369                         menu_active = 0;
01370                         if (ztc.confmode & ZT_CONF_TALKER) {
01371                                  ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
01372                                  confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
01373                         } else {
01374                            ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
01375                            confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
01376                         }
01377                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
01378                            ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
01379                            ret = -1;
01380                            break;
01381                         }
01382                         if (ztc.confmode & ZT_CONF_TALKER) {
01383                            if (!ast_streamfile(chan, "conf-unmuted", chan->language))
01384                               ast_waitstream(chan, "");
01385                         } else {
01386                            if (!ast_streamfile(chan, "conf-muted", chan->language))
01387                               ast_waitstream(chan, "");
01388                         }
01389                         break;
01390                      case '2': /* Un/Lock the Conference */
01391                         menu_active = 0;
01392                         if (conf->locked) {
01393                            conf->locked = 0;
01394                            if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
01395                               ast_waitstream(chan, "");
01396                         } else {
01397                            conf->locked = 1;
01398                            if (!ast_streamfile(chan, "conf-lockednow", chan->language))
01399                               ast_waitstream(chan, "");
01400                         }
01401                         break;
01402                      case '3': /* Eject last user */
01403                         menu_active = 0;
01404                         usr = conf->lastuser;
01405                         if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
01406                            if(!ast_streamfile(chan, "conf-errormenu", chan->language))
01407                               ast_waitstream(chan, "");
01408                         } else 
01409                            usr->adminflags |= ADMINFLAG_KICKME;
01410                         ast_stopstream(chan);
01411                         break;   
01412                      case '4':
01413                         tweak_listen_volume(user, VOL_DOWN);
01414                         break;
01415                      case '6':
01416                         tweak_listen_volume(user, VOL_UP);
01417                         break;
01418                      case '7':
01419                         tweak_talk_volume(user, VOL_DOWN);
01420                         break;
01421                      case '8':
01422                         menu_active = 0;
01423                         break;
01424                      case '9':
01425                         tweak_talk_volume(user, VOL_UP);
01426                         break;
01427                      default:
01428                         menu_active = 0;
01429                         /* Play an error message! */
01430                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
01431                            ast_waitstream(chan, "");
01432                         break;
01433                      }
01434                   }
01435                } else {
01436                   /* User menu */
01437                   if (!menu_active) {
01438                      menu_active = 1;
01439                      if (!ast_streamfile(chan, "conf-usermenu", chan->language)) {
01440                         dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
01441                         ast_stopstream(chan);
01442                      } else
01443                         dtmf = 0;
01444                   } else 
01445                      dtmf = f->subclass;
01446                   if (dtmf) {
01447                      switch(dtmf) {
01448                      case '1': /* Un/Mute */
01449                         menu_active = 0;
01450                         if (ztc.confmode & ZT_CONF_TALKER) {
01451                                  ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
01452                                  confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
01453                         } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
01454                            ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
01455                            confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
01456                         }
01457                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
01458                            ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
01459                            ret = -1;
01460                            break;
01461                         }
01462                         if (ztc.confmode & ZT_CONF_TALKER) {
01463                            if (!ast_streamfile(chan, "conf-unmuted", chan->language))
01464                               ast_waitstream(chan, "");
01465                         } else {
01466                            if (!ast_streamfile(chan, "conf-muted", chan->language))
01467                               ast_waitstream(chan, "");
01468                         }
01469                         break;
01470                      case '4':
01471                         tweak_listen_volume(user, VOL_DOWN);
01472                         break;
01473                      case '6':
01474                         tweak_listen_volume(user, VOL_UP);
01475                         break;
01476                      case '7':
01477                         tweak_talk_volume(user, VOL_DOWN);
01478                         break;
01479                      case '8':
01480                         menu_active = 0;
01481                         break;
01482                      case '9':
01483                         tweak_talk_volume(user, VOL_UP);
01484                         break;
01485                      default:
01486                         menu_active = 0;
01487                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
01488                            ast_waitstream(chan, "");
01489                         break;
01490                      }
01491                   }
01492                }
01493                if (musiconhold)
01494                      ast_moh_start(chan, NULL);
01495 
01496                if (ioctl(fd, ZT_SETCONF, &ztc)) {
01497                   ast_log(LOG_WARNING, "Error setting conference\n");
01498                   close(fd);
01499                   ast_mutex_unlock(&conflock);
01500                   goto outrun;
01501                }
01502 
01503                conf_flush(fd, chan);
01504             } else if (option_debug) {
01505                ast_log(LOG_DEBUG,
01506                   "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",
01507                   chan->name, f->frametype, f->subclass);
01508             }
01509             ast_frfree(f);
01510          } else if (outfd > -1) {
01511             res = read(outfd, buf, CONF_SIZE);
01512             if (res > 0) {
01513                memset(&fr, 0, sizeof(fr));
01514                fr.frametype = AST_FRAME_VOICE;
01515                fr.subclass = AST_FORMAT_SLINEAR;
01516                fr.datalen = res;
01517                fr.samples = res/2;
01518                fr.data = buf;
01519                fr.offset = AST_FRIENDLY_OFFSET;
01520                if (user->listen.actual)
01521                   ast_frame_adjust_volume(&fr, user->listen.actual);
01522                if (ast_write(chan, &fr) < 0) {
01523                   ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
01524                }
01525             } else 
01526                ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
01527          }
01528          lastmarked = currentmarked;
01529       }
01530    }
01531 
01532    if (musiconhold)
01533       ast_moh_stop(chan);
01534    
01535    if (using_pseudo)
01536       close(fd);
01537    else {
01538       /* Take out of conference */
01539       ztc.chan = 0;  
01540       ztc.confno = 0;
01541       ztc.confmode = 0;
01542       if (ioctl(fd, ZT_SETCONF, &ztc)) {
01543          ast_log(LOG_WARNING, "Error setting conference\n");
01544       }
01545    }
01546 
01547    reset_volumes(user);
01548 
01549    ast_mutex_lock(&conflock);
01550    if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
01551       conf_play(chan, conf, LEAVE);
01552 
01553    if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
01554       if (ast_fileexists(user->namerecloc, NULL, NULL)) {
01555          if ((conf->chan) && (conf->users > 1)) {
01556             if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
01557                ast_waitstream(conf->chan, "");
01558             if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
01559                ast_waitstream(conf->chan, "");
01560          }
01561          ast_filedelete(user->namerecloc, NULL);
01562       }
01563    }
01564    ast_mutex_unlock(&conflock);
01565 
01566  outrun:
01567    ast_mutex_lock(&conflock);
01568 
01569    if (confflags & CONFFLAG_MONITORTALKER && dsp)
01570       ast_dsp_free(dsp);
01571    
01572    if (user->user_no) { /* Only cleanup users who really joined! */
01573       manager_event(EVENT_FLAG_CALL, "MeetmeLeave", 
01574                "Channel: %s\r\n"
01575                "Uniqueid: %s\r\n"
01576                "Meetme: %s\r\n"
01577                "Usernum: %d\r\n",
01578                chan->name, chan->uniqueid, conf->confno, user->user_no);
01579       conf->users--;
01580       if (confflags & CONFFLAG_MARKEDUSER) 
01581          conf->markedusers--;
01582       if (!conf->users) {
01583          /* No more users -- close this one out */
01584          conf_free(conf);
01585       } else {
01586          /* Remove the user struct */ 
01587          if (user == conf->firstuser) {
01588             if (user->nextuser) {
01589                /* There is another entry */
01590                user->nextuser->prevuser = NULL;
01591             } else {
01592                /* We are the only entry */
01593                conf->lastuser = NULL;
01594             }
01595             /* In either case */
01596             conf->firstuser = user->nextuser;
01597          } else if (user == conf->lastuser){
01598             if (user->prevuser)
01599                user->prevuser->nextuser = NULL;
01600             else
01601                ast_log(LOG_ERROR, "Bad bad bad!  We're the last, not the first, but nobody before us??\n");
01602             conf->lastuser = user->prevuser;
01603          } else {
01604             if (user->nextuser)
01605                user->nextuser->prevuser = user->prevuser;
01606             else
01607                ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
01608             if (user->prevuser)
01609                user->prevuser->nextuser = user->nextuser;
01610             else
01611                ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
01612          }
01613       }
01614       /* Return the number of seconds the user was in the conf */
01615       snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
01616       pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
01617    }
01618    free(user);
01619    ast_mutex_unlock(&conflock);
01620 
01621    return ret;
01622 }
01623 
01624 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin,
01625                struct ast_flags *confflags)
01626 {
01627    struct ast_config *cfg;
01628    struct ast_variable *var;
01629    struct ast_conference *cnf;
01630 
01631    /* Check first in the conference list */
01632    ast_mutex_lock(&conflock);
01633    for (cnf = confs; cnf; cnf = cnf->next) {
01634       if (!strcmp(confno, cnf->confno)) 
01635          break;
01636    }
01637    ast_mutex_unlock(&conflock);
01638 
01639    if (!cnf) {
01640       if (dynamic) {
01641          /* No need to parse meetme.conf */
01642          ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
01643          if (dynamic_pin) {
01644             if (dynamic_pin[0] == 'q') {
01645                /* Query the user to enter a PIN */
01646                if (ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0) < 0)
01647                   return NULL;
01648             }
01649             cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
01650          } else {
01651             cnf = build_conf(confno, "", "", make, dynamic);
01652          }
01653       } else {
01654          /* Check the config */
01655          cfg = ast_config_load(CONFIG_FILE_NAME);
01656          if (!cfg) {
01657             ast_log(LOG_WARNING, "No %s file :(\n", CONFIG_FILE_NAME);
01658             return NULL;
01659          }
01660          var = ast_variable_browse(cfg, "rooms");
01661          while (var) {
01662             if (!strcasecmp(var->name, "conf")) {
01663                /* Separate the PIN */
01664                char *pin, *pinadmin, *conf;
01665 
01666                if ((pinadmin = ast_strdupa(var->value))) {
01667                   conf = strsep(&pinadmin, "|,");
01668                   pin = strsep(&pinadmin, "|,");
01669                   if (!strcasecmp(conf, confno)) {
01670                      /* Bingo it's a valid conference */
01671                      if (pin)
01672                         if (pinadmin)
01673                            cnf = build_conf(confno, pin, pinadmin, make, dynamic);
01674                         else
01675                            cnf = build_conf(confno, pin, "", make, dynamic);
01676                      else
01677                         if (pinadmin)
01678                            cnf = build_conf(confno, "", pinadmin, make, dynamic);
01679                         else
01680                            cnf = build_conf(confno, "", "", make, dynamic);
01681                      break;
01682                   }
01683                }
01684             }
01685             var = var->next;
01686          }
01687          if (!var) {
01688             ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
01689          }
01690          ast_config_destroy(cfg);
01691       }
01692    } else if (dynamic_pin) {
01693       /* Correct for the user selecting 'D' instead of 'd' to have
01694          someone join into a conference that has already been created
01695          with a pin. */
01696       if (dynamic_pin[0] == 'q')
01697          dynamic_pin[0] = '\0';
01698    }
01699 
01700    if (cnf) {
01701       if (confflags && !cnf->chan &&
01702           !ast_test_flag(confflags, CONFFLAG_QUIET) &&
01703           ast_test_flag(confflags, CONFFLAG_INTROUSER)) {
01704          ast_log(LOG_WARNING, "No Zap channel available for conference, user introduction disabled (is chan_zap loaded?)\n");
01705          ast_clear_flag(confflags, CONFFLAG_INTROUSER);
01706       }
01707       
01708       if (confflags && !cnf->chan &&
01709           ast_test_flag(confflags, CONFFLAG_RECORDCONF)) {
01710          ast_log(LOG_WARNING, "No Zap channel available for conference, conference recording disabled (is chan_zap loaded?)\n");
01711          ast_clear_flag(confflags, CONFFLAG_RECORDCONF);
01712       }
01713    }
01714 
01715    return cnf;
01716 }
01717 
01718 /*--- count_exec: The MeetmeCount application */
01719 static int count_exec(struct ast_channel *chan, void *data)
01720 {
01721    struct localuser *u;
01722    int res = 0;
01723    struct ast_conference *conf;
01724    int count;
01725    char *confnum, *localdata;
01726    char val[80] = "0"; 
01727 
01728    if (ast_strlen_zero(data)) {
01729       ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
01730       return -1;
01731    }
01732 
01733    LOCAL_USER_ADD(u);
01734    
01735    localdata = ast_strdupa(data);
01736    if (!localdata) {
01737       ast_log(LOG_ERROR, "Out of memory!\n");
01738       LOCAL_USER_REMOVE(u);
01739       return -1;
01740    }
01741    
01742    confnum = strsep(&localdata,"|");       
01743    conf = find_conf(chan, confnum, 0, 0, NULL, NULL);
01744    if (conf)
01745       count = conf->users;
01746    else
01747       count = 0;
01748 
01749    if (!ast_strlen_zero(localdata)){
01750       /* have var so load it and exit */
01751       snprintf(val, sizeof(val), "%d",count);
01752       pbx_builtin_setvar_helper(chan, localdata, val);
01753    } else {
01754       if (chan->_state != AST_STATE_UP)
01755          ast_answer(chan);
01756       res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
01757    }
01758    LOCAL_USER_REMOVE(u);
01759 
01760    return res;
01761 }
01762 
01763 /*--- conf_exec: The meetme() application */
01764 static int conf_exec(struct ast_channel *chan, void *data)
01765 {
01766    int res=-1;
01767    struct localuser *u;
01768    char confno[AST_MAX_EXTENSION] = "";
01769    int allowretry = 0;
01770    int retrycnt = 0;
01771    struct ast_conference *cnf;
01772    struct ast_flags confflags = {0};
01773    int dynamic = 0;
01774    int empty = 0, empty_no_pin = 0;
01775    int always_prompt = 0;
01776    char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
01777 
01778    LOCAL_USER_ADD(u);
01779 
01780    if (ast_strlen_zero(data)) {
01781       allowretry = 1;
01782       notdata = "";
01783    } else {
01784       notdata = data;
01785    }
01786    
01787    if (chan->_state != AST_STATE_UP)
01788       ast_answer(chan);
01789 
01790    info = ast_strdupa(notdata);
01791 
01792    if (info) {
01793       char *tmp = strsep(&info, "|");
01794       ast_copy_string(confno, tmp, sizeof(confno));
01795       if (ast_strlen_zero(confno)) {
01796          allowretry = 1;
01797       }
01798    }
01799    if (info)
01800       inflags = strsep(&info, "|");
01801    if (info)
01802       inpin = strsep(&info, "|");
01803    if (inpin)
01804       ast_copy_string(the_pin, inpin, sizeof(the_pin));
01805 
01806    if (inflags) {
01807       ast_app_parse_options(meetme_opts, &confflags, NULL, inflags);
01808       dynamic = ast_test_flag(&confflags, CONFFLAG_DYNAMIC | CONFFLAG_DYNAMICPIN);
01809       if (ast_test_flag(&confflags, CONFFLAG_DYNAMICPIN) && !inpin)
01810          strcpy(the_pin, "q");
01811 
01812       empty = ast_test_flag(&confflags, CONFFLAG_EMPTY | CONFFLAG_EMPTYNOPIN);
01813       empty_no_pin = ast_test_flag(&confflags, CONFFLAG_EMPTYNOPIN);
01814       always_prompt = ast_test_flag(&confflags, CONFFLAG_ALWAYSPROMPT);
01815    }
01816 
01817    do {
01818       if (retrycnt > 3)
01819          allowretry = 0;
01820       if (empty) {
01821          int i, map[1024] = { 0, };
01822          struct ast_config *cfg;
01823          struct ast_variable *var;
01824          int confno_int;
01825 
01826          ast_mutex_lock(&conflock);
01827          for (cnf = confs; cnf; cnf = cnf->next) {
01828             if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
01829                /* Disqualify in use conference */
01830                if (confno_int >= 0 && confno_int < 1024)
01831                   map[confno_int]++;
01832             }
01833          }
01834          ast_mutex_unlock(&conflock);
01835 
01836          /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
01837          if ((empty_no_pin) || (!dynamic)) {
01838             cfg = ast_config_load(CONFIG_FILE_NAME);
01839             if (cfg) {
01840                var = ast_variable_browse(cfg, "rooms");
01841                while (var) {
01842                   if (!strcasecmp(var->name, "conf")) {
01843                      char *stringp = ast_strdupa(var->value);
01844                      if (stringp) {
01845                         char *confno_tmp = strsep(&stringp, "|,");
01846                         int found = 0;
01847                         if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
01848                            if ((confno_int >= 0) && (confno_int < 1024)) {
01849                               if (stringp && empty_no_pin) {
01850                                  map[confno_int]++;
01851                               }
01852                            }
01853                         }
01854                         if (!dynamic) {
01855                            /* For static:  run through the list and see if this conference is empty */
01856                            ast_mutex_lock(&conflock);
01857                            cnf = confs;
01858                            while (cnf) {
01859                               if (!strcmp(confno_tmp, cnf->confno)) {
01860                                  /* The conference exists, therefore it's not empty */
01861                                  found = 1;
01862                                  break;
01863                               }
01864                               cnf = cnf->next;
01865                            }
01866                            ast_mutex_unlock(&conflock);
01867                            if (!found) {
01868                               /* At this point, we have a confno_tmp (static conference) that is empty */
01869                               if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
01870                                  /* Case 1:  empty_no_pin and pin is nonexistent (NULL)
01871                                   * Case 2:  empty_no_pin and pin is blank (but not NULL)
01872                                   * Case 3:  not empty_no_pin
01873                                   */
01874                                  ast_copy_string(confno, confno_tmp, sizeof(confno));
01875                                  break;
01876                                  /* XXX the map is not complete (but we do have a confno) */
01877                               }
01878                            }
01879                         }
01880                      } else {
01881                         ast_log(LOG_ERROR, "Out of memory\n");
01882                      }
01883                   }
01884                   var = var->next;
01885                }
01886                ast_config_destroy(cfg);
01887             }
01888          }
01889 
01890          /* Select first conference number not in use */
01891          if (ast_strlen_zero(confno) && dynamic) {
01892             for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
01893                if (!map[i]) {
01894                   snprintf(confno, sizeof(confno), "%d", i);
01895                   break;
01896                }
01897             }
01898          }
01899 
01900          /* Not found? */
01901          if (ast_strlen_zero(confno)) {
01902             res = ast_streamfile(chan, "conf-noempty", chan->language);
01903             if (!res)
01904                ast_waitstream(chan, "");
01905          } else {
01906             if (sscanf(confno, "%d", &confno_int) == 1) {
01907                res = ast_streamfile(chan, "conf-enteringno", chan->language);
01908                if (!res) {
01909                   ast_waitstream(chan, "");
01910                   res = ast_say_digits(chan, confno_int, "", chan->language);
01911                }
01912             } else {
01913                ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
01914             }
01915          }
01916       }
01917 
01918       while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
01919          /* Prompt user for conference number */
01920          res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
01921          if (res < 0) {
01922             /* Don't try to validate when we catch an error */
01923             confno[0] = '\0';
01924             allowretry = 0;
01925             break;
01926          }
01927       }
01928       if (!ast_strlen_zero(confno)) {
01929          /* Check the validity of the conference */
01930          cnf = find_conf(chan, confno, 1, dynamic, the_pin, &confflags);
01931          if (!cnf) {
01932             res = ast_streamfile(chan, "conf-invalid", chan->language);
01933             if (!res)
01934                ast_waitstream(chan, "");
01935             res = -1;
01936             if (allowretry)
01937                confno[0] = '\0';
01938          } else {
01939             if ((!ast_strlen_zero(cnf->pin) &&
01940                  !ast_test_flag(&confflags, CONFFLAG_ADMIN)) ||
01941                 (!ast_strlen_zero(cnf->pinadmin) &&
01942                  ast_test_flag(&confflags, CONFFLAG_ADMIN))) {
01943                char pin[AST_MAX_EXTENSION]="";
01944                int j;
01945 
01946                /* Allow the pin to be retried up to 3 times */
01947                for (j = 0; j < 3; j++) {
01948                   if (*the_pin && (always_prompt == 0)) {
01949                      ast_copy_string(pin, the_pin, sizeof(pin));
01950                      res = 0;
01951                   } else {
01952                      /* Prompt user for pin if pin is required */
01953                      res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
01954                   }
01955                   if (res >= 0) {
01956                      if (!strcasecmp(pin, cnf->pin) ||
01957                          (!ast_strlen_zero(cnf->pinadmin) &&
01958                           !strcasecmp(pin, cnf->pinadmin))) {
01959                         /* Pin correct */
01960                         allowretry = 0;
01961                         if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin)) 
01962                            ast_set_flag(&confflags, CONFFLAG_ADMIN);
01963                         /* Run the conference */
01964                         res = conf_run(chan, cnf, confflags.flags);
01965                         break;
01966                      } else {
01967                         /* Pin invalid */
01968                         res = ast_streamfile(chan, "conf-invalidpin", chan->language);
01969                         if (!res)
01970                            ast_waitstream(chan, AST_DIGIT_ANY);
01971                         if (res < 0)
01972                            break;
01973                         pin[0] = res;
01974                         pin[1] = '\0';
01975                         res = -1;
01976                         if (allowretry)
01977                            confno[0] = '\0';
01978                      }
01979                   } else {
01980                      /* failed when getting the pin */
01981                      res = -1;
01982                      allowretry = 0;
01983                      /* see if we need to get rid of the conference */
01984                      ast_mutex_lock(&conflock);
01985                      if (!cnf->users) {
01986                         conf_free(cnf);   
01987                      }
01988                      ast_mutex_unlock(&conflock);
01989                      break;
01990                   }
01991 
01992                   /* Don't retry pin with a static pin */
01993                   if (*the_pin && (always_prompt==0)) {
01994                      break;
01995                   }
01996                }
01997             } else {
01998                /* No pin required */
01999                allowretry = 0;
02000 
02001                /* Run the conference */
02002                res = conf_run(chan, cnf, confflags.flags);
02003             }
02004          }
02005       }
02006    } while (allowretry);
02007    
02008    LOCAL_USER_REMOVE(u);
02009    
02010    return res;
02011 }
02012 
02013 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
02014    struct ast_conf_user *user = NULL;
02015    char usrno[1024] = "";
02016 
02017    if (conf && callerident) {
02018       user = conf->firstuser;
02019       while (user) {
02020          snprintf(usrno, sizeof(usrno), "%d", user->user_no);
02021          if (strcmp(usrno, callerident) == 0)
02022             return user;
02023          user = user->nextuser;
02024       }
02025    }
02026    return NULL;
02027 }
02028 
02029 /*--- admin_exec: The MeetMeadmin application */
02030 /* MeetMeAdmin(confno, command, caller) */
02031 static int admin_exec(struct ast_channel *chan, void *data) {
02032    char *params, *command = NULL, *caller = NULL, *conf = NULL;
02033    struct ast_conference *cnf;
02034    struct ast_conf_user *user = NULL;
02035    struct localuser *u;
02036    
02037    LOCAL_USER_ADD(u);
02038 
02039    ast_mutex_lock(&conflock);
02040    /* The param has the conference number the user and the command to execute */
02041    if (!ast_strlen_zero(data)) {    
02042       params = ast_strdupa((char *) data);
02043       conf = strsep(&params, "|");
02044       command = strsep(&params, "|");
02045       caller = strsep(&params, "|");
02046       
02047       if (!command) {
02048          ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
02049          ast_mutex_unlock(&conflock);
02050          LOCAL_USER_REMOVE(u);
02051          return -1;
02052       }
02053       for (cnf = confs; cnf; cnf = cnf->next) {
02054          if (!strcmp(cnf->confno, conf))
02055             break;
02056       }
02057       
02058       if (caller)
02059          user = find_user(cnf, caller);
02060       
02061       if (cnf) {
02062          switch((int) (*command)) {
02063          case 76: /* L: Lock */ 
02064             cnf->locked = 1;
02065             break;
02066          case 108: /* l: Unlock */ 
02067             cnf->locked = 0;
02068             break;
02069          case 75: /* K: kick all users*/
02070             user = cnf->firstuser;
02071             while(user) {
02072                user->adminflags |= ADMINFLAG_KICKME;
02073                if (user->nextuser) {
02074                   user = user->nextuser;
02075                } else {
02076                   break;
02077                }
02078             }
02079             break;
02080          case 101: /* e: Eject last user*/
02081             user = cnf->lastuser;
02082             if (!(user->userflags & CONFFLAG_ADMIN)) {
02083                user->adminflags |= ADMINFLAG_KICKME;
02084                break;
02085             } else
02086                ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
02087             break;
02088          case 77: /* M: Mute */ 
02089             if (user) {
02090                user->adminflags |= ADMINFLAG_MUTED;
02091             } else {
02092                ast_log(LOG_NOTICE, "Specified User not found!\n");
02093             }
02094             break;
02095          case 78: /* N: Mute all users */
02096             user = cnf->firstuser;
02097             while(user) {
02098                if (user && !(user->userflags & CONFFLAG_ADMIN))
02099                   user->adminflags |= ADMINFLAG_MUTED;
02100                if (user->nextuser) {
02101                   user = user->nextuser;
02102                } else {
02103                   break;
02104                }
02105             }
02106             break;               
02107          case 109: /* m: Unmute */ 
02108             if (user && (user->adminflags & ADMINFLAG_MUTED)) {
02109                user->adminflags ^= ADMINFLAG_MUTED;
02110             } else {
02111                ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
02112             }
02113             break;
02114          case  110: /* n: Unmute all users */
02115             user = cnf->firstuser;
02116             while(user) {
02117                if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
02118                   user->adminflags ^= ADMINFLAG_MUTED;
02119                }
02120                if (user->nextuser) {
02121                   user = user->nextuser;
02122                } else {
02123                   break;
02124                }
02125             }
02126             break;
02127          case 107: /* k: Kick user */ 
02128             if (user) {
02129                user->adminflags |= ADMINFLAG_KICKME;
02130             } else {
02131                ast_log(LOG_NOTICE, "Specified User not found!");
02132             }
02133             break;
02134          }
02135       } else {
02136          ast_log(LOG_NOTICE, "Conference Number not found\n");
02137       }
02138    }
02139    ast_mutex_unlock(&conflock);
02140 
02141    LOCAL_USER_REMOVE(u);
02142    
02143    return 0;
02144 }
02145 
02146 static void *recordthread(void *args)
02147 {
02148    struct ast_conference *cnf = args;
02149    struct ast_frame *f=NULL;
02150    int flags;
02151    struct ast_filestream *s;
02152    int res=0;
02153 
02154    if (!cnf || !cnf->chan) {
02155       pthread_exit(0);
02156    }
02157    ast_stopstream(cnf->chan);
02158    flags = O_CREAT|O_TRUNC|O_WRONLY;
02159    s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
02160 
02161    if (s) {
02162       cnf->recording = MEETME_RECORD_ACTIVE;
02163       while (ast_waitfor(cnf->chan, -1) > -1) {
02164          f = ast_read(cnf->chan);
02165          if (!f) {
02166             res = -1;
02167             break;
02168          }
02169          if (f->frametype == AST_FRAME_VOICE) {
02170             res = ast_writestream(s, f);
02171             if (res) 
02172                break;
02173          }
02174          ast_frfree(f);
02175          if (cnf->recording == MEETME_RECORD_TERMINATE) {
02176             ast_mutex_lock(&conflock);
02177             ast_mutex_unlock(&conflock);
02178             break;
02179          }
02180       }
02181       cnf->recording = MEETME_RECORD_OFF;
02182       ast_closestream(s);
02183    }
02184    pthread_exit(0);
02185 }
02186 
02187 static void load_config(void)
02188 {
02189    struct ast_config *cfg;
02190    char *val;
02191 
02192    audio_buffers = DEFAULT_AUDIO_BUFFERS;
02193 
02194    if (!(cfg = ast_config_load(CONFIG_FILE_NAME)))
02195       return;
02196 
02197    if ((val = ast_variable_retrieve(cfg, "general", "audiobuffers"))) {
02198       if ((sscanf(val, "%d", &audio_buffers) != 1)) {
02199          ast_log(LOG_WARNING, "audiobuffers setting must be a number, not '%s'\n", val);
02200          audio_buffers = DEFAULT_AUDIO_BUFFERS;
02201       } else if ((audio_buffers < ZT_DEFAULT_NUM_BUFS) || (audio_buffers > ZT_MAX_NUM_BUFS)) {
02202          ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n",
02203             ZT_DEFAULT_NUM_BUFS, ZT_MAX_NUM_BUFS);
02204          audio_buffers = DEFAULT_AUDIO_BUFFERS;
02205       }
02206       if (audio_buffers != DEFAULT_AUDIO_BUFFERS)
02207          ast_log(LOG_NOTICE, "Audio buffers per channel set to %d\n", audio_buffers);
02208    }
02209 
02210    ast_config_destroy(cfg);
02211 }
02212 
02213 int unload_module(void)
02214 {
02215    int res;
02216    
02217    res = ast_cli_unregister(&cli_show_confs);
02218    res |= ast_cli_unregister(&cli_conf);
02219    res |= ast_unregister_application(app3);
02220    res |= ast_unregister_application(app2);
02221    res |= ast_unregister_application(app);
02222 
02223    STANDARD_HANGUP_LOCALUSERS;
02224 
02225    return res;
02226 }
02227 
02228 int load_module(void)
02229 {
02230    int res;
02231 
02232    load_config();
02233 
02234    res = ast_cli_register(&cli_show_confs);
02235    res |= ast_cli_register(&cli_conf);
02236    res |= ast_register_application(app3, admin_exec, synopsis3, descrip3);
02237    res |= ast_register_application(app2, count_exec, synopsis2, descrip2);
02238    res |= ast_register_application(app, conf_exec, synopsis, descrip);
02239 
02240    return res;
02241 }
02242 
02243 int reload(void)
02244 {
02245    load_config();
02246 
02247    return 0;
02248 }
02249 
02250 char *description(void)
02251 {
02252    return (char *) tdesc;
02253 }
02254 
02255 int usecount(void)
02256 {
02257    int res;
02258 
02259    STANDARD_USECOUNT(res);
02260 
02261    return res;
02262 }
02263 
02264 char *key()
02265 {
02266    return ASTERISK_GPL_KEY;
02267 }
02268  

Generated on Fri May 26 01:45:27 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6