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