Mon Mar 20 08:25:36 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

app_meetme.c

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

Generated on Mon Mar 20 08:25:36 2006 for Asterisk - the Open Source PBX by  doxygen 1.3.9.1