00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "asterisk.h"
00027
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 184675 $")
00029
00030 #include <math.h>
00031 #include <signal.h>
00032 #include <sys/time.h>
00033 #include <sys/wait.h>
00034 #include <sys/stat.h>
00035 #include <pthread.h>
00036
00037 #include "asterisk/paths.h"
00038 #include "asterisk/network.h"
00039 #include "asterisk/file.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/module.h"
00043 #include "asterisk/astdb.h"
00044 #include "asterisk/callerid.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/image.h"
00047 #include "asterisk/say.h"
00048 #include "asterisk/app.h"
00049 #include "asterisk/dsp.h"
00050 #include "asterisk/musiconhold.h"
00051 #include "asterisk/utils.h"
00052 #include "asterisk/lock.h"
00053 #include "asterisk/strings.h"
00054 #include "asterisk/agi.h"
00055 #include "asterisk/manager.h"
00056 #include "asterisk/ast_version.h"
00057 #include "asterisk/speech.h"
00058 #include "asterisk/manager.h"
00059 #include "asterisk/features.h"
00060
00061 #define MAX_ARGS 128
00062 #define AGI_NANDFS_RETRY 3
00063 #define AGI_BUF_LEN 2048
00064
00065 static char *app = "AGI";
00066
00067 static char *eapp = "EAGI";
00068
00069 static char *deadapp = "DeadAGI";
00070
00071 static char *synopsis = "Executes an AGI compliant application";
00072 static char *esynopsis = "Executes an EAGI compliant application";
00073 static char *deadsynopsis = "Executes AGI on a hungup channel";
00074
00075 static char *descrip =
00076 " [E|Dead]AGI(command,args): Executes an Asterisk Gateway Interface compliant\n"
00077 "program on a channel. AGI allows Asterisk to launch external programs written\n"
00078 "in any language to control a telephony channel, play audio, read DTMF digits,\n"
00079 "etc. by communicating with the AGI protocol on stdin and stdout.\n"
00080 " As of 1.6.0, this channel will not stop dialplan execution on hangup inside\n"
00081 "of this application. Dialplan execution will continue normally, even upon\n"
00082 "hangup until the AGI application signals a desire to stop (either by exiting\n"
00083 "or, in the case of a net script, by closing the connection).\n"
00084 " A locally executed AGI script will receive SIGHUP on hangup from the channel\n"
00085 "except when using DeadAGI. A fast AGI server will correspondingly receive a\n"
00086 "HANGUP in OOB data. Both of these signals may be disabled by setting the\n"
00087 "AGISIGHUP channel variable to \"no\" before executing the AGI application.\n"
00088 " Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n"
00089 "on file descriptor 3.\n\n"
00090 " Use the CLI command 'agi show' to list available agi commands.\n"
00091 " This application sets the following channel variable upon completion:\n"
00092 " AGISTATUS The status of the attempt to the run the AGI script\n"
00093 " text string, one of SUCCESS | FAILURE | NOTFOUND | HANGUP\n";
00094
00095 static int agidebug = 0;
00096
00097 #define TONE_BLOCK_SIZE 200
00098
00099
00100 #define MAX_AGI_CONNECT 2000
00101
00102 #define AGI_PORT 4573
00103
00104 enum agi_result {
00105 AGI_RESULT_FAILURE = -1,
00106 AGI_RESULT_SUCCESS,
00107 AGI_RESULT_SUCCESS_FAST,
00108 AGI_RESULT_SUCCESS_ASYNC,
00109 AGI_RESULT_NOTFOUND,
00110 AGI_RESULT_HANGUP,
00111 };
00112
00113 static agi_command *find_command(char *cmds[], int exact);
00114
00115 AST_THREADSTORAGE(agi_buf);
00116 #define AGI_BUF_INITSIZE 256
00117
00118 int ast_agi_send(int fd, struct ast_channel *chan, char *fmt, ...)
00119 {
00120 int res = 0;
00121 va_list ap;
00122 struct ast_str *buf;
00123
00124 if (!(buf = ast_str_thread_get(&agi_buf, AGI_BUF_INITSIZE)))
00125 return -1;
00126
00127 va_start(ap, fmt);
00128 res = ast_str_set_va(&buf, 0, fmt, ap);
00129 va_end(ap);
00130
00131 if (res == -1) {
00132 ast_log(LOG_ERROR, "Out of memory\n");
00133 return -1;
00134 }
00135
00136 if (agidebug) {
00137 if (chan) {
00138 ast_verbose("<%s>AGI Tx >> %s", chan->name, buf->str);
00139 } else {
00140 ast_verbose("AGI Tx >> %s", buf->str);
00141 }
00142 }
00143
00144 return ast_carefulwrite(fd, buf->str, buf->used, 100);
00145 }
00146
00147
00148 struct agi_cmd {
00149 char *cmd_buffer;
00150 char *cmd_id;
00151 AST_LIST_ENTRY(agi_cmd) entry;
00152 };
00153
00154 static void free_agi_cmd(struct agi_cmd *cmd)
00155 {
00156 ast_free(cmd->cmd_buffer);
00157 ast_free(cmd->cmd_id);
00158 ast_free(cmd);
00159 }
00160
00161
00162 static void agi_destroy_commands_cb(void *data)
00163 {
00164 struct agi_cmd *cmd;
00165 AST_LIST_HEAD(, agi_cmd) *chan_cmds = data;
00166 AST_LIST_LOCK(chan_cmds);
00167 while ( (cmd = AST_LIST_REMOVE_HEAD(chan_cmds, entry)) ) {
00168 free_agi_cmd(cmd);
00169 }
00170 AST_LIST_UNLOCK(chan_cmds);
00171 AST_LIST_HEAD_DESTROY(chan_cmds);
00172 ast_free(chan_cmds);
00173 }
00174
00175
00176 static const struct ast_datastore_info agi_commands_datastore_info = {
00177 .type = "AsyncAGI",
00178 .destroy = agi_destroy_commands_cb
00179 };
00180
00181 static const char mandescr_asyncagi[] =
00182 "Description: Add an AGI command to the execute queue of the channel in Async AGI\n"
00183 "Variables:\n"
00184 " *Channel: Channel that is currently in Async AGI\n"
00185 " *Command: Application to execute\n"
00186 " CommandID: comand id. This will be sent back in CommandID header of AsyncAGI exec event notification\n"
00187 "\n";
00188
00189 static struct agi_cmd *get_agi_cmd(struct ast_channel *chan)
00190 {
00191 struct ast_datastore *store;
00192 struct agi_cmd *cmd;
00193 AST_LIST_HEAD(, agi_cmd) *agi_commands;
00194
00195 ast_channel_lock(chan);
00196 store = ast_channel_datastore_find(chan, &agi_commands_datastore_info, NULL);
00197 ast_channel_unlock(chan);
00198 if (!store) {
00199 ast_log(LOG_ERROR, "Hu? datastore disappeared at Async AGI on Channel %s!\n", chan->name);
00200 return NULL;
00201 }
00202 agi_commands = store->data;
00203 AST_LIST_LOCK(agi_commands);
00204 cmd = AST_LIST_REMOVE_HEAD(agi_commands, entry);
00205 AST_LIST_UNLOCK(agi_commands);
00206 return cmd;
00207 }
00208
00209
00210 static int add_agi_cmd(struct ast_channel *chan, const char *cmd_buff, const char *cmd_id)
00211 {
00212 struct ast_datastore *store;
00213 struct agi_cmd *cmd;
00214 AST_LIST_HEAD(, agi_cmd) *agi_commands;
00215
00216 store = ast_channel_datastore_find(chan, &agi_commands_datastore_info, NULL);
00217 if (!store) {
00218 ast_log(LOG_WARNING, "Channel %s is not at Async AGI.\n", chan->name);
00219 return -1;
00220 }
00221 agi_commands = store->data;
00222 cmd = ast_calloc(1, sizeof(*cmd));
00223 if (!cmd) {
00224 return -1;
00225 }
00226 cmd->cmd_buffer = ast_strdup(cmd_buff);
00227 if (!cmd->cmd_buffer) {
00228 ast_free(cmd);
00229 return -1;
00230 }
00231 cmd->cmd_id = ast_strdup(cmd_id);
00232 if (!cmd->cmd_id) {
00233 ast_free(cmd->cmd_buffer);
00234 ast_free(cmd);
00235 return -1;
00236 }
00237 AST_LIST_LOCK(agi_commands);
00238 AST_LIST_INSERT_TAIL(agi_commands, cmd, entry);
00239 AST_LIST_UNLOCK(agi_commands);
00240 return 0;
00241 }
00242
00243 static int add_to_agi(struct ast_channel *chan)
00244 {
00245 struct ast_datastore *datastore;
00246 AST_LIST_HEAD(, agi_cmd) *agi_cmds_list;
00247
00248
00249 ast_channel_lock(chan);
00250 datastore = ast_channel_datastore_find(chan, &agi_commands_datastore_info, NULL);
00251 ast_channel_unlock(chan);
00252 if (datastore) {
00253
00254
00255 return 0;
00256 }
00257
00258
00259
00260 datastore = ast_datastore_alloc(&agi_commands_datastore_info, "AGI");
00261 if (!datastore) {
00262 return -1;
00263 }
00264 agi_cmds_list = ast_calloc(1, sizeof(*agi_cmds_list));
00265 if (!agi_cmds_list) {
00266 ast_log(LOG_ERROR, "Unable to allocate Async AGI commands list.\n");
00267 ast_datastore_free(datastore);
00268 return -1;
00269 }
00270 datastore->data = agi_cmds_list;
00271 AST_LIST_HEAD_INIT(agi_cmds_list);
00272 ast_channel_lock(chan);
00273 ast_channel_datastore_add(chan, datastore);
00274 ast_channel_unlock(chan);
00275 return 0;
00276 }
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287 static char *handle_cli_agi_add_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00288 {
00289 struct ast_channel *chan;
00290 switch (cmd) {
00291 case CLI_INIT:
00292 e->command = "agi exec";
00293 e->usage = "Usage: agi exec <channel name> <app and arguments> [id]\n"
00294 " Add AGI command to the execute queue of the specified channel in Async AGI\n";
00295 return NULL;
00296 case CLI_GENERATE:
00297 if (a->pos == 2)
00298 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00299 return NULL;
00300 }
00301
00302 if (a->argc < 4)
00303 return CLI_SHOWUSAGE;
00304 chan = ast_get_channel_by_name_locked(a->argv[2]);
00305 if (!chan) {
00306 ast_log(LOG_WARNING, "Channel %s does not exists or cannot lock it\n", a->argv[2]);
00307 return CLI_FAILURE;
00308 }
00309 if (add_agi_cmd(chan, a->argv[3], (a->argc > 4 ? a->argv[4] : ""))) {
00310 ast_log(LOG_WARNING, "failed to add AGI command to queue of channel %s\n", chan->name);
00311 ast_channel_unlock(chan);
00312 return CLI_FAILURE;
00313 }
00314 ast_log(LOG_DEBUG, "Added AGI command to channel %s queue\n", chan->name);
00315 ast_channel_unlock(chan);
00316 return CLI_SUCCESS;
00317 }
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330 static int action_add_agi_cmd(struct mansession *s, const struct message *m)
00331 {
00332 const char *channel = astman_get_header(m, "Channel");
00333 const char *cmdbuff = astman_get_header(m, "Command");
00334 const char *cmdid = astman_get_header(m, "CommandID");
00335 struct ast_channel *chan;
00336 char buf[256];
00337 if (ast_strlen_zero(channel) || ast_strlen_zero(cmdbuff)) {
00338 astman_send_error(s, m, "Both, Channel and Command are *required*");
00339 return 0;
00340 }
00341 chan = ast_get_channel_by_name_locked(channel);
00342 if (!chan) {
00343 snprintf(buf, sizeof(buf), "Channel %s does not exists or cannot get its lock", channel);
00344 astman_send_error(s, m, buf);
00345 return 0;
00346 }
00347 if (add_agi_cmd(chan, cmdbuff, cmdid)) {
00348 snprintf(buf, sizeof(buf), "Failed to add AGI command to channel %s queue", chan->name);
00349 astman_send_error(s, m, buf);
00350 ast_channel_unlock(chan);
00351 return 0;
00352 }
00353 astman_send_ack(s, m, "Added AGI command to queue");
00354 ast_channel_unlock(chan);
00355 return 0;
00356 }
00357
00358 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead);
00359 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced, int argc, char *argv[]);
00360 static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], int *efd)
00361 {
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379 #define AGI_BUF_SIZE 1024
00380 #define AMI_BUF_SIZE 2048
00381 struct ast_frame *f;
00382 struct agi_cmd *cmd;
00383 int res, fds[2];
00384 int timeout = 100;
00385 char agi_buffer[AGI_BUF_SIZE + 1];
00386 char ami_buffer[AMI_BUF_SIZE];
00387 enum agi_result returnstatus = AGI_RESULT_SUCCESS_ASYNC;
00388 AGI async_agi;
00389
00390 if (efd) {
00391 ast_log(LOG_WARNING, "Async AGI does not support Enhanced AGI yet\n");
00392 return AGI_RESULT_FAILURE;
00393 }
00394
00395
00396 if (add_to_agi(chan)) {
00397 ast_log(LOG_ERROR, "failed to start Async AGI on channel %s\n", chan->name);
00398 return AGI_RESULT_FAILURE;
00399 }
00400
00401
00402
00403 res = pipe(fds);
00404 if (res) {
00405 ast_log(LOG_ERROR, "failed to create Async AGI pipe\n");
00406
00407
00408
00409 return AGI_RESULT_FAILURE;
00410 }
00411
00412
00413
00414 async_agi.fd = fds[1];
00415 async_agi.ctrl = fds[1];
00416 async_agi.audio = -1;
00417 async_agi.fast = 0;
00418
00419
00420
00421 setup_env(chan, "async", fds[1], 0, 0, NULL);
00422
00423 res = read(fds[0], agi_buffer, AGI_BUF_SIZE);
00424 if (!res) {
00425 ast_log(LOG_ERROR, "failed to read from Async AGI pipe on channel %s\n", chan->name);
00426 returnstatus = AGI_RESULT_FAILURE;
00427 goto quit;
00428 }
00429 agi_buffer[res] = '\0';
00430
00431
00432
00433 ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, 1);
00434 manager_event(EVENT_FLAG_AGI, "AsyncAGI", "SubEvent: Start\r\nChannel: %s\r\nEnv: %s\r\n", chan->name, ami_buffer);
00435 while (1) {
00436
00437 if (ast_check_hangup(chan)) {
00438 ast_log(LOG_DEBUG, "ast_check_hangup returned true on chan %s\n", chan->name);
00439 break;
00440 }
00441
00442
00443 cmd = get_agi_cmd(chan);
00444 if (cmd) {
00445
00446
00447 res = agi_handle_command(chan, &async_agi, cmd->cmd_buffer, 0);
00448 if (res < 0) {
00449 free_agi_cmd(cmd);
00450 break;
00451 }
00452
00453
00454 res = read(fds[0], agi_buffer, AGI_BUF_SIZE);
00455 if (!res) {
00456 returnstatus = AGI_RESULT_FAILURE;
00457 ast_log(LOG_ERROR, "failed to read from AsyncAGI pipe on channel %s\n", chan->name);
00458 free_agi_cmd(cmd);
00459 break;
00460 }
00461
00462
00463
00464 agi_buffer[res] = '\0';
00465 ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, 1);
00466 if (ast_strlen_zero(cmd->cmd_id))
00467 manager_event(EVENT_FLAG_AGI, "AsyncAGI", "SubEvent: Exec\r\nChannel: %s\r\nResult: %s\r\n", chan->name, ami_buffer);
00468 else
00469 manager_event(EVENT_FLAG_AGI, "AsyncAGI", "SubEvent: Exec\r\nChannel: %s\r\nCommandID: %s\r\nResult: %s\r\n", chan->name, cmd->cmd_id, ami_buffer);
00470 free_agi_cmd(cmd);
00471 } else {
00472
00473 res = ast_waitfor(chan, timeout);
00474 if (res < 0) {
00475 ast_log(LOG_DEBUG, "ast_waitfor returned <= 0 on chan %s\n", chan->name);
00476 break;
00477 }
00478 if (res == 0)
00479 continue;
00480 f = ast_read(chan);
00481 if (!f) {
00482 ast_log(LOG_DEBUG, "No frame read on channel %s, going out ...\n", chan->name);
00483 returnstatus = AGI_RESULT_HANGUP;
00484 break;
00485 }
00486
00487
00488 if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
00489 ast_log(LOG_DEBUG, "Got HANGUP frame on channel %s, going out ...\n", chan->name);
00490 ast_frfree(f);
00491 break;
00492 }
00493 ast_frfree(f);
00494 }
00495 }
00496
00497 if (async_agi.speech) {
00498 ast_speech_destroy(async_agi.speech);
00499 }
00500 quit:
00501
00502
00503 manager_event(EVENT_FLAG_AGI, "AsyncAGI", "SubEvent: End\r\nChannel: %s\r\n", chan->name);
00504
00505
00506 close(fds[0]);
00507 close(fds[1]);
00508
00509
00510
00511
00512
00513 return returnstatus;
00514
00515 #undef AGI_BUF_SIZE
00516 #undef AMI_BUF_SIZE
00517 }
00518
00519
00520
00521 static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid)
00522 {
00523 int s, flags, res, port = AGI_PORT;
00524 struct pollfd pfds[1];
00525 char *host, *c, *script = "";
00526 struct sockaddr_in addr_in;
00527 struct hostent *hp;
00528 struct ast_hostent ahp;
00529
00530
00531 host = ast_strdupa(agiurl + 6);
00532
00533 if ((c = strchr(host, '/'))) {
00534 *c = '\0';
00535 c++;
00536 script = c;
00537 }
00538 if ((c = strchr(host, ':'))) {
00539 *c = '\0';
00540 c++;
00541 port = atoi(c);
00542 }
00543 if (efd) {
00544 ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n");
00545 return -1;
00546 }
00547 if (!(hp = ast_gethostbyname(host, &ahp))) {
00548 ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host);
00549 return -1;
00550 }
00551 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
00552 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
00553 return -1;
00554 }
00555 if ((flags = fcntl(s, F_GETFL)) < 0) {
00556 ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno));
00557 close(s);
00558 return -1;
00559 }
00560 if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
00561 ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno));
00562 close(s);
00563 return -1;
00564 }
00565 memset(&addr_in, 0, sizeof(addr_in));
00566 addr_in.sin_family = AF_INET;
00567 addr_in.sin_port = htons(port);
00568 memcpy(&addr_in.sin_addr, hp->h_addr, sizeof(addr_in.sin_addr));
00569 if (connect(s, (struct sockaddr *)&addr_in, sizeof(addr_in)) && (errno != EINPROGRESS)) {
00570 ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno));
00571 close(s);
00572 return AGI_RESULT_FAILURE;
00573 }
00574
00575 pfds[0].fd = s;
00576 pfds[0].events = POLLOUT;
00577 while ((res = ast_poll(pfds, 1, MAX_AGI_CONNECT)) != 1) {
00578 if (errno != EINTR) {
00579 if (!res) {
00580 ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n",
00581 agiurl, MAX_AGI_CONNECT);
00582 } else
00583 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
00584 close(s);
00585 return AGI_RESULT_FAILURE;
00586 }
00587 }
00588
00589 if (ast_agi_send(s, NULL, "agi_network: yes\n") < 0) {
00590 if (errno != EINTR) {
00591 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno));
00592 close(s);
00593 return AGI_RESULT_FAILURE;
00594 }
00595 }
00596
00597
00598
00599 if (!ast_strlen_zero(script))
00600 ast_agi_send(s, NULL, "agi_network_script: %s\n", script);
00601
00602 ast_debug(4, "Wow, connected!\n");
00603 fds[0] = s;
00604 fds[1] = s;
00605 *opid = -1;
00606 return AGI_RESULT_SUCCESS_FAST;
00607 }
00608
00609 static enum agi_result launch_script(struct ast_channel *chan, char *script, char *argv[], int *fds, int *efd, int *opid)
00610 {
00611 char tmp[256];
00612 int pid, toast[2], fromast[2], audio[2], res;
00613 struct stat st;
00614
00615 if (!strncasecmp(script, "agi://", 6))
00616 return launch_netscript(script, argv, fds, efd, opid);
00617 if (!strncasecmp(script, "agi:async", sizeof("agi:async")-1))
00618 return launch_asyncagi(chan, argv, efd);
00619
00620 if (script[0] != '/') {
00621 snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_AGI_DIR, script);
00622 script = tmp;
00623 }
00624
00625
00626 if (stat(script, &st)) {
00627 ast_log(LOG_WARNING, "Failed to execute '%s': File does not exist.\n", script);
00628 return AGI_RESULT_NOTFOUND;
00629 }
00630
00631 if (pipe(toast)) {
00632 ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
00633 return AGI_RESULT_FAILURE;
00634 }
00635 if (pipe(fromast)) {
00636 ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
00637 close(toast[0]);
00638 close(toast[1]);
00639 return AGI_RESULT_FAILURE;
00640 }
00641 if (efd) {
00642 if (pipe(audio)) {
00643 ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
00644 close(fromast[0]);
00645 close(fromast[1]);
00646 close(toast[0]);
00647 close(toast[1]);
00648 return AGI_RESULT_FAILURE;
00649 }
00650 res = fcntl(audio[1], F_GETFL);
00651 if (res > -1)
00652 res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK);
00653 if (res < 0) {
00654 ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
00655 close(fromast[0]);
00656 close(fromast[1]);
00657 close(toast[0]);
00658 close(toast[1]);
00659 close(audio[0]);
00660 close(audio[1]);
00661 return AGI_RESULT_FAILURE;
00662 }
00663 }
00664
00665 if ((pid = ast_safe_fork(1)) < 0) {
00666 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
00667 return AGI_RESULT_FAILURE;
00668 }
00669 if (!pid) {
00670
00671 setenv("AST_CONFIG_DIR", ast_config_AST_CONFIG_DIR, 1);
00672 setenv("AST_CONFIG_FILE", ast_config_AST_CONFIG_FILE, 1);
00673 setenv("AST_MODULE_DIR", ast_config_AST_MODULE_DIR, 1);
00674 setenv("AST_SPOOL_DIR", ast_config_AST_SPOOL_DIR, 1);
00675 setenv("AST_MONITOR_DIR", ast_config_AST_MONITOR_DIR, 1);
00676 setenv("AST_VAR_DIR", ast_config_AST_VAR_DIR, 1);
00677 setenv("AST_DATA_DIR", ast_config_AST_DATA_DIR, 1);
00678 setenv("AST_LOG_DIR", ast_config_AST_LOG_DIR, 1);
00679 setenv("AST_AGI_DIR", ast_config_AST_AGI_DIR, 1);
00680 setenv("AST_KEY_DIR", ast_config_AST_KEY_DIR, 1);
00681 setenv("AST_RUN_DIR", ast_config_AST_RUN_DIR, 1);
00682
00683
00684 ast_set_priority(0);
00685
00686
00687 dup2(fromast[0], STDIN_FILENO);
00688 dup2(toast[1], STDOUT_FILENO);
00689 if (efd)
00690 dup2(audio[0], STDERR_FILENO + 1);
00691 else
00692 close(STDERR_FILENO + 1);
00693
00694
00695 ast_close_fds_above_n(STDERR_FILENO + 1);
00696
00697
00698
00699 execv(script, argv);
00700
00701 ast_child_verbose(1, "Failed to execute '%s': %s", script, strerror(errno));
00702
00703 fprintf(stdout, "failure\n");
00704 fflush(stdout);
00705 _exit(1);
00706 }
00707 ast_verb(3, "Launched AGI Script %s\n", script);
00708 fds[0] = toast[0];
00709 fds[1] = fromast[1];
00710 if (efd)
00711 *efd = audio[1];
00712
00713 close(toast[1]);
00714 close(fromast[0]);
00715
00716 if (efd)
00717 close(audio[0]);
00718
00719 *opid = pid;
00720 return AGI_RESULT_SUCCESS;
00721 }
00722
00723 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced, int argc, char *argv[])
00724 {
00725 int count;
00726
00727
00728
00729 ast_agi_send(fd, chan, "agi_request: %s\n", request);
00730 ast_agi_send(fd, chan, "agi_channel: %s\n", chan->name);
00731 ast_agi_send(fd, chan, "agi_language: %s\n", chan->language);
00732 ast_agi_send(fd, chan, "agi_type: %s\n", chan->tech->type);
00733 ast_agi_send(fd, chan, "agi_uniqueid: %s\n", chan->uniqueid);
00734 ast_agi_send(fd, chan, "agi_version: %s\n", ast_get_version());
00735
00736
00737 ast_agi_send(fd, chan, "agi_callerid: %s\n", S_OR(chan->cid.cid_num, "unknown"));
00738 ast_agi_send(fd, chan, "agi_calleridname: %s\n", S_OR(chan->cid.cid_name, "unknown"));
00739 ast_agi_send(fd, chan, "agi_callingpres: %d\n", chan->cid.cid_pres);
00740 ast_agi_send(fd, chan, "agi_callingani2: %d\n", chan->cid.cid_ani2);
00741 ast_agi_send(fd, chan, "agi_callington: %d\n", chan->cid.cid_ton);
00742 ast_agi_send(fd, chan, "agi_callingtns: %d\n", chan->cid.cid_tns);
00743 ast_agi_send(fd, chan, "agi_dnid: %s\n", S_OR(chan->cid.cid_dnid, "unknown"));
00744 ast_agi_send(fd, chan, "agi_rdnis: %s\n", S_OR(chan->cid.cid_rdnis, "unknown"));
00745
00746
00747 ast_agi_send(fd, chan, "agi_context: %s\n", chan->context);
00748 ast_agi_send(fd, chan, "agi_extension: %s\n", chan->exten);
00749 ast_agi_send(fd, chan, "agi_priority: %d\n", chan->priority);
00750 ast_agi_send(fd, chan, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
00751
00752
00753 ast_agi_send(fd, chan, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
00754 ast_agi_send(fd, chan, "agi_threadid: %ld\n", (long)pthread_self());
00755
00756
00757
00758 for(count = 1; count < argc; count++)
00759 ast_agi_send(fd, chan, "agi_arg_%d: %s\n", count, argv[count]);
00760
00761
00762 ast_agi_send(fd, chan, "\n");
00763 }
00764
00765 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00766 {
00767 int res = 0;
00768
00769
00770 if (chan->_state != AST_STATE_UP)
00771 res = ast_answer(chan);
00772
00773 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00774 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00775 }
00776
00777 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00778 {
00779 int res, to;
00780
00781 if (argc != 4)
00782 return RESULT_SHOWUSAGE;
00783 if (sscanf(argv[3], "%d", &to) != 1)
00784 return RESULT_SHOWUSAGE;
00785 res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
00786 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00787 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00788 }
00789
00790 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00791 {
00792 int res;
00793
00794 if (argc != 3)
00795 return RESULT_SHOWUSAGE;
00796
00797
00798
00799
00800
00801
00802
00803
00804 res = ast_sendtext(chan, argv[2]);
00805 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00806 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00807 }
00808
00809 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00810 {
00811 int res;
00812
00813 if (argc != 3)
00814 return RESULT_SHOWUSAGE;
00815
00816 res = ast_recvchar(chan,atoi(argv[2]));
00817 if (res == 0) {
00818 ast_agi_send(agi->fd, chan, "200 result=%d (timeout)\n", res);
00819 return RESULT_SUCCESS;
00820 }
00821 if (res > 0) {
00822 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00823 return RESULT_SUCCESS;
00824 }
00825 ast_agi_send(agi->fd, chan, "200 result=%d (hangup)\n", res);
00826 return RESULT_FAILURE;
00827 }
00828
00829 static int handle_recvtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00830 {
00831 char *buf;
00832
00833 if (argc != 3)
00834 return RESULT_SHOWUSAGE;
00835
00836 buf = ast_recvtext(chan, atoi(argv[2]));
00837 if (buf) {
00838 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", buf);
00839 ast_free(buf);
00840 } else {
00841 ast_agi_send(agi->fd, chan, "200 result=-1\n");
00842 }
00843 return RESULT_SUCCESS;
00844 }
00845
00846 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00847 {
00848 int res, x;
00849
00850 if (argc != 3)
00851 return RESULT_SHOWUSAGE;
00852
00853 if (!strncasecmp(argv[2],"on",2)) {
00854 x = 1;
00855 } else {
00856 x = 0;
00857 }
00858 if (!strncasecmp(argv[2],"mate",4)) {
00859 x = 2;
00860 }
00861 if (!strncasecmp(argv[2],"tdd",3)) {
00862 x = 1;
00863 }
00864 res = ast_channel_setoption(chan, AST_OPTION_TDD, &x, sizeof(char), 0);
00865 if (res != RESULT_SUCCESS) {
00866 ast_agi_send(agi->fd, chan, "200 result=0\n");
00867 } else {
00868 ast_agi_send(agi->fd, chan, "200 result=1\n");
00869 }
00870 return RESULT_SUCCESS;
00871 }
00872
00873 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00874 {
00875 int res;
00876
00877 if (argc != 3) {
00878 return RESULT_SHOWUSAGE;
00879 }
00880
00881 res = ast_send_image(chan, argv[2]);
00882 if (!ast_check_hangup(chan)) {
00883 res = 0;
00884 }
00885 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00886 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00887 }
00888
00889 static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00890 {
00891 int res = 0, skipms = 3000;
00892 char *fwd = "#", *rev = "*", *suspend = NULL, *stop = NULL;
00893
00894 if (argc < 5 || argc > 9) {
00895 return RESULT_SHOWUSAGE;
00896 }
00897
00898 if (!ast_strlen_zero(argv[4])) {
00899 stop = argv[4];
00900 }
00901
00902 if ((argc > 5) && (sscanf(argv[5], "%d", &skipms) != 1)) {
00903 return RESULT_SHOWUSAGE;
00904 }
00905
00906 if (argc > 6 && !ast_strlen_zero(argv[6])) {
00907 fwd = argv[6];
00908 }
00909
00910 if (argc > 7 && !ast_strlen_zero(argv[7])) {
00911 rev = argv[7];
00912 }
00913
00914 if (argc > 8 && !ast_strlen_zero(argv[8])) {
00915 suspend = argv[8];
00916 }
00917
00918 res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, suspend, NULL, skipms, NULL);
00919
00920 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00921
00922 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00923 }
00924
00925 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00926 {
00927 int res, vres;
00928 struct ast_filestream *fs, *vfs;
00929 long sample_offset = 0, max_length;
00930 char *edigits = "";
00931
00932 if (argc < 4 || argc > 5)
00933 return RESULT_SHOWUSAGE;
00934
00935 if (argv[3])
00936 edigits = argv[3];
00937
00938 if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
00939 return RESULT_SHOWUSAGE;
00940
00941 if (!(fs = ast_openstream(chan, argv[2], chan->language))) {
00942 ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", 0, sample_offset);
00943 return RESULT_SUCCESS;
00944 }
00945
00946 if ((vfs = ast_openvstream(chan, argv[2], chan->language)))
00947 ast_debug(1, "Ooh, found a video stream, too\n");
00948
00949 ast_verb(3, "Playing '%s' (escape_digits=%s) (sample_offset %ld)\n", argv[2], edigits, sample_offset);
00950
00951 ast_seekstream(fs, 0, SEEK_END);
00952 max_length = ast_tellstream(fs);
00953 ast_seekstream(fs, sample_offset, SEEK_SET);
00954 res = ast_applystream(chan, fs);
00955 if (vfs)
00956 vres = ast_applystream(chan, vfs);
00957 ast_playstream(fs);
00958 if (vfs)
00959 ast_playstream(vfs);
00960
00961 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
00962
00963
00964 sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length;
00965 ast_stopstream(chan);
00966 if (res == 1) {
00967
00968 return RESULT_SUCCESS;
00969 }
00970 ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", res, sample_offset);
00971 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00972 }
00973
00974
00975 static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00976 {
00977 int res, vres;
00978 struct ast_filestream *fs, *vfs;
00979 long sample_offset = 0, max_length;
00980 int timeout = 0;
00981 char *edigits = "";
00982
00983 if ( argc < 4 || argc > 5 )
00984 return RESULT_SHOWUSAGE;
00985
00986 if ( argv[3] )
00987 edigits = argv[3];
00988
00989 if ( argc == 5 )
00990 timeout = atoi(argv[4]);
00991 else if (chan->pbx->dtimeoutms) {
00992
00993 timeout = chan->pbx->dtimeoutms;
00994 }
00995
00996 if (!(fs = ast_openstream(chan, argv[2], chan->language))) {
00997 ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", 0, sample_offset);
00998 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
00999 return RESULT_SUCCESS;
01000 }
01001
01002 if ((vfs = ast_openvstream(chan, argv[2], chan->language)))
01003 ast_debug(1, "Ooh, found a video stream, too\n");
01004
01005 ast_verb(3, "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout);
01006
01007 ast_seekstream(fs, 0, SEEK_END);
01008 max_length = ast_tellstream(fs);
01009 ast_seekstream(fs, sample_offset, SEEK_SET);
01010 res = ast_applystream(chan, fs);
01011 if (vfs)
01012 vres = ast_applystream(chan, vfs);
01013 ast_playstream(fs);
01014 if (vfs)
01015 ast_playstream(vfs);
01016
01017 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
01018
01019
01020 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
01021 ast_stopstream(chan);
01022 if (res == 1) {
01023
01024 return RESULT_SUCCESS;
01025 }
01026
01027
01028 if (res == 0 ) {
01029 res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
01030
01031 if ( !strchr(edigits,res) )
01032 res=0;
01033 }
01034
01035 ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", res, sample_offset);
01036 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01037 }
01038
01039
01040
01041
01042
01043
01044 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01045 {
01046 int res, num;
01047
01048 if (argc < 4 || argc > 5)
01049 return RESULT_SHOWUSAGE;
01050 if (sscanf(argv[2], "%d", &num) != 1)
01051 return RESULT_SHOWUSAGE;
01052 res = ast_say_number_full(chan, num, argv[3], chan->language, argc > 4 ? argv[4] : NULL, agi->audio, agi->ctrl);
01053 if (res == 1)
01054 return RESULT_SUCCESS;
01055 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01056 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01057 }
01058
01059 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01060 {
01061 int res, num;
01062
01063 if (argc != 4)
01064 return RESULT_SHOWUSAGE;
01065 if (sscanf(argv[2], "%d", &num) != 1)
01066 return RESULT_SHOWUSAGE;
01067
01068 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
01069 if (res == 1)
01070 return RESULT_SUCCESS;
01071 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01072 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01073 }
01074
01075 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01076 {
01077 int res;
01078
01079 if (argc != 4)
01080 return RESULT_SHOWUSAGE;
01081
01082 res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
01083 if (res == 1)
01084 return RESULT_SUCCESS;
01085 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01086 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01087 }
01088
01089 static int handle_saydate(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01090 {
01091 int res, num;
01092
01093 if (argc != 4)
01094 return RESULT_SHOWUSAGE;
01095 if (sscanf(argv[2], "%d", &num) != 1)
01096 return RESULT_SHOWUSAGE;
01097 res = ast_say_date(chan, num, argv[3], chan->language);
01098 if (res == 1)
01099 return RESULT_SUCCESS;
01100 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01101 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01102 }
01103
01104 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01105 {
01106 int res, num;
01107
01108 if (argc != 4)
01109 return RESULT_SHOWUSAGE;
01110 if (sscanf(argv[2], "%d", &num) != 1)
01111 return RESULT_SHOWUSAGE;
01112 res = ast_say_time(chan, num, argv[3], chan->language);
01113 if (res == 1)
01114 return RESULT_SUCCESS;
01115 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01116 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01117 }
01118
01119 static int handle_saydatetime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01120 {
01121 int res = 0;
01122 time_t unixtime;
01123 char *format, *zone = NULL;
01124
01125 if (argc < 4)
01126 return RESULT_SHOWUSAGE;
01127
01128 if (argc > 4) {
01129 format = argv[4];
01130 } else {
01131
01132 if (!strcasecmp(chan->language, "de")) {
01133 format = "A dBY HMS";
01134 } else {
01135 format = "ABdY 'digits/at' IMp";
01136 }
01137 }
01138
01139 if (argc > 5 && !ast_strlen_zero(argv[5]))
01140 zone = argv[5];
01141
01142 if (ast_get_time_t(argv[2], &unixtime, 0, NULL))
01143 return RESULT_SHOWUSAGE;
01144
01145 res = ast_say_date_with_format(chan, unixtime, argv[3], chan->language, format, zone);
01146 if (res == 1)
01147 return RESULT_SUCCESS;
01148
01149 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01150 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01151 }
01152
01153 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01154 {
01155 int res;
01156
01157 if (argc != 4)
01158 return RESULT_SHOWUSAGE;
01159
01160 res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
01161 if (res == 1)
01162 return RESULT_SUCCESS;
01163 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01164 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01165 }
01166
01167 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01168 {
01169 int res, max, timeout;
01170 char data[1024];
01171
01172 if (argc < 3)
01173 return RESULT_SHOWUSAGE;
01174 if (argc >= 4)
01175 timeout = atoi(argv[3]);
01176 else
01177 timeout = 0;
01178 if (argc >= 5)
01179 max = atoi(argv[4]);
01180 else
01181 max = 1024;
01182 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
01183 if (res == 2)
01184 return RESULT_SUCCESS;
01185 else if (res == 1)
01186 ast_agi_send(agi->fd, chan, "200 result=%s (timeout)\n", data);
01187 else if (res < 0 )
01188 ast_agi_send(agi->fd, chan, "200 result=-1\n");
01189 else
01190 ast_agi_send(agi->fd, chan, "200 result=%s\n", data);
01191 return RESULT_SUCCESS;
01192 }
01193
01194 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01195 {
01196
01197 if (argc != 3)
01198 return RESULT_SHOWUSAGE;
01199 ast_copy_string(chan->context, argv[2], sizeof(chan->context));
01200 ast_agi_send(agi->fd, chan, "200 result=0\n");
01201 return RESULT_SUCCESS;
01202 }
01203
01204 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01205 {
01206 if (argc != 3)
01207 return RESULT_SHOWUSAGE;
01208 ast_copy_string(chan->exten, argv[2], sizeof(chan->exten));
01209 ast_agi_send(agi->fd, chan, "200 result=0\n");
01210 return RESULT_SUCCESS;
01211 }
01212
01213 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01214 {
01215 int pri;
01216
01217 if (argc != 3)
01218 return RESULT_SHOWUSAGE;
01219
01220 if (sscanf(argv[2], "%d", &pri) != 1) {
01221 if ((pri = ast_findlabel_extension(chan, chan->context, chan->exten, argv[2], chan->cid.cid_num)) < 1)
01222 return RESULT_SHOWUSAGE;
01223 }
01224
01225 ast_explicit_goto(chan, NULL, NULL, pri);
01226 ast_agi_send(agi->fd, chan, "200 result=0\n");
01227 return RESULT_SUCCESS;
01228 }
01229
01230 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01231 {
01232 struct ast_filestream *fs;
01233 struct ast_frame *f;
01234 struct timeval start;
01235 long sample_offset = 0;
01236 int res = 0;
01237 int ms;
01238
01239 struct ast_dsp *sildet=NULL;
01240 int totalsilence = 0;
01241 int dspsilence = 0;
01242 int silence = 0;
01243 int gotsilence = 0;
01244 char *silencestr = NULL;
01245 int rfmt = 0;
01246
01247
01248
01249 if (argc < 6)
01250 return RESULT_SHOWUSAGE;
01251 if (sscanf(argv[5], "%d", &ms) != 1)
01252 return RESULT_SHOWUSAGE;
01253
01254 if (argc > 6)
01255 silencestr = strchr(argv[6],'s');
01256 if ((argc > 7) && (!silencestr))
01257 silencestr = strchr(argv[7],'s');
01258 if ((argc > 8) && (!silencestr))
01259 silencestr = strchr(argv[8],'s');
01260
01261 if (silencestr) {
01262 if (strlen(silencestr) > 2) {
01263 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
01264 silencestr++;
01265 silencestr++;
01266 if (silencestr)
01267 silence = atoi(silencestr);
01268 if (silence > 0)
01269 silence *= 1000;
01270 }
01271 }
01272 }
01273
01274 if (silence > 0) {
01275 rfmt = chan->readformat;
01276 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
01277 if (res < 0) {
01278 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
01279 return -1;
01280 }
01281 sildet = ast_dsp_new();
01282 if (!sildet) {
01283 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
01284 return -1;
01285 }
01286 ast_dsp_set_threshold(sildet, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
01287 }
01288
01289
01290
01291
01292 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
01293 res = ast_streamfile(chan, "beep", chan->language);
01294
01295 if ((argc > 7) && (!strchr(argv[7], '=')))
01296 res = ast_streamfile(chan, "beep", chan->language);
01297
01298 if (!res)
01299 res = ast_waitstream(chan, argv[4]);
01300 if (res) {
01301 ast_agi_send(agi->fd, chan, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
01302 } else {
01303 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, AST_FILE_MODE);
01304 if (!fs) {
01305 res = -1;
01306 ast_agi_send(agi->fd, chan, "200 result=%d (writefile)\n", res);
01307 if (sildet)
01308 ast_dsp_free(sildet);
01309 return RESULT_FAILURE;
01310 }
01311
01312
01313 ast_indicate(chan, AST_CONTROL_VIDUPDATE);
01314
01315 chan->stream = fs;
01316 ast_applystream(chan,fs);
01317
01318 ast_seekstream(fs, sample_offset, SEEK_SET);
01319 ast_truncstream(fs);
01320
01321 start = ast_tvnow();
01322 while ((ms < 0) || ast_tvdiff_ms(ast_tvnow(), start) < ms) {
01323 res = ast_waitfor(chan, ms - ast_tvdiff_ms(ast_tvnow(), start));
01324 if (res < 0) {
01325 ast_closestream(fs);
01326 ast_agi_send(agi->fd, chan, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
01327 if (sildet)
01328 ast_dsp_free(sildet);
01329 return RESULT_FAILURE;
01330 }
01331 f = ast_read(chan);
01332 if (!f) {
01333 ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
01334 ast_closestream(fs);
01335 if (sildet)
01336 ast_dsp_free(sildet);
01337 return RESULT_FAILURE;
01338 }
01339 switch(f->frametype) {
01340 case AST_FRAME_DTMF:
01341 if (strchr(argv[4], f->subclass)) {
01342
01343
01344
01345 ast_stream_rewind(fs, 200);
01346 ast_truncstream(fs);
01347 sample_offset = ast_tellstream(fs);
01348 ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
01349 ast_closestream(fs);
01350 ast_frfree(f);
01351 if (sildet)
01352 ast_dsp_free(sildet);
01353 return RESULT_SUCCESS;
01354 }
01355 break;
01356 case AST_FRAME_VOICE:
01357 ast_writestream(fs, f);
01358
01359
01360
01361 sample_offset = ast_tellstream(fs);
01362 if (silence > 0) {
01363 dspsilence = 0;
01364 ast_dsp_silence(sildet, f, &dspsilence);
01365 if (dspsilence) {
01366 totalsilence = dspsilence;
01367 } else {
01368 totalsilence = 0;
01369 }
01370 if (totalsilence > silence) {
01371
01372 gotsilence = 1;
01373 break;
01374 }
01375 }
01376 break;
01377 case AST_FRAME_VIDEO:
01378 ast_writestream(fs, f);
01379 default:
01380
01381 break;
01382 }
01383 ast_frfree(f);
01384 if (gotsilence)
01385 break;
01386 }
01387
01388 if (gotsilence) {
01389 ast_stream_rewind(fs, silence-1000);
01390 ast_truncstream(fs);
01391 sample_offset = ast_tellstream(fs);
01392 }
01393 ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
01394 ast_closestream(fs);
01395 }
01396
01397 if (silence > 0) {
01398 res = ast_set_read_format(chan, rfmt);
01399 if (res)
01400 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
01401 ast_dsp_free(sildet);
01402 }
01403
01404 return RESULT_SUCCESS;
01405 }
01406
01407 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01408 {
01409 double timeout;
01410 struct timeval whentohangup = { 0, 0 };
01411
01412 if (argc != 3)
01413 return RESULT_SHOWUSAGE;
01414 if (sscanf(argv[2], "%lf", &timeout) != 1)
01415 return RESULT_SHOWUSAGE;
01416 if (timeout < 0)
01417 timeout = 0;
01418 if (timeout) {
01419 whentohangup.tv_sec = timeout;
01420 whentohangup.tv_usec = (timeout - whentohangup.tv_sec) * 1000000.0;
01421 }
01422 ast_channel_setwhentohangup_tv(chan, whentohangup);
01423 ast_agi_send(agi->fd, chan, "200 result=0\n");
01424 return RESULT_SUCCESS;
01425 }
01426
01427 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01428 {
01429 struct ast_channel *c;
01430
01431 if (argc == 1) {
01432
01433 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
01434 ast_agi_send(agi->fd, chan, "200 result=1\n");
01435 return RESULT_SUCCESS;
01436 } else if (argc == 2) {
01437
01438 c = ast_get_channel_by_name_locked(argv[1]);
01439 if (c) {
01440
01441 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
01442 ast_agi_send(agi->fd, chan, "200 result=1\n");
01443 ast_channel_unlock(c);
01444 return RESULT_SUCCESS;
01445 }
01446
01447 ast_agi_send(agi->fd, chan, "200 result=-1\n");
01448 return RESULT_SUCCESS;
01449 } else {
01450 return RESULT_SHOWUSAGE;
01451 }
01452 }
01453
01454 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01455 {
01456 int res;
01457 struct ast_app *app_to_exec;
01458
01459 if (argc < 2)
01460 return RESULT_SHOWUSAGE;
01461
01462 ast_verb(3, "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
01463
01464 if ((app_to_exec = pbx_findapp(argv[1]))) {
01465 if(!strcasecmp(argv[1], PARK_APP_NAME)) {
01466 ast_masq_park_call(chan, NULL, 0, NULL);
01467 }
01468 if (ast_compat_res_agi && !ast_strlen_zero(argv[2])) {
01469 char *compat = alloca(strlen(argv[2]) * 2 + 1), *cptr, *vptr;
01470 for (cptr = compat, vptr = argv[2]; *vptr; vptr++) {
01471 if (*vptr == ',') {
01472 *cptr++ = '\\';
01473 *cptr++ = ',';
01474 } else if (*vptr == '|') {
01475 *cptr++ = ',';
01476 } else {
01477 *cptr++ = *vptr;
01478 }
01479 }
01480 *cptr = '\0';
01481 res = pbx_exec(chan, app_to_exec, compat);
01482 } else {
01483 res = pbx_exec(chan, app_to_exec, argv[2]);
01484 }
01485 } else {
01486 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
01487 res = -2;
01488 }
01489 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01490
01491
01492 return res;
01493 }
01494
01495 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01496 {
01497 char tmp[256]="";
01498 char *l = NULL, *n = NULL;
01499
01500 if (argv[2]) {
01501 ast_copy_string(tmp, argv[2], sizeof(tmp));
01502 ast_callerid_parse(tmp, &n, &l);
01503 if (l)
01504 ast_shrink_phone_number(l);
01505 else
01506 l = "";
01507 if (!n)
01508 n = "";
01509 ast_set_callerid(chan, l, n, NULL);
01510 }
01511
01512 ast_agi_send(agi->fd, chan, "200 result=1\n");
01513 return RESULT_SUCCESS;
01514 }
01515
01516 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01517 {
01518 struct ast_channel *c;
01519 if (argc == 2) {
01520
01521 ast_agi_send(agi->fd, chan, "200 result=%d\n", chan->_state);
01522 return RESULT_SUCCESS;
01523 } else if (argc == 3) {
01524
01525 c = ast_get_channel_by_name_locked(argv[2]);
01526 if (c) {
01527 ast_agi_send(agi->fd, chan, "200 result=%d\n", c->_state);
01528 ast_channel_unlock(c);
01529 return RESULT_SUCCESS;
01530 }
01531
01532 ast_agi_send(agi->fd, chan, "200 result=-1\n");
01533 return RESULT_SUCCESS;
01534 } else {
01535 return RESULT_SHOWUSAGE;
01536 }
01537 }
01538
01539 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01540 {
01541 if (argv[3])
01542 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
01543
01544 ast_agi_send(agi->fd, chan, "200 result=1\n");
01545 return RESULT_SUCCESS;
01546 }
01547
01548 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01549 {
01550 char *ret;
01551 char tempstr[1024];
01552
01553 if (argc != 3)
01554 return RESULT_SHOWUSAGE;
01555
01556
01557 if (!ast_strlen_zero(argv[2]) && (argv[2][strlen(argv[2]) - 1] == ')')) {
01558 ret = ast_func_read(chan, argv[2], tempstr, sizeof(tempstr)) ? NULL : tempstr;
01559 } else {
01560 pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
01561 }
01562
01563 if (ret)
01564 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", ret);
01565 else
01566 ast_agi_send(agi->fd, chan, "200 result=0\n");
01567
01568 return RESULT_SUCCESS;
01569 }
01570
01571 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01572 {
01573 char tmp[4096];
01574 struct ast_channel *chan2=NULL;
01575
01576 if ((argc != 4) && (argc != 5))
01577 return RESULT_SHOWUSAGE;
01578 if (argc == 5) {
01579 chan2 = ast_get_channel_by_name_locked(argv[4]);
01580 } else {
01581 chan2 = chan;
01582 }
01583 if (chan2) {
01584 pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
01585 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", tmp);
01586 } else {
01587 ast_agi_send(agi->fd, chan, "200 result=0\n");
01588 }
01589 if (chan2 && (chan2 != chan))
01590 ast_channel_unlock(chan2);
01591 return RESULT_SUCCESS;
01592 }
01593
01594 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01595 {
01596 int level = 0;
01597
01598 if (argc < 2)
01599 return RESULT_SHOWUSAGE;
01600
01601 if (argv[2])
01602 sscanf(argv[2], "%d", &level);
01603
01604 ast_verb(level, "%s: %s\n", chan->data, argv[1]);
01605
01606 ast_agi_send(agi->fd, chan, "200 result=1\n");
01607
01608 return RESULT_SUCCESS;
01609 }
01610
01611 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01612 {
01613 int res;
01614 struct ast_str *buf;
01615
01616 if (argc != 4)
01617 return RESULT_SHOWUSAGE;
01618
01619 if (!(buf = ast_str_create(16))) {
01620 ast_agi_send(agi->fd, chan, "200 result=-1\n");
01621 return RESULT_SUCCESS;
01622 }
01623
01624 do {
01625 res = ast_db_get(argv[2], argv[3], buf->str, buf->len);
01626 buf->used = strlen(buf->str);
01627 if (buf->used < buf->len - 1) {
01628 break;
01629 }
01630 if (ast_str_make_space(&buf, buf->len * 2)) {
01631 break;
01632 }
01633 } while (1);
01634
01635 if (res)
01636 ast_agi_send(agi->fd, chan, "200 result=0\n");
01637 else
01638 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", buf->str);
01639
01640 ast_free(buf);
01641 return RESULT_SUCCESS;
01642 }
01643
01644 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01645 {
01646 int res;
01647
01648 if (argc != 5)
01649 return RESULT_SHOWUSAGE;
01650 res = ast_db_put(argv[2], argv[3], argv[4]);
01651 ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01652 return RESULT_SUCCESS;
01653 }
01654
01655 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01656 {
01657 int res;
01658
01659 if (argc != 4)
01660 return RESULT_SHOWUSAGE;
01661 res = ast_db_del(argv[2], argv[3]);
01662 ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01663 return RESULT_SUCCESS;
01664 }
01665
01666 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01667 {
01668 int res;
01669
01670 if ((argc < 3) || (argc > 4))
01671 return RESULT_SHOWUSAGE;
01672 if (argc == 4)
01673 res = ast_db_deltree(argv[2], argv[3]);
01674 else
01675 res = ast_db_deltree(argv[2], NULL);
01676
01677 ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01678 return RESULT_SUCCESS;
01679 }
01680
01681 static char *handle_cli_agi_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01682 {
01683 switch (cmd) {
01684 case CLI_INIT:
01685 e->command = "agi set debug [on|off]";
01686 e->usage =
01687 "Usage: agi set debug [on|off]\n"
01688 " Enables/disables dumping of AGI transactions for\n"
01689 " debugging purposes.\n";
01690 return NULL;
01691
01692 case CLI_GENERATE:
01693 return NULL;
01694 }
01695
01696 if (a->argc != e->args)
01697 return CLI_SHOWUSAGE;
01698
01699 if (strncasecmp(a->argv[3], "off", 3) == 0) {
01700 agidebug = 0;
01701 } else if (strncasecmp(a->argv[3], "on", 2) == 0) {
01702 agidebug = 1;
01703 } else {
01704 return CLI_SHOWUSAGE;
01705 }
01706 ast_cli(a->fd, "AGI Debugging %sabled\n", agidebug ? "En" : "Dis");
01707 return CLI_SUCCESS;
01708 }
01709
01710 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
01711 {
01712 ast_agi_send(agi->fd, chan, "200 result=0\n");
01713 return RESULT_SUCCESS;
01714 }
01715
01716 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01717 {
01718 if (!strncasecmp(argv[2], "on", 2))
01719 ast_moh_start(chan, argc > 3 ? argv[3] : NULL, NULL);
01720 else if (!strncasecmp(argv[2], "off", 3))
01721 ast_moh_stop(chan);
01722 ast_agi_send(agi->fd, chan, "200 result=0\n");
01723 return RESULT_SUCCESS;
01724 }
01725
01726 static int handle_speechcreate(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01727 {
01728
01729 if (agi->speech) {
01730 ast_agi_send(agi->fd, chan, "200 result=0\n");
01731 return RESULT_SUCCESS;
01732 }
01733
01734 if ((agi->speech = ast_speech_new(argv[2], AST_FORMAT_SLINEAR)))
01735 ast_agi_send(agi->fd, chan, "200 result=1\n");
01736 else
01737 ast_agi_send(agi->fd, chan, "200 result=0\n");
01738
01739 return RESULT_SUCCESS;
01740 }
01741
01742 static int handle_speechset(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01743 {
01744
01745 if (argc != 3)
01746 return RESULT_SHOWUSAGE;
01747
01748
01749 if (!agi->speech) {
01750 ast_agi_send(agi->fd, chan, "200 result=0\n");
01751 return RESULT_SUCCESS;
01752 }
01753
01754 ast_speech_change(agi->speech, argv[2], argv[3]);
01755 ast_agi_send(agi->fd, chan, "200 result=1\n");
01756
01757 return RESULT_SUCCESS;
01758 }
01759
01760 static int handle_speechdestroy(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01761 {
01762 if (agi->speech) {
01763 ast_speech_destroy(agi->speech);
01764 agi->speech = NULL;
01765 ast_agi_send(agi->fd, chan, "200 result=1\n");
01766 } else {
01767 ast_agi_send(agi->fd, chan, "200 result=0\n");
01768 }
01769
01770 return RESULT_SUCCESS;
01771 }
01772
01773 static int handle_speechloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01774 {
01775 if (argc != 5)
01776 return RESULT_SHOWUSAGE;
01777
01778 if (!agi->speech) {
01779 ast_agi_send(agi->fd, chan, "200 result=0\n");
01780 return RESULT_SUCCESS;
01781 }
01782
01783 if (ast_speech_grammar_load(agi->speech, argv[3], argv[4]))
01784 ast_agi_send(agi->fd, chan, "200 result=0\n");
01785 else
01786 ast_agi_send(agi->fd, chan, "200 result=1\n");
01787
01788 return RESULT_SUCCESS;
01789 }
01790
01791 static int handle_speechunloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01792 {
01793 if (argc != 4)
01794 return RESULT_SHOWUSAGE;
01795
01796 if (!agi->speech) {
01797 ast_agi_send(agi->fd, chan, "200 result=0\n");
01798 return RESULT_SUCCESS;
01799 }
01800
01801 if (ast_speech_grammar_unload(agi->speech, argv[3]))
01802 ast_agi_send(agi->fd, chan, "200 result=0\n");
01803 else
01804 ast_agi_send(agi->fd, chan, "200 result=1\n");
01805
01806 return RESULT_SUCCESS;
01807 }
01808
01809 static int handle_speechactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01810 {
01811 if (argc != 4)
01812 return RESULT_SHOWUSAGE;
01813
01814 if (!agi->speech) {
01815 ast_agi_send(agi->fd, chan, "200 result=0\n");
01816 return RESULT_SUCCESS;
01817 }
01818
01819 if (ast_speech_grammar_activate(agi->speech, argv[3]))
01820 ast_agi_send(agi->fd, chan, "200 result=0\n");
01821 else
01822 ast_agi_send(agi->fd, chan, "200 result=1\n");
01823
01824 return RESULT_SUCCESS;
01825 }
01826
01827 static int handle_speechdeactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01828 {
01829 if (argc != 4)
01830 return RESULT_SHOWUSAGE;
01831
01832 if (!agi->speech) {
01833 ast_agi_send(agi->fd, chan, "200 result=0\n");
01834 return RESULT_SUCCESS;
01835 }
01836
01837 if (ast_speech_grammar_deactivate(agi->speech, argv[3]))
01838 ast_agi_send(agi->fd, chan, "200 result=0\n");
01839 else
01840 ast_agi_send(agi->fd, chan, "200 result=1\n");
01841
01842 return RESULT_SUCCESS;
01843 }
01844
01845 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang, int offset)
01846 {
01847 struct ast_filestream *fs = NULL;
01848
01849 if (!(fs = ast_openstream(chan, filename, preflang)))
01850 return -1;
01851
01852 if (offset)
01853 ast_seekstream(fs, offset, SEEK_SET);
01854
01855 if (ast_applystream(chan, fs))
01856 return -1;
01857
01858 if (ast_playstream(fs))
01859 return -1;
01860
01861 return 0;
01862 }
01863
01864 static int handle_speechrecognize(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01865 {
01866 struct ast_speech *speech = agi->speech;
01867 char *prompt, dtmf = 0, tmp[4096] = "", *buf = tmp;
01868 int timeout = 0, offset = 0, old_read_format = 0, res = 0, i = 0;
01869 long current_offset = 0;
01870 const char *reason = NULL;
01871 struct ast_frame *fr = NULL;
01872 struct ast_speech_result *result = NULL;
01873 size_t left = sizeof(tmp);
01874 time_t start = 0, current;
01875
01876 if (argc < 4)
01877 return RESULT_SHOWUSAGE;
01878
01879 if (!speech) {
01880 ast_agi_send(agi->fd, chan, "200 result=0\n");
01881 return RESULT_SUCCESS;
01882 }
01883
01884 prompt = argv[2];
01885 timeout = atoi(argv[3]);
01886
01887
01888 if (argc == 5)
01889 offset = atoi(argv[4]);
01890
01891
01892 old_read_format = chan->readformat;
01893 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
01894 ast_agi_send(agi->fd, chan, "200 result=0\n");
01895 return RESULT_SUCCESS;
01896 }
01897
01898
01899 if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
01900 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
01901 ast_speech_start(speech);
01902 }
01903
01904
01905 speech_streamfile(chan, prompt, chan->language, offset);
01906
01907
01908 while (ast_strlen_zero(reason)) {
01909
01910 ast_sched_runq(chan->sched);
01911
01912
01913 if ((res = ast_sched_wait(chan->sched)) < 0)
01914 res = 1000;
01915
01916
01917 if (ast_waitfor(chan, res) > 0) {
01918 if (!(fr = ast_read(chan))) {
01919 reason = "hangup";
01920 break;
01921 }
01922 }
01923
01924
01925 if ((timeout > 0) && (start > 0)) {
01926 time(¤t);
01927 if ((current - start) >= timeout) {
01928 reason = "timeout";
01929 if (fr)
01930 ast_frfree(fr);
01931 break;
01932 }
01933 }
01934
01935
01936 ast_mutex_lock(&speech->lock);
01937
01938
01939 if (ast_test_flag(speech, AST_SPEECH_QUIET) && chan->stream) {
01940 current_offset = ast_tellstream(chan->stream);
01941 ast_stopstream(chan);
01942 ast_clear_flag(speech, AST_SPEECH_QUIET);
01943 }
01944
01945
01946 switch (speech->state) {
01947 case AST_SPEECH_STATE_READY:
01948
01949 if ((timeout > 0) && ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL))) {
01950 ast_stopstream(chan);
01951 time(&start);
01952 }
01953
01954 if (fr && fr->frametype == AST_FRAME_VOICE)
01955 ast_speech_write(speech, fr->data.ptr, fr->datalen);
01956 break;
01957 case AST_SPEECH_STATE_WAIT:
01958
01959 if ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL)) {
01960 ast_stopstream(chan);
01961
01962 if (!ast_strlen_zero(speech->processing_sound) && strcasecmp(speech->processing_sound, "none"))
01963 speech_streamfile(chan, speech->processing_sound, chan->language, 0);
01964 }
01965 break;
01966 case AST_SPEECH_STATE_DONE:
01967
01968 speech->results = ast_speech_results_get(speech);
01969
01970 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
01971 reason = "speech";
01972 break;
01973 default:
01974 break;
01975 }
01976 ast_mutex_unlock(&speech->lock);
01977
01978
01979 if (fr) {
01980 if (fr->frametype == AST_FRAME_DTMF) {
01981 reason = "dtmf";
01982 dtmf = fr->subclass;
01983 } else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass == AST_CONTROL_HANGUP) {
01984 reason = "hangup";
01985 }
01986 ast_frfree(fr);
01987 }
01988 }
01989
01990 if (!strcasecmp(reason, "speech")) {
01991
01992 for (result = speech->results; result; result = AST_LIST_NEXT(result, list)) {
01993
01994 ast_build_string(&buf, &left, "%sscore%d=%d text%d=\"%s\" grammar%d=%s", (i > 0 ? " " : ""), i, result->score, i, result->text, i, result->grammar);
01995
01996 i++;
01997 }
01998
01999 ast_agi_send(agi->fd, chan, "200 result=1 (speech) endpos=%ld results=%d %s\n", current_offset, i, tmp);
02000 } else if (!strcasecmp(reason, "dtmf")) {
02001 ast_agi_send(agi->fd, chan, "200 result=1 (digit) digit=%c endpos=%ld\n", dtmf, current_offset);
02002 } else if (!strcasecmp(reason, "hangup") || !strcasecmp(reason, "timeout")) {
02003 ast_agi_send(agi->fd, chan, "200 result=1 (%s) endpos=%ld\n", reason, current_offset);
02004 } else {
02005 ast_agi_send(agi->fd, chan, "200 result=0 endpos=%ld\n", current_offset);
02006 }
02007
02008 return RESULT_SUCCESS;
02009 }
02010
02011 static char usage_setmusic[] =
02012 " Usage: SET MUSIC ON <on|off> <class>\n"
02013 " Enables/Disables the music on hold generator. If <class> is\n"
02014 " not specified, then the default music on hold class will be used.\n"
02015 " Always returns 0.\n";
02016
02017 static char usage_dbput[] =
02018 " Usage: DATABASE PUT <family> <key> <value>\n"
02019 " Adds or updates an entry in the Asterisk database for a\n"
02020 " given family, key, and value.\n"
02021 " Returns 1 if successful, 0 otherwise.\n";
02022
02023 static char usage_dbget[] =
02024 " Usage: DATABASE GET <family> <key>\n"
02025 " Retrieves an entry in the Asterisk database for a\n"
02026 " given family and key.\n"
02027 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
02028 " is set and returns the variable in parentheses.\n"
02029 " Example return code: 200 result=1 (testvariable)\n";
02030
02031 static char usage_dbdel[] =
02032 " Usage: DATABASE DEL <family> <key>\n"
02033 " Deletes an entry in the Asterisk database for a\n"
02034 " given family and key.\n"
02035 " Returns 1 if successful, 0 otherwise.\n";
02036
02037 static char usage_dbdeltree[] =
02038 " Usage: DATABASE DELTREE <family> [keytree]\n"
02039 " Deletes a family or specific keytree within a family\n"
02040 " in the Asterisk database.\n"
02041 " Returns 1 if successful, 0 otherwise.\n";
02042
02043 static char usage_verbose[] =
02044 " Usage: VERBOSE <message> <level>\n"
02045 " Sends <message> to the console via verbose message system.\n"
02046 " <level> is the the verbose level (1-4)\n"
02047 " Always returns 1.\n";
02048
02049 static char usage_getvariable[] =
02050 " Usage: GET VARIABLE <variablename>\n"
02051 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
02052 " is set and returns the variable in parentheses.\n"
02053 " example return code: 200 result=1 (testvariable)\n";
02054
02055 static char usage_getvariablefull[] =
02056 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
02057 " Returns 0 if <variablename> is not set or channel does not exist. Returns 1\n"
02058 "if <variablename> is set and returns the variable in parenthesis. Understands\n"
02059 "complex variable names and builtin variables, unlike GET VARIABLE.\n"
02060 " example return code: 200 result=1 (testvariable)\n";
02061
02062 static char usage_setvariable[] =
02063 " Usage: SET VARIABLE <variablename> <value>\n";
02064
02065 static char usage_channelstatus[] =
02066 " Usage: CHANNEL STATUS [<channelname>]\n"
02067 " Returns the status of the specified channel.\n"
02068 " If no channel name is given the returns the status of the\n"
02069 " current channel. Return values:\n"
02070 " 0 Channel is down and available\n"
02071 " 1 Channel is down, but reserved\n"
02072 " 2 Channel is off hook\n"
02073 " 3 Digits (or equivalent) have been dialed\n"
02074 " 4 Line is ringing\n"
02075 " 5 Remote end is ringing\n"
02076 " 6 Line is up\n"
02077 " 7 Line is busy\n";
02078
02079 static char usage_setcallerid[] =
02080 " Usage: SET CALLERID <number>\n"
02081 " Changes the callerid of the current channel.\n";
02082
02083 static char usage_exec[] =
02084 " Usage: EXEC <application> <options>\n"
02085 " Executes <application> with given <options>.\n"
02086 " Returns whatever the application returns, or -2 on failure to find application\n";
02087
02088 static char usage_hangup[] =
02089 " Usage: HANGUP [<channelname>]\n"
02090 " Hangs up the specified channel.\n"
02091 " If no channel name is given, hangs up the current channel\n";
02092
02093 static char usage_answer[] =
02094 " Usage: ANSWER\n"
02095 " Answers channel if not already in answer state. Returns -1 on\n"
02096 " channel failure, or 0 if successful.\n";
02097
02098 static char usage_waitfordigit[] =
02099 " Usage: WAIT FOR DIGIT <timeout>\n"
02100 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
02101 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
02102 " the numerical value of the ascii of the digit if one is received. Use -1\n"
02103 " for the timeout value if you desire the call to block indefinitely.\n";
02104
02105 static char usage_sendtext[] =
02106 " Usage: SEND TEXT \"<text to send>\"\n"
02107 " Sends the given text on a channel. Most channels do not support the\n"
02108 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
02109 " support text transmission. Returns -1 only on error/hangup. Text\n"
02110 " consisting of greater than one word should be placed in quotes since the\n"
02111 " command only accepts a single argument.\n";
02112
02113 static char usage_recvchar[] =
02114 " Usage: RECEIVE CHAR <timeout>\n"
02115 " Receives a character of text on a channel. Specify timeout to be the\n"
02116 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
02117 " do not support the reception of text. Returns the decimal value of the character\n"
02118 " if one is received, or 0 if the channel does not support text reception. Returns\n"
02119 " -1 only on error/hangup.\n";
02120
02121 static char usage_recvtext[] =
02122 " Usage: RECEIVE TEXT <timeout>\n"
02123 " Receives a string of text on a channel. Specify timeout to be the\n"
02124 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
02125 " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n";
02126
02127 static char usage_tddmode[] =
02128 " Usage: TDD MODE <on|off>\n"
02129 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
02130 " successful, or 0 if channel is not TDD-capable.\n";
02131
02132 static char usage_sendimage[] =
02133 " Usage: SEND IMAGE <image>\n"
02134 " Sends the given image on a channel. Most channels do not support the\n"
02135 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
02136 " support image transmission. Returns -1 only on error/hangup. Image names\n"
02137 " should not include extensions.\n";
02138
02139 static char usage_streamfile[] =
02140 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
02141 " Send the given file, allowing playback to be interrupted by the given\n"
02142 " digits, if any. Use double quotes for the digits if you wish none to be\n"
02143 " permitted. If sample offset is provided then the audio will seek to sample\n"
02144 " offset before play starts. Returns 0 if playback completes without a digit\n"
02145 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
02146 " or -1 on error or if the channel was disconnected. Remember, the file\n"
02147 " extension must not be included in the filename.\n";
02148
02149 static char usage_controlstreamfile[] =
02150 " Usage: CONTROL STREAM FILE <filename> <escape digits> [skipms] [ffchar] [rewchr] [pausechr]\n"
02151 " Send the given file, allowing playback to be controled by the given\n"
02152 " digits, if any. Use double quotes for the digits if you wish none to be\n"
02153 " permitted. Returns 0 if playback completes without a digit\n"
02154 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
02155 " or -1 on error or if the channel was disconnected. Remember, the file\n"
02156 " extension must not be included in the filename.\n\n"
02157 " Note: ffchar and rewchar default to * and # respectively.\n";
02158
02159 static char usage_getoption[] =
02160 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
02161 " Behaves similar to STREAM FILE but used with a timeout option.\n";
02162
02163 static char usage_saynumber[] =
02164 " Usage: SAY NUMBER <number> <escape digits> [gender]\n"
02165 " Say a given number, returning early if any of the given DTMF digits\n"
02166 " are received on the channel. Returns 0 if playback completes without a digit\n"
02167 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02168 " -1 on error/hangup.\n";
02169
02170 static char usage_saydigits[] =
02171 " Usage: SAY DIGITS <number> <escape digits>\n"
02172 " Say a given digit string, returning early if any of the given DTMF digits\n"
02173 " are received on the channel. Returns 0 if playback completes without a digit\n"
02174 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02175 " -1 on error/hangup.\n";
02176
02177 static char usage_sayalpha[] =
02178 " Usage: SAY ALPHA <number> <escape digits>\n"
02179 " Say a given character string, returning early if any of the given DTMF digits\n"
02180 " are received on the channel. Returns 0 if playback completes without a digit\n"
02181 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02182 " -1 on error/hangup.\n";
02183
02184 static char usage_saydate[] =
02185 " Usage: SAY DATE <date> <escape digits>\n"
02186 " Say a given date, returning early if any of the given DTMF digits are\n"
02187 " received on the channel. <date> is number of seconds elapsed since 00:00:00\n"
02188 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
02189 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02190 " digit if one was pressed or -1 on error/hangup.\n";
02191
02192 static char usage_saytime[] =
02193 " Usage: SAY TIME <time> <escape digits>\n"
02194 " Say a given time, returning early if any of the given DTMF digits are\n"
02195 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
02196 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
02197 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02198 " digit if one was pressed or -1 on error/hangup.\n";
02199
02200 static char usage_saydatetime[] =
02201 " Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n"
02202 " Say a given time, returning early if any of the given DTMF digits are\n"
02203 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
02204 " on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n"
02205 " the time should be said in. See voicemail.conf (defaults to \"ABdY\n"
02206 " 'digits/at' IMp\"). Acceptable values for [timezone] can be found in\n"
02207 " /usr/share/zoneinfo. Defaults to machine default. Returns 0 if playback\n"
02208 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02209 " digit if one was pressed or -1 on error/hangup.\n";
02210
02211 static char usage_sayphonetic[] =
02212 " Usage: SAY PHONETIC <string> <escape digits>\n"
02213 " Say a given character string with phonetics, returning early if any of the\n"
02214 " given DTMF digits are received on the channel. Returns 0 if playback\n"
02215 " completes without a digit pressed, the ASCII numerical value of the digit\n"
02216 " if one was pressed, or -1 on error/hangup.\n";
02217
02218 static char usage_getdata[] =
02219 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
02220 " Stream the given file, and recieve DTMF data. Returns the digits received\n"
02221 "from the channel at the other end.\n";
02222
02223 static char usage_setcontext[] =
02224 " Usage: SET CONTEXT <desired context>\n"
02225 " Sets the context for continuation upon exiting the application.\n";
02226
02227 static char usage_setextension[] =
02228 " Usage: SET EXTENSION <new extension>\n"
02229 " Changes the extension for continuation upon exiting the application.\n";
02230
02231 static char usage_setpriority[] =
02232 " Usage: SET PRIORITY <priority>\n"
02233 " Changes the priority for continuation upon exiting the application.\n"
02234 " The priority must be a valid priority or label.\n";
02235
02236 static char usage_recordfile[] =
02237 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
02238 " [offset samples] [BEEP] [s=silence]\n"
02239 " Record to a file until a given dtmf digit in the sequence is received\n"
02240 " Returns -1 on hangup or error. The format will specify what kind of file\n"
02241 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
02242 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
02243 " to the offset without exceeding the end of the file. \"silence\" is the number\n"
02244 " of seconds of silence allowed before the function returns despite the\n"
02245 " lack of dtmf digits or reaching timeout. Silence value must be\n"
02246 " preceeded by \"s=\" and is also optional.\n";
02247
02248 static char usage_autohangup[] =
02249 " Usage: SET AUTOHANGUP <time>\n"
02250 " Cause the channel to automatically hangup at <time> seconds in the\n"
02251 " future. Of course it can be hungup before then as well. Setting to 0 will\n"
02252 " cause the autohangup feature to be disabled on this channel.\n";
02253
02254 static char usage_noop[] =
02255 " Usage: NoOp\n"
02256 " Does nothing.\n";
02257
02258 static char usage_speechcreate[] =
02259 " Usage: SPEECH CREATE <engine>\n"
02260 " Create a speech object to be used by the other Speech AGI commands.\n";
02261
02262 static char usage_speechset[] =
02263 " Usage: SPEECH SET <name> <value>\n"
02264 " Set an engine-specific setting.\n";
02265
02266 static char usage_speechdestroy[] =
02267 " Usage: SPEECH DESTROY\n"
02268 " Destroy the speech object created by SPEECH CREATE.\n";
02269
02270 static char usage_speechloadgrammar[] =
02271 " Usage: SPEECH LOAD GRAMMAR <grammar name> <path to grammar>\n"
02272 " Loads the specified grammar as the specified name.\n";
02273
02274 static char usage_speechunloadgrammar[] =
02275 " Usage: SPEECH UNLOAD GRAMMAR <grammar name>\n"
02276 " Unloads the specified grammar.\n";
02277
02278 static char usage_speechactivategrammar[] =
02279 " Usage: SPEECH ACTIVATE GRAMMAR <grammar name>\n"
02280 " Activates the specified grammar on the speech object.\n";
02281
02282 static char usage_speechdeactivategrammar[] =
02283 " Usage: SPEECH DEACTIVATE GRAMMAR <grammar name>\n"
02284 " Deactivates the specified grammar on the speech object.\n";
02285
02286 static char usage_speechrecognize[] =
02287 " Usage: SPEECH RECOGNIZE <prompt> <timeout> [<offset>]\n"
02288 " Plays back given prompt while listening for speech and dtmf.\n";
02289
02290
02291
02292
02293 static struct agi_command commands[] = {
02294 { { "answer", NULL }, handle_answer, "Answer channel", usage_answer , 0 },
02295 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus , 0 },
02296 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel , 1 },
02297 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree , 1 },
02298 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget , 1 },
02299 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput , 1 },
02300 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec , 1 },
02301 { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata , 0 },
02302 { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull , 1 },
02303 { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption , 0 },
02304 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable , 1 },
02305 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup , 0 },
02306 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop , 1 },
02307 { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar , 0 },
02308 { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext , 0 },
02309 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile , 0 },
02310 { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha , 0 },
02311 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits , 0 },
02312 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber , 0 },
02313 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic , 0 },
02314 { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate , 0 },
02315 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime , 0 },
02316 { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime , 0 },
02317 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage , 0 },
02318 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext , 0 },
02319 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup , 0 },
02320 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid , 0 },
02321 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext , 0 },
02322 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension , 0 },
02323 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic , 0 },
02324 { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority , 0 },
02325 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable , 1 },
02326 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile , 0 },
02327 { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile , 0 },
02328 { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode , 0 },
02329 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose , 1 },
02330 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit , 0 },
02331 { { "speech", "create", NULL }, handle_speechcreate, "Creates a speech object", usage_speechcreate, 0 },
02332 { { "speech", "set", NULL }, handle_speechset, "Sets a speech engine setting", usage_speechset, 0 },
02333 { { "speech", "destroy", NULL }, handle_speechdestroy, "Destroys a speech object", usage_speechdestroy, 1 },
02334 { { "speech", "load", "grammar", NULL }, handle_speechloadgrammar, "Loads a grammar", usage_speechloadgrammar, 0 },
02335 { { "speech", "unload", "grammar", NULL }, handle_speechunloadgrammar, "Unloads a grammar", usage_speechunloadgrammar, 1 },
02336 { { "speech", "activate", "grammar", NULL }, handle_speechactivategrammar, "Activates a grammar", usage_speechactivategrammar, 0 },
02337 { { "speech", "deactivate", "grammar", NULL }, handle_speechdeactivategrammar, "Deactivates a grammar", usage_speechdeactivategrammar, 0 },
02338 { { "speech", "recognize", NULL }, handle_speechrecognize, "Recognizes speech", usage_speechrecognize, 0 },
02339 };
02340
02341 static AST_RWLIST_HEAD_STATIC(agi_commands, agi_command);
02342
02343 static char *help_workhorse(int fd, char *match[])
02344 {
02345 char fullcmd[80], matchstr[80];
02346 struct agi_command *e;
02347
02348 if (match)
02349 ast_join(matchstr, sizeof(matchstr), match);
02350
02351 ast_cli(fd, "%5.5s %30.30s %s\n","Dead","Command","Description");
02352 AST_RWLIST_RDLOCK(&agi_commands);
02353 AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
02354 if (!e->cmda[0])
02355 break;
02356
02357 if ((e->cmda[0])[0] == '_')
02358 continue;
02359 ast_join(fullcmd, sizeof(fullcmd), e->cmda);
02360 if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr)))
02361 continue;
02362 ast_cli(fd, "%5.5s %30.30s %s\n", e->dead ? "Yes" : "No" , fullcmd, e->summary);
02363 }
02364 AST_RWLIST_UNLOCK(&agi_commands);
02365
02366 return CLI_SUCCESS;
02367 }
02368
02369 int ast_agi_register(struct ast_module *mod, agi_command *cmd)
02370 {
02371 char fullcmd[80];
02372
02373 ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
02374
02375 if (!find_command(cmd->cmda,1)) {
02376 cmd->mod = mod;
02377 AST_RWLIST_WRLOCK(&agi_commands);
02378 AST_LIST_INSERT_TAIL(&agi_commands, cmd, list);
02379 AST_RWLIST_UNLOCK(&agi_commands);
02380 if (mod != ast_module_info->self)
02381 ast_module_ref(ast_module_info->self);
02382 ast_verb(2, "AGI Command '%s' registered\n",fullcmd);
02383 return 1;
02384 } else {
02385 ast_log(LOG_WARNING, "Command already registered!\n");
02386 return 0;
02387 }
02388 }
02389
02390 int ast_agi_unregister(struct ast_module *mod, agi_command *cmd)
02391 {
02392 struct agi_command *e;
02393 int unregistered = 0;
02394 char fullcmd[80];
02395
02396 ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
02397
02398 AST_RWLIST_WRLOCK(&agi_commands);
02399 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&agi_commands, e, list) {
02400 if (cmd == e) {
02401 AST_RWLIST_REMOVE_CURRENT(list);
02402 if (mod != ast_module_info->self)
02403 ast_module_unref(ast_module_info->self);
02404 unregistered=1;
02405 break;
02406 }
02407 }
02408 AST_RWLIST_TRAVERSE_SAFE_END;
02409 AST_RWLIST_UNLOCK(&agi_commands);
02410 if (unregistered)
02411 ast_verb(2, "AGI Command '%s' unregistered\n",fullcmd);
02412 else
02413 ast_log(LOG_WARNING, "Unable to unregister command: '%s'!\n",fullcmd);
02414 return unregistered;
02415 }
02416
02417 int ast_agi_register_multiple(struct ast_module *mod, struct agi_command *cmd, unsigned int len)
02418 {
02419 unsigned int i, x = 0;
02420
02421 for (i = 0; i < len; i++) {
02422 if (ast_agi_register(mod, cmd + i) == 1) {
02423 x++;
02424 continue;
02425 }
02426
02427
02428
02429
02430 for (; x > 0; x--) {
02431
02432
02433
02434
02435
02436
02437
02438
02439 (void) ast_agi_unregister(mod, cmd + x - 1);
02440 }
02441 return -1;
02442 }
02443
02444 return 0;
02445 }
02446
02447 int ast_agi_unregister_multiple(struct ast_module *mod, struct agi_command *cmd, unsigned int len)
02448 {
02449 unsigned int i;
02450 int res = 0;
02451
02452 for (i = 0; i < len; i++) {
02453
02454
02455
02456
02457 res |= ast_agi_unregister(mod, cmd + i);
02458 }
02459
02460 return res;
02461 }
02462
02463 static agi_command *find_command(char *cmds[], int exact)
02464 {
02465 int y, match;
02466 struct agi_command *e;
02467
02468 AST_RWLIST_RDLOCK(&agi_commands);
02469 AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
02470 if (!e->cmda[0])
02471 break;
02472
02473 match = 1;
02474 for (y = 0; match && cmds[y]; y++) {
02475
02476
02477
02478 if (!e->cmda[y] && !exact)
02479 break;
02480
02481 if (!e->cmda[y]) {
02482 AST_RWLIST_UNLOCK(&agi_commands);
02483 return NULL;
02484 }
02485 if (strcasecmp(e->cmda[y], cmds[y]))
02486 match = 0;
02487 }
02488
02489
02490 if ((exact > -1) && e->cmda[y])
02491 match = 0;
02492 if (match) {
02493 AST_RWLIST_UNLOCK(&agi_commands);
02494 return e;
02495 }
02496 }
02497 AST_RWLIST_UNLOCK(&agi_commands);
02498 return NULL;
02499 }
02500
02501 static int parse_args(char *s, int *max, char *argv[])
02502 {
02503 int x = 0, quoted = 0, escaped = 0, whitespace = 1;
02504 char *cur;
02505
02506 cur = s;
02507 while(*s) {
02508 switch(*s) {
02509 case '"':
02510
02511 if (escaped)
02512 goto normal;
02513 else
02514 quoted = !quoted;
02515 if (quoted && whitespace) {
02516
02517 argv[x++] = cur;
02518 whitespace=0;
02519 }
02520 escaped = 0;
02521 break;
02522 case ' ':
02523 case '\t':
02524 if (!quoted && !escaped) {
02525
02526
02527 whitespace = 1;
02528 *(cur++) = '\0';
02529 } else
02530
02531 goto normal;
02532 break;
02533 case '\\':
02534
02535 if (escaped) {
02536 goto normal;
02537 } else {
02538 escaped=1;
02539 }
02540 break;
02541 default:
02542 normal:
02543 if (whitespace) {
02544 if (x >= MAX_ARGS -1) {
02545 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
02546 break;
02547 }
02548
02549 argv[x++] = cur;
02550 whitespace=0;
02551 }
02552 *(cur++) = *s;
02553 escaped=0;
02554 }
02555 s++;
02556 }
02557
02558 *(cur++) = '\0';
02559 argv[x] = NULL;
02560 *max = x;
02561 return 0;
02562 }
02563
02564 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead)
02565 {
02566 char *argv[MAX_ARGS];
02567 int argc = MAX_ARGS, res;
02568 agi_command *c;
02569 const char *ami_res = "Unknown Result";
02570 char *ami_cmd = ast_strdupa(buf);
02571 int command_id = ast_random(), resultcode = 200;
02572
02573 manager_event(EVENT_FLAG_AGI, "AGIExec",
02574 "SubEvent: Start\r\n"
02575 "Channel: %s\r\n"
02576 "CommandId: %d\r\n"
02577 "Command: %s\r\n", chan->name, command_id, ami_cmd);
02578 parse_args(buf, &argc, argv);
02579 if ((c = find_command(argv, 0)) && (!dead || (dead && c->dead))) {
02580
02581
02582 if (c->mod != ast_module_info->self)
02583 ast_module_ref(c->mod);
02584
02585
02586 if (chan->cdr && !ast_check_hangup(chan) && strcasecmp(argv[0], "EXEC"))
02587 ast_cdr_setapp(chan->cdr, "AGI", buf);
02588
02589 res = c->handler(chan, agi, argc, argv);
02590 if (c->mod != ast_module_info->self)
02591 ast_module_unref(c->mod);
02592 switch (res) {
02593 case RESULT_SHOWUSAGE: ami_res = "Usage"; resultcode = 520; break;
02594 case RESULT_FAILURE: ami_res = "Failure"; resultcode = -1; break;
02595 case RESULT_SUCCESS: ami_res = "Success"; resultcode = 200; break;
02596 }
02597 manager_event(EVENT_FLAG_AGI, "AGIExec",
02598 "SubEvent: End\r\n"
02599 "Channel: %s\r\n"
02600 "CommandId: %d\r\n"
02601 "Command: %s\r\n"
02602 "ResultCode: %d\r\n"
02603 "Result: %s\r\n", chan->name, command_id, ami_cmd, resultcode, ami_res);
02604 switch(res) {
02605 case RESULT_SHOWUSAGE:
02606 ast_agi_send(agi->fd, chan, "520-Invalid command syntax. Proper usage follows:\n");
02607 ast_agi_send(agi->fd, chan, "%s", c->usage);
02608 ast_agi_send(agi->fd, chan, "520 End of proper usage.\n");
02609 break;
02610 case RESULT_FAILURE:
02611
02612
02613 return -1;
02614 }
02615 } else if ((c = find_command(argv, 0))) {
02616 ast_agi_send(agi->fd, chan, "511 Command Not Permitted on a dead channel\n");
02617 manager_event(EVENT_FLAG_AGI, "AGIExec",
02618 "SubEvent: End\r\n"
02619 "Channel: %s\r\n"
02620 "CommandId: %d\r\n"
02621 "Command: %s\r\n"
02622 "ResultCode: 511\r\n"
02623 "Result: Command not permitted on a dead channel\r\n", chan->name, command_id, ami_cmd);
02624 } else {
02625 ast_agi_send(agi->fd, chan, "510 Invalid or unknown command\n");
02626 manager_event(EVENT_FLAG_AGI, "AGIExec",
02627 "SubEvent: End\r\n"
02628 "Channel: %s\r\n"
02629 "CommandId: %d\r\n"
02630 "Command: %s\r\n"
02631 "ResultCode: 510\r\n"
02632 "Result: Invalid or unknown command\r\n", chan->name, command_id, ami_cmd);
02633 }
02634 return 0;
02635 }
02636 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
02637 {
02638 struct ast_channel *c;
02639 int outfd, ms, needhup = 0;
02640 enum agi_result returnstatus = AGI_RESULT_SUCCESS;
02641 struct ast_frame *f;
02642 char buf[AGI_BUF_LEN];
02643 char *res = NULL;
02644 FILE *readf;
02645
02646
02647 int retry = AGI_NANDFS_RETRY;
02648 int send_sighup;
02649 const char *sighup_str;
02650
02651 ast_channel_lock(chan);
02652 sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
02653 send_sighup = ast_strlen_zero(sighup_str) || !ast_false(sighup_str);
02654 ast_channel_unlock(chan);
02655
02656 if (!(readf = fdopen(agi->ctrl, "r"))) {
02657 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
02658 if (send_sighup && pid > -1)
02659 kill(pid, SIGHUP);
02660 close(agi->ctrl);
02661 return AGI_RESULT_FAILURE;
02662 }
02663
02664 setlinebuf(readf);
02665 setup_env(chan, request, agi->fd, (agi->audio > -1), argc, argv);
02666 for (;;) {
02667 if (needhup) {
02668 needhup = 0;
02669 dead = 1;
02670 if (send_sighup) {
02671 if (pid > -1) {
02672 kill(pid, SIGHUP);
02673 } else if (agi->fast) {
02674 send(agi->ctrl, "HANGUP\n", 7, MSG_OOB);
02675 }
02676 }
02677 }
02678 ms = -1;
02679 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
02680 if (c) {
02681 retry = AGI_NANDFS_RETRY;
02682
02683 f = ast_read(c);
02684 if (!f) {
02685 ast_debug(1, "%s hungup\n", chan->name);
02686 returnstatus = AGI_RESULT_HANGUP;
02687 needhup = 1;
02688 continue;
02689 } else {
02690
02691 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
02692
02693 if (write(agi->audio, f->data.ptr, f->datalen) < 0) {
02694 }
02695 }
02696 ast_frfree(f);
02697 }
02698 } else if (outfd > -1) {
02699 size_t len = sizeof(buf);
02700 size_t buflen = 0;
02701
02702 retry = AGI_NANDFS_RETRY;
02703 buf[0] = '\0';
02704
02705 while (buflen < (len - 1)) {
02706 res = fgets(buf + buflen, len, readf);
02707 if (feof(readf))
02708 break;
02709 if (ferror(readf) && ((errno != EINTR) && (errno != EAGAIN)))
02710 break;
02711 if (res != NULL && !agi->fast)
02712 break;
02713 buflen = strlen(buf);
02714 if (buflen && buf[buflen - 1] == '\n')
02715 break;
02716 len -= buflen;
02717 if (agidebug)
02718 ast_verbose( "AGI Rx << temp buffer %s - errno %s\n", buf, strerror(errno));
02719 }
02720
02721 if (!buf[0]) {
02722
02723 if (returnstatus) {
02724 returnstatus = -1;
02725 }
02726 ast_verb(3, "<%s>AGI Script %s completed, returning %d\n", chan->name, request, returnstatus);
02727 if (pid > 0)
02728 waitpid(pid, status, 0);
02729
02730 pid = -1;
02731 break;
02732 }
02733
02734
02735 if (*buf && strncasecmp(buf, "failure", 7) == 0) {
02736 returnstatus = AGI_RESULT_FAILURE;
02737 break;
02738 }
02739
02740
02741 if (*buf && buf[strlen(buf) - 1] == '\n')
02742 buf[strlen(buf) - 1] = 0;
02743 if (agidebug)
02744 ast_verbose("<%s>AGI Rx << %s\n", chan->name, buf);
02745 returnstatus |= agi_handle_command(chan, agi, buf, dead);
02746
02747 if (returnstatus < 0) {
02748 needhup = 1;
02749 continue;
02750 }
02751 } else {
02752 if (--retry <= 0) {
02753 ast_log(LOG_WARNING, "No channel, no fd?\n");
02754 returnstatus = AGI_RESULT_FAILURE;
02755 break;
02756 }
02757 }
02758 }
02759 if (agi->speech) {
02760 ast_speech_destroy(agi->speech);
02761 }
02762
02763 if (send_sighup) {
02764 if (pid > -1) {
02765 if (kill(pid, SIGHUP)) {
02766 ast_log(LOG_WARNING, "unable to send SIGHUP to AGI process %d: %s\n", pid, strerror(errno));
02767 } else {
02768 usleep(1);
02769 }
02770 waitpid(pid, status, WNOHANG);
02771 } else if (agi->fast) {
02772 send(agi->ctrl, "HANGUP\n", 7, MSG_OOB);
02773 }
02774 }
02775 fclose(readf);
02776 return returnstatus;
02777 }
02778
02779 static char *handle_cli_agi_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02780 {
02781 struct agi_command *command;
02782 char fullcmd[80];
02783
02784 switch (cmd) {
02785 case CLI_INIT:
02786 e->command = "agi show";
02787 e->usage =
02788 "Usage: agi show [topic]\n"
02789 " When called with a topic as an argument, displays usage\n"
02790 " information on the given command. If called without a\n"
02791 " topic, it provides a list of AGI commands.\n";
02792 case CLI_GENERATE:
02793 return NULL;
02794 }
02795 if (a->argc < e->args)
02796 return CLI_SHOWUSAGE;
02797 if (a->argc > e->args) {
02798 command = find_command(a->argv + e->args, 1);
02799 if (command) {
02800 ast_cli(a->fd, "%s", command->usage);
02801 ast_cli(a->fd, " Runs Dead : %s\n", command->dead ? "Yes" : "No");
02802 } else {
02803 if (find_command(a->argv + e->args, -1)) {
02804 return help_workhorse(a->fd, a->argv + e->args);
02805 } else {
02806 ast_join(fullcmd, sizeof(fullcmd), a->argv + e->args);
02807 ast_cli(a->fd, "No such command '%s'.\n", fullcmd);
02808 }
02809 }
02810 } else {
02811 return help_workhorse(a->fd, NULL);
02812 }
02813 return CLI_SUCCESS;
02814 }
02815
02816
02817
02818
02819 static void write_html_escaped(FILE *htmlfile, char *str)
02820 {
02821 char *cur = str;
02822
02823 while(*cur) {
02824 switch (*cur) {
02825 case '<':
02826 fprintf(htmlfile, "%s", "<");
02827 break;
02828 case '>':
02829 fprintf(htmlfile, "%s", ">");
02830 break;
02831 case '&':
02832 fprintf(htmlfile, "%s", "&");
02833 break;
02834 case '"':
02835 fprintf(htmlfile, "%s", """);
02836 break;
02837 default:
02838 fprintf(htmlfile, "%c", *cur);
02839 break;
02840 }
02841 cur++;
02842 }
02843
02844 return;
02845 }
02846
02847 static int write_htmldump(char *filename)
02848 {
02849 struct agi_command *command;
02850 char fullcmd[80];
02851 FILE *htmlfile;
02852
02853 if (!(htmlfile = fopen(filename, "wt")))
02854 return -1;
02855
02856 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
02857 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
02858 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
02859
02860 AST_RWLIST_RDLOCK(&agi_commands);
02861 AST_RWLIST_TRAVERSE(&agi_commands, command, list) {
02862 char *stringp, *tempstr;
02863
02864 if (!command->cmda[0])
02865 break;
02866
02867 if ((command->cmda[0])[0] == '_')
02868 continue;
02869 ast_join(fullcmd, sizeof(fullcmd), command->cmda);
02870
02871 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
02872 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TH></TR>\n", fullcmd, command->summary);
02873
02874 stringp = command->usage;
02875 tempstr = strsep(&stringp, "\n");
02876
02877 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">");
02878 write_html_escaped(htmlfile, tempstr);
02879 fprintf(htmlfile, "</TD></TR>\n");
02880 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
02881
02882 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
02883 write_html_escaped(htmlfile, tempstr);
02884 fprintf(htmlfile, "<BR>\n");
02885 }
02886 fprintf(htmlfile, "</TD></TR>\n");
02887 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
02888 }
02889 AST_RWLIST_UNLOCK(&agi_commands);
02890 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
02891 fclose(htmlfile);
02892 return 0;
02893 }
02894
02895 static char *handle_cli_agi_dumphtml_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02896 {
02897 switch (cmd) {
02898 case CLI_INIT:
02899 e->command = "agi dumphtml";
02900 e->usage =
02901 "Usage: agi dumphtml <filename>\n"
02902 " Dumps the AGI command list in HTML format to the given\n"
02903 " file.\n";
02904 return NULL;
02905 case CLI_GENERATE:
02906 return NULL;
02907 }
02908 if (a->argc < e->args + 1)
02909 return CLI_SHOWUSAGE;
02910
02911 if (write_htmldump(a->argv[2]) < 0) {
02912 ast_cli(a->fd, "Could not create file '%s'\n", a->argv[2]);
02913 return CLI_SHOWUSAGE;
02914 }
02915 ast_cli(a->fd, "AGI HTML commands dumped to: %s\n", a->argv[2]);
02916 return CLI_SUCCESS;
02917 }
02918
02919 static char *handle_cli_agi_dump_html(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02920 {
02921 switch (cmd) {
02922 case CLI_INIT:
02923 e->command = "agi dump html";
02924 e->usage =
02925 "Usage: agi dump html <filename>\n"
02926 " Dumps the AGI command list in HTML format to the given\n"
02927 " file.\n";
02928 return NULL;
02929 case CLI_GENERATE:
02930 return NULL;
02931 }
02932 if (a->argc != e->args + 1)
02933 return CLI_SHOWUSAGE;
02934
02935 if (write_htmldump(a->argv[e->args]) < 0) {
02936 ast_cli(a->fd, "Could not create file '%s'\n", a->argv[e->args]);
02937 return CLI_SHOWUSAGE;
02938 }
02939 ast_cli(a->fd, "AGI HTML commands dumped to: %s\n", a->argv[e->args]);
02940 return CLI_SUCCESS;
02941 }
02942
02943 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
02944 {
02945 enum agi_result res;
02946 char buf[AGI_BUF_LEN] = "", *tmp = buf;
02947 int fds[2], efd = -1, pid;
02948 AST_DECLARE_APP_ARGS(args,
02949 AST_APP_ARG(arg)[MAX_ARGS];
02950 );
02951 AGI agi;
02952
02953 if (ast_strlen_zero(data)) {
02954 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
02955 return -1;
02956 }
02957 if (dead)
02958 ast_debug(3, "Hungup channel detected, running agi in dead mode.\n");
02959 ast_copy_string(buf, data, sizeof(buf));
02960 memset(&agi, 0, sizeof(agi));
02961 AST_STANDARD_APP_ARGS(args, tmp);
02962 args.argv[args.argc] = NULL;
02963 #if 0
02964
02965 if (chan->_state != AST_STATE_UP) {
02966 if (ast_answer(chan))
02967 return -1;
02968 }
02969 #endif
02970 res = launch_script(chan, args.argv[0], args.argv, fds, enhanced ? &efd : NULL, &pid);
02971
02972
02973 if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {
02974 int status = 0;
02975 agi.fd = fds[1];
02976 agi.ctrl = fds[0];
02977 agi.audio = efd;
02978 agi.fast = (res == AGI_RESULT_SUCCESS_FAST) ? 1 : 0;
02979 res = run_agi(chan, args.argv[0], &agi, pid, &status, dead, args.argc, args.argv);
02980
02981 if ((res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) && status)
02982 res = AGI_RESULT_FAILURE;
02983 if (fds[1] != fds[0])
02984 close(fds[1]);
02985 if (efd > -1)
02986 close(efd);
02987 }
02988 ast_safe_fork_cleanup();
02989
02990 switch (res) {
02991 case AGI_RESULT_SUCCESS:
02992 case AGI_RESULT_SUCCESS_FAST:
02993 case AGI_RESULT_SUCCESS_ASYNC:
02994 pbx_builtin_setvar_helper(chan, "AGISTATUS", "SUCCESS");
02995 break;
02996 case AGI_RESULT_FAILURE:
02997 pbx_builtin_setvar_helper(chan, "AGISTATUS", "FAILURE");
02998 break;
02999 case AGI_RESULT_NOTFOUND:
03000 pbx_builtin_setvar_helper(chan, "AGISTATUS", "NOTFOUND");
03001 break;
03002 case AGI_RESULT_HANGUP:
03003 pbx_builtin_setvar_helper(chan, "AGISTATUS", "HANGUP");
03004 return -1;
03005 }
03006
03007 return 0;
03008 }
03009
03010 static int agi_exec(struct ast_channel *chan, void *data)
03011 {
03012 if (!ast_check_hangup(chan))
03013 return agi_exec_full(chan, data, 0, 0);
03014 else
03015 return agi_exec_full(chan, data, 0, 1);
03016 }
03017
03018 static int eagi_exec(struct ast_channel *chan, void *data)
03019 {
03020 int readformat, res;
03021
03022 if (ast_check_hangup(chan)) {
03023 ast_log(LOG_ERROR, "EAGI cannot be run on a dead/hungup channel, please use AGI.\n");
03024 return 0;
03025 }
03026 readformat = chan->readformat;
03027 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
03028 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
03029 return -1;
03030 }
03031 res = agi_exec_full(chan, data, 1, 0);
03032 if (!res) {
03033 if (ast_set_read_format(chan, readformat)) {
03034 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
03035 }
03036 }
03037 return res;
03038 }
03039
03040 static int deadagi_exec(struct ast_channel *chan, void *data)
03041 {
03042 ast_log(LOG_WARNING, "DeadAGI has been deprecated, please use AGI in all cases!\n");
03043 return agi_exec(chan, data);
03044 }
03045
03046 static struct ast_cli_entry cli_agi_dumphtml_deprecated = AST_CLI_DEFINE(handle_cli_agi_dumphtml_deprecated, "Dumps a list of AGI commands in HTML format");
03047
03048 static struct ast_cli_entry cli_agi[] = {
03049 AST_CLI_DEFINE(handle_cli_agi_add_cmd, "Add AGI command to a channel in Async AGI"),
03050 AST_CLI_DEFINE(handle_cli_agi_debug, "Enable/Disable AGI debugging"),
03051 AST_CLI_DEFINE(handle_cli_agi_show, "List AGI commands or specific help"),
03052 AST_CLI_DEFINE(handle_cli_agi_dump_html, "Dumps a list of AGI commands in HTML format", .deprecate_cmd = &cli_agi_dumphtml_deprecated)
03053 };
03054
03055 static int unload_module(void)
03056 {
03057 ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
03058
03059
03060
03061 (void) ast_agi_unregister_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
03062 ast_unregister_application(eapp);
03063 ast_unregister_application(deadapp);
03064 ast_manager_unregister("AGI");
03065 return ast_unregister_application(app);
03066 }
03067
03068 static int load_module(void)
03069 {
03070 ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
03071
03072
03073
03074 (void) ast_agi_register_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
03075 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
03076 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
03077 ast_manager_register2("AGI", EVENT_FLAG_AGI, action_add_agi_cmd, "Add an AGI command to execute by Async AGI", mandescr_asyncagi);
03078 return ast_register_application(app, agi_exec, synopsis, descrip);
03079 }
03080
03081 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Asterisk Gateway Interface (AGI)",
03082 .load = load_module,
03083 .unload = unload_module,
03084 );