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