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