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