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