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