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