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