Fix incorrect endpos when sildet is used during AGI record (bug 1210)
[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                         if (sildet)
554                                 ast_dsp_free(sildet);
555                         return RESULT_FAILURE;
556                 }
557                 
558                 chan->stream = fs;
559                 ast_applystream(chan,fs);
560                 /* really should have checks */
561                 ast_seekstream(fs, sample_offset, SEEK_SET);
562                 ast_truncstream(fs);
563                 
564                 gettimeofday(&start, NULL);
565                 gettimeofday(&tv, NULL);
566                 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
567                         res = ast_waitfor(chan, -1);
568                         if (res < 0) {
569                                 ast_closestream(fs);
570                                 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
571                                 if (sildet)
572                                         ast_dsp_free(sildet);
573                                 return RESULT_FAILURE;
574                         }
575                         f = ast_read(chan);
576                         if (!f) {
577                                 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
578                                 ast_closestream(fs);
579                                 if (sildet)
580                                         ast_dsp_free(sildet);
581                                 return RESULT_FAILURE;
582                         }
583                         switch(f->frametype) {
584                         case AST_FRAME_DTMF:
585                                 if (strchr(argv[4], f->subclass)) {
586                                         /* This is an interrupting chracter */
587                                         sample_offset = ast_tellstream(fs);
588                                         fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
589                                         ast_closestream(fs);
590                                         ast_frfree(f);
591                                         if (sildet)
592                                                 ast_dsp_free(sildet);
593                                         return RESULT_SUCCESS;
594                                 }
595                                 break;
596                         case AST_FRAME_VOICE:
597                                 ast_writestream(fs, f);
598                                 /* this is a safe place to check progress since we know that fs
599                                  * is valid after a write, and it will then have our current
600                                  * location */
601                                 sample_offset = ast_tellstream(fs);
602                                 if (silence > 0) {
603                                         dspsilence = 0;
604                                         ast_dsp_silence(sildet, f, &dspsilence);
605                                         if (dspsilence) {
606                                                 totalsilence = dspsilence;
607                                         } else {
608                                                 totalsilence = 0;
609                                         }
610                                         if (totalsilence > silence) {
611                                              /* Ended happily with silence */
612                                                 ast_frfree(f);
613                                                 gotsilence = 1;
614                                                 break;
615                                         }
616                                 }
617                                 break;
618                         }
619                         ast_frfree(f);
620                         gettimeofday(&tv, NULL);
621                         if (gotsilence)
622                                 break;
623         }
624
625                 if (gotsilence) {
626                         ast_stream_rewind(fs, silence-1000);
627                         ast_truncstream(fs);
628                         sample_offset = ast_tellstream(fs);
629                 }               
630                 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
631                 ast_closestream(fs);
632         } else
633                 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
634
635         if (silence > 0) {
636                 res = ast_set_read_format(chan, rfmt);
637                 if (res)
638                         ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
639                 ast_dsp_free(sildet);
640         }
641         return RESULT_SUCCESS;
642 }
643
644 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
645 {
646         int timeout;
647
648         if (argc != 3)
649                 return RESULT_SHOWUSAGE;
650         if (sscanf(argv[2], "%d", &timeout) != 1)
651                 return RESULT_SHOWUSAGE;
652         if (timeout < 0)
653                 timeout = 0;
654         if (timeout)
655                 chan->whentohangup = time(NULL) + timeout;
656         else
657                 chan->whentohangup = 0;
658         fdprintf(agi->fd, "200 result=0\n");
659         return RESULT_SUCCESS;
660 }
661
662 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
663 {
664         struct ast_channel *c;
665         if (argc==1) {
666             /* no argument: hangup the current channel */
667             ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
668             fdprintf(agi->fd, "200 result=1\n");
669             return RESULT_SUCCESS;
670         } else if (argc==2) {
671             /* one argument: look for info on the specified channel */
672             c = ast_channel_walk(NULL);
673             while (c) {
674                 if (strcasecmp(argv[1],c->name)==0) {
675                     /* we have a matching channel */
676                     ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
677                     fdprintf(agi->fd, "200 result=1\n");
678                     return RESULT_SUCCESS;
679                 }
680                 c = ast_channel_walk(c);
681             }
682             /* if we get this far no channel name matched the argument given */
683             fdprintf(agi->fd, "200 result=-1\n");
684             return RESULT_SUCCESS;
685         } else {
686             return RESULT_SHOWUSAGE;
687         }
688 }
689
690 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
691 {
692         int res;
693         struct ast_app *app;
694
695         if (argc < 2)
696                 return RESULT_SHOWUSAGE;
697
698         if (option_verbose > 2)
699                 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
700
701         app = pbx_findapp(argv[1]);
702
703         if (app) {
704                 res = pbx_exec(chan, app, argv[2], 1);
705         } else {
706                 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
707                 res = -2;
708         }
709         fdprintf(agi->fd, "200 result=%d\n", res);
710
711         return res;
712 }
713
714 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
715 {
716         if (argv[2])
717                 ast_set_callerid(chan, argv[2], 0);
718
719 /*      strncpy(chan->callerid, argv[2], sizeof(chan->callerid)-1);
720 */      fdprintf(agi->fd, "200 result=1\n");
721         return RESULT_SUCCESS;
722 }
723
724 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
725 {
726         struct ast_channel *c;
727         if (argc==2) {
728             /* no argument: supply info on the current channel */
729             fdprintf(agi->fd, "200 result=%d\n", chan->_state);
730             return RESULT_SUCCESS;
731         } else if (argc==3) {
732             /* one argument: look for info on the specified channel */
733             c = ast_channel_walk(NULL);
734             while (c) {
735                 if (strcasecmp(argv[2],c->name)==0) {
736                     fdprintf(agi->fd, "200 result=%d\n", c->_state);
737                     return RESULT_SUCCESS;
738                 }
739                 c = ast_channel_walk(c);
740             }
741             /* if we get this far no channel name matched the argument given */
742             fdprintf(agi->fd, "200 result=-1\n");
743             return RESULT_SUCCESS;
744         } else {
745             return RESULT_SHOWUSAGE;
746         }
747 }
748
749 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
750 {
751         if (argv[3])
752                 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
753
754         fdprintf(agi->fd, "200 result=1\n");
755         return RESULT_SUCCESS;
756 }
757
758 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
759 {
760         char *tempstr;
761
762         if ((tempstr = pbx_builtin_getvar_helper(chan, argv[2])) ) 
763                         fdprintf(agi->fd, "200 result=1 (%s)\n", tempstr);
764         else
765                         fdprintf(agi->fd, "200 result=0\n");
766
767         return RESULT_SUCCESS;
768 }
769
770 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
771 {
772         int level = 0;
773         char *prefix;
774
775         if (argc < 2)
776                 return RESULT_SHOWUSAGE;
777
778         if (argv[2])
779                 sscanf(argv[2], "%d", &level);
780
781         switch (level) {
782                 case 4:
783                         prefix = VERBOSE_PREFIX_4;
784                         break;
785                 case 3:
786                         prefix = VERBOSE_PREFIX_3;
787                         break;
788                 case 2:
789                         prefix = VERBOSE_PREFIX_2;
790                         break;
791                 case 1:
792                 default:
793                         prefix = VERBOSE_PREFIX_1;
794                         break;
795         }
796
797         if (level <= option_verbose)
798                 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
799         
800         fdprintf(agi->fd, "200 result=1\n");
801         
802         return RESULT_SUCCESS;
803 }
804
805 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
806 {
807         int res;
808         char tmp[256];
809         if (argc != 4)
810                 return RESULT_SHOWUSAGE;
811         res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
812         if (res) 
813                         fdprintf(agi->fd, "200 result=0\n");
814         else
815                         fdprintf(agi->fd, "200 result=1 (%s)\n", tmp);
816
817         return RESULT_SUCCESS;
818 }
819
820 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
821 {
822         int res;
823         if (argc != 5)
824                 return RESULT_SHOWUSAGE;
825         res = ast_db_put(argv[2], argv[3], argv[4]);
826         if (res) 
827                         fdprintf(agi->fd, "200 result=0\n");
828         else
829                         fdprintf(agi->fd, "200 result=1\n");
830
831         return RESULT_SUCCESS;
832 }
833
834 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
835 {
836         int res;
837         if (argc != 4)
838                 return RESULT_SHOWUSAGE;
839         res = ast_db_del(argv[2], argv[3]);
840         if (res) 
841                 fdprintf(agi->fd, "200 result=0\n");
842         else
843                 fdprintf(agi->fd, "200 result=1\n");
844
845         return RESULT_SUCCESS;
846 }
847
848 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
849 {
850         int res;
851         if ((argc < 3) || (argc > 4))
852                 return RESULT_SHOWUSAGE;
853         if (argc == 4)
854                 res = ast_db_deltree(argv[2], argv[3]);
855         else
856                 res = ast_db_deltree(argv[2], NULL);
857
858         if (res) 
859                 fdprintf(agi->fd, "200 result=0\n");
860         else
861                 fdprintf(agi->fd, "200 result=1\n");
862         return RESULT_SUCCESS;
863 }
864
865 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
866 {
867         fdprintf(agi->fd, "200 result=0\n");
868         return RESULT_SUCCESS;
869 }
870
871 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
872 {
873         if (!strncasecmp(argv[2],"on",2)) {
874                 if (argc > 3)
875                         ast_moh_start(chan, argv[3]);
876                 else
877                         ast_moh_start(chan, NULL);
878         }
879         if (!strncasecmp(argv[2],"off",3)) {
880                 ast_moh_stop(chan);
881         }
882         fdprintf(agi->fd, "200 result=0\n");
883         return RESULT_SUCCESS;
884 }
885
886 static char usage_setmusic[] =
887 " Usage: SET MUSIC ON <on|off> <class>\n"
888 "       Enables/Disables the music on hold generator.  If <class> is\n"
889 " not specified then the default music on hold class will be used.\n"
890 " Always returns 0\n";
891
892 static char usage_dbput[] =
893 " Usage: DATABASE PUT <family> <key> <value>\n"
894 "       Adds or updates an entry in the Asterisk database for a\n"
895 " given family, key, and value.\n"
896 " Returns 1 if succesful, 0 otherwise\n";
897
898 static char usage_dbget[] =
899 " Usage: DATABASE GET <family> <key>\n"
900 "       Retrieves an entry in the Asterisk database for a\n"
901 " given family and key.\n"
902 "       Returns 0 if <key> is not set.  Returns 1 if <key>\n"
903 " is set and returns the variable in parenthesis\n"
904 " example return code: 200 result=1 (testvariable)\n";
905
906 static char usage_dbdel[] =
907 " Usage: DATABASE DEL <family> <key>\n"
908 "       Deletes an entry in the Asterisk database for a\n"
909 " given family and key.\n"
910 " Returns 1 if succesful, 0 otherwise\n";
911
912 static char usage_dbdeltree[] =
913 " Usage: DATABASE DELTREE <family> [keytree]\n"
914 "       Deletes a family or specific keytree withing a family\n"
915 " in the Asterisk database.\n"
916 " Returns 1 if succesful, 0 otherwise\n";
917
918 static char usage_verbose[] =
919 " Usage: VERBOSE <message> <level>\n"
920 "       Sends <message> to the console via verbose message system.\n"
921 "       <level> is the the verbose level (1-4)\n"
922 "       Always returns 1\n";
923
924 static char usage_getvariable[] =
925 " Usage: GET VARIABLE <variablename>\n"
926 "       Returns 0 if <variablename> is not set.  Returns 1 if <variablename>\n"
927 " is set and returns the variable in parenthesis\n"
928 " example return code: 200 result=1 (testvariable)\n";
929
930 static char usage_setvariable[] =
931 " Usage: SET VARIABLE <variablename> <value>\n";
932
933 static char usage_channelstatus[] =
934 " Usage: CHANNEL STATUS [<channelname>]\n"
935 "       Returns the status of the specified channel.\n" 
936 "       If no channel name is given the returns the status of the\n"
937 "       current channel.\n"
938 "       Return values:\n"
939 " 0 Channel is down and available\n"
940 " 1 Channel is down, but reserved\n"
941 " 2 Channel is off hook\n"
942 " 3 Digits (or equivalent) have been dialed\n"
943 " 4 Line is ringing\n"
944 " 5 Remote end is ringing\n"
945 " 6 Line is up\n"
946 " 7 Line is busy\n";
947
948 static char usage_setcallerid[] =
949 " Usage: SET CALLERID <number>\n"
950 "       Changes the callerid of the current channel.\n";
951
952 static char usage_exec[] =
953 " Usage: EXEC <application> <options>\n"
954 "       Executes <application> with given <options>.\n"
955 "       Returns whatever the application returns, or -2 on failure to find application\n";
956
957 static char usage_hangup[] =
958 " Usage: HANGUP [<channelname>]\n"
959 "       Hangs up the specified channel.\n"
960 "       If no channel name is given, hangs up the current channel\n";
961
962 static char usage_answer[] = 
963 " Usage: ANSWER\n"
964 "        Answers channel if not already in answer state. Returns -1 on\n"
965 " channel failure, or 0 if successful.\n";
966
967 static char usage_waitfordigit[] = 
968 " Usage: WAIT FOR DIGIT <timeout>\n"
969 "        Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
970 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
971 " the numerical value of the ascii of the digit if one is received.  Use -1\n"
972 " for the timeout value if you desire the call to block indefinitely.\n";
973
974 static char usage_sendtext[] =
975 " Usage: SEND TEXT \"<text to send>\"\n"
976 "        Sends the given text on a channel.  Most channels do not support the\n"
977 " transmission of text.  Returns 0 if text is sent, or if the channel does not\n"
978 " support text transmission.  Returns -1 only on error/hangup.  Text\n"
979 " consisting of greater than one word should be placed in quotes since the\n"
980 " command only accepts a single argument.\n";
981
982 static char usage_recvchar[] =
983 " Usage: RECEIVE CHAR <timeout>\n"
984 "        Receives a character of text on a channel.  Specify timeout to be the\n"
985 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
986 " do not support the reception of text.  Returns the decimal value of the character\n"
987 " if one is received, or 0 if the channel does not support text reception.  Returns\n"
988 " -1 only on error/hangup.\n";
989
990 static char usage_tddmode[] =
991 " Usage: TDD MODE <on|off>\n"
992 "        Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
993 " successful, or 0 if channel is not TDD-capable.\n";
994
995 static char usage_sendimage[] =
996 " Usage: SEND IMAGE <image>\n"
997 "        Sends the given image on a channel.  Most channels do not support the\n"
998 " transmission of images.  Returns 0 if image is sent, or if the channel does not\n"
999 " support image transmission.  Returns -1 only on error/hangup.  Image names\n"
1000 " should not include extensions.\n";
1001
1002 static char usage_streamfile[] =
1003 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
1004 "        Send the given file, allowing playback to be interrupted by the given\n"
1005 " digits, if any.  Use double quotes for the digits if you wish none to be\n"
1006 " permitted.  If sample offset is provided then the audio will seek to sample\n"
1007 " offset before play starts.  Returns 0 if playback completes without a digit\n"
1008 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
1009 " or -1 on error or if the channel was disconnected.  Remember, the file\n"
1010 " extension must not be included in the filename.\n";
1011
1012 static char usage_saynumber[] =
1013 " Usage: SAY NUMBER <number> <escape digits>\n"
1014 "        Say a given number, returning early if any of the given DTMF digits\n"
1015 " are received on the channel.  Returns 0 if playback completes without a digit\n"
1016 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1017 " -1 on error/hangup.\n";
1018
1019 static char usage_saydigits[] =
1020 " Usage: SAY DIGITS <number> <escape digits>\n"
1021 "        Say a given digit string, returning early if any of the given DTMF digits\n"
1022 " are received on the channel.  Returns 0 if playback completes without a digit\n"
1023 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
1024 " -1 on error/hangup.\n";
1025
1026 static char usage_saytime[] =
1027 " Usage: SAY TIME <time> <escape digits>\n"
1028 "        Say a given time, returning early if any of the given DTMF digits are\n"
1029 " received on the channel.  <time> is number of seconds elapsed since 00:00:00\n"
1030 " on January 1, 1970, Coordinated Universal Time (UTC).  Returns 0 if playback\n"
1031 " completes without a digit being pressed, or the ASCII numerical value of the\n"
1032 " digit if one was pressed or -1 on error/hangup.\n";
1033
1034 static char usage_getdata[] =
1035 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
1036 "        Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
1037 "from the channel at the other end.\n";
1038
1039 static char usage_setcontext[] =
1040 " Usage: SET CONTEXT <desired context>\n"
1041 "        Sets the context for continuation upon exiting the application.\n";
1042
1043 static char usage_setextension[] =
1044 " Usage: SET EXTENSION <new extension>\n"
1045 "        Changes the extension for continuation upon exiting the application.\n";
1046
1047 static char usage_setpriority[] =
1048 " Usage: SET PRIORITY <num>\n"
1049 "        Changes the priority for continuation upon exiting the application.\n";
1050
1051 static char usage_recordfile[] =
1052 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [offset samples] [BEEP] [s=silence]\n"
1053 "        Record to a file until a given dtmf digit in the sequence is received\n"
1054 " Returns -1 on hangup or error.  The format will specify what kind of file\n"
1055 " will be recorded.  The timeout is the maximum record time in milliseconds, or\n"
1056 " -1 for no timeout. Offset samples is optional, and if provided will seek to\n"
1057 " the offset without exceeding the end of the file.  \"silence\" is the number\n"
1058 " of seconds of silence allowed before the function returns despite the\n"
1059 " lack of dtmf digits or reaching timeout.  Silence value must be\n"
1060 " preceeded by \"s=\" and is optional.\n";
1061
1062
1063 static char usage_autohangup[] =
1064 " Usage: SET AUTOHANGUP <time>\n"
1065 "    Cause the channel to automatically hangup at <time> seconds in the\n"
1066 "future.  Of course it can be hungup before then as well.   Setting to\n"
1067 "0 will cause the autohangup feature to be disabled on this channel.\n";
1068
1069 static char usage_noop[] =
1070 " Usage: NOOP\n"
1071 "    Does nothing.\n";
1072
1073 static agi_command commands[] = {
1074         { { "answer", NULL }, handle_answer, "Asserts answer", usage_answer },
1075         { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
1076         { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
1077         { { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
1078         { { "tdd", "mode", NULL }, handle_tddmode, "Sends text to channels supporting it", usage_tddmode },
1079         { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
1080         { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
1081         { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
1082         { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
1083         { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
1084         { { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
1085         { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
1086         { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
1087         { { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
1088         { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
1089         { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
1090         { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
1091         { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
1092         { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
1093         { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
1094         { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
1095         { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
1096         { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
1097         { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
1098         { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
1099         { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
1100         { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
1101         { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
1102         { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }
1103 };
1104
1105 static void join(char *s, int len, char *w[])
1106 {
1107         int x;
1108         /* Join words into a string */
1109         strcpy(s, "");
1110         for (x=0;w[x];x++) {
1111                 if (x)
1112                         strncat(s, " ", len - strlen(s));
1113                 strncat(s, w[x], len - strlen(s));
1114         }
1115 }
1116
1117 static int help_workhorse(int fd, char *match[])
1118 {
1119         char fullcmd[80];
1120         char matchstr[80];
1121         int x;
1122         struct agi_command *e;
1123         if (match)
1124                 join(matchstr, sizeof(matchstr), match);
1125         for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1126                 e = &commands[x]; 
1127                 if (e)
1128                         join(fullcmd, sizeof(fullcmd), e->cmda);
1129                 /* Hide commands that start with '_' */
1130                 if (fullcmd[0] == '_')
1131                         continue;
1132                 if (match) {
1133                         if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1134                                 continue;
1135                         }
1136                 }
1137                 ast_cli(fd, "%20.20s   %s\n", fullcmd, e->summary);
1138         }
1139         return 0;
1140 }
1141
1142 static agi_command *find_command(char *cmds[], int exact)
1143 {
1144         int x;
1145         int y;
1146         int match;
1147         for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
1148                 /* start optimistic */
1149                 match = 1;
1150                 for (y=0;match && cmds[y]; y++) {
1151                         /* If there are no more words in the command (and we're looking for
1152                            an exact match) or there is a difference between the two words,
1153                            then this is not a match */
1154                         if (!commands[x].cmda[y] && !exact)
1155                                 break;
1156                         /* don't segfault if the next part of a command doesn't exist */
1157                         if (!commands[x].cmda[y]) return NULL;
1158                         if (strcasecmp(commands[x].cmda[y], cmds[y]))
1159                                 match = 0;
1160                 }
1161                 /* If more words are needed to complete the command then this is not
1162                    a candidate (unless we're looking for a really inexact answer  */
1163                 if ((exact > -1) && commands[x].cmda[y])
1164                         match = 0;
1165                 if (match)
1166                         return &commands[x];
1167         }
1168         return NULL;
1169 }
1170
1171
1172 static int parse_args(char *s, int *max, char *argv[])
1173 {
1174         int x=0;
1175         int quoted=0;
1176         int escaped=0;
1177         int whitespace=1;
1178         char *cur;
1179
1180         cur = s;
1181         while(*s) {
1182                 switch(*s) {
1183                 case '"':
1184                         /* If it's escaped, put a literal quote */
1185                         if (escaped) 
1186                                 goto normal;
1187                         else 
1188                                 quoted = !quoted;
1189                         if (quoted && whitespace) {
1190                                 /* If we're starting a quote, coming off white space start a new word, too */
1191                                 argv[x++] = cur;
1192                                 whitespace=0;
1193                         }
1194                         escaped = 0;
1195                 break;
1196                 case ' ':
1197                 case '\t':
1198                         if (!quoted && !escaped) {
1199                                 /* If we're not quoted, mark this as whitespace, and
1200                                    end the previous argument */
1201                                 whitespace = 1;
1202                                 *(cur++) = '\0';
1203                         } else
1204                                 /* Otherwise, just treat it as anything else */ 
1205                                 goto normal;
1206                         break;
1207                 case '\\':
1208                         /* If we're escaped, print a literal, otherwise enable escaping */
1209                         if (escaped) {
1210                                 goto normal;
1211                         } else {
1212                                 escaped=1;
1213                         }
1214                         break;
1215                 default:
1216 normal:
1217                         if (whitespace) {
1218                                 if (x >= MAX_ARGS -1) {
1219                                         ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1220                                         break;
1221                                 }
1222                                 /* Coming off of whitespace, start the next argument */
1223                                 argv[x++] = cur;
1224                                 whitespace=0;
1225                         }
1226                         *(cur++) = *s;
1227                         escaped=0;
1228                 }
1229                 s++;
1230         }
1231         /* Null terminate */
1232         *(cur++) = '\0';
1233         argv[x] = NULL;
1234         *max = x;
1235         return 0;
1236 }
1237
1238 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
1239 {
1240         char *argv[MAX_ARGS];
1241         int argc = 0;
1242         int res;
1243         agi_command *c;
1244         argc = MAX_ARGS;
1245         parse_args(buf, &argc, argv);
1246 #if     0
1247         { int x;
1248         for (x=0;x<argc;x++) 
1249                 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
1250 #endif
1251         c = find_command(argv, 0);
1252         if (c) {
1253                 res = c->handler(chan, agi, argc, argv);
1254                 switch(res) {
1255                 case RESULT_SHOWUSAGE:
1256                         fdprintf(agi->fd, "520-Invalid command syntax.  Proper usage follows:\n");
1257                         fdprintf(agi->fd, c->usage);
1258                         fdprintf(agi->fd, "520 End of proper usage.\n");
1259                         break;
1260                 case AST_PBX_KEEPALIVE:
1261                         /* We've been asked to keep alive, so do so */
1262                         return AST_PBX_KEEPALIVE;
1263                         break;
1264                 case RESULT_FAILURE:
1265                         /* They've already given the failure.  We've been hung up on so handle this
1266                            appropriately */
1267                         return -1;
1268                 }
1269         } else {
1270                 fdprintf(agi->fd, "510 Invalid or unknown command\n");
1271         }
1272         return 0;
1273 }
1274 #define RETRY   3
1275 static int run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead)
1276 {
1277         struct ast_channel *c;
1278         int outfd;
1279         int ms;
1280         int returnstatus = 0;
1281         struct ast_frame *f;
1282         char buf[2048];
1283         FILE *readf;
1284         /* how many times we'll retry if ast_waitfor_nandfs will return without either 
1285           channel or file descriptor in case select is interrupted by a system call (EINTR) */
1286         int retry = RETRY;
1287
1288         if (!(readf = fdopen(agi->ctrl, "r"))) {
1289                 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
1290                 kill(pid, SIGHUP);
1291                 return -1;
1292         }
1293         setlinebuf(readf);
1294         setup_env(chan, request, agi->fd, (agi->audio > -1));
1295         for (;;) {
1296                 ms = -1;
1297                 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
1298                 if (c) {
1299                         retry = RETRY;
1300                         /* Idle the channel until we get a command */
1301                         f = ast_read(c);
1302                         if (!f) {
1303                                 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
1304                                 returnstatus = -1;
1305                                 break;
1306                         } else {
1307                                 /* If it's voice, write it to the audio pipe */
1308                                 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
1309                                         /* Write, ignoring errors */
1310                                         write(agi->audio, f->data, f->datalen);
1311                                 }
1312                                 ast_frfree(f);
1313                         }
1314                 } else if (outfd > -1) {
1315                         retry = RETRY;
1316                         if (!fgets(buf, sizeof(buf), readf)) {
1317                                 /* Program terminated */
1318                                 if (returnstatus)
1319                                         returnstatus = -1;
1320                                 if (option_verbose > 2) 
1321                                         ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
1322                                 /* No need to kill the pid anymore, since they closed us */
1323                                 pid = -1;
1324                                 break;
1325                         }
1326                           /* get rid of trailing newline, if any */
1327                         if (*buf && buf[strlen(buf) - 1] == '\n')
1328                                 buf[strlen(buf) - 1] = 0;
1329
1330                         returnstatus |= agi_handle_command(chan, agi, buf);
1331                         /* If the handle_command returns -1, we need to stop */
1332                         if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
1333                                 break;
1334                         }
1335                 } else {
1336                         if (--retry <= 0) {
1337                                 ast_log(LOG_WARNING, "No channel, no fd?\n");
1338                                 returnstatus = -1;
1339                                 break;
1340                         }
1341                 }
1342         }
1343         /* Notify process */
1344         if (pid > -1)
1345                 kill(pid, SIGHUP);
1346         fclose(readf);
1347         return returnstatus;
1348 }
1349
1350 static int handle_showagi(int fd, int argc, char *argv[]) {
1351         struct agi_command *e;
1352         char fullcmd[80];
1353         if ((argc < 2))
1354                 return RESULT_SHOWUSAGE;
1355         if (argc > 2) {
1356                 e = find_command(argv + 2, 1);
1357                 if (e) 
1358                         ast_cli(fd, e->usage);
1359                 else {
1360                         if (find_command(argv + 2, -1)) {
1361                                 return help_workhorse(fd, argv + 1);
1362                         } else {
1363                                 join(fullcmd, sizeof(fullcmd), argv+1);
1364                                 ast_cli(fd, "No such command '%s'.\n", fullcmd);
1365                         }
1366                 }
1367         } else {
1368                 return help_workhorse(fd, NULL);
1369         }
1370         return RESULT_SUCCESS;
1371 }
1372
1373 static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
1374         struct agi_command *e;
1375         char fullcmd[80];
1376         char *tempstr;
1377         int x;
1378         FILE *htmlfile;
1379
1380         if ((argc < 3))
1381                 return RESULT_SHOWUSAGE;
1382
1383         if (!(htmlfile = fopen(argv[2], "wt"))) {
1384                 ast_cli(fd, "Could not create file '%s'\n", argv[2]);
1385                 return RESULT_SHOWUSAGE;
1386         }
1387
1388         fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
1389         fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
1390
1391
1392         fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
1393
1394         for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
1395                 char *stringp=NULL;
1396                 e = &commands[x]; 
1397                 if (e)
1398                         join(fullcmd, sizeof(fullcmd), e->cmda);
1399                 /* Hide commands that start with '_' */
1400                 if (fullcmd[0] == '_')
1401                         continue;
1402
1403                 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
1404                 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
1405
1406
1407                 stringp=e->usage;
1408                 tempstr = strsep(&stringp, "\n");
1409
1410                 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
1411                 
1412                 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
1413                 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
1414                 fprintf(htmlfile, "%s<BR>\n",tempstr);
1415
1416                 }
1417                 fprintf(htmlfile, "</TD></TR>\n");
1418                 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
1419
1420         }
1421
1422         fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
1423         fclose(htmlfile);
1424         ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
1425         return RESULT_SUCCESS;
1426 }
1427
1428 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
1429 {
1430         int res=0;
1431         struct localuser *u;
1432         char *args,*ringy;
1433         char tmp[256];
1434         int fds[2];
1435         int efd = -1;
1436         int pid;
1437         char *stringp=tmp;
1438         AGI agi;
1439         if (!data || !strlen(data)) {
1440                 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
1441                 return -1;
1442         }
1443
1444
1445         memset(&agi, 0, sizeof(agi));
1446         strncpy(tmp, data, sizeof(tmp)-1);
1447         strsep(&stringp, "|");
1448         args = strsep(&stringp, "|");
1449         ringy = strsep(&stringp,"|");
1450         if (!args)
1451                 args = "";
1452         LOCAL_USER_ADD(u);
1453 #if 0
1454          /* Answer if need be */
1455         if (chan->_state != AST_STATE_UP) {
1456                 if (ringy) { /* if for ringing first */
1457                         /* a little ringy-dingy first */
1458                         ast_indicate(chan, AST_CONTROL_RINGING);  
1459                         sleep(3); 
1460                 }
1461                 if (ast_answer(chan)) {
1462                         LOCAL_USER_REMOVE(u);
1463                         return -1;
1464                 }
1465         }
1466 #endif
1467         res = launch_script(tmp, args, fds, enhanced ? &efd : NULL, &pid);
1468         if (!res) {
1469                 agi.fd = fds[1];
1470                 agi.ctrl = fds[0];
1471                 agi.audio = efd;
1472                 res = run_agi(chan, tmp, &agi, pid, dead);
1473                 close(fds[0]);
1474                 close(fds[1]);
1475                 if (efd > -1)
1476                         close(efd);
1477         }
1478         LOCAL_USER_REMOVE(u);
1479         return res;
1480 }
1481
1482 static int agi_exec(struct ast_channel *chan, void *data)
1483 {
1484         return agi_exec_full(chan, data, 0, 0);
1485 }
1486
1487 static int eagi_exec(struct ast_channel *chan, void *data)
1488 {
1489         int readformat;
1490         int res;
1491         readformat = chan->readformat;
1492         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
1493                 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
1494                 return -1;
1495         }
1496         res = agi_exec_full(chan, data, 1, 0);
1497         if (!res) {
1498                 if (ast_set_read_format(chan, readformat)) {
1499                         ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
1500                 }
1501         }
1502         return res;
1503 }
1504
1505 static int deadagi_exec(struct ast_channel *chan, void *data)
1506 {
1507         return agi_exec_full(chan, data, 0, 1);
1508 }
1509
1510 static char showagi_help[] =
1511 "Usage: show agi [topic]\n"
1512 "       When called with a topic as an argument, displays usage\n"
1513 "       information on the given command.  If called without a\n"
1514 "       topic, it provides a list of AGI commands.\n";
1515
1516
1517 static char dumpagihtml_help[] =
1518 "Usage: dump agihtml <filename>\n"
1519 "       Dumps the agi command list in html format to given filename\n";
1520
1521 static struct ast_cli_entry showagi = 
1522 { { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
1523
1524 static struct ast_cli_entry dumpagihtml = 
1525 { { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
1526
1527 int unload_module(void)
1528 {
1529         STANDARD_HANGUP_LOCALUSERS;
1530         ast_cli_unregister(&showagi);
1531         ast_cli_unregister(&dumpagihtml);
1532         ast_unregister_application(eapp);
1533         ast_unregister_application(deadapp);
1534         return ast_unregister_application(app);
1535 }
1536
1537 int load_module(void)
1538 {
1539         ast_cli_register(&showagi);
1540         ast_cli_register(&dumpagihtml);
1541         ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
1542         ast_register_application(eapp, eagi_exec, esynopsis, descrip);
1543         return ast_register_application(app, agi_exec, synopsis, descrip);
1544 }
1545
1546 char *description(void)
1547 {
1548         return tdesc;
1549 }
1550
1551 int usecount(void)
1552 {
1553         int res;
1554         STANDARD_USECOUNT(res);
1555         return res;
1556 }
1557
1558 char *key()
1559 {
1560         return ASTERISK_GPL_KEY;
1561 }
1562