app_meetme.c 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * Meet me conference bridge
  5. *
  6. * Copyright (C) 1999-2004, Digium, Inc.
  7. *
  8. * Mark Spencer <markster@digium.com>
  9. *
  10. * This program is free software, distributed under the terms of
  11. * the GNU General Public License
  12. */
  13. #include <asterisk/lock.h>
  14. #include <asterisk/file.h>
  15. #include <asterisk/logger.h>
  16. #include <asterisk/channel.h>
  17. #include <asterisk/pbx.h>
  18. #include <asterisk/module.h>
  19. #include <asterisk/config.h>
  20. #include <asterisk/app.h>
  21. #include <asterisk/musiconhold.h>
  22. #include <asterisk/manager.h>
  23. #include <asterisk/options.h>
  24. #include <asterisk/cli.h>
  25. #include <asterisk/say.h>
  26. #include <asterisk/utils.h>
  27. #include <stdlib.h>
  28. #include <unistd.h>
  29. #include <string.h>
  30. #include <errno.h>
  31. #include <stdlib.h>
  32. #include <sys/ioctl.h>
  33. #ifdef __linux__
  34. #include <linux/zaptel.h>
  35. #else
  36. #include <zaptel.h>
  37. #endif /* __linux__ */
  38. static char *tdesc = "MeetMe conference bridge";
  39. static char *app = "MeetMe";
  40. static char *app2 = "MeetMeCount";
  41. static char *app3 = "MeetMeAdmin";
  42. static char *synopsis = "MeetMe conference bridge";
  43. static char *synopsis2 = "MeetMe participant count";
  44. static char *synopsis3 = "MeetMe conference Administration";
  45. static char *descrip =
  46. " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
  47. "If the conference number is omitted, the user will be prompted to enter\n"
  48. "one. \n"
  49. "MeetMe returns 0 if user pressed # to exit (see option 'p'), otherwise -1.\n"
  50. "Please note: A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING TO WORK!\n\n"
  51. "The option string may contain zero or more of the following characters:\n"
  52. " 'm' -- set monitor only mode (Listen only, no talking)\n"
  53. " 't' -- set talk only mode. (Talk only, no listening)\n"
  54. " 'p' -- allow user to exit the conference by pressing '#'\n"
  55. " 'X' -- allow user to exit the conference by entering a valid single\n"
  56. " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
  57. " if that variable is not defined.\n"
  58. " 'd' -- dynamically add conference\n"
  59. " 'D' -- dynamically add conference, prompting for a PIN\n"
  60. " 'e' -- select an empty conference\n"
  61. " 'E' -- select an empty pinless conference\n"
  62. " 'v' -- video mode\n"
  63. " 'q' -- quiet mode (don't play enter/leave sounds)\n"
  64. " 'M' -- enable music on hold when the conference has a single caller\n"
  65. " 'x' -- close the conference when last marked user exits\n"
  66. " 'w' -- wait until the marked user enters the conference\n"
  67. " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
  68. " Default: conf-background.agi\n"
  69. " (Note: This does not work with non-Zap channels in the same conference)\n"
  70. " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
  71. " 'a' -- set admin mode\n"
  72. " 'A' -- set marked mode\n";
  73. static char *descrip2 =
  74. " MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
  75. "MeetMe conference. If var is specified, playback will be skipped and the value\n"
  76. "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
  77. "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
  78. static char *descrip3 =
  79. " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
  80. " 'K' -- Kick all users out of conference\n"
  81. " 'k' -- Kick one user out of conference\n"
  82. " 'L' -- Lock conference\n"
  83. " 'l' -- Unlock conference\n"
  84. " 'M' -- Mute conference\n"
  85. " 'm' -- Unmute conference\n"
  86. "";
  87. STANDARD_LOCAL_USER;
  88. LOCAL_USER_DECL;
  89. static struct ast_conference {
  90. char confno[AST_MAX_EXTENSION]; /* Conference */
  91. struct ast_channel *chan; /* Announcements channel */
  92. int fd; /* Announcements fd */
  93. int zapconf; /* Zaptel Conf # */
  94. int users; /* Number of active users */
  95. int markedusers; /* Number of marked users */
  96. struct ast_conf_user *firstuser; /* Pointer to the first user struct */
  97. struct ast_conf_user *lastuser; /* Pointer to the last user struct */
  98. time_t start; /* Start time (s) */
  99. int isdynamic; /* Created on the fly? */
  100. int locked; /* Is the conference locked? */
  101. char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */
  102. struct ast_conference *next;
  103. } *confs;
  104. struct ast_conf_user {
  105. int user_no; /* User Number */
  106. struct ast_conf_user *prevuser; /* Pointer to the previous user */
  107. struct ast_conf_user *nextuser; /* Pointer to the next user */
  108. int userflags; /* Flags as set in the conference */
  109. int adminflags; /* Flags set by the Admin */
  110. struct ast_channel *chan; /* Connected channel */
  111. char usrvalue[50]; /* Custom User Value */
  112. time_t jointime; /* Time the user joined the conference */
  113. };
  114. #define ADMINFLAG_MUTED (1 << 1) /* User is muted */
  115. #define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
  116. AST_MUTEX_DEFINE_STATIC(conflock);
  117. static int admin_exec(struct ast_channel *chan, void *data);
  118. #include "enter.h"
  119. #include "leave.h"
  120. #define ENTER 0
  121. #define LEAVE 1
  122. #define CONF_SIZE 320
  123. #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
  124. #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
  125. #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
  126. #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user what '*' is pressed */
  127. #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
  128. #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
  129. #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
  130. #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
  131. #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */
  132. #define CONFFLAG_MARKEDEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
  133. #define CONFFLAG_WAITMARKED (1 << 11) /* If set, the MeetMe will wait until a marked user enters */
  134. #define CONFFLAG_EXIT_CONTEXT (1 << 12) /* If set, the MeetMe will exit to the specified context */
  135. #define CONFFLAG_MARKEDUSER (1 << 13) /* If set, the user will be marked */
  136. static int careful_write(int fd, unsigned char *data, int len)
  137. {
  138. int res;
  139. int x;
  140. while(len) {
  141. x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
  142. res = ioctl(fd, ZT_IOMUX, &x);
  143. if (res >= 0)
  144. res = write(fd, data, len);
  145. if (res < 1) {
  146. if (errno != EAGAIN) {
  147. ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
  148. return -1;
  149. } else
  150. return 0;
  151. }
  152. len -= res;
  153. data += res;
  154. }
  155. return 0;
  156. }
  157. static void conf_play(struct ast_conference *conf, int sound)
  158. {
  159. unsigned char *data;
  160. int len;
  161. ast_mutex_lock(&conflock);
  162. switch(sound) {
  163. case ENTER:
  164. data = enter;
  165. len = sizeof(enter);
  166. break;
  167. case LEAVE:
  168. data = leave;
  169. len = sizeof(leave);
  170. break;
  171. default:
  172. data = NULL;
  173. len = 0;
  174. }
  175. if (data)
  176. careful_write(conf->fd, data, len);
  177. ast_mutex_unlock(&conflock);
  178. }
  179. static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic)
  180. {
  181. struct ast_conference *cnf;
  182. struct zt_confinfo ztc;
  183. ast_mutex_lock(&conflock);
  184. cnf = confs;
  185. while(cnf) {
  186. if (!strcmp(confno, cnf->confno))
  187. break;
  188. cnf = cnf->next;
  189. }
  190. if (!cnf && (make || dynamic)) {
  191. cnf = malloc(sizeof(struct ast_conference));
  192. if (cnf) {
  193. /* Make a new one */
  194. memset(cnf, 0, sizeof(struct ast_conference));
  195. strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
  196. strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
  197. cnf->markedusers = 0;
  198. cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo");
  199. if (cnf->chan) {
  200. cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
  201. } else {
  202. ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
  203. cnf->fd = open("/dev/zap/pseudo", O_RDWR);
  204. if (cnf->fd < 0) {
  205. ast_log(LOG_WARNING, "Unable to open pseudo device\n");
  206. free(cnf);
  207. cnf = NULL;
  208. goto cnfout;
  209. }
  210. }
  211. memset(&ztc, 0, sizeof(ztc));
  212. /* Setup a new zap conference */
  213. ztc.chan = 0;
  214. ztc.confno = -1;
  215. ztc.confmode = ZT_CONF_CONFANN;
  216. if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
  217. ast_log(LOG_WARNING, "Error setting conference\n");
  218. if (cnf->chan)
  219. ast_hangup(cnf->chan);
  220. else
  221. close(cnf->fd);
  222. free(cnf);
  223. cnf = NULL;
  224. goto cnfout;
  225. }
  226. /* Fill the conference struct */
  227. cnf->start = time(NULL);
  228. cnf->zapconf = ztc.confno;
  229. cnf->isdynamic = dynamic;
  230. cnf->firstuser = NULL;
  231. cnf->lastuser = NULL;
  232. cnf->locked = 0;
  233. if (option_verbose > 2)
  234. ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
  235. cnf->next = confs;
  236. confs = cnf;
  237. } else
  238. ast_log(LOG_WARNING, "Out of memory\n");
  239. }
  240. cnfout:
  241. ast_mutex_unlock(&conflock);
  242. return cnf;
  243. }
  244. static int confs_show(int fd, int argc, char **argv)
  245. {
  246. ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
  247. return RESULT_SUCCESS;
  248. }
  249. static char show_confs_usage[] =
  250. "Deprecated! Please use 'meetme' instead.\n";
  251. static struct ast_cli_entry cli_show_confs = {
  252. { "show", "conferences", NULL }, confs_show,
  253. "Show status of conferences", show_confs_usage, NULL };
  254. static int conf_cmd(int fd, int argc, char **argv) {
  255. /* Process the command */
  256. struct ast_conference *cnf;
  257. struct ast_conf_user *user;
  258. int hr, min, sec;
  259. int i = 0, total = 0;
  260. time_t now;
  261. char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
  262. char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
  263. char cmdline[1024] = "";
  264. if (argc > 8)
  265. ast_cli(fd, "Invalid Arguments.\n");
  266. /* Check for length so no buffer will overflow... */
  267. for (i = 0; i < argc; i++) {
  268. if (strlen(argv[i]) > 100)
  269. ast_cli(fd, "Invalid Arguments.\n");
  270. }
  271. if (argc == 1) {
  272. /* 'MeetMe': List all the conferences */
  273. now = time(NULL);
  274. cnf = confs;
  275. if (!cnf) {
  276. ast_cli(fd, "No active MeetMe conferences.\n");
  277. return RESULT_SUCCESS;
  278. }
  279. ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
  280. while(cnf) {
  281. if (cnf->markedusers == 0)
  282. strncpy(cmdline, "N/A ", sizeof(cmdline) - 1);
  283. else
  284. snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
  285. hr = (now - cnf->start) / 3600;
  286. min = ((now - cnf->start) % 3600) / 60;
  287. sec = (now - cnf->start) % 60;
  288. ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
  289. total += cnf->users;
  290. cnf = cnf->next;
  291. }
  292. ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
  293. return RESULT_SUCCESS;
  294. }
  295. if (argc < 3)
  296. return RESULT_SHOWUSAGE;
  297. strncpy(cmdline, argv[2], sizeof(cmdline) - 1); /* Argv 2: conference number */
  298. if (strstr(argv[1], "lock")) {
  299. if (strcmp(argv[1], "lock") == 0) {
  300. /* Lock */
  301. strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
  302. } else {
  303. /* Unlock */
  304. strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
  305. }
  306. } else if (strstr(argv[1], "mute")) {
  307. if (argc < 4)
  308. return RESULT_SHOWUSAGE;
  309. if (strcmp(argv[1], "mute") == 0) {
  310. /* Mute */
  311. strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
  312. strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
  313. } else {
  314. /* Unmute */
  315. strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
  316. strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
  317. }
  318. } else if (strcmp(argv[1], "kick") == 0) {
  319. if (argc < 4)
  320. return RESULT_SHOWUSAGE;
  321. if (strcmp(argv[3], "all") == 0) {
  322. /* Kick all */
  323. strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
  324. } else {
  325. /* Kick a single user */
  326. strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
  327. strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
  328. }
  329. } else if(strcmp(argv[1], "list") == 0) {
  330. /* List all the users in a conference */
  331. if (!confs) {
  332. ast_cli(fd, "No active conferences.\n");
  333. return RESULT_SUCCESS;
  334. }
  335. cnf = confs;
  336. /* Find the right conference */
  337. while(cnf) {
  338. if (strcmp(cnf->confno, argv[2]) == 0)
  339. break;
  340. if (cnf->next) {
  341. cnf = cnf->next;
  342. } else {
  343. ast_cli(fd, "No such conference: %s.\n",argv[2]);
  344. return RESULT_SUCCESS;
  345. }
  346. }
  347. /* Show all the users */
  348. user = cnf->firstuser;
  349. while(user) {
  350. ast_cli(fd, "User #: %i Channel: %s %s %s\n", user->user_no, user->chan->name, (user->userflags & CONFFLAG_ADMIN) ? "(Admin)" : "", (user->userflags & CONFFLAG_MONITOR) ? "(Listen only)" : "" );
  351. user = user->nextuser;
  352. }
  353. return RESULT_SUCCESS;
  354. } else
  355. return RESULT_SHOWUSAGE;
  356. ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
  357. admin_exec(NULL, cmdline);
  358. return 0;
  359. }
  360. static char *complete_confcmd(char *line, char *word, int pos, int state) {
  361. #define CONF_COMMANDS 6
  362. int which = 0, x = 0;
  363. struct ast_conference *cnf = NULL;
  364. struct ast_conf_user *usr = NULL;
  365. char *confno = NULL;
  366. char usrno[50] = "";
  367. char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
  368. char *myline;
  369. if (pos == 1) {
  370. /* Command */
  371. for (x = 0;x < CONF_COMMANDS; x++) {
  372. if (!strncasecmp(cmds[x], word, strlen(word))) {
  373. if (++which > state) {
  374. return strdup(cmds[x]);
  375. }
  376. }
  377. }
  378. } else if (pos == 2) {
  379. /* Conference Number */
  380. ast_mutex_lock(&conflock);
  381. cnf = confs;
  382. while(cnf) {
  383. if (!strncasecmp(word, cnf->confno, strlen(word))) {
  384. if (++which > state)
  385. break;
  386. }
  387. cnf = cnf->next;
  388. }
  389. ast_mutex_unlock(&conflock);
  390. return cnf ? strdup(cnf->confno) : NULL;
  391. } else if (pos == 3) {
  392. /* User Number || Conf Command option*/
  393. if (strstr(line, "mute") || strstr(line, "kick")) {
  394. if ((state == 0) && (strstr(line, "kick")) && !(strncasecmp(word, "all", strlen(word)))) {
  395. return strdup("all");
  396. }
  397. which++;
  398. ast_mutex_lock(&conflock);
  399. cnf = confs;
  400. /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
  401. myline = ast_strdupa(line);
  402. if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
  403. while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
  404. ;
  405. }
  406. while(cnf) {
  407. if (strcmp(confno, cnf->confno) == 0) {
  408. break;
  409. }
  410. cnf = cnf->next;
  411. }
  412. if (cnf) {
  413. /* Search for the user */
  414. usr = cnf->firstuser;
  415. while(usr) {
  416. snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
  417. if (!strncasecmp(word, usrno, strlen(word))) {
  418. if (++which > state)
  419. break;
  420. }
  421. usr = usr->nextuser;
  422. }
  423. }
  424. ast_mutex_unlock(&conflock);
  425. return usr ? strdup(usrno) : NULL;
  426. }
  427. }
  428. return NULL;
  429. }
  430. static char conf_usage[] =
  431. "Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
  432. " Executes a command for the conference or on a conferee\n";
  433. static struct ast_cli_entry cli_conf = {
  434. { "meetme", NULL, NULL }, conf_cmd,
  435. "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
  436. static int confnonzero(void *ptr)
  437. {
  438. struct ast_conference *conf = ptr;
  439. int res;
  440. ast_mutex_lock(&conflock);
  441. res = (conf->markedusers == 0);
  442. ast_mutex_unlock(&conflock);
  443. return res;
  444. }
  445. /* Remove the conference from the list and free it.
  446. We assume that this was called while holding conflock. */
  447. static int conf_free(struct ast_conference *conf)
  448. {
  449. struct ast_conference *prev = NULL, *cur = confs;
  450. while(cur) {
  451. if (cur == conf) {
  452. if (prev)
  453. prev->next = conf->next;
  454. else
  455. confs = conf->next;
  456. break;
  457. }
  458. prev = cur;
  459. cur = cur->next;
  460. }
  461. if (!cur)
  462. ast_log(LOG_WARNING, "Conference not found\n");
  463. if (conf->chan)
  464. ast_hangup(conf->chan);
  465. else
  466. close(conf->fd);
  467. free(conf);
  468. return 0;
  469. }
  470. static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
  471. {
  472. struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
  473. int fd;
  474. struct zt_confinfo ztc;
  475. struct ast_frame *f;
  476. struct ast_channel *c;
  477. struct ast_frame fr;
  478. int outfd;
  479. int ms;
  480. int nfds;
  481. int res;
  482. int flags;
  483. int retryzap;
  484. int origfd;
  485. int musiconhold = 0;
  486. int firstpass = 0;
  487. int origquiet;
  488. int ret = -1;
  489. int x;
  490. int menu_active = 0;
  491. int using_pseudo = 0;
  492. struct ast_app *app;
  493. char *agifile;
  494. char *agifiledefault = "conf-background.agi";
  495. char meetmesecs[30] = "";
  496. char exitcontext[AST_MAX_EXTENSION] = "";
  497. int dtmf;
  498. ZT_BUFFERINFO bi;
  499. char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
  500. char *buf = __buf + AST_FRIENDLY_OFFSET;
  501. if (!user) {
  502. ast_log(LOG_ERROR, "Out of memory\n");
  503. return(ret);
  504. }
  505. memset(user, 0, sizeof(struct ast_conf_user));
  506. user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
  507. time(&user->jointime);
  508. if (conf->locked) {
  509. /* Sorry, but this confernce is locked! */
  510. if (!ast_streamfile(chan, "conf-locked", chan->language))
  511. ast_waitstream(chan, "");
  512. goto outrun;
  513. }
  514. conf->users++;
  515. if (confflags & CONFFLAG_MARKEDUSER)
  516. conf->markedusers++;
  517. ast_mutex_lock(&conflock);
  518. if (conf->firstuser == NULL) {
  519. /* Fill the first new User struct */
  520. user->user_no = 1;
  521. user->nextuser = NULL;
  522. user->prevuser = NULL;
  523. conf->firstuser = user;
  524. conf->lastuser = user;
  525. } else {
  526. /* Fill the new user struct */
  527. user->user_no = conf->lastuser->user_no + 1;
  528. user->prevuser = conf->lastuser;
  529. user->nextuser = NULL;
  530. if (conf->lastuser->nextuser != NULL) {
  531. ast_log(LOG_WARNING, "Error in User Management!\n");
  532. ast_mutex_unlock(&conflock);
  533. goto outrun;
  534. } else {
  535. conf->lastuser->nextuser = user;
  536. conf->lastuser = user;
  537. }
  538. }
  539. user->chan = chan;
  540. user->userflags = confflags;
  541. user->adminflags = 0;
  542. ast_mutex_unlock(&conflock);
  543. origquiet = confflags & CONFFLAG_QUIET;
  544. if (confflags & CONFFLAG_EXIT_CONTEXT) {
  545. if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
  546. strncpy(exitcontext, agifile, sizeof(exitcontext) - 1);
  547. else if (!ast_strlen_zero(chan->macrocontext))
  548. strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1);
  549. else
  550. strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1);
  551. }
  552. while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers == 0)) {
  553. confflags &= ~CONFFLAG_QUIET;
  554. confflags |= origquiet;
  555. /* XXX Announce that we're waiting on the conference lead to join */
  556. if (!(confflags & CONFFLAG_QUIET)) {
  557. res = ast_streamfile(chan, "vm-dialout", chan->language);
  558. if (!res)
  559. res = ast_waitstream(chan, "");
  560. } else
  561. res = 0;
  562. /* If we're waiting with hold music, set to silent mode */
  563. if (!res) {
  564. confflags |= CONFFLAG_QUIET;
  565. ast_moh_start(chan, NULL);
  566. res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf);
  567. ast_moh_stop(chan);
  568. }
  569. if (res < 0) {
  570. ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name);
  571. goto outrun;
  572. }
  573. }
  574. if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
  575. if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
  576. if (ast_waitstream(chan, "") < 0)
  577. goto outrun;
  578. } else
  579. goto outrun;
  580. }
  581. /* Set it into linear mode (write) */
  582. if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
  583. ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
  584. goto outrun;
  585. }
  586. /* Set it into linear mode (read) */
  587. if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
  588. ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
  589. goto outrun;
  590. }
  591. ast_indicate(chan, -1);
  592. retryzap = strcasecmp(chan->type, "Zap");
  593. zapretry:
  594. origfd = chan->fds[0];
  595. if (retryzap) {
  596. fd = open("/dev/zap/pseudo", O_RDWR);
  597. if (fd < 0) {
  598. ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
  599. goto outrun;
  600. }
  601. using_pseudo = 1;
  602. /* Make non-blocking */
  603. flags = fcntl(fd, F_GETFL);
  604. if (flags < 0) {
  605. ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
  606. close(fd);
  607. goto outrun;
  608. }
  609. if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
  610. ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
  611. close(fd);
  612. goto outrun;
  613. }
  614. /* Setup buffering information */
  615. memset(&bi, 0, sizeof(bi));
  616. bi.bufsize = CONF_SIZE/2;
  617. bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
  618. bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
  619. bi.numbufs = 4;
  620. if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
  621. ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
  622. close(fd);
  623. goto outrun;
  624. }
  625. x = 1;
  626. if (ioctl(fd, ZT_SETLINEAR, &x)) {
  627. ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
  628. close(fd);
  629. goto outrun;
  630. }
  631. nfds = 1;
  632. } else {
  633. /* XXX Make sure we're not running on a pseudo channel XXX */
  634. fd = chan->fds[0];
  635. nfds = 0;
  636. }
  637. memset(&ztc, 0, sizeof(ztc));
  638. /* Check to see if we're in a conference... */
  639. ztc.chan = 0;
  640. if (ioctl(fd, ZT_GETCONF, &ztc)) {
  641. ast_log(LOG_WARNING, "Error getting conference\n");
  642. close(fd);
  643. goto outrun;
  644. }
  645. if (ztc.confmode) {
  646. /* Whoa, already in a conference... Retry... */
  647. if (!retryzap) {
  648. ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
  649. retryzap = 1;
  650. goto zapretry;
  651. }
  652. }
  653. memset(&ztc, 0, sizeof(ztc));
  654. /* Add us to the conference */
  655. ztc.chan = 0;
  656. ztc.confno = conf->zapconf;
  657. if (confflags & CONFFLAG_MONITOR)
  658. ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
  659. else if (confflags & CONFFLAG_TALKER)
  660. ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
  661. else
  662. ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
  663. if (ioctl(fd, ZT_SETCONF, &ztc)) {
  664. ast_log(LOG_WARNING, "Error setting conference\n");
  665. close(fd);
  666. goto outrun;
  667. }
  668. ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
  669. manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
  670. "Channel: %s\r\n"
  671. "Uniqueid: %s\r\n"
  672. "Meetme: %s\r\n"
  673. "Usernum: %i\r\n",
  674. chan->name, chan->uniqueid, conf->confno, user->user_no);
  675. if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
  676. firstpass = 1;
  677. if (!(confflags & CONFFLAG_QUIET))
  678. conf_play(conf, ENTER);
  679. }
  680. if (confflags & CONFFLAG_AGI) {
  681. /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
  682. or use default filename of conf-background.agi */
  683. agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
  684. if (!agifile)
  685. agifile = agifiledefault;
  686. if (!strcasecmp(chan->type,"Zap")) {
  687. /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
  688. x = 1;
  689. ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
  690. }
  691. /* Find a pointer to the agi app and execute the script */
  692. app = pbx_findapp("agi");
  693. if (app) {
  694. ret = pbx_exec(chan, app, agifile, 1);
  695. } else {
  696. ast_log(LOG_WARNING, "Could not find application (agi)\n");
  697. ret = -2;
  698. }
  699. if (!strcasecmp(chan->type,"Zap")) {
  700. /* Remove CONFMUTE mode on Zap channel */
  701. x = 0;
  702. ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
  703. }
  704. } else {
  705. if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
  706. /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
  707. x = 1;
  708. ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
  709. }
  710. for(;;) {
  711. outfd = -1;
  712. ms = -1;
  713. c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
  714. /* Update the struct with the actual confflags */
  715. user->userflags = confflags;
  716. /* trying to add moh for single person conf */
  717. if (confflags & CONFFLAG_MOH) {
  718. if (conf->users == 1) {
  719. if (musiconhold == 0) {
  720. ast_moh_start(chan, NULL);
  721. musiconhold = 1;
  722. }
  723. } else {
  724. if (musiconhold) {
  725. ast_moh_stop(chan);
  726. musiconhold = 0;
  727. }
  728. }
  729. }
  730. /* Leave if the last marked user left */
  731. if (conf->markedusers == 0 && confflags & CONFFLAG_MARKEDEXIT) {
  732. ret = -1;
  733. break;
  734. }
  735. /* Check if the admin changed my modes */
  736. if (user->adminflags) {
  737. /* Set the new modes */
  738. if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
  739. ztc.confmode ^= ZT_CONF_TALKER;
  740. if (ioctl(fd, ZT_SETCONF, &ztc)) {
  741. ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
  742. ret = -1;
  743. break;
  744. }
  745. }
  746. if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
  747. ztc.confmode |= ZT_CONF_TALKER;
  748. if (ioctl(fd, ZT_SETCONF, &ztc)) {
  749. ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
  750. ret = -1;
  751. break;
  752. }
  753. }
  754. if (user->adminflags & ADMINFLAG_KICKME) {
  755. //You have been kicked.
  756. if (!ast_streamfile(chan, "conf-kicked", chan->language))
  757. ast_waitstream(chan, "");
  758. ret = 0;
  759. break;
  760. }
  761. } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
  762. ztc.confmode |= ZT_CONF_TALKER;
  763. if (ioctl(fd, ZT_SETCONF, &ztc)) {
  764. ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
  765. ret = -1;
  766. break;
  767. }
  768. }
  769. if (c) {
  770. if (c->fds[0] != origfd) {
  771. if (using_pseudo) {
  772. /* Kill old pseudo */
  773. close(fd);
  774. }
  775. ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
  776. retryzap = 0;
  777. using_pseudo = 0;
  778. goto zapretry;
  779. }
  780. f = ast_read(c);
  781. if (!f)
  782. break;
  783. if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
  784. char tmp[2];
  785. tmp[0] = f->subclass;
  786. tmp[1] = '\0';
  787. if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->callerid)) {
  788. strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
  789. strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
  790. chan->priority = 0;
  791. ret = 0;
  792. break;
  793. }
  794. } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
  795. ret = 0;
  796. break;
  797. } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
  798. if (musiconhold) {
  799. ast_moh_stop(chan);
  800. }
  801. if ((confflags & CONFFLAG_ADMIN)) {
  802. /* Admin menu */
  803. if (!menu_active) {
  804. menu_active = 1;
  805. /* Record this sound! */
  806. if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
  807. dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
  808. else
  809. dtmf = 0;
  810. } else
  811. dtmf = f->subclass;
  812. if (dtmf) {
  813. switch(dtmf) {
  814. case '1': /* Un/Mute */
  815. menu_active = 0;
  816. if (ztc.confmode & ZT_CONF_TALKER) {
  817. ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
  818. confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
  819. } else {
  820. ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
  821. confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
  822. }
  823. if (ioctl(fd, ZT_SETCONF, &ztc)) {
  824. ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
  825. ret = -1;
  826. break;
  827. }
  828. if (ztc.confmode & ZT_CONF_TALKER) {
  829. if (!ast_streamfile(chan, "conf-unmuted", chan->language))
  830. ast_waitstream(chan, "");
  831. } else {
  832. if (!ast_streamfile(chan, "conf-muted", chan->language))
  833. ast_waitstream(chan, "");
  834. }
  835. break;
  836. case '2': /* Un/Lock the Conference */
  837. menu_active = 0;
  838. if (conf->locked) {
  839. conf->locked = 0;
  840. if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
  841. ast_waitstream(chan, "");
  842. } else {
  843. conf->locked = 1;
  844. if (!ast_streamfile(chan, "conf-lockednow", chan->language))
  845. ast_waitstream(chan, "");
  846. }
  847. break;
  848. default:
  849. menu_active = 0;
  850. /* Play an error message! */
  851. if (!ast_streamfile(chan, "conf-errormenu", chan->language))
  852. ast_waitstream(chan, "");
  853. break;
  854. }
  855. }
  856. } else {
  857. /* User menu */
  858. if (!menu_active) {
  859. menu_active = 1;
  860. /* Record this sound! */
  861. if (!ast_streamfile(chan, "conf-usermenu", chan->language))
  862. dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
  863. else
  864. dtmf = 0;
  865. } else
  866. dtmf = f->subclass;
  867. if (dtmf) {
  868. switch(dtmf) {
  869. case '1': /* Un/Mute */
  870. menu_active = 0;
  871. if (ztc.confmode & ZT_CONF_TALKER) {
  872. ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
  873. confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
  874. } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
  875. ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
  876. confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
  877. }
  878. if (ioctl(fd, ZT_SETCONF, &ztc)) {
  879. ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
  880. ret = -1;
  881. break;
  882. }
  883. if (ztc.confmode & ZT_CONF_TALKER) {
  884. if (!ast_streamfile(chan, "conf-unmuted", chan->language))
  885. ast_waitstream(chan, "");
  886. } else {
  887. if (!ast_streamfile(chan, "conf-muted", chan->language))
  888. ast_waitstream(chan, "");
  889. }
  890. break;
  891. default:
  892. menu_active = 0;
  893. /* Play an error message! */
  894. if (!ast_streamfile(chan, "conf-errormenu", chan->language))
  895. ast_waitstream(chan, "");
  896. break;
  897. }
  898. }
  899. }
  900. if (musiconhold) {
  901. ast_moh_start(chan, NULL);
  902. }
  903. } else if (using_pseudo) {
  904. if (f->frametype == AST_FRAME_VOICE) {
  905. if (f->subclass == AST_FORMAT_SLINEAR) {
  906. /* Carefully write */
  907. careful_write(fd, f->data, f->datalen);
  908. } else
  909. ast_log(LOG_WARNING, "Huh? Got a non-linear (%d) frame in the conference\n", f->subclass);
  910. }
  911. }
  912. ast_frfree(f);
  913. } else if (outfd > -1) {
  914. res = read(outfd, buf, CONF_SIZE);
  915. if (res > 0) {
  916. memset(&fr, 0, sizeof(fr));
  917. fr.frametype = AST_FRAME_VOICE;
  918. fr.subclass = AST_FORMAT_SLINEAR;
  919. fr.datalen = res;
  920. fr.samples = res/2;
  921. fr.data = buf;
  922. fr.offset = AST_FRIENDLY_OFFSET;
  923. if (ast_write(chan, &fr) < 0) {
  924. ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
  925. /* break; */
  926. }
  927. } else
  928. ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
  929. }
  930. }
  931. }
  932. if (using_pseudo)
  933. close(fd);
  934. else {
  935. /* Take out of conference */
  936. /* Add us to the conference */
  937. ztc.chan = 0;
  938. ztc.confno = 0;
  939. ztc.confmode = 0;
  940. if (ioctl(fd, ZT_SETCONF, &ztc)) {
  941. ast_log(LOG_WARNING, "Error setting conference\n");
  942. }
  943. }
  944. if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
  945. conf_play(conf, LEAVE);
  946. outrun:
  947. ast_mutex_lock(&conflock);
  948. if (user->user_no) { /* Only cleanup users who really joined! */
  949. manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
  950. "Channel: %s\r\n"
  951. "Uniqueid: %s\r\n"
  952. "Meetme: %s\r\n"
  953. "Usernum: %i\r\n",
  954. chan->name, chan->uniqueid, conf->confno, user->user_no);
  955. conf->users--;
  956. if (confflags & CONFFLAG_MARKEDUSER)
  957. conf->markedusers--;
  958. if (!conf->users) {
  959. /* No more users -- close this one out */
  960. conf_free(conf);
  961. } else {
  962. /* Remove the user struct */
  963. if (user == conf->firstuser) {
  964. if (user->nextuser) {
  965. /* There is another entry */
  966. user->nextuser->prevuser = NULL;
  967. } else {
  968. /* We are the only entry */
  969. conf->lastuser = NULL;
  970. }
  971. /* In either case */
  972. conf->firstuser = user->nextuser;
  973. } else if (user == conf->lastuser){
  974. if (user->prevuser)
  975. user->prevuser->nextuser = NULL;
  976. else
  977. ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
  978. conf->lastuser = user->prevuser;
  979. } else {
  980. if (user->nextuser)
  981. user->nextuser->prevuser = user->prevuser;
  982. else
  983. ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
  984. if (user->prevuser)
  985. user->prevuser->nextuser = user->nextuser;
  986. else
  987. ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
  988. }
  989. }
  990. /* Return the number of seconds the user was in the conf */
  991. snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime));
  992. pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
  993. }
  994. free(user);
  995. ast_mutex_unlock(&conflock);
  996. return ret;
  997. }
  998. static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
  999. {
  1000. struct ast_config *cfg;
  1001. struct ast_variable *var;
  1002. struct ast_conference *cnf;
  1003. /* Check first in the conference list */
  1004. ast_mutex_lock(&conflock);
  1005. cnf = confs;
  1006. while (cnf) {
  1007. if (!strcmp(confno, cnf->confno))
  1008. break;
  1009. cnf = cnf->next;
  1010. }
  1011. ast_mutex_unlock(&conflock);
  1012. if (!cnf) {
  1013. if (dynamic) {
  1014. /* No need to parse meetme.conf */
  1015. ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
  1016. if (dynamic_pin) {
  1017. if (dynamic_pin[0] == 'q') {
  1018. /* Query the user to enter a PIN */
  1019. ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
  1020. }
  1021. cnf = build_conf(confno, dynamic_pin, make, dynamic);
  1022. } else {
  1023. cnf = build_conf(confno, "", make, dynamic);
  1024. }
  1025. } else {
  1026. /* Check the config */
  1027. cfg = ast_load("meetme.conf");
  1028. if (!cfg) {
  1029. ast_log(LOG_WARNING, "No meetme.conf file :(\n");
  1030. return NULL;
  1031. }
  1032. var = ast_variable_browse(cfg, "rooms");
  1033. while(var) {
  1034. if (!strcasecmp(var->name, "conf")) {
  1035. /* Separate the PIN */
  1036. char *pin, *conf;
  1037. if ((pin = ast_strdupa(var->value))) {
  1038. conf = strsep(&pin, "|,");
  1039. if (!strcasecmp(conf, confno)) {
  1040. /* Bingo it's a valid conference */
  1041. if (pin)
  1042. cnf = build_conf(confno, pin, make, dynamic);
  1043. else
  1044. cnf = build_conf(confno, "", make, dynamic);
  1045. break;
  1046. }
  1047. }
  1048. }
  1049. var = var->next;
  1050. }
  1051. if (!var) {
  1052. ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
  1053. }
  1054. ast_destroy(cfg);
  1055. }
  1056. } else if (dynamic_pin) {
  1057. /* Correct for the user selecting 'D' instead of 'd' to have
  1058. someone join into a conference that has already been created
  1059. with a pin. */
  1060. if (dynamic_pin[0] == 'q')
  1061. dynamic_pin[0] = '\0';
  1062. }
  1063. return cnf;
  1064. }
  1065. /*--- count_exec: The MeetmeCount application */
  1066. static int count_exec(struct ast_channel *chan, void *data)
  1067. {
  1068. struct localuser *u;
  1069. int res = 0;
  1070. struct ast_conference *conf;
  1071. int count;
  1072. char *confnum, *localdata;
  1073. char val[80] = "0";
  1074. if (!data || ast_strlen_zero(data)) {
  1075. ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
  1076. return -1;
  1077. }
  1078. localdata = ast_strdupa(data);
  1079. LOCAL_USER_ADD(u);
  1080. confnum = strsep(&localdata,"|");
  1081. conf = find_conf(chan, confnum, 0, 0, NULL);
  1082. if (conf)
  1083. count = conf->users;
  1084. else
  1085. count = 0;
  1086. if (localdata && !ast_strlen_zero(localdata)){
  1087. /* have var so load it and exit */
  1088. snprintf(val,sizeof(val), "%i",count);
  1089. pbx_builtin_setvar_helper(chan, localdata,val);
  1090. } else {
  1091. if (chan->_state != AST_STATE_UP)
  1092. ast_answer(chan);
  1093. res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
  1094. }
  1095. LOCAL_USER_REMOVE(u);
  1096. return res;
  1097. }
  1098. /*--- conf_exec: The meetme() application */
  1099. static int conf_exec(struct ast_channel *chan, void *data)
  1100. {
  1101. int res=-1;
  1102. struct localuser *u;
  1103. char confno[AST_MAX_EXTENSION] = "";
  1104. int allowretry = 0;
  1105. int retrycnt = 0;
  1106. struct ast_conference *cnf;
  1107. int confflags = 0;
  1108. int dynamic = 0;
  1109. int empty = 0, empty_no_pin = 0;
  1110. char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
  1111. if (!data || ast_strlen_zero(data)) {
  1112. allowretry = 1;
  1113. notdata = "";
  1114. } else {
  1115. notdata = data;
  1116. }
  1117. LOCAL_USER_ADD(u);
  1118. if (chan->_state != AST_STATE_UP)
  1119. ast_answer(chan);
  1120. info = ast_strdupa((char *)notdata);
  1121. if (info) {
  1122. char *tmp = strsep(&info, "|");
  1123. strncpy(confno, tmp, sizeof(confno) - 1);
  1124. if (ast_strlen_zero(confno)) {
  1125. allowretry = 1;
  1126. }
  1127. }
  1128. if (info)
  1129. inflags = strsep(&info, "|");
  1130. if (info)
  1131. inpin = strsep(&info, "|");
  1132. if (inpin)
  1133. strncpy(the_pin, inpin, sizeof(the_pin) - 1);
  1134. if (inflags) {
  1135. if (strchr(inflags, 'a'))
  1136. confflags |= CONFFLAG_ADMIN;
  1137. if (strchr(inflags, 'm'))
  1138. confflags |= CONFFLAG_MONITOR;
  1139. if (strchr(inflags, 'p'))
  1140. confflags |= CONFFLAG_POUNDEXIT;
  1141. if (strchr(inflags, 's'))
  1142. confflags |= CONFFLAG_STARMENU;
  1143. if (strchr(inflags, 't'))
  1144. confflags |= CONFFLAG_TALKER;
  1145. if (strchr(inflags, 'q'))
  1146. confflags |= CONFFLAG_QUIET;
  1147. if (strchr(inflags, 'M'))
  1148. confflags |= CONFFLAG_MOH;
  1149. if (strchr(inflags, 'x'))
  1150. confflags |= CONFFLAG_MARKEDEXIT;
  1151. if (strchr(inflags, 'X'))
  1152. confflags |= CONFFLAG_EXIT_CONTEXT;
  1153. if (strchr(inflags, 'A'))
  1154. confflags |= CONFFLAG_MARKEDUSER;
  1155. if (strchr(inflags, 'b'))
  1156. confflags |= CONFFLAG_AGI;
  1157. if (strchr(inflags, 'w'))
  1158. confflags |= CONFFLAG_WAITMARKED;
  1159. if (strchr(inflags, 'd'))
  1160. dynamic = 1;
  1161. if (strchr(inflags, 'D')) {
  1162. dynamic = 1;
  1163. if (! inpin) {
  1164. strncpy(the_pin, "q", sizeof(the_pin) - 1);
  1165. }
  1166. }
  1167. if (strchr(inflags, 'e'))
  1168. empty = 1;
  1169. if (strchr(inflags, 'E')) {
  1170. empty = 1;
  1171. empty_no_pin = 1;
  1172. }
  1173. }
  1174. do {
  1175. if (retrycnt > 3)
  1176. allowretry = 0;
  1177. if (empty) {
  1178. int i, map[1024];
  1179. struct ast_config *cfg;
  1180. struct ast_variable *var;
  1181. int confno_int;
  1182. memset(map, 0, sizeof(map));
  1183. ast_mutex_lock(&conflock);
  1184. cnf = confs;
  1185. while (cnf) {
  1186. if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
  1187. /* Disqualify in use conference */
  1188. if (confno_int >= 0 && confno_int < 1024)
  1189. map[confno_int]++;
  1190. }
  1191. cnf = cnf->next;
  1192. }
  1193. ast_mutex_unlock(&conflock);
  1194. /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
  1195. if ((empty_no_pin) || (!dynamic)) {
  1196. cfg = ast_load("meetme.conf");
  1197. if (cfg) {
  1198. var = ast_variable_browse(cfg, "rooms");
  1199. while(var) {
  1200. if (!strcasecmp(var->name, "conf")) {
  1201. char *stringp = ast_strdupa(var->value);
  1202. if (stringp) {
  1203. char *confno_tmp = strsep(&stringp, "|,");
  1204. int found = 0;
  1205. if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
  1206. if ((confno_int >= 0) && (confno_int < 1024)) {
  1207. if (stringp && empty_no_pin) {
  1208. map[confno_int]++;
  1209. }
  1210. }
  1211. }
  1212. if (! dynamic) {
  1213. /* For static: run through the list and see if this conference is empty */
  1214. ast_mutex_lock(&conflock);
  1215. cnf = confs;
  1216. while (cnf) {
  1217. if (!strcmp(confno_tmp, cnf->confno)) {
  1218. /* The conference exists, therefore it's not empty */
  1219. found = 1;
  1220. break;
  1221. }
  1222. cnf = cnf->next;
  1223. }
  1224. ast_mutex_unlock(&conflock);
  1225. if (!found) {
  1226. /* At this point, we have a confno_tmp (static conference) that is empty */
  1227. if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
  1228. /* Case 1: empty_no_pin and pin is nonexistent (NULL)
  1229. * Case 2: empty_no_pin and pin is blank (but not NULL)
  1230. * Case 3: not empty_no_pin
  1231. */
  1232. strncpy(confno, confno_tmp, sizeof(confno) - 1);
  1233. break;
  1234. /* XXX the map is not complete (but we do have a confno) */
  1235. }
  1236. }
  1237. }
  1238. } else {
  1239. ast_log(LOG_ERROR, "Out of memory\n");
  1240. }
  1241. }
  1242. var = var->next;
  1243. }
  1244. ast_destroy(cfg);
  1245. }
  1246. }
  1247. /* Select first conference number not in use */
  1248. if (ast_strlen_zero(confno) && dynamic) {
  1249. for (i=0;i<1024;i++) {
  1250. if (!map[i]) {
  1251. snprintf(confno, sizeof(confno), "%d", i);
  1252. break;
  1253. }
  1254. }
  1255. }
  1256. /* Not found? */
  1257. if (ast_strlen_zero(confno)) {
  1258. res = ast_streamfile(chan, "conf-noempty", chan->language);
  1259. if (!res)
  1260. ast_waitstream(chan, "");
  1261. } else {
  1262. if (sscanf(confno, "%d", &confno_int) == 1) {
  1263. res = ast_streamfile(chan, "conf-enteringno", chan->language);
  1264. if (!res) {
  1265. ast_waitstream(chan, "");
  1266. res = ast_say_digits(chan, confno_int, "", chan->language);
  1267. }
  1268. } else {
  1269. ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
  1270. }
  1271. }
  1272. }
  1273. while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
  1274. /* Prompt user for conference number */
  1275. res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
  1276. if (res < 0) {
  1277. /* Don't try to validate when we catch an error */
  1278. confno[0] = '\0';
  1279. allowretry = 0;
  1280. break;
  1281. }
  1282. }
  1283. if (!ast_strlen_zero(confno)) {
  1284. /* Check the validity of the conference */
  1285. cnf = find_conf(chan, confno, 1, dynamic, the_pin);
  1286. if (!cnf) {
  1287. res = ast_streamfile(chan, "conf-invalid", chan->language);
  1288. if (!res)
  1289. ast_waitstream(chan, "");
  1290. res = -1;
  1291. if (allowretry)
  1292. confno[0] = '\0';
  1293. } else {
  1294. if (!ast_strlen_zero(cnf->pin)) {
  1295. char pin[AST_MAX_EXTENSION]="";
  1296. int j;
  1297. /* Allow the pin to be retried up to 3 times */
  1298. for (j=0; j<3; j++) {
  1299. if (*the_pin) {
  1300. strncpy(pin, the_pin, sizeof(pin) - 1);
  1301. res = 0;
  1302. } else {
  1303. /* Prompt user for pin if pin is required */
  1304. res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
  1305. }
  1306. if (res >= 0) {
  1307. if (!strcasecmp(pin, cnf->pin)) {
  1308. /* Pin correct */
  1309. allowretry = 0;
  1310. /* Run the conference */
  1311. res = conf_run(chan, cnf, confflags);
  1312. break;
  1313. } else {
  1314. /* Pin invalid */
  1315. res = ast_streamfile(chan, "conf-invalidpin", chan->language);
  1316. if (!res)
  1317. ast_waitstream(chan, AST_DIGIT_ANY);
  1318. if (res < 0)
  1319. break;
  1320. pin[0] = res;
  1321. pin[1] = '\0';
  1322. res = -1;
  1323. if (allowretry)
  1324. confno[0] = '\0';
  1325. }
  1326. } else {
  1327. /* failed when getting the pin */
  1328. res = -1;
  1329. allowretry = 0;
  1330. /* see if we need to get rid of the conference */
  1331. ast_mutex_lock(&conflock);
  1332. if (!cnf->users) {
  1333. conf_free(cnf);
  1334. }
  1335. ast_mutex_unlock(&conflock);
  1336. break;
  1337. }
  1338. /* Don't retry pin with a static pin */
  1339. if (*the_pin) {
  1340. break;
  1341. }
  1342. }
  1343. } else {
  1344. /* No pin required */
  1345. allowretry = 0;
  1346. /* Run the conference */
  1347. res = conf_run(chan, cnf, confflags);
  1348. }
  1349. }
  1350. }
  1351. } while (allowretry);
  1352. LOCAL_USER_REMOVE(u);
  1353. return res;
  1354. }
  1355. static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
  1356. struct ast_conf_user *user = NULL;
  1357. char usrno[1024] = "";
  1358. if (conf && callerident) {
  1359. user = conf->firstuser;
  1360. while(user) {
  1361. snprintf(usrno, sizeof(usrno), "%i", user->user_no);
  1362. if (strcmp(usrno, callerident) == 0)
  1363. return user;
  1364. user = user->nextuser;
  1365. }
  1366. }
  1367. return NULL;
  1368. }
  1369. /*--- admin_exec: The MeetMeadmin application */
  1370. /* MeetMeAdmin(confno, command, caller) */
  1371. static int admin_exec(struct ast_channel *chan, void *data) {
  1372. char *params, *command = NULL, *caller = NULL, *conf = NULL;
  1373. struct ast_conference *cnf;
  1374. struct ast_conf_user *user = NULL;
  1375. ast_mutex_lock(&conflock);
  1376. /* The param has the conference number the user and the command to execute */
  1377. if (data && !ast_strlen_zero(data)) {
  1378. params = ast_strdupa((char *) data);
  1379. conf = strsep(&params, "|");
  1380. command = strsep(&params, "|");
  1381. caller = strsep(&params, "|");
  1382. if (!command) {
  1383. ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
  1384. ast_mutex_unlock(&conflock);
  1385. return -1;
  1386. }
  1387. cnf = confs;
  1388. while (cnf) {
  1389. if (strcmp(cnf->confno, conf) == 0)
  1390. break;
  1391. cnf = cnf->next;
  1392. }
  1393. if (caller)
  1394. user = find_user(cnf, caller);
  1395. if (cnf) {
  1396. switch((int) (*command)) {
  1397. case 76: /* L: Lock */
  1398. cnf->locked = 1;
  1399. break;
  1400. case 108: /* l: Unlock */
  1401. cnf->locked = 0;
  1402. break;
  1403. case 75: /* K: kick all users*/
  1404. user = cnf->firstuser;
  1405. while(user) {
  1406. user->adminflags |= ADMINFLAG_KICKME;
  1407. if (user->nextuser) {
  1408. user = user->nextuser;
  1409. } else {
  1410. break;
  1411. }
  1412. }
  1413. break;
  1414. case 77: /* M: Mute */
  1415. if (user) {
  1416. user->adminflags |= ADMINFLAG_MUTED;
  1417. } else {
  1418. ast_log(LOG_NOTICE, "Specified User not found!");
  1419. }
  1420. break;
  1421. case 109: /* m: Unmute */
  1422. if (user && (user->adminflags & ADMINFLAG_MUTED)) {
  1423. user->adminflags ^= ADMINFLAG_MUTED;
  1424. } else {
  1425. ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
  1426. }
  1427. break;
  1428. case 107: /* k: Kick user */
  1429. if (user) {
  1430. user->adminflags |= ADMINFLAG_KICKME;
  1431. } else {
  1432. ast_log(LOG_NOTICE, "Specified User not found!");
  1433. }
  1434. break;
  1435. }
  1436. } else {
  1437. ast_log(LOG_NOTICE, "Conference Number not found\n");
  1438. }
  1439. }
  1440. ast_mutex_unlock(&conflock);
  1441. return 0;
  1442. }
  1443. int unload_module(void)
  1444. {
  1445. STANDARD_HANGUP_LOCALUSERS;
  1446. ast_cli_unregister(&cli_show_confs);
  1447. ast_cli_unregister(&cli_conf);
  1448. ast_unregister_application(app3);
  1449. ast_unregister_application(app2);
  1450. return ast_unregister_application(app);
  1451. }
  1452. int load_module(void)
  1453. {
  1454. ast_cli_register(&cli_show_confs);
  1455. ast_cli_register(&cli_conf);
  1456. ast_register_application(app3, admin_exec, synopsis3, descrip3);
  1457. ast_register_application(app2, count_exec, synopsis2, descrip2);
  1458. return ast_register_application(app, conf_exec, synopsis, descrip);
  1459. }
  1460. char *description(void)
  1461. {
  1462. return tdesc;
  1463. }
  1464. int usecount(void)
  1465. {
  1466. int res;
  1467. STANDARD_USECOUNT(res);
  1468. return res;
  1469. }
  1470. char *key()
  1471. {
  1472. return ASTERISK_GPL_KEY;
  1473. }