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