app_agi.c 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * Asterisk Gateway Interface
  5. *
  6. * Copyright (C) 1999, Mark Spencer
  7. *
  8. * Mark Spencer <markster@linux-support.net>
  9. *
  10. * This program is free software, distributed under the terms of
  11. * the GNU General Public License
  12. */
  13. #include <sys/types.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/astdb.h>
  20. #include <math.h>
  21. #include <stdlib.h>
  22. #include <unistd.h>
  23. #include <string.h>
  24. #include <stdlib.h>
  25. #include <signal.h>
  26. #include <sys/time.h>
  27. #include <stdio.h>
  28. #include <fcntl.h>
  29. #include <errno.h>
  30. #include <asterisk/cli.h>
  31. #include <asterisk/logger.h>
  32. #include <asterisk/options.h>
  33. #include <asterisk/image.h>
  34. #include <asterisk/say.h>
  35. #include <asterisk/app.h>
  36. #include <asterisk/dsp.h>
  37. #include <asterisk/musiconhold.h>
  38. #include "../asterisk.h"
  39. #include "../astconf.h"
  40. #include <pthread.h>
  41. #define MAX_ARGS 128
  42. /* Recycle some stuff from the CLI interface */
  43. #define fdprintf ast_cli
  44. typedef struct agi_state {
  45. int fd; /* FD for general output */
  46. int audio; /* FD for audio output */
  47. int ctrl; /* FD for input control */
  48. } AGI;
  49. typedef struct agi_command {
  50. /* Null terminated list of the words of the command */
  51. char *cmda[AST_MAX_CMD_LEN];
  52. /* Handler for the command (channel, AGI state, # of arguments, argument list).
  53. Returns RESULT_SHOWUSAGE for improper arguments */
  54. int (*handler)(struct ast_channel *chan, AGI *agi, int argc, char *argv[]);
  55. /* Summary of the command (< 60 characters) */
  56. char *summary;
  57. /* Detailed usage information */
  58. char *usage;
  59. } agi_command;
  60. static char *tdesc = "Asterisk Gateway Interface (AGI)";
  61. static char *app = "AGI";
  62. static char *eapp = "EAGI";
  63. static char *synopsis = "Executes an AGI compliant application";
  64. static char *esynopsis = "Executes an EAGI compliant application";
  65. static char *descrip =
  66. " [E]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
  67. "program on a channel. AGI allows Asterisk to launch external programs\n"
  68. "written in any language to control a telephony channel, play audio,\n"
  69. "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
  70. "and stdout.\n"
  71. "Returns -1 on hangup or if application requested hangup, or\n"
  72. "0 on non-hangup exit. \n"
  73. "Using 'EAGI' provides enhanced AGI, with incoming audio available out of band"
  74. "on file descriptor 3\n\n"
  75. "Use the CLI command 'show agi' to list available agi commands\n";
  76. STANDARD_LOCAL_USER;
  77. LOCAL_USER_DECL;
  78. #define TONE_BLOCK_SIZE 200
  79. static int launch_script(char *script, char *args, int *fds, int *efd, int *opid)
  80. {
  81. char tmp[256];
  82. int pid;
  83. int toast[2];
  84. int fromast[2];
  85. int audio[2];
  86. int x;
  87. int res;
  88. if (script[0] != '/') {
  89. snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
  90. script = tmp;
  91. }
  92. if (pipe(toast)) {
  93. ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
  94. return -1;
  95. }
  96. if (pipe(fromast)) {
  97. ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
  98. close(toast[0]);
  99. close(toast[1]);
  100. return -1;
  101. }
  102. if (efd) {
  103. if (pipe(audio)) {
  104. ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
  105. close(fromast[0]);
  106. close(fromast[1]);
  107. close(toast[0]);
  108. close(toast[1]);
  109. return -1;
  110. }
  111. res = fcntl(audio[1], F_GETFL);
  112. if (res > -1)
  113. res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
  114. if (res < 0) {
  115. ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
  116. close(fromast[0]);
  117. close(fromast[1]);
  118. close(toast[0]);
  119. close(toast[1]);
  120. close(audio[0]);
  121. close(audio[1]);
  122. return -1;
  123. }
  124. }
  125. pid = fork();
  126. if (pid < 0) {
  127. ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
  128. return -1;
  129. }
  130. if (!pid) {
  131. /* Redirect stdin and out, provide enhanced audio channel if desired */
  132. dup2(fromast[0], STDIN_FILENO);
  133. dup2(toast[1], STDOUT_FILENO);
  134. if (efd) {
  135. dup2(audio[0], STDERR_FILENO + 1);
  136. } else {
  137. close(STDERR_FILENO + 1);
  138. }
  139. /* Close everything but stdin/out/error */
  140. for (x=STDERR_FILENO + 2;x<1024;x++)
  141. close(x);
  142. /* Execute script */
  143. execl(script, script, args, (char *)NULL);
  144. /* Can't use ast_log since FD's are closed */
  145. fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno));
  146. exit(1);
  147. }
  148. if (option_verbose > 2)
  149. ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
  150. fds[0] = toast[0];
  151. fds[1] = fromast[1];
  152. if (efd) {
  153. *efd = audio[1];
  154. }
  155. /* close what we're not using in the parent */
  156. close(toast[1]);
  157. close(fromast[0]);
  158. if (efd) {
  159. // [PHM 12/18/03]
  160. close(audio[0]);
  161. }
  162. *opid = pid;
  163. return 0;
  164. }
  165. static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced)
  166. {
  167. /* Print initial environment, with agi_request always being the first
  168. thing */
  169. fdprintf(fd, "agi_request: %s\n", request);
  170. fdprintf(fd, "agi_channel: %s\n", chan->name);
  171. fdprintf(fd, "agi_language: %s\n", chan->language);
  172. fdprintf(fd, "agi_type: %s\n", chan->type);
  173. fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid);
  174. /* ANI/DNIS */
  175. fdprintf(fd, "agi_callerid: %s\n", chan->callerid ? chan->callerid : "unknown");
  176. fdprintf(fd, "agi_dnid: %s\n", chan->dnid ? chan->dnid : "unknown");
  177. fdprintf(fd, "agi_rdnis: %s\n", chan->rdnis ? chan->rdnis : "unknown");
  178. /* Context information */
  179. fdprintf(fd, "agi_context: %s\n", chan->context);
  180. fdprintf(fd, "agi_extension: %s\n", chan->exten);
  181. fdprintf(fd, "agi_priority: %d\n", chan->priority);
  182. fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
  183. /* User information */
  184. fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
  185. /* End with empty return */
  186. fdprintf(fd, "\n");
  187. }
  188. static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  189. {
  190. int res;
  191. res = 0;
  192. if (chan->_state != AST_STATE_UP) {
  193. /* Answer the chan */
  194. res = ast_answer(chan);
  195. }
  196. fdprintf(agi->fd, "200 result=%d\n", res);
  197. if (res >= 0)
  198. return RESULT_SUCCESS;
  199. else
  200. return RESULT_FAILURE;
  201. }
  202. static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  203. {
  204. int res;
  205. int to;
  206. if (argc != 4)
  207. return RESULT_SHOWUSAGE;
  208. if (sscanf(argv[3], "%i", &to) != 1)
  209. return RESULT_SHOWUSAGE;
  210. res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
  211. fdprintf(agi->fd, "200 result=%d\n", res);
  212. if (res >= 0)
  213. return RESULT_SUCCESS;
  214. else
  215. return RESULT_FAILURE;
  216. }
  217. static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  218. {
  219. int res;
  220. if (argc != 3)
  221. return RESULT_SHOWUSAGE;
  222. /* At the moment, the parser (perhaps broken) returns with
  223. the last argument PLUS the newline at the end of the input
  224. buffer. This probably needs to be fixed, but I wont do that
  225. because other stuff may break as a result. The right way
  226. would probably be to strip off the trailing newline before
  227. parsing, then here, add a newline at the end of the string
  228. before sending it to ast_sendtext --DUDE */
  229. res = ast_sendtext(chan, argv[2]);
  230. fdprintf(agi->fd, "200 result=%d\n", res);
  231. if (res >= 0)
  232. return RESULT_SUCCESS;
  233. else
  234. return RESULT_FAILURE;
  235. }
  236. static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  237. {
  238. int res;
  239. if (argc != 3)
  240. return RESULT_SHOWUSAGE;
  241. res = ast_recvchar(chan,atoi(argv[2]));
  242. if (res == 0) {
  243. fdprintf(agi->fd, "200 result=%d (timeout)\n", res);
  244. return RESULT_SUCCESS;
  245. }
  246. if (res > 0) {
  247. fdprintf(agi->fd, "200 result=%d\n", res);
  248. return RESULT_SUCCESS;
  249. }
  250. else {
  251. fdprintf(agi->fd, "200 result=%d (hangup)\n", res);
  252. return RESULT_FAILURE;
  253. }
  254. }
  255. static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  256. {
  257. int res,x;
  258. if (argc != 3)
  259. return RESULT_SHOWUSAGE;
  260. if (!strncasecmp(argv[2],"on",2)) x = 1; else x = 0;
  261. if (!strncasecmp(argv[2],"mate",4)) x = 2;
  262. if (!strncasecmp(argv[2],"tdd",3)) x = 1;
  263. res = ast_channel_setoption(chan,AST_OPTION_TDD,&x,sizeof(char),0);
  264. fdprintf(agi->fd, "200 result=%d\n", res);
  265. if (res >= 0)
  266. return RESULT_SUCCESS;
  267. else
  268. return RESULT_FAILURE;
  269. }
  270. static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  271. {
  272. int res;
  273. if (argc != 3)
  274. return RESULT_SHOWUSAGE;
  275. res = ast_send_image(chan, argv[2]);
  276. if (!ast_check_hangup(chan))
  277. res = 0;
  278. fdprintf(agi->fd, "200 result=%d\n", res);
  279. if (res >= 0)
  280. return RESULT_SUCCESS;
  281. else
  282. return RESULT_FAILURE;
  283. }
  284. static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  285. {
  286. int res;
  287. struct ast_filestream *fs;
  288. long sample_offset = 0;
  289. long max_length;
  290. if (argc < 4)
  291. return RESULT_SHOWUSAGE;
  292. if (argc > 5)
  293. return RESULT_SHOWUSAGE;
  294. if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
  295. return RESULT_SHOWUSAGE;
  296. fs = ast_openstream(chan, argv[2], chan->language);
  297. if(!fs){
  298. fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
  299. ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
  300. return RESULT_FAILURE;
  301. }
  302. ast_seekstream(fs, 0, SEEK_END);
  303. max_length = ast_tellstream(fs);
  304. ast_seekstream(fs, sample_offset, SEEK_SET);
  305. res = ast_applystream(chan, fs);
  306. res = ast_playstream(fs);
  307. if (res) {
  308. fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
  309. if (res >= 0)
  310. return RESULT_SHOWUSAGE;
  311. else
  312. return RESULT_FAILURE;
  313. }
  314. res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
  315. /* this is to check for if ast_waitstream closed the stream, we probably are at
  316. * the end of the stream, return that amount, else check for the amount */
  317. sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
  318. ast_stopstream(chan);
  319. if (res == 1) {
  320. /* Stop this command, don't print a result line, as there is a new command */
  321. return RESULT_SUCCESS;
  322. }
  323. fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
  324. if (res >= 0)
  325. return RESULT_SUCCESS;
  326. else
  327. return RESULT_FAILURE;
  328. }
  329. static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  330. {
  331. int res;
  332. int num;
  333. if (argc != 4)
  334. return RESULT_SHOWUSAGE;
  335. if (sscanf(argv[2], "%i", &num) != 1)
  336. return RESULT_SHOWUSAGE;
  337. res = ast_say_number_full(chan, num, argv[3], chan->language, agi->audio, agi->ctrl);
  338. if (res == 1)
  339. return RESULT_SUCCESS;
  340. fdprintf(agi->fd, "200 result=%d\n", res);
  341. if (res >= 0)
  342. return RESULT_SUCCESS;
  343. else
  344. return RESULT_FAILURE;
  345. }
  346. static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  347. {
  348. int res;
  349. int num;
  350. if (argc != 4)
  351. return RESULT_SHOWUSAGE;
  352. if (sscanf(argv[2], "%i", &num) != 1)
  353. return RESULT_SHOWUSAGE;
  354. res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
  355. if (res == 1) /* New command */
  356. return RESULT_SUCCESS;
  357. fdprintf(agi->fd, "200 result=%d\n", res);
  358. if (res >= 0)
  359. return RESULT_SUCCESS;
  360. else
  361. return RESULT_FAILURE;
  362. }
  363. static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  364. {
  365. int res;
  366. char data[1024];
  367. int max;
  368. int timeout;
  369. if (argc < 3)
  370. return RESULT_SHOWUSAGE;
  371. if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
  372. if (argc >= 5) max = atoi(argv[4]); else max = 1024;
  373. res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
  374. if (res == 2) /* New command */
  375. return RESULT_SUCCESS;
  376. else if (res == 1)
  377. fdprintf(agi->fd, "200 result=%s (timeout)\n", data);
  378. else if (res < 0 )
  379. fdprintf(agi->fd, "200 result=-1\n");
  380. else
  381. fdprintf(agi->fd, "200 result=%s\n", data);
  382. if (res >= 0)
  383. return RESULT_SUCCESS;
  384. else
  385. return RESULT_FAILURE;
  386. }
  387. static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  388. {
  389. if (argc != 3)
  390. return RESULT_SHOWUSAGE;
  391. strncpy(chan->context, argv[2], sizeof(chan->context)-1);
  392. fdprintf(agi->fd, "200 result=0\n");
  393. return RESULT_SUCCESS;
  394. }
  395. static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  396. {
  397. if (argc != 3)
  398. return RESULT_SHOWUSAGE;
  399. strncpy(chan->exten, argv[2], sizeof(chan->exten)-1);
  400. fdprintf(agi->fd, "200 result=0\n");
  401. return RESULT_SUCCESS;
  402. }
  403. static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  404. {
  405. int pri;
  406. if (argc != 3)
  407. return RESULT_SHOWUSAGE;
  408. if (sscanf(argv[2], "%i", &pri) != 1)
  409. return RESULT_SHOWUSAGE;
  410. chan->priority = pri - 1;
  411. fdprintf(agi->fd, "200 result=0\n");
  412. return RESULT_SUCCESS;
  413. }
  414. static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  415. {
  416. struct ast_filestream *fs;
  417. struct ast_frame *f;
  418. struct timeval tv, start;
  419. long sample_offset = 0;
  420. int res = 0;
  421. int ms;
  422. struct ast_dsp *sildet=NULL; /* silence detector dsp */
  423. int totalsilence = 0;
  424. int dspsilence = 0;
  425. int silence = 0; /* amount of silence to allow */
  426. int gotsilence = 0; /* did we timeout for silence? */
  427. char *silencestr=NULL;
  428. int rfmt=0;
  429. /* XXX EAGI FIXME XXX */
  430. if (argc < 6)
  431. return RESULT_SHOWUSAGE;
  432. if (sscanf(argv[5], "%i", &ms) != 1)
  433. return RESULT_SHOWUSAGE;
  434. if (argc > 6)
  435. silencestr = strchr(argv[6],'s');
  436. if ((argc > 7) && (!silencestr))
  437. silencestr = strchr(argv[7],'s');
  438. if ((argc > 8) && (!silencestr))
  439. silencestr = strchr(argv[8],'s');
  440. if (silencestr) {
  441. if (strlen(silencestr) > 2) {
  442. if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
  443. silencestr++;
  444. silencestr++;
  445. if (silencestr)
  446. silence = atoi(silencestr);
  447. if (silence > 0)
  448. silence *= 1000;
  449. }
  450. }
  451. }
  452. if (silence > 0) {
  453. rfmt = chan->readformat;
  454. res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
  455. if (res < 0) {
  456. ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
  457. return -1;
  458. }
  459. sildet = ast_dsp_new();
  460. if (!sildet) {
  461. ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
  462. return -1;
  463. }
  464. ast_dsp_set_threshold(sildet, 256);
  465. }
  466. /* backward compatibility, if no offset given, arg[6] would have been
  467. * caught below and taken to be a beep, else if it is a digit then it is a
  468. * offset */
  469. if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
  470. res = ast_streamfile(chan, "beep", chan->language);
  471. if ((argc > 7) && (!strchr(argv[7], '=')))
  472. res = ast_streamfile(chan, "beep", chan->language);
  473. if (!res)
  474. res = ast_waitstream(chan, argv[4]);
  475. if (!res) {
  476. fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644);
  477. if (!fs) {
  478. res = -1;
  479. fdprintf(agi->fd, "200 result=%d (writefile)\n", res);
  480. if (sildet)
  481. ast_dsp_free(sildet);
  482. return RESULT_FAILURE;
  483. }
  484. chan->stream = fs;
  485. ast_applystream(chan,fs);
  486. /* really should have checks */
  487. ast_seekstream(fs, sample_offset, SEEK_SET);
  488. ast_truncstream(fs);
  489. gettimeofday(&start, NULL);
  490. gettimeofday(&tv, NULL);
  491. while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
  492. res = ast_waitfor(chan, -1);
  493. if (res < 0) {
  494. ast_closestream(fs);
  495. fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
  496. if (sildet)
  497. ast_dsp_free(sildet);
  498. return RESULT_FAILURE;
  499. }
  500. f = ast_read(chan);
  501. if (!f) {
  502. fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
  503. ast_closestream(fs);
  504. if (sildet)
  505. ast_dsp_free(sildet);
  506. return RESULT_FAILURE;
  507. }
  508. switch(f->frametype) {
  509. case AST_FRAME_DTMF:
  510. if (strchr(argv[4], f->subclass)) {
  511. /* This is an interrupting chracter */
  512. sample_offset = ast_tellstream(fs);
  513. fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
  514. ast_closestream(fs);
  515. ast_frfree(f);
  516. if (sildet)
  517. ast_dsp_free(sildet);
  518. return RESULT_SUCCESS;
  519. }
  520. break;
  521. case AST_FRAME_VOICE:
  522. ast_writestream(fs, f);
  523. /* this is a safe place to check progress since we know that fs
  524. * is valid after a write, and it will then have our current
  525. * location */
  526. sample_offset = ast_tellstream(fs);
  527. if (silence > 0) {
  528. dspsilence = 0;
  529. ast_dsp_silence(sildet, f, &dspsilence);
  530. if (dspsilence) {
  531. totalsilence = dspsilence;
  532. } else {
  533. totalsilence = 0;
  534. }
  535. if (totalsilence > silence) {
  536. /* Ended happily with silence */
  537. ast_frfree(f);
  538. gotsilence = 1;
  539. break;
  540. }
  541. }
  542. break;
  543. }
  544. ast_frfree(f);
  545. gettimeofday(&tv, NULL);
  546. if (gotsilence)
  547. break;
  548. }
  549. if (gotsilence) {
  550. ast_stream_rewind(fs, silence-1000);
  551. ast_truncstream(fs);
  552. sample_offset = ast_tellstream(fs);
  553. }
  554. fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
  555. ast_closestream(fs);
  556. } else
  557. fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
  558. if (silence > 0) {
  559. res = ast_set_read_format(chan, rfmt);
  560. if (res)
  561. ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
  562. ast_dsp_free(sildet);
  563. }
  564. return RESULT_SUCCESS;
  565. }
  566. static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  567. {
  568. int timeout;
  569. if (argc != 3)
  570. return RESULT_SHOWUSAGE;
  571. if (sscanf(argv[2], "%d", &timeout) != 1)
  572. return RESULT_SHOWUSAGE;
  573. if (timeout < 0)
  574. timeout = 0;
  575. if (timeout)
  576. chan->whentohangup = time(NULL) + timeout;
  577. else
  578. chan->whentohangup = 0;
  579. fdprintf(agi->fd, "200 result=0\n");
  580. return RESULT_SUCCESS;
  581. }
  582. static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  583. {
  584. struct ast_channel *c;
  585. if (argc==1) {
  586. /* no argument: hangup the current channel */
  587. ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
  588. fdprintf(agi->fd, "200 result=1\n");
  589. return RESULT_SUCCESS;
  590. } else if (argc==2) {
  591. /* one argument: look for info on the specified channel */
  592. c = ast_channel_walk(NULL);
  593. while (c) {
  594. if (strcasecmp(argv[1],c->name)==0) {
  595. /* we have a matching channel */
  596. ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
  597. fdprintf(agi->fd, "200 result=1\n");
  598. return RESULT_SUCCESS;
  599. }
  600. c = ast_channel_walk(c);
  601. }
  602. /* if we get this far no channel name matched the argument given */
  603. fdprintf(agi->fd, "200 result=-1\n");
  604. return RESULT_SUCCESS;
  605. } else {
  606. return RESULT_SHOWUSAGE;
  607. }
  608. }
  609. static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  610. {
  611. int res;
  612. struct ast_app *app;
  613. if (argc < 2)
  614. return RESULT_SHOWUSAGE;
  615. if (option_verbose > 2)
  616. ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
  617. app = pbx_findapp(argv[1]);
  618. if (app) {
  619. res = pbx_exec(chan, app, argv[2], 1);
  620. } else {
  621. ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
  622. res = -2;
  623. }
  624. fdprintf(agi->fd, "200 result=%d\n", res);
  625. return res;
  626. }
  627. static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  628. {
  629. if (argv[2])
  630. ast_set_callerid(chan, argv[2], 0);
  631. /* strncpy(chan->callerid, argv[2], sizeof(chan->callerid)-1);
  632. */ fdprintf(agi->fd, "200 result=1\n");
  633. return RESULT_SUCCESS;
  634. }
  635. static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  636. {
  637. struct ast_channel *c;
  638. if (argc==2) {
  639. /* no argument: supply info on the current channel */
  640. fdprintf(agi->fd, "200 result=%d\n", chan->_state);
  641. return RESULT_SUCCESS;
  642. } else if (argc==3) {
  643. /* one argument: look for info on the specified channel */
  644. c = ast_channel_walk(NULL);
  645. while (c) {
  646. if (strcasecmp(argv[2],c->name)==0) {
  647. fdprintf(agi->fd, "200 result=%d\n", c->_state);
  648. return RESULT_SUCCESS;
  649. }
  650. c = ast_channel_walk(c);
  651. }
  652. /* if we get this far no channel name matched the argument given */
  653. fdprintf(agi->fd, "200 result=-1\n");
  654. return RESULT_SUCCESS;
  655. } else {
  656. return RESULT_SHOWUSAGE;
  657. }
  658. }
  659. static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  660. {
  661. if (argv[3])
  662. pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
  663. fdprintf(agi->fd, "200 result=1\n");
  664. return RESULT_SUCCESS;
  665. }
  666. static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  667. {
  668. char *tempstr;
  669. if ((tempstr = pbx_builtin_getvar_helper(chan, argv[2])) )
  670. fdprintf(agi->fd, "200 result=1 (%s)\n", tempstr);
  671. else
  672. fdprintf(agi->fd, "200 result=0\n");
  673. return RESULT_SUCCESS;
  674. }
  675. static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  676. {
  677. int level = 0;
  678. char *prefix;
  679. if (argc < 2)
  680. return RESULT_SHOWUSAGE;
  681. if (argv[2])
  682. sscanf(argv[2], "%d", &level);
  683. switch (level) {
  684. case 4:
  685. prefix = VERBOSE_PREFIX_4;
  686. break;
  687. case 3:
  688. prefix = VERBOSE_PREFIX_3;
  689. break;
  690. case 2:
  691. prefix = VERBOSE_PREFIX_2;
  692. break;
  693. case 1:
  694. default:
  695. prefix = VERBOSE_PREFIX_1;
  696. break;
  697. }
  698. if (level <= option_verbose)
  699. ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
  700. fdprintf(agi->fd, "200 result=1\n");
  701. return RESULT_SUCCESS;
  702. }
  703. static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  704. {
  705. int res;
  706. char tmp[256];
  707. if (argc != 4)
  708. return RESULT_SHOWUSAGE;
  709. res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
  710. if (res)
  711. fdprintf(agi->fd, "200 result=0\n");
  712. else
  713. fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
  714. return RESULT_SUCCESS;
  715. }
  716. static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  717. {
  718. int res;
  719. if (argc != 5)
  720. return RESULT_SHOWUSAGE;
  721. res = ast_db_put(argv[2], argv[3], argv[4]);
  722. if (res)
  723. fdprintf(agi->fd, "200 result=0\n");
  724. else
  725. fdprintf(agi->fd, "200 result=1\n");
  726. return RESULT_SUCCESS;
  727. }
  728. static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  729. {
  730. int res;
  731. if (argc != 4)
  732. return RESULT_SHOWUSAGE;
  733. res = ast_db_del(argv[2], argv[3]);
  734. if (res)
  735. fdprintf(agi->fd, "200 result=0\n");
  736. else
  737. fdprintf(agi->fd, "200 result=1\n");
  738. return RESULT_SUCCESS;
  739. }
  740. static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
  741. {
  742. int res;
  743. if ((argc < 3) || (argc > 4))
  744. return RESULT_SHOWUSAGE;
  745. if (argc == 4)
  746. res = ast_db_deltree(argv[2], argv[3]);
  747. else
  748. res = ast_db_deltree(argv[2], NULL);
  749. if (res)
  750. fdprintf(agi->fd, "200 result=0\n");
  751. else
  752. fdprintf(agi->fd, "200 result=1\n");
  753. return RESULT_SUCCESS;
  754. }
  755. static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
  756. {
  757. fdprintf(agi->fd, "200 result=0\n");
  758. return RESULT_SUCCESS;
  759. }
  760. static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
  761. {
  762. if (!strncasecmp(argv[2],"on",2)) {
  763. if (argc > 3)
  764. ast_moh_start(chan, argv[3]);
  765. else
  766. ast_moh_start(chan, NULL);
  767. }
  768. if (!strncasecmp(argv[2],"off",3)) {
  769. ast_moh_stop(chan);
  770. }
  771. fdprintf(agi->fd, "200 result=0\n");
  772. return RESULT_SUCCESS;
  773. }
  774. static char usage_setmusic[] =
  775. " Usage: SET MUSIC ON <on|off> <class>\n"
  776. " Enables/Disables the music on hold generator. If <class> is\n"
  777. " not specified then the default music on hold class will be used.\n"
  778. " Always returns 0\n";
  779. static char usage_dbput[] =
  780. " Usage: DATABASE PUT <family> <key> <value>\n"
  781. " Adds or updates an entry in the Asterisk database for a\n"
  782. " given family, key, and value.\n"
  783. " Returns 1 if succesful, 0 otherwise\n";
  784. static char usage_dbget[] =
  785. " Usage: DATABASE GET <family> <key>\n"
  786. " Retrieves an entry in the Asterisk database for a\n"
  787. " given family and key.\n"
  788. " Returns 0 if <key> is not set. Returns 1 if <key>\n"
  789. " is set and returns the variable in parenthesis\n"
  790. " example return code: 200 result=1 (testvariable)\n";
  791. static char usage_dbdel[] =
  792. " Usage: DATABASE DEL <family> <key>\n"
  793. " Deletes an entry in the Asterisk database for a\n"
  794. " given family and key.\n"
  795. " Returns 1 if succesful, 0 otherwise\n";
  796. static char usage_dbdeltree[] =
  797. " Usage: DATABASE DELTREE <family> [keytree]\n"
  798. " Deletes a family or specific keytree withing a family\n"
  799. " in the Asterisk database.\n"
  800. " Returns 1 if succesful, 0 otherwise\n";
  801. static char usage_verbose[] =
  802. " Usage: VERBOSE <message> <level>\n"
  803. " Sends <message> to the console via verbose message system.\n"
  804. " <level> is the the verbose level (1-4)\n"
  805. " Always returns 1\n";
  806. static char usage_getvariable[] =
  807. " Usage: GET VARIABLE <variablename>\n"
  808. " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
  809. " is set and returns the variable in parenthesis\n"
  810. " example return code: 200 result=1 (testvariable)\n";
  811. static char usage_setvariable[] =
  812. " Usage: SET VARIABLE <variablename> <value>\n";
  813. static char usage_channelstatus[] =
  814. " Usage: CHANNEL STATUS [<channelname>]\n"
  815. " Returns the status of the specified channel.\n"
  816. " If no channel name is given the returns the status of the\n"
  817. " current channel.\n"
  818. " Return values:\n"
  819. " 0 Channel is down and available\n"
  820. " 1 Channel is down, but reserved\n"
  821. " 2 Channel is off hook\n"
  822. " 3 Digits (or equivalent) have been dialed\n"
  823. " 4 Line is ringing\n"
  824. " 5 Remote end is ringing\n"
  825. " 6 Line is up\n"
  826. " 7 Line is busy\n";
  827. static char usage_setcallerid[] =
  828. " Usage: SET CALLERID <number>\n"
  829. " Changes the callerid of the current channel.\n";
  830. static char usage_exec[] =
  831. " Usage: EXEC <application> <options>\n"
  832. " Executes <application> with given <options>.\n"
  833. " Returns whatever the application returns, or -2 on failure to find application\n";
  834. static char usage_hangup[] =
  835. " Usage: HANGUP [<channelname>]\n"
  836. " Hangs up the specified channel.\n"
  837. " If no channel name is given, hangs up the current channel\n";
  838. static char usage_answer[] =
  839. " Usage: ANSWER\n"
  840. " Answers channel if not already in answer state. Returns -1 on\n"
  841. " channel failure, or 0 if successful.\n";
  842. static char usage_waitfordigit[] =
  843. " Usage: WAIT FOR DIGIT <timeout>\n"
  844. " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
  845. " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
  846. " the numerical value of the ascii of the digit if one is received. Use -1\n"
  847. " for the timeout value if you desire the call to block indefinitely.\n";
  848. static char usage_sendtext[] =
  849. " Usage: SEND TEXT \"<text to send>\"\n"
  850. " Sends the given text on a channel. Most channels do not support the\n"
  851. " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
  852. " support text transmission. Returns -1 only on error/hangup. Text\n"
  853. " consisting of greater than one word should be placed in quotes since the\n"
  854. " command only accepts a single argument.\n";
  855. static char usage_recvchar[] =
  856. " Usage: RECEIVE CHAR <timeout>\n"
  857. " Receives a character of text on a channel. Specify timeout to be the\n"
  858. " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
  859. " do not support the reception of text. Returns the decimal value of the character\n"
  860. " if one is received, or 0 if the channel does not support text reception. Returns\n"
  861. " -1 only on error/hangup.\n";
  862. static char usage_tddmode[] =
  863. " Usage: TDD MODE <on|off>\n"
  864. " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
  865. " successful, or 0 if channel is not TDD-capable.\n";
  866. static char usage_sendimage[] =
  867. " Usage: SEND IMAGE <image>\n"
  868. " Sends the given image on a channel. Most channels do not support the\n"
  869. " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
  870. " support image transmission. Returns -1 only on error/hangup. Image names\n"
  871. " should not include extensions.\n";
  872. static char usage_streamfile[] =
  873. " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
  874. " Send the given file, allowing playback to be interrupted by the given\n"
  875. " digits, if any. Use double quotes for the digits if you wish none to be\n"
  876. " permitted. If sample offset is provided then the audio will seek to sample\n"
  877. " offset before play starts. Returns 0 if playback completes without a digit\n"
  878. " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
  879. " or -1 on error or if the channel was disconnected. Remember, the file\n"
  880. " extension must not be included in the filename.\n";
  881. static char usage_saynumber[] =
  882. " Usage: SAY NUMBER <number> <escape digits>\n"
  883. " Say a given number, returning early if any of the given DTMF digits\n"
  884. " are received on the channel. Returns 0 if playback completes without a digit\n"
  885. " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
  886. " -1 on error/hangup.\n";
  887. static char usage_saydigits[] =
  888. " Usage: SAY DIGITS <number> <escape digits>\n"
  889. " Say a given digit string, returning early if any of the given DTMF digits\n"
  890. " are received on the channel. Returns 0 if playback completes without a digit\n"
  891. " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
  892. " -1 on error/hangup.\n";
  893. static char usage_getdata[] =
  894. " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
  895. " Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
  896. "from the channel at the other end.\n";
  897. static char usage_setcontext[] =
  898. " Usage: SET CONTEXT <desired context>\n"
  899. " Sets the context for continuation upon exiting the application.\n";
  900. static char usage_setextension[] =
  901. " Usage: SET EXTENSION <new extension>\n"
  902. " Changes the extension for continuation upon exiting the application.\n";
  903. static char usage_setpriority[] =
  904. " Usage: SET PRIORITY <num>\n"
  905. " Changes the priority for continuation upon exiting the application.\n";
  906. static char usage_recordfile[] =
  907. " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [offset samples] [BEEP] [s=silence]\n"
  908. " Record to a file until a given dtmf digit in the sequence is received\n"
  909. " Returns -1 on hangup or error. The format will specify what kind of file\n"
  910. " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
  911. " -1 for no timeout. Offset samples is optional, and if provided will seek to\n"
  912. " the offset without exceeding the end of the file. \"silence\" is the number\n"
  913. " of seconds of silence allowed before the function returns despite the\n"
  914. " lack of dtmf digits or reaching timeout. Silence value must be\n"
  915. " preceeded by \"s=\" and is optional.\n";
  916. static char usage_autohangup[] =
  917. " Usage: SET AUTOHANGUP <time>\n"
  918. " Cause the channel to automatically hangup at <time> seconds in the\n"
  919. "future. Of course it can be hungup before then as well. Setting to\n"
  920. "0 will cause the autohangup feature to be disabled on this channel.\n";
  921. static char usage_noop[] =
  922. " Usage: NOOP\n"
  923. " Does nothing.\n";
  924. static agi_command commands[] = {
  925. { { "answer", NULL }, handle_answer, "Asserts answer", usage_answer },
  926. { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
  927. { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
  928. { { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
  929. { { "tdd", "mode", NULL }, handle_tddmode, "Sends text to channels supporting it", usage_tddmode },
  930. { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
  931. { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
  932. { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
  933. { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
  934. { { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
  935. { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
  936. { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
  937. { { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
  938. { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
  939. { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
  940. { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
  941. { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
  942. { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
  943. { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
  944. { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
  945. { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
  946. { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
  947. { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
  948. { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
  949. { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
  950. { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
  951. { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
  952. { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }
  953. };
  954. static void join(char *s, int len, char *w[])
  955. {
  956. int x;
  957. /* Join words into a string */
  958. strcpy(s, "");
  959. for (x=0;w[x];x++) {
  960. if (x)
  961. strncat(s, " ", len - strlen(s));
  962. strncat(s, w[x], len - strlen(s));
  963. }
  964. }
  965. static int help_workhorse(int fd, char *match[])
  966. {
  967. char fullcmd[80];
  968. char matchstr[80];
  969. int x;
  970. struct agi_command *e;
  971. if (match)
  972. join(matchstr, sizeof(matchstr), match);
  973. for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
  974. e = &commands[x];
  975. if (e)
  976. join(fullcmd, sizeof(fullcmd), e->cmda);
  977. /* Hide commands that start with '_' */
  978. if (fullcmd[0] == '_')
  979. continue;
  980. if (match) {
  981. if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
  982. continue;
  983. }
  984. }
  985. ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
  986. }
  987. return 0;
  988. }
  989. static agi_command *find_command(char *cmds[], int exact)
  990. {
  991. int x;
  992. int y;
  993. int match;
  994. for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
  995. /* start optimistic */
  996. match = 1;
  997. for (y=0;match && cmds[y]; y++) {
  998. /* If there are no more words in the command (and we're looking for
  999. an exact match) or there is a difference between the two words,
  1000. then this is not a match */
  1001. if (!commands[x].cmda[y] && !exact)
  1002. break;
  1003. /* don't segfault if the next part of a command doesn't exist */
  1004. if (!commands[x].cmda[y]) return NULL;
  1005. if (strcasecmp(commands[x].cmda[y], cmds[y]))
  1006. match = 0;
  1007. }
  1008. /* If more words are needed to complete the command then this is not
  1009. a candidate (unless we're looking for a really inexact answer */
  1010. if ((exact > -1) && commands[x].cmda[y])
  1011. match = 0;
  1012. if (match)
  1013. return &commands[x];
  1014. }
  1015. return NULL;
  1016. }
  1017. static int parse_args(char *s, int *max, char *argv[])
  1018. {
  1019. int x=0;
  1020. int quoted=0;
  1021. int escaped=0;
  1022. int whitespace=1;
  1023. char *cur;
  1024. cur = s;
  1025. while(*s) {
  1026. switch(*s) {
  1027. case '"':
  1028. /* If it's escaped, put a literal quote */
  1029. if (escaped)
  1030. goto normal;
  1031. else
  1032. quoted = !quoted;
  1033. if (quoted && whitespace) {
  1034. /* If we're starting a quote, coming off white space start a new word, too */
  1035. argv[x++] = cur;
  1036. whitespace=0;
  1037. }
  1038. escaped = 0;
  1039. break;
  1040. case ' ':
  1041. case '\t':
  1042. if (!quoted && !escaped) {
  1043. /* If we're not quoted, mark this as whitespace, and
  1044. end the previous argument */
  1045. whitespace = 1;
  1046. *(cur++) = '\0';
  1047. } else
  1048. /* Otherwise, just treat it as anything else */
  1049. goto normal;
  1050. break;
  1051. case '\\':
  1052. /* If we're escaped, print a literal, otherwise enable escaping */
  1053. if (escaped) {
  1054. goto normal;
  1055. } else {
  1056. escaped=1;
  1057. }
  1058. break;
  1059. default:
  1060. normal:
  1061. if (whitespace) {
  1062. if (x >= MAX_ARGS -1) {
  1063. ast_log(LOG_WARNING, "Too many arguments, truncating\n");
  1064. break;
  1065. }
  1066. /* Coming off of whitespace, start the next argument */
  1067. argv[x++] = cur;
  1068. whitespace=0;
  1069. }
  1070. *(cur++) = *s;
  1071. escaped=0;
  1072. }
  1073. s++;
  1074. }
  1075. /* Null terminate */
  1076. *(cur++) = '\0';
  1077. argv[x] = NULL;
  1078. *max = x;
  1079. return 0;
  1080. }
  1081. static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
  1082. {
  1083. char *argv[MAX_ARGS];
  1084. int argc = 0;
  1085. int res;
  1086. agi_command *c;
  1087. argc = MAX_ARGS;
  1088. parse_args(buf, &argc, argv);
  1089. #if 0
  1090. { int x;
  1091. for (x=0;x<argc;x++)
  1092. fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
  1093. #endif
  1094. c = find_command(argv, 0);
  1095. if (c) {
  1096. res = c->handler(chan, agi, argc, argv);
  1097. switch(res) {
  1098. case RESULT_SHOWUSAGE:
  1099. fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n");
  1100. fdprintf(agi->fd, c->usage);
  1101. fdprintf(agi->fd, "520 End of proper usage.\n");
  1102. break;
  1103. case AST_PBX_KEEPALIVE:
  1104. /* We've been asked to keep alive, so do so */
  1105. return AST_PBX_KEEPALIVE;
  1106. break;
  1107. case RESULT_FAILURE:
  1108. /* They've already given the failure. We've been hung up on so handle this
  1109. appropriately */
  1110. return -1;
  1111. }
  1112. } else {
  1113. fdprintf(agi->fd, "510 Invalid or unknown command\n");
  1114. }
  1115. return 0;
  1116. }
  1117. #define RETRY 3
  1118. static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid)
  1119. {
  1120. struct ast_channel *c;
  1121. int outfd;
  1122. int ms;
  1123. int returnstatus = 0;
  1124. struct ast_frame *f;
  1125. char buf[2048];
  1126. FILE *readf;
  1127. //how many times we'll retry if ast_waitfor_nandfs will return without either channel or file descriptor in case select is interrupted by a system call (EINTR)
  1128. int retry = RETRY;
  1129. if (!(readf = fdopen(agi->ctrl, "r"))) {
  1130. ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
  1131. kill(pid, SIGHUP);
  1132. close(agi->ctrl);
  1133. return -1;
  1134. }
  1135. setlinebuf(readf);
  1136. setup_env(chan, request, agi->fd, (agi->audio > -1));
  1137. for (;;) {
  1138. ms = -1;
  1139. c = ast_waitfor_nandfds(&chan, 1, &agi->ctrl, 1, NULL, &outfd, &ms);
  1140. if (c) {
  1141. retry = RETRY;
  1142. /* Idle the channel until we get a command */
  1143. f = ast_read(c);
  1144. if (!f) {
  1145. ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
  1146. returnstatus = -1;
  1147. break;
  1148. } else {
  1149. /* If it's voice, write it to the audio pipe */
  1150. if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
  1151. /* Write, ignoring errors */
  1152. write(agi->audio, f->data, f->datalen);
  1153. }
  1154. ast_frfree(f);
  1155. }
  1156. } else if (outfd > -1) {
  1157. retry = RETRY;
  1158. if (!fgets(buf, sizeof(buf), readf)) {
  1159. /* Program terminated */
  1160. if (returnstatus)
  1161. returnstatus = -1;
  1162. if (option_verbose > 2)
  1163. ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
  1164. /* No need to kill the pid anymore, since they closed us */
  1165. pid = -1;
  1166. break;
  1167. }
  1168. /* get rid of trailing newline, if any */
  1169. if (*buf && buf[strlen(buf) - 1] == '\n')
  1170. buf[strlen(buf) - 1] = 0;
  1171. returnstatus |= agi_handle_command(chan, agi, buf);
  1172. /* If the handle_command returns -1, we need to stop */
  1173. if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
  1174. break;
  1175. }
  1176. } else {
  1177. if (--retry <= 0) {
  1178. ast_log(LOG_WARNING, "No channel, no fd?\n");
  1179. returnstatus = -1;
  1180. break;
  1181. }
  1182. }
  1183. }
  1184. /* Notify process */
  1185. if (pid > -1)
  1186. kill(pid, SIGHUP);
  1187. fclose(readf);
  1188. return returnstatus;
  1189. }
  1190. static int handle_showagi(int fd, int argc, char *argv[]) {
  1191. struct agi_command *e;
  1192. char fullcmd[80];
  1193. if ((argc < 2))
  1194. return RESULT_SHOWUSAGE;
  1195. if (argc > 2) {
  1196. e = find_command(argv + 2, 1);
  1197. if (e)
  1198. ast_cli(fd, e->usage);
  1199. else {
  1200. if (find_command(argv + 2, -1)) {
  1201. return help_workhorse(fd, argv + 1);
  1202. } else {
  1203. join(fullcmd, sizeof(fullcmd), argv+1);
  1204. ast_cli(fd, "No such command '%s'.\n", fullcmd);
  1205. }
  1206. }
  1207. } else {
  1208. return help_workhorse(fd, NULL);
  1209. }
  1210. return RESULT_SUCCESS;
  1211. }
  1212. static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
  1213. struct agi_command *e;
  1214. char fullcmd[80];
  1215. char *tempstr;
  1216. int x;
  1217. FILE *htmlfile;
  1218. if ((argc < 3))
  1219. return RESULT_SHOWUSAGE;
  1220. if (!(htmlfile = fopen(argv[2], "wt"))) {
  1221. ast_cli(fd, "Could not create file '%s'\n", argv[2]);
  1222. return RESULT_SHOWUSAGE;
  1223. }
  1224. fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
  1225. fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
  1226. fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
  1227. for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
  1228. char *stringp=NULL;
  1229. e = &commands[x];
  1230. if (e)
  1231. join(fullcmd, sizeof(fullcmd), e->cmda);
  1232. /* Hide commands that start with '_' */
  1233. if (fullcmd[0] == '_')
  1234. continue;
  1235. fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
  1236. fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
  1237. stringp=e->usage;
  1238. tempstr = strsep(&stringp, "\n");
  1239. fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
  1240. fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
  1241. while ((tempstr = strsep(&stringp, "\n")) != NULL) {
  1242. fprintf(htmlfile, "%s<BR>\n",tempstr);
  1243. }
  1244. fprintf(htmlfile, "</TD></TR>\n");
  1245. fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
  1246. }
  1247. fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
  1248. fclose(htmlfile);
  1249. ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
  1250. return RESULT_SUCCESS;
  1251. }
  1252. static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced)
  1253. {
  1254. int res=0;
  1255. struct localuser *u;
  1256. char *args,*ringy;
  1257. char tmp[256];
  1258. int fds[2];
  1259. int efd = -1;
  1260. int pid;
  1261. char *stringp=tmp;
  1262. AGI agi;
  1263. if (!data || !strlen(data)) {
  1264. ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
  1265. return -1;
  1266. }
  1267. memset(&agi, 0, sizeof(agi));
  1268. strncpy(tmp, data, sizeof(tmp)-1);
  1269. strsep(&stringp, "|");
  1270. args = strsep(&stringp, "|");
  1271. ringy = strsep(&stringp,"|");
  1272. if (!args)
  1273. args = "";
  1274. LOCAL_USER_ADD(u);
  1275. #if 0
  1276. /* Answer if need be */
  1277. if (chan->_state != AST_STATE_UP) {
  1278. if (ringy) { /* if for ringing first */
  1279. /* a little ringy-dingy first */
  1280. ast_indicate(chan, AST_CONTROL_RINGING);
  1281. sleep(3);
  1282. }
  1283. if (ast_answer(chan)) {
  1284. LOCAL_USER_REMOVE(u);
  1285. return -1;
  1286. }
  1287. }
  1288. #endif
  1289. res = launch_script(tmp, args, fds, enhanced ? &efd : NULL, &pid);
  1290. if (!res) {
  1291. agi.fd = fds[1];
  1292. agi.ctrl = fds[0];
  1293. agi.audio = efd;
  1294. res = run_agi(chan, tmp, &agi, pid);
  1295. /* close of fds[0] handled by run_agi function. */
  1296. /*close(fds[0]); */
  1297. close(fds[1]);
  1298. if (efd > -1)
  1299. close(efd);
  1300. }
  1301. LOCAL_USER_REMOVE(u);
  1302. return res;
  1303. }
  1304. static int agi_exec(struct ast_channel *chan, void *data)
  1305. {
  1306. return agi_exec_full(chan, data, 0);
  1307. }
  1308. static int eagi_exec(struct ast_channel *chan, void *data)
  1309. {
  1310. int readformat;
  1311. int res;
  1312. readformat = chan->readformat;
  1313. if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
  1314. ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
  1315. return -1;
  1316. }
  1317. res = agi_exec_full(chan, data, 1);
  1318. if (!res) {
  1319. if (ast_set_read_format(chan, readformat)) {
  1320. ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
  1321. }
  1322. }
  1323. return res;
  1324. }
  1325. static char showagi_help[] =
  1326. "Usage: show agi [topic]\n"
  1327. " When called with a topic as an argument, displays usage\n"
  1328. " information on the given command. If called without a\n"
  1329. " topic, it provides a list of AGI commands.\n";
  1330. static char dumpagihtml_help[] =
  1331. "Usage: dump agihtml <filename>\n"
  1332. " Dumps the agi command list in html format to given filename\n";
  1333. static struct ast_cli_entry showagi =
  1334. { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
  1335. static struct ast_cli_entry dumpagihtml =
  1336. { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
  1337. int unload_module(void)
  1338. {
  1339. STANDARD_HANGUP_LOCALUSERS;
  1340. ast_cli_unregister(&showagi);
  1341. ast_cli_unregister(&dumpagihtml);
  1342. ast_unregister_application(eapp);
  1343. return ast_unregister_application(app);
  1344. }
  1345. int load_module(void)
  1346. {
  1347. ast_cli_register(&showagi);
  1348. ast_cli_register(&dumpagihtml);
  1349. ast_register_application(eapp, eagi_exec, esynopsis, descrip);
  1350. return ast_register_application(app, agi_exec, synopsis, descrip);
  1351. }
  1352. char *description(void)
  1353. {
  1354. return tdesc;
  1355. }
  1356. int usecount(void)
  1357. {
  1358. int res;
  1359. STANDARD_USECOUNT(res);
  1360. return res;
  1361. }
  1362. char *key()
  1363. {
  1364. return ASTERISK_GPL_KEY;
  1365. }