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