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