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: 211569 $")
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_asyncagi_break(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00778 {
00779 ast_agi_send(agi->fd, chan, "200 result=0\n");
00780 return RESULT_FAILURE;
00781 }
00782
00783 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00784 {
00785 int res, to;
00786
00787 if (argc != 4)
00788 return RESULT_SHOWUSAGE;
00789 if (sscanf(argv[3], "%30d", &to) != 1)
00790 return RESULT_SHOWUSAGE;
00791 res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl);
00792 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00793 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00794 }
00795
00796 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00797 {
00798 int res;
00799
00800 if (argc != 3)
00801 return RESULT_SHOWUSAGE;
00802
00803
00804
00805
00806
00807
00808
00809
00810 res = ast_sendtext(chan, argv[2]);
00811 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00812 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00813 }
00814
00815 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00816 {
00817 int res;
00818
00819 if (argc != 3)
00820 return RESULT_SHOWUSAGE;
00821
00822 res = ast_recvchar(chan,atoi(argv[2]));
00823 if (res == 0) {
00824 ast_agi_send(agi->fd, chan, "200 result=%d (timeout)\n", res);
00825 return RESULT_SUCCESS;
00826 }
00827 if (res > 0) {
00828 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00829 return RESULT_SUCCESS;
00830 }
00831 ast_agi_send(agi->fd, chan, "200 result=%d (hangup)\n", res);
00832 return RESULT_FAILURE;
00833 }
00834
00835 static int handle_recvtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00836 {
00837 char *buf;
00838
00839 if (argc != 3)
00840 return RESULT_SHOWUSAGE;
00841
00842 buf = ast_recvtext(chan, atoi(argv[2]));
00843 if (buf) {
00844 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", buf);
00845 ast_free(buf);
00846 } else {
00847 ast_agi_send(agi->fd, chan, "200 result=-1\n");
00848 }
00849 return RESULT_SUCCESS;
00850 }
00851
00852 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00853 {
00854 int res, x;
00855
00856 if (argc != 3)
00857 return RESULT_SHOWUSAGE;
00858
00859 if (!strncasecmp(argv[2],"on",2)) {
00860 x = 1;
00861 } else {
00862 x = 0;
00863 }
00864 if (!strncasecmp(argv[2],"mate",4)) {
00865 x = 2;
00866 }
00867 if (!strncasecmp(argv[2],"tdd",3)) {
00868 x = 1;
00869 }
00870 res = ast_channel_setoption(chan, AST_OPTION_TDD, &x, sizeof(char), 0);
00871 if (res != RESULT_SUCCESS) {
00872 ast_agi_send(agi->fd, chan, "200 result=0\n");
00873 } else {
00874 ast_agi_send(agi->fd, chan, "200 result=1\n");
00875 }
00876 return RESULT_SUCCESS;
00877 }
00878
00879 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00880 {
00881 int res;
00882
00883 if (argc != 3) {
00884 return RESULT_SHOWUSAGE;
00885 }
00886
00887 res = ast_send_image(chan, argv[2]);
00888 if (!ast_check_hangup(chan)) {
00889 res = 0;
00890 }
00891 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00892 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00893 }
00894
00895 static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00896 {
00897 int res = 0, skipms = 3000;
00898 char *fwd = "#", *rev = "*", *suspend = NULL, *stop = NULL;
00899
00900 if (argc < 5 || argc > 9) {
00901 return RESULT_SHOWUSAGE;
00902 }
00903
00904 if (!ast_strlen_zero(argv[4])) {
00905 stop = argv[4];
00906 }
00907
00908 if ((argc > 5) && (sscanf(argv[5], "%30d", &skipms) != 1)) {
00909 return RESULT_SHOWUSAGE;
00910 }
00911
00912 if (argc > 6 && !ast_strlen_zero(argv[6])) {
00913 fwd = argv[6];
00914 }
00915
00916 if (argc > 7 && !ast_strlen_zero(argv[7])) {
00917 rev = argv[7];
00918 }
00919
00920 if (argc > 8 && !ast_strlen_zero(argv[8])) {
00921 suspend = argv[8];
00922 }
00923
00924 res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, suspend, NULL, skipms, NULL);
00925
00926 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
00927
00928 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00929 }
00930
00931 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00932 {
00933 int res, vres;
00934 struct ast_filestream *fs, *vfs;
00935 long sample_offset = 0, max_length;
00936 char *edigits = "";
00937
00938 if (argc < 4 || argc > 5)
00939 return RESULT_SHOWUSAGE;
00940
00941 if (argv[3])
00942 edigits = argv[3];
00943
00944 if ((argc > 4) && (sscanf(argv[4], "%30ld", &sample_offset) != 1))
00945 return RESULT_SHOWUSAGE;
00946
00947 if (!(fs = ast_openstream(chan, argv[2], chan->language))) {
00948 ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", 0, sample_offset);
00949 return RESULT_SUCCESS;
00950 }
00951
00952 if ((vfs = ast_openvstream(chan, argv[2], chan->language)))
00953 ast_debug(1, "Ooh, found a video stream, too\n");
00954
00955 ast_verb(3, "Playing '%s' (escape_digits=%s) (sample_offset %ld)\n", argv[2], edigits, sample_offset);
00956
00957 ast_seekstream(fs, 0, SEEK_END);
00958 max_length = ast_tellstream(fs);
00959 ast_seekstream(fs, sample_offset, SEEK_SET);
00960 res = ast_applystream(chan, fs);
00961 if (vfs)
00962 vres = ast_applystream(chan, vfs);
00963 ast_playstream(fs);
00964 if (vfs)
00965 ast_playstream(vfs);
00966
00967 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
00968
00969
00970 sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length;
00971 ast_stopstream(chan);
00972 if (res == 1) {
00973
00974 return RESULT_SUCCESS;
00975 }
00976 ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", res, sample_offset);
00977 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
00978 }
00979
00980
00981 static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
00982 {
00983 int res, vres;
00984 struct ast_filestream *fs, *vfs;
00985 long sample_offset = 0, max_length;
00986 int timeout = 0;
00987 char *edigits = "";
00988
00989 if ( argc < 4 || argc > 5 )
00990 return RESULT_SHOWUSAGE;
00991
00992 if ( argv[3] )
00993 edigits = argv[3];
00994
00995 if ( argc == 5 )
00996 timeout = atoi(argv[4]);
00997 else if (chan->pbx->dtimeoutms) {
00998
00999 timeout = chan->pbx->dtimeoutms;
01000 }
01001
01002 if (!(fs = ast_openstream(chan, argv[2], chan->language))) {
01003 ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", 0, sample_offset);
01004 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
01005 return RESULT_SUCCESS;
01006 }
01007
01008 if ((vfs = ast_openvstream(chan, argv[2], chan->language)))
01009 ast_debug(1, "Ooh, found a video stream, too\n");
01010
01011 ast_verb(3, "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout);
01012
01013 ast_seekstream(fs, 0, SEEK_END);
01014 max_length = ast_tellstream(fs);
01015 ast_seekstream(fs, sample_offset, SEEK_SET);
01016 res = ast_applystream(chan, fs);
01017 if (vfs)
01018 vres = ast_applystream(chan, vfs);
01019 ast_playstream(fs);
01020 if (vfs)
01021 ast_playstream(vfs);
01022
01023 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl);
01024
01025
01026 sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
01027 ast_stopstream(chan);
01028 if (res == 1) {
01029
01030 return RESULT_SUCCESS;
01031 }
01032
01033
01034 if (res == 0 ) {
01035 res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl);
01036
01037 if ( !strchr(edigits,res) )
01038 res=0;
01039 }
01040
01041 ast_agi_send(agi->fd, chan, "200 result=%d endpos=%ld\n", res, sample_offset);
01042 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01043 }
01044
01045
01046
01047
01048
01049
01050 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01051 {
01052 int res, num;
01053
01054 if (argc < 4 || argc > 5)
01055 return RESULT_SHOWUSAGE;
01056 if (sscanf(argv[2], "%30d", &num) != 1)
01057 return RESULT_SHOWUSAGE;
01058 res = ast_say_number_full(chan, num, argv[3], chan->language, argc > 4 ? argv[4] : NULL, agi->audio, agi->ctrl);
01059 if (res == 1)
01060 return RESULT_SUCCESS;
01061 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01062 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01063 }
01064
01065 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01066 {
01067 int res, num;
01068
01069 if (argc != 4)
01070 return RESULT_SHOWUSAGE;
01071 if (sscanf(argv[2], "%30d", &num) != 1)
01072 return RESULT_SHOWUSAGE;
01073
01074 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
01075 if (res == 1)
01076 return RESULT_SUCCESS;
01077 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01078 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01079 }
01080
01081 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01082 {
01083 int res;
01084
01085 if (argc != 4)
01086 return RESULT_SHOWUSAGE;
01087
01088 res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
01089 if (res == 1)
01090 return RESULT_SUCCESS;
01091 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01092 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01093 }
01094
01095 static int handle_saydate(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01096 {
01097 int res, num;
01098
01099 if (argc != 4)
01100 return RESULT_SHOWUSAGE;
01101 if (sscanf(argv[2], "%30d", &num) != 1)
01102 return RESULT_SHOWUSAGE;
01103 res = ast_say_date(chan, num, argv[3], chan->language);
01104 if (res == 1)
01105 return RESULT_SUCCESS;
01106 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01107 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01108 }
01109
01110 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01111 {
01112 int res, num;
01113
01114 if (argc != 4)
01115 return RESULT_SHOWUSAGE;
01116 if (sscanf(argv[2], "%30d", &num) != 1)
01117 return RESULT_SHOWUSAGE;
01118 res = ast_say_time(chan, num, argv[3], chan->language);
01119 if (res == 1)
01120 return RESULT_SUCCESS;
01121 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01122 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01123 }
01124
01125 static int handle_saydatetime(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01126 {
01127 int res = 0;
01128 time_t unixtime;
01129 char *format, *zone = NULL;
01130
01131 if (argc < 4)
01132 return RESULT_SHOWUSAGE;
01133
01134 if (argc > 4) {
01135 format = argv[4];
01136 } else {
01137
01138 if (!strcasecmp(chan->language, "de")) {
01139 format = "A dBY HMS";
01140 } else {
01141 format = "ABdY 'digits/at' IMp";
01142 }
01143 }
01144
01145 if (argc > 5 && !ast_strlen_zero(argv[5]))
01146 zone = argv[5];
01147
01148 if (ast_get_time_t(argv[2], &unixtime, 0, NULL))
01149 return RESULT_SHOWUSAGE;
01150
01151 res = ast_say_date_with_format(chan, unixtime, argv[3], chan->language, format, zone);
01152 if (res == 1)
01153 return RESULT_SUCCESS;
01154
01155 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01156 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01157 }
01158
01159 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01160 {
01161 int res;
01162
01163 if (argc != 4)
01164 return RESULT_SHOWUSAGE;
01165
01166 res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl);
01167 if (res == 1)
01168 return RESULT_SUCCESS;
01169 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01170 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
01171 }
01172
01173 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01174 {
01175 int res, max, timeout;
01176 char data[1024];
01177
01178 if (argc < 3)
01179 return RESULT_SHOWUSAGE;
01180 if (argc >= 4)
01181 timeout = atoi(argv[3]);
01182 else
01183 timeout = 0;
01184 if (argc >= 5)
01185 max = atoi(argv[4]);
01186 else
01187 max = 1024;
01188 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl);
01189 if (res == 2)
01190 return RESULT_SUCCESS;
01191 else if (res == 1)
01192 ast_agi_send(agi->fd, chan, "200 result=%s (timeout)\n", data);
01193 else if (res < 0 )
01194 ast_agi_send(agi->fd, chan, "200 result=-1\n");
01195 else
01196 ast_agi_send(agi->fd, chan, "200 result=%s\n", data);
01197 return RESULT_SUCCESS;
01198 }
01199
01200 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01201 {
01202
01203 if (argc != 3)
01204 return RESULT_SHOWUSAGE;
01205 ast_copy_string(chan->context, argv[2], sizeof(chan->context));
01206 ast_agi_send(agi->fd, chan, "200 result=0\n");
01207 return RESULT_SUCCESS;
01208 }
01209
01210 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01211 {
01212 if (argc != 3)
01213 return RESULT_SHOWUSAGE;
01214 ast_copy_string(chan->exten, argv[2], sizeof(chan->exten));
01215 ast_agi_send(agi->fd, chan, "200 result=0\n");
01216 return RESULT_SUCCESS;
01217 }
01218
01219 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01220 {
01221 int pri;
01222
01223 if (argc != 3)
01224 return RESULT_SHOWUSAGE;
01225
01226 if (sscanf(argv[2], "%30d", &pri) != 1) {
01227 if ((pri = ast_findlabel_extension(chan, chan->context, chan->exten, argv[2], chan->cid.cid_num)) < 1)
01228 return RESULT_SHOWUSAGE;
01229 }
01230
01231 ast_explicit_goto(chan, NULL, NULL, pri);
01232 ast_agi_send(agi->fd, chan, "200 result=0\n");
01233 return RESULT_SUCCESS;
01234 }
01235
01236 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01237 {
01238 struct ast_filestream *fs;
01239 struct ast_frame *f;
01240 struct timeval start;
01241 long sample_offset = 0;
01242 int res = 0;
01243 int ms;
01244
01245 struct ast_dsp *sildet=NULL;
01246 int totalsilence = 0;
01247 int dspsilence = 0;
01248 int silence = 0;
01249 int gotsilence = 0;
01250 char *silencestr = NULL;
01251 int rfmt = 0;
01252
01253
01254
01255 if (argc < 6)
01256 return RESULT_SHOWUSAGE;
01257 if (sscanf(argv[5], "%30d", &ms) != 1)
01258 return RESULT_SHOWUSAGE;
01259
01260 if (argc > 6)
01261 silencestr = strchr(argv[6],'s');
01262 if ((argc > 7) && (!silencestr))
01263 silencestr = strchr(argv[7],'s');
01264 if ((argc > 8) && (!silencestr))
01265 silencestr = strchr(argv[8],'s');
01266
01267 if (silencestr) {
01268 if (strlen(silencestr) > 2) {
01269 if ((silencestr[0] == 's') && (silencestr[1] == '=')) {
01270 silencestr++;
01271 silencestr++;
01272 if (silencestr)
01273 silence = atoi(silencestr);
01274 if (silence > 0)
01275 silence *= 1000;
01276 }
01277 }
01278 }
01279
01280 if (silence > 0) {
01281 rfmt = chan->readformat;
01282 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
01283 if (res < 0) {
01284 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
01285 return -1;
01286 }
01287 sildet = ast_dsp_new();
01288 if (!sildet) {
01289 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
01290 return -1;
01291 }
01292 ast_dsp_set_threshold(sildet, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
01293 }
01294
01295
01296
01297
01298 if ((argc >6) && (sscanf(argv[6], "%30ld", &sample_offset) != 1) && (!strchr(argv[6], '=')))
01299 res = ast_streamfile(chan, "beep", chan->language);
01300
01301 if ((argc > 7) && (!strchr(argv[7], '=')))
01302 res = ast_streamfile(chan, "beep", chan->language);
01303
01304 if (!res)
01305 res = ast_waitstream(chan, argv[4]);
01306 if (res) {
01307 ast_agi_send(agi->fd, chan, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
01308 } else {
01309 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, AST_FILE_MODE);
01310 if (!fs) {
01311 res = -1;
01312 ast_agi_send(agi->fd, chan, "200 result=%d (writefile)\n", res);
01313 if (sildet)
01314 ast_dsp_free(sildet);
01315 return RESULT_FAILURE;
01316 }
01317
01318
01319 ast_indicate(chan, AST_CONTROL_VIDUPDATE);
01320
01321 chan->stream = fs;
01322 ast_applystream(chan,fs);
01323
01324 ast_seekstream(fs, sample_offset, SEEK_SET);
01325 ast_truncstream(fs);
01326
01327 start = ast_tvnow();
01328 while ((ms < 0) || ast_tvdiff_ms(ast_tvnow(), start) < ms) {
01329 res = ast_waitfor(chan, ms - ast_tvdiff_ms(ast_tvnow(), start));
01330 if (res < 0) {
01331 ast_closestream(fs);
01332 ast_agi_send(agi->fd, chan, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
01333 if (sildet)
01334 ast_dsp_free(sildet);
01335 return RESULT_FAILURE;
01336 }
01337 f = ast_read(chan);
01338 if (!f) {
01339 ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset);
01340 ast_closestream(fs);
01341 if (sildet)
01342 ast_dsp_free(sildet);
01343 return RESULT_FAILURE;
01344 }
01345 switch(f->frametype) {
01346 case AST_FRAME_DTMF:
01347 if (strchr(argv[4], f->subclass)) {
01348
01349
01350
01351 ast_stream_rewind(fs, 200);
01352 ast_truncstream(fs);
01353 sample_offset = ast_tellstream(fs);
01354 ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
01355 ast_closestream(fs);
01356 ast_frfree(f);
01357 if (sildet)
01358 ast_dsp_free(sildet);
01359 return RESULT_SUCCESS;
01360 }
01361 break;
01362 case AST_FRAME_VOICE:
01363 ast_writestream(fs, f);
01364
01365
01366
01367 sample_offset = ast_tellstream(fs);
01368 if (silence > 0) {
01369 dspsilence = 0;
01370 ast_dsp_silence(sildet, f, &dspsilence);
01371 if (dspsilence) {
01372 totalsilence = dspsilence;
01373 } else {
01374 totalsilence = 0;
01375 }
01376 if (totalsilence > silence) {
01377
01378 gotsilence = 1;
01379 break;
01380 }
01381 }
01382 break;
01383 case AST_FRAME_VIDEO:
01384 ast_writestream(fs, f);
01385 default:
01386
01387 break;
01388 }
01389 ast_frfree(f);
01390 if (gotsilence)
01391 break;
01392 }
01393
01394 if (gotsilence) {
01395 ast_stream_rewind(fs, silence-1000);
01396 ast_truncstream(fs);
01397 sample_offset = ast_tellstream(fs);
01398 }
01399 ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
01400 ast_closestream(fs);
01401 }
01402
01403 if (silence > 0) {
01404 res = ast_set_read_format(chan, rfmt);
01405 if (res)
01406 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
01407 ast_dsp_free(sildet);
01408 }
01409
01410 return RESULT_SUCCESS;
01411 }
01412
01413 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01414 {
01415 double timeout;
01416 struct timeval whentohangup = { 0, 0 };
01417
01418 if (argc != 3)
01419 return RESULT_SHOWUSAGE;
01420 if (sscanf(argv[2], "%30lf", &timeout) != 1)
01421 return RESULT_SHOWUSAGE;
01422 if (timeout < 0)
01423 timeout = 0;
01424 if (timeout) {
01425 whentohangup.tv_sec = timeout;
01426 whentohangup.tv_usec = (timeout - whentohangup.tv_sec) * 1000000.0;
01427 }
01428 ast_channel_setwhentohangup_tv(chan, whentohangup);
01429 ast_agi_send(agi->fd, chan, "200 result=0\n");
01430 return RESULT_SUCCESS;
01431 }
01432
01433 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01434 {
01435 struct ast_channel *c;
01436
01437 if (argc == 1) {
01438
01439 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
01440 ast_agi_send(agi->fd, chan, "200 result=1\n");
01441 return RESULT_SUCCESS;
01442 } else if (argc == 2) {
01443
01444 c = ast_get_channel_by_name_locked(argv[1]);
01445 if (c) {
01446
01447 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
01448 ast_agi_send(agi->fd, chan, "200 result=1\n");
01449 ast_channel_unlock(c);
01450 return RESULT_SUCCESS;
01451 }
01452
01453 ast_agi_send(agi->fd, chan, "200 result=-1\n");
01454 return RESULT_SUCCESS;
01455 } else {
01456 return RESULT_SHOWUSAGE;
01457 }
01458 }
01459
01460 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01461 {
01462 int res;
01463 struct ast_app *app_to_exec;
01464
01465 if (argc < 2)
01466 return RESULT_SHOWUSAGE;
01467
01468 ast_verb(3, "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]);
01469
01470 if ((app_to_exec = pbx_findapp(argv[1]))) {
01471 if(!strcasecmp(argv[1], PARK_APP_NAME)) {
01472 ast_masq_park_call(chan, NULL, 0, NULL);
01473 }
01474 if (ast_compat_res_agi && !ast_strlen_zero(argv[2])) {
01475 char *compat = alloca(strlen(argv[2]) * 2 + 1), *cptr, *vptr;
01476 for (cptr = compat, vptr = argv[2]; *vptr; vptr++) {
01477 if (*vptr == ',') {
01478 *cptr++ = '\\';
01479 *cptr++ = ',';
01480 } else if (*vptr == '|') {
01481 *cptr++ = ',';
01482 } else {
01483 *cptr++ = *vptr;
01484 }
01485 }
01486 *cptr = '\0';
01487 res = pbx_exec(chan, app_to_exec, compat);
01488 } else {
01489 res = pbx_exec(chan, app_to_exec, argv[2]);
01490 }
01491 } else {
01492 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
01493 res = -2;
01494 }
01495 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01496
01497
01498 return res;
01499 }
01500
01501 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01502 {
01503 char tmp[256]="";
01504 char *l = NULL, *n = NULL;
01505
01506 if (argv[2]) {
01507 ast_copy_string(tmp, argv[2], sizeof(tmp));
01508 ast_callerid_parse(tmp, &n, &l);
01509 if (l)
01510 ast_shrink_phone_number(l);
01511 else
01512 l = "";
01513 if (!n)
01514 n = "";
01515 ast_set_callerid(chan, l, n, NULL);
01516 }
01517
01518 ast_agi_send(agi->fd, chan, "200 result=1\n");
01519 return RESULT_SUCCESS;
01520 }
01521
01522 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01523 {
01524 struct ast_channel *c;
01525 if (argc == 2) {
01526
01527 ast_agi_send(agi->fd, chan, "200 result=%d\n", chan->_state);
01528 return RESULT_SUCCESS;
01529 } else if (argc == 3) {
01530
01531 c = ast_get_channel_by_name_locked(argv[2]);
01532 if (c) {
01533 ast_agi_send(agi->fd, chan, "200 result=%d\n", c->_state);
01534 ast_channel_unlock(c);
01535 return RESULT_SUCCESS;
01536 }
01537
01538 ast_agi_send(agi->fd, chan, "200 result=-1\n");
01539 return RESULT_SUCCESS;
01540 } else {
01541 return RESULT_SHOWUSAGE;
01542 }
01543 }
01544
01545 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01546 {
01547 if (argv[3])
01548 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
01549
01550 ast_agi_send(agi->fd, chan, "200 result=1\n");
01551 return RESULT_SUCCESS;
01552 }
01553
01554 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01555 {
01556 char *ret;
01557 char tempstr[1024];
01558
01559 if (argc != 3)
01560 return RESULT_SHOWUSAGE;
01561
01562
01563 if (!ast_strlen_zero(argv[2]) && (argv[2][strlen(argv[2]) - 1] == ')')) {
01564 ret = ast_func_read(chan, argv[2], tempstr, sizeof(tempstr)) ? NULL : tempstr;
01565 } else {
01566 pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
01567 }
01568
01569 if (ret)
01570 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", ret);
01571 else
01572 ast_agi_send(agi->fd, chan, "200 result=0\n");
01573
01574 return RESULT_SUCCESS;
01575 }
01576
01577 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01578 {
01579 char tmp[4096];
01580 struct ast_channel *chan2=NULL;
01581
01582 if ((argc != 4) && (argc != 5))
01583 return RESULT_SHOWUSAGE;
01584 if (argc == 5) {
01585 chan2 = ast_get_channel_by_name_locked(argv[4]);
01586 } else {
01587 chan2 = chan;
01588 }
01589 if (chan2) {
01590 pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
01591 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", tmp);
01592 } else {
01593 ast_agi_send(agi->fd, chan, "200 result=0\n");
01594 }
01595 if (chan2 && (chan2 != chan))
01596 ast_channel_unlock(chan2);
01597 return RESULT_SUCCESS;
01598 }
01599
01600 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01601 {
01602 int level = 0;
01603
01604 if (argc < 2)
01605 return RESULT_SHOWUSAGE;
01606
01607 if (argv[2])
01608 sscanf(argv[2], "%30d", &level);
01609
01610 ast_verb(level, "%s: %s\n", chan->data, argv[1]);
01611
01612 ast_agi_send(agi->fd, chan, "200 result=1\n");
01613
01614 return RESULT_SUCCESS;
01615 }
01616
01617 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01618 {
01619 int res;
01620 struct ast_str *buf;
01621
01622 if (argc != 4)
01623 return RESULT_SHOWUSAGE;
01624
01625 if (!(buf = ast_str_create(16))) {
01626 ast_agi_send(agi->fd, chan, "200 result=-1\n");
01627 return RESULT_SUCCESS;
01628 }
01629
01630 do {
01631 res = ast_db_get(argv[2], argv[3], buf->str, buf->len);
01632 buf->used = strlen(buf->str);
01633 if (buf->used < buf->len - 1) {
01634 break;
01635 }
01636 if (ast_str_make_space(&buf, buf->len * 2)) {
01637 break;
01638 }
01639 } while (1);
01640
01641 if (res)
01642 ast_agi_send(agi->fd, chan, "200 result=0\n");
01643 else
01644 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", buf->str);
01645
01646 ast_free(buf);
01647 return RESULT_SUCCESS;
01648 }
01649
01650 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01651 {
01652 int res;
01653
01654 if (argc != 5)
01655 return RESULT_SHOWUSAGE;
01656 res = ast_db_put(argv[2], argv[3], argv[4]);
01657 ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01658 return RESULT_SUCCESS;
01659 }
01660
01661 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01662 {
01663 int res;
01664
01665 if (argc != 4)
01666 return RESULT_SHOWUSAGE;
01667 res = ast_db_del(argv[2], argv[3]);
01668 ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01669 return RESULT_SUCCESS;
01670 }
01671
01672 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01673 {
01674 int res;
01675
01676 if ((argc < 3) || (argc > 4))
01677 return RESULT_SHOWUSAGE;
01678 if (argc == 4)
01679 res = ast_db_deltree(argv[2], argv[3]);
01680 else
01681 res = ast_db_deltree(argv[2], NULL);
01682
01683 ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01684 return RESULT_SUCCESS;
01685 }
01686
01687 static char *handle_cli_agi_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01688 {
01689 switch (cmd) {
01690 case CLI_INIT:
01691 e->command = "agi set debug [on|off]";
01692 e->usage =
01693 "Usage: agi set debug [on|off]\n"
01694 " Enables/disables dumping of AGI transactions for\n"
01695 " debugging purposes.\n";
01696 return NULL;
01697
01698 case CLI_GENERATE:
01699 return NULL;
01700 }
01701
01702 if (a->argc != e->args)
01703 return CLI_SHOWUSAGE;
01704
01705 if (strncasecmp(a->argv[3], "off", 3) == 0) {
01706 agidebug = 0;
01707 } else if (strncasecmp(a->argv[3], "on", 2) == 0) {
01708 agidebug = 1;
01709 } else {
01710 return CLI_SHOWUSAGE;
01711 }
01712 ast_cli(a->fd, "AGI Debugging %sabled\n", agidebug ? "En" : "Dis");
01713 return CLI_SUCCESS;
01714 }
01715
01716 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
01717 {
01718 ast_agi_send(agi->fd, chan, "200 result=0\n");
01719 return RESULT_SUCCESS;
01720 }
01721
01722 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01723 {
01724 if (!strncasecmp(argv[2], "on", 2))
01725 ast_moh_start(chan, argc > 3 ? argv[3] : NULL, NULL);
01726 else if (!strncasecmp(argv[2], "off", 3))
01727 ast_moh_stop(chan);
01728 ast_agi_send(agi->fd, chan, "200 result=0\n");
01729 return RESULT_SUCCESS;
01730 }
01731
01732 static int handle_speechcreate(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01733 {
01734
01735 if (agi->speech) {
01736 ast_agi_send(agi->fd, chan, "200 result=0\n");
01737 return RESULT_SUCCESS;
01738 }
01739
01740 if ((agi->speech = ast_speech_new(argv[2], AST_FORMAT_SLINEAR)))
01741 ast_agi_send(agi->fd, chan, "200 result=1\n");
01742 else
01743 ast_agi_send(agi->fd, chan, "200 result=0\n");
01744
01745 return RESULT_SUCCESS;
01746 }
01747
01748 static int handle_speechset(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01749 {
01750
01751 if (argc != 3)
01752 return RESULT_SHOWUSAGE;
01753
01754
01755 if (!agi->speech) {
01756 ast_agi_send(agi->fd, chan, "200 result=0\n");
01757 return RESULT_SUCCESS;
01758 }
01759
01760 ast_speech_change(agi->speech, argv[2], argv[3]);
01761 ast_agi_send(agi->fd, chan, "200 result=1\n");
01762
01763 return RESULT_SUCCESS;
01764 }
01765
01766 static int handle_speechdestroy(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01767 {
01768 if (agi->speech) {
01769 ast_speech_destroy(agi->speech);
01770 agi->speech = NULL;
01771 ast_agi_send(agi->fd, chan, "200 result=1\n");
01772 } else {
01773 ast_agi_send(agi->fd, chan, "200 result=0\n");
01774 }
01775
01776 return RESULT_SUCCESS;
01777 }
01778
01779 static int handle_speechloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01780 {
01781 if (argc != 5)
01782 return RESULT_SHOWUSAGE;
01783
01784 if (!agi->speech) {
01785 ast_agi_send(agi->fd, chan, "200 result=0\n");
01786 return RESULT_SUCCESS;
01787 }
01788
01789 if (ast_speech_grammar_load(agi->speech, argv[3], argv[4]))
01790 ast_agi_send(agi->fd, chan, "200 result=0\n");
01791 else
01792 ast_agi_send(agi->fd, chan, "200 result=1\n");
01793
01794 return RESULT_SUCCESS;
01795 }
01796
01797 static int handle_speechunloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01798 {
01799 if (argc != 4)
01800 return RESULT_SHOWUSAGE;
01801
01802 if (!agi->speech) {
01803 ast_agi_send(agi->fd, chan, "200 result=0\n");
01804 return RESULT_SUCCESS;
01805 }
01806
01807 if (ast_speech_grammar_unload(agi->speech, argv[3]))
01808 ast_agi_send(agi->fd, chan, "200 result=0\n");
01809 else
01810 ast_agi_send(agi->fd, chan, "200 result=1\n");
01811
01812 return RESULT_SUCCESS;
01813 }
01814
01815 static int handle_speechactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01816 {
01817 if (argc != 4)
01818 return RESULT_SHOWUSAGE;
01819
01820 if (!agi->speech) {
01821 ast_agi_send(agi->fd, chan, "200 result=0\n");
01822 return RESULT_SUCCESS;
01823 }
01824
01825 if (ast_speech_grammar_activate(agi->speech, argv[3]))
01826 ast_agi_send(agi->fd, chan, "200 result=0\n");
01827 else
01828 ast_agi_send(agi->fd, chan, "200 result=1\n");
01829
01830 return RESULT_SUCCESS;
01831 }
01832
01833 static int handle_speechdeactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01834 {
01835 if (argc != 4)
01836 return RESULT_SHOWUSAGE;
01837
01838 if (!agi->speech) {
01839 ast_agi_send(agi->fd, chan, "200 result=0\n");
01840 return RESULT_SUCCESS;
01841 }
01842
01843 if (ast_speech_grammar_deactivate(agi->speech, argv[3]))
01844 ast_agi_send(agi->fd, chan, "200 result=0\n");
01845 else
01846 ast_agi_send(agi->fd, chan, "200 result=1\n");
01847
01848 return RESULT_SUCCESS;
01849 }
01850
01851 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang, int offset)
01852 {
01853 struct ast_filestream *fs = NULL;
01854
01855 if (!(fs = ast_openstream(chan, filename, preflang)))
01856 return -1;
01857
01858 if (offset)
01859 ast_seekstream(fs, offset, SEEK_SET);
01860
01861 if (ast_applystream(chan, fs))
01862 return -1;
01863
01864 if (ast_playstream(fs))
01865 return -1;
01866
01867 return 0;
01868 }
01869
01870 static int handle_speechrecognize(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01871 {
01872 struct ast_speech *speech = agi->speech;
01873 char *prompt, dtmf = 0, tmp[4096] = "", *buf = tmp;
01874 int timeout = 0, offset = 0, old_read_format = 0, res = 0, i = 0;
01875 long current_offset = 0;
01876 const char *reason = NULL;
01877 struct ast_frame *fr = NULL;
01878 struct ast_speech_result *result = NULL;
01879 size_t left = sizeof(tmp);
01880 time_t start = 0, current;
01881
01882 if (argc < 4)
01883 return RESULT_SHOWUSAGE;
01884
01885 if (!speech) {
01886 ast_agi_send(agi->fd, chan, "200 result=0\n");
01887 return RESULT_SUCCESS;
01888 }
01889
01890 prompt = argv[2];
01891 timeout = atoi(argv[3]);
01892
01893
01894 if (argc == 5)
01895 offset = atoi(argv[4]);
01896
01897
01898 old_read_format = chan->readformat;
01899 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
01900 ast_agi_send(agi->fd, chan, "200 result=0\n");
01901 return RESULT_SUCCESS;
01902 }
01903
01904
01905 if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
01906 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
01907 ast_speech_start(speech);
01908 }
01909
01910
01911 speech_streamfile(chan, prompt, chan->language, offset);
01912
01913
01914 while (ast_strlen_zero(reason)) {
01915
01916 ast_sched_runq(chan->sched);
01917
01918
01919 if ((res = ast_sched_wait(chan->sched)) < 0)
01920 res = 1000;
01921
01922
01923 if (ast_waitfor(chan, res) > 0) {
01924 if (!(fr = ast_read(chan))) {
01925 reason = "hangup";
01926 break;
01927 }
01928 }
01929
01930
01931 if ((timeout > 0) && (start > 0)) {
01932 time(¤t);
01933 if ((current - start) >= timeout) {
01934 reason = "timeout";
01935 if (fr)
01936 ast_frfree(fr);
01937 break;
01938 }
01939 }
01940
01941
01942 ast_mutex_lock(&speech->lock);
01943
01944
01945 if (ast_test_flag(speech, AST_SPEECH_QUIET) && chan->stream) {
01946 current_offset = ast_tellstream(chan->stream);
01947 ast_stopstream(chan);
01948 ast_clear_flag(speech, AST_SPEECH_QUIET);
01949 }
01950
01951
01952 switch (speech->state) {
01953 case AST_SPEECH_STATE_READY:
01954
01955 if ((timeout > 0) && ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL))) {
01956 ast_stopstream(chan);
01957 time(&start);
01958 }
01959
01960 if (fr && fr->frametype == AST_FRAME_VOICE)
01961 ast_speech_write(speech, fr->data.ptr, fr->datalen);
01962 break;
01963 case AST_SPEECH_STATE_WAIT:
01964
01965 if ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL)) {
01966 ast_stopstream(chan);
01967
01968 if (!ast_strlen_zero(speech->processing_sound) && strcasecmp(speech->processing_sound, "none"))
01969 speech_streamfile(chan, speech->processing_sound, chan->language, 0);
01970 }
01971 break;
01972 case AST_SPEECH_STATE_DONE:
01973
01974 speech->results = ast_speech_results_get(speech);
01975
01976 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
01977 reason = "speech";
01978 break;
01979 default:
01980 break;
01981 }
01982 ast_mutex_unlock(&speech->lock);
01983
01984
01985 if (fr) {
01986 if (fr->frametype == AST_FRAME_DTMF) {
01987 reason = "dtmf";
01988 dtmf = fr->subclass;
01989 } else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass == AST_CONTROL_HANGUP) {
01990 reason = "hangup";
01991 }
01992 ast_frfree(fr);
01993 }
01994 }
01995
01996 if (!strcasecmp(reason, "speech")) {
01997
01998 for (result = speech->results; result; result = AST_LIST_NEXT(result, list)) {
01999
02000 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);
02001
02002 i++;
02003 }
02004
02005 ast_agi_send(agi->fd, chan, "200 result=1 (speech) endpos=%ld results=%d %s\n", current_offset, i, tmp);
02006 } else if (!strcasecmp(reason, "dtmf")) {
02007 ast_agi_send(agi->fd, chan, "200 result=1 (digit) digit=%c endpos=%ld\n", dtmf, current_offset);
02008 } else if (!strcasecmp(reason, "hangup") || !strcasecmp(reason, "timeout")) {
02009 ast_agi_send(agi->fd, chan, "200 result=1 (%s) endpos=%ld\n", reason, current_offset);
02010 } else {
02011 ast_agi_send(agi->fd, chan, "200 result=0 endpos=%ld\n", current_offset);
02012 }
02013
02014 return RESULT_SUCCESS;
02015 }
02016
02017 static char usage_setmusic[] =
02018 " Usage: SET MUSIC ON <on|off> <class>\n"
02019 " Enables/Disables the music on hold generator. If <class> is\n"
02020 " not specified, then the default music on hold class will be used.\n"
02021 " Always returns 0.\n";
02022
02023 static char usage_dbput[] =
02024 " Usage: DATABASE PUT <family> <key> <value>\n"
02025 " Adds or updates an entry in the Asterisk database for a\n"
02026 " given family, key, and value.\n"
02027 " Returns 1 if successful, 0 otherwise.\n";
02028
02029 static char usage_dbget[] =
02030 " Usage: DATABASE GET <family> <key>\n"
02031 " Retrieves an entry in the Asterisk database for a\n"
02032 " given family and key.\n"
02033 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
02034 " is set and returns the variable in parentheses.\n"
02035 " Example return code: 200 result=1 (testvariable)\n";
02036
02037 static char usage_dbdel[] =
02038 " Usage: DATABASE DEL <family> <key>\n"
02039 " Deletes an entry in the Asterisk database for a\n"
02040 " given family and key.\n"
02041 " Returns 1 if successful, 0 otherwise.\n";
02042
02043 static char usage_dbdeltree[] =
02044 " Usage: DATABASE DELTREE <family> [keytree]\n"
02045 " Deletes a family or specific keytree within a family\n"
02046 " in the Asterisk database.\n"
02047 " Returns 1 if successful, 0 otherwise.\n";
02048
02049 static char usage_verbose[] =
02050 " Usage: VERBOSE <message> <level>\n"
02051 " Sends <message> to the console via verbose message system.\n"
02052 " <level> is the the verbose level (1-4)\n"
02053 " Always returns 1.\n";
02054
02055 static char usage_getvariable[] =
02056 " Usage: GET VARIABLE <variablename>\n"
02057 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
02058 " is set and returns the variable in parentheses.\n"
02059 " example return code: 200 result=1 (testvariable)\n";
02060
02061 static char usage_getvariablefull[] =
02062 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
02063 " Returns 0 if <variablename> is not set or channel does not exist. Returns 1\n"
02064 "if <variablename> is set and returns the variable in parenthesis. Understands\n"
02065 "complex variable names and builtin variables, unlike GET VARIABLE.\n"
02066 " example return code: 200 result=1 (testvariable)\n";
02067
02068 static char usage_setvariable[] =
02069 " Usage: SET VARIABLE <variablename> <value>\n";
02070
02071 static char usage_channelstatus[] =
02072 " Usage: CHANNEL STATUS [<channelname>]\n"
02073 " Returns the status of the specified channel.\n"
02074 " If no channel name is given the returns the status of the\n"
02075 " current channel. Return values:\n"
02076 " 0 Channel is down and available\n"
02077 " 1 Channel is down, but reserved\n"
02078 " 2 Channel is off hook\n"
02079 " 3 Digits (or equivalent) have been dialed\n"
02080 " 4 Line is ringing\n"
02081 " 5 Remote end is ringing\n"
02082 " 6 Line is up\n"
02083 " 7 Line is busy\n";
02084
02085 static char usage_setcallerid[] =
02086 " Usage: SET CALLERID <number>\n"
02087 " Changes the callerid of the current channel.\n";
02088
02089 static char usage_exec[] =
02090 " Usage: EXEC <application> <options>\n"
02091 " Executes <application> with given <options>.\n"
02092 " Returns whatever the application returns, or -2 on failure to find application\n";
02093
02094 static char usage_hangup[] =
02095 " Usage: HANGUP [<channelname>]\n"
02096 " Hangs up the specified channel.\n"
02097 " If no channel name is given, hangs up the current channel\n";
02098
02099 static char usage_answer[] =
02100 " Usage: ANSWER\n"
02101 " Answers channel if not already in answer state. Returns -1 on\n"
02102 " channel failure, or 0 if successful.\n";
02103
02104 static char usage_waitfordigit[] =
02105 " Usage: WAIT FOR DIGIT <timeout>\n"
02106 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
02107 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
02108 " the numerical value of the ascii of the digit if one is received. Use -1\n"
02109 " for the timeout value if you desire the call to block indefinitely.\n";
02110
02111 static char usage_sendtext[] =
02112 " Usage: SEND TEXT \"<text to send>\"\n"
02113 " Sends the given text on a channel. Most channels do not support the\n"
02114 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
02115 " support text transmission. Returns -1 only on error/hangup. Text\n"
02116 " consisting of greater than one word should be placed in quotes since the\n"
02117 " command only accepts a single argument.\n";
02118
02119 static char usage_recvchar[] =
02120 " Usage: RECEIVE CHAR <timeout>\n"
02121 " Receives a character of text on a channel. Specify timeout to be the\n"
02122 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
02123 " do not support the reception of text. Returns the decimal value of the character\n"
02124 " if one is received, or 0 if the channel does not support text reception. Returns\n"
02125 " -1 only on error/hangup.\n";
02126
02127 static char usage_recvtext[] =
02128 " Usage: RECEIVE TEXT <timeout>\n"
02129 " Receives a string of text on a channel. Specify timeout to be the\n"
02130 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
02131 " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n";
02132
02133 static char usage_tddmode[] =
02134 " Usage: TDD MODE <on|off>\n"
02135 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
02136 " successful, or 0 if channel is not TDD-capable.\n";
02137
02138 static char usage_sendimage[] =
02139 " Usage: SEND IMAGE <image>\n"
02140 " Sends the given image on a channel. Most channels do not support the\n"
02141 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
02142 " support image transmission. Returns -1 only on error/hangup. Image names\n"
02143 " should not include extensions.\n";
02144
02145 static char usage_streamfile[] =
02146 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
02147 " Send the given file, allowing playback to be interrupted by the given\n"
02148 " digits, if any. Use double quotes for the digits if you wish none to be\n"
02149 " permitted. If sample offset is provided then the audio will seek to sample\n"
02150 " offset before play starts. Returns 0 if playback completes without a digit\n"
02151 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
02152 " or -1 on error or if the channel was disconnected. Remember, the file\n"
02153 " extension must not be included in the filename.\n";
02154
02155 static char usage_controlstreamfile[] =
02156 " Usage: CONTROL STREAM FILE <filename> <escape digits> [skipms] [ffchar] [rewchr] [pausechr]\n"
02157 " Send the given file, allowing playback to be controled by the given\n"
02158 " digits, if any. Use double quotes for the digits if you wish none to be\n"
02159 " permitted. Returns 0 if playback completes without a digit\n"
02160 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
02161 " or -1 on error or if the channel was disconnected. Remember, the file\n"
02162 " extension must not be included in the filename.\n\n"
02163 " Note: ffchar and rewchar default to * and # respectively.\n";
02164
02165 static char usage_getoption[] =
02166 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
02167 " Behaves similar to STREAM FILE but used with a timeout option.\n";
02168
02169 static char usage_saynumber[] =
02170 " Usage: SAY NUMBER <number> <escape digits> [gender]\n"
02171 " Say a given number, returning early if any of the given DTMF digits\n"
02172 " are received on the channel. Returns 0 if playback completes without a digit\n"
02173 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02174 " -1 on error/hangup.\n";
02175
02176 static char usage_saydigits[] =
02177 " Usage: SAY DIGITS <number> <escape digits>\n"
02178 " Say a given digit string, returning early if any of the given DTMF digits\n"
02179 " are received on the channel. Returns 0 if playback completes without a digit\n"
02180 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02181 " -1 on error/hangup.\n";
02182
02183 static char usage_sayalpha[] =
02184 " Usage: SAY ALPHA <number> <escape digits>\n"
02185 " Say a given character string, returning early if any of the given DTMF digits\n"
02186 " are received on the channel. Returns 0 if playback completes without a digit\n"
02187 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02188 " -1 on error/hangup.\n";
02189
02190 static char usage_saydate[] =
02191 " Usage: SAY DATE <date> <escape digits>\n"
02192 " Say a given date, returning early if any of the given DTMF digits are\n"
02193 " received on the channel. <date> is number of seconds elapsed since 00:00:00\n"
02194 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
02195 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02196 " digit if one was pressed or -1 on error/hangup.\n";
02197
02198 static char usage_saytime[] =
02199 " Usage: SAY TIME <time> <escape digits>\n"
02200 " Say a given time, returning early if any of the given DTMF digits are\n"
02201 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
02202 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
02203 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02204 " digit if one was pressed or -1 on error/hangup.\n";
02205
02206 static char usage_saydatetime[] =
02207 " Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n"
02208 " Say a given time, returning early if any of the given DTMF digits are\n"
02209 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
02210 " on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n"
02211 " the time should be said in. See voicemail.conf (defaults to \"ABdY\n"
02212 " 'digits/at' IMp\"). Acceptable values for [timezone] can be found in\n"
02213 " /usr/share/zoneinfo. Defaults to machine default. Returns 0 if playback\n"
02214 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02215 " digit if one was pressed or -1 on error/hangup.\n";
02216
02217 static char usage_sayphonetic[] =
02218 " Usage: SAY PHONETIC <string> <escape digits>\n"
02219 " Say a given character string with phonetics, returning early if any of the\n"
02220 " given DTMF digits are received on the channel. Returns 0 if playback\n"
02221 " completes without a digit pressed, the ASCII numerical value of the digit\n"
02222 " if one was pressed, or -1 on error/hangup.\n";
02223
02224 static char usage_getdata[] =
02225 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
02226 " Stream the given file, and receive DTMF data. Returns the digits received\n"
02227 "from the channel at the other end.\n";
02228
02229 static char usage_setcontext[] =
02230 " Usage: SET CONTEXT <desired context>\n"
02231 " Sets the context for continuation upon exiting the application.\n";
02232
02233 static char usage_setextension[] =
02234 " Usage: SET EXTENSION <new extension>\n"
02235 " Changes the extension for continuation upon exiting the application.\n";
02236
02237 static char usage_setpriority[] =
02238 " Usage: SET PRIORITY <priority>\n"
02239 " Changes the priority for continuation upon exiting the application.\n"
02240 " The priority must be a valid priority or label.\n";
02241
02242 static char usage_recordfile[] =
02243 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
02244 " [offset samples] [BEEP] [s=silence]\n"
02245 " Record to a file until a given dtmf digit in the sequence is received\n"
02246 " Returns -1 on hangup or error. The format will specify what kind of file\n"
02247 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
02248 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
02249 " to the offset without exceeding the end of the file. \"silence\" is the number\n"
02250 " of seconds of silence allowed before the function returns despite the\n"
02251 " lack of dtmf digits or reaching timeout. Silence value must be\n"
02252 " preceeded by \"s=\" and is also optional.\n";
02253
02254 static char usage_autohangup[] =
02255 " Usage: SET AUTOHANGUP <time>\n"
02256 " Cause the channel to automatically hangup at <time> seconds in the\n"
02257 " future. Of course it can be hungup before then as well. Setting to 0 will\n"
02258 " cause the autohangup feature to be disabled on this channel.\n";
02259
02260 static char usage_noop[] =
02261 " Usage: NoOp\n"
02262 " Does nothing.\n";
02263
02264 static char usage_speechcreate[] =
02265 " Usage: SPEECH CREATE <engine>\n"
02266 " Create a speech object to be used by the other Speech AGI commands.\n";
02267
02268 static char usage_speechset[] =
02269 " Usage: SPEECH SET <name> <value>\n"
02270 " Set an engine-specific setting.\n";
02271
02272 static char usage_speechdestroy[] =
02273 " Usage: SPEECH DESTROY\n"
02274 " Destroy the speech object created by SPEECH CREATE.\n";
02275
02276 static char usage_speechloadgrammar[] =
02277 " Usage: SPEECH LOAD GRAMMAR <grammar name> <path to grammar>\n"
02278 " Loads the specified grammar as the specified name.\n";
02279
02280 static char usage_speechunloadgrammar[] =
02281 " Usage: SPEECH UNLOAD GRAMMAR <grammar name>\n"
02282 " Unloads the specified grammar.\n";
02283
02284 static char usage_speechactivategrammar[] =
02285 " Usage: SPEECH ACTIVATE GRAMMAR <grammar name>\n"
02286 " Activates the specified grammar on the speech object.\n";
02287
02288 static char usage_speechdeactivategrammar[] =
02289 " Usage: SPEECH DEACTIVATE GRAMMAR <grammar name>\n"
02290 " Deactivates the specified grammar on the speech object.\n";
02291
02292 static char usage_speechrecognize[] =
02293 " Usage: SPEECH RECOGNIZE <prompt> <timeout> [<offset>]\n"
02294 " Plays back given prompt while listening for speech and dtmf.\n";
02295
02296 static char usage_asyncagi_break[] =
02297 " Usage: ASYNCAGI BREAK\n"
02298 " Returns control to the dialplan\n";
02299
02300
02301
02302
02303 static struct agi_command commands[] = {
02304 { { "answer", NULL }, handle_answer, "Answer channel", usage_answer , 0 },
02305 { { "asyncagi", "break", NULL }, handle_asyncagi_break, "Exit AsyncAGI processing", usage_asyncagi_break, 1 },
02306 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus , 0 },
02307 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel , 1 },
02308 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree , 1 },
02309 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget , 1 },
02310 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput , 1 },
02311 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec , 1 },
02312 { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata , 0 },
02313 { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull , 1 },
02314 { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption , 0 },
02315 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable , 1 },
02316 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup , 0 },
02317 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop , 1 },
02318 { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar , 0 },
02319 { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext , 0 },
02320 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile , 0 },
02321 { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha , 0 },
02322 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits , 0 },
02323 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber , 0 },
02324 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic , 0 },
02325 { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate , 0 },
02326 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime , 0 },
02327 { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime , 0 },
02328 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage , 0 },
02329 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext , 0 },
02330 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup , 0 },
02331 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid , 0 },
02332 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext , 0 },
02333 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension , 0 },
02334 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic , 0 },
02335 { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority , 0 },
02336 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable , 1 },
02337 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile , 0 },
02338 { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile , 0 },
02339 { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode , 0 },
02340 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose , 1 },
02341 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit , 0 },
02342 { { "speech", "create", NULL }, handle_speechcreate, "Creates a speech object", usage_speechcreate, 0 },
02343 { { "speech", "set", NULL }, handle_speechset, "Sets a speech engine setting", usage_speechset, 0 },
02344 { { "speech", "destroy", NULL }, handle_speechdestroy, "Destroys a speech object", usage_speechdestroy, 1 },
02345 { { "speech", "load", "grammar", NULL }, handle_speechloadgrammar, "Loads a grammar", usage_speechloadgrammar, 0 },
02346 { { "speech", "unload", "grammar", NULL }, handle_speechunloadgrammar, "Unloads a grammar", usage_speechunloadgrammar, 1 },
02347 { { "speech", "activate", "grammar", NULL }, handle_speechactivategrammar, "Activates a grammar", usage_speechactivategrammar, 0 },
02348 { { "speech", "deactivate", "grammar", NULL }, handle_speechdeactivategrammar, "Deactivates a grammar", usage_speechdeactivategrammar, 0 },
02349 { { "speech", "recognize", NULL }, handle_speechrecognize, "Recognizes speech", usage_speechrecognize, 0 },
02350 };
02351
02352 static AST_RWLIST_HEAD_STATIC(agi_commands, agi_command);
02353
02354 static char *help_workhorse(int fd, char *match[])
02355 {
02356 char fullcmd[80], matchstr[80];
02357 struct agi_command *e;
02358
02359 if (match)
02360 ast_join(matchstr, sizeof(matchstr), match);
02361
02362 ast_cli(fd, "%5.5s %30.30s %s\n","Dead","Command","Description");
02363 AST_RWLIST_RDLOCK(&agi_commands);
02364 AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
02365 if (!e->cmda[0])
02366 break;
02367
02368 if ((e->cmda[0])[0] == '_')
02369 continue;
02370 ast_join(fullcmd, sizeof(fullcmd), e->cmda);
02371 if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr)))
02372 continue;
02373 ast_cli(fd, "%5.5s %30.30s %s\n", e->dead ? "Yes" : "No" , fullcmd, e->summary);
02374 }
02375 AST_RWLIST_UNLOCK(&agi_commands);
02376
02377 return CLI_SUCCESS;
02378 }
02379
02380 int ast_agi_register(struct ast_module *mod, agi_command *cmd)
02381 {
02382 char fullcmd[80];
02383
02384 ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
02385
02386 if (!find_command(cmd->cmda,1)) {
02387 cmd->mod = mod;
02388 AST_RWLIST_WRLOCK(&agi_commands);
02389 AST_LIST_INSERT_TAIL(&agi_commands, cmd, list);
02390 AST_RWLIST_UNLOCK(&agi_commands);
02391 if (mod != ast_module_info->self)
02392 ast_module_ref(ast_module_info->self);
02393 ast_verb(2, "AGI Command '%s' registered\n",fullcmd);
02394 return 1;
02395 } else {
02396 ast_log(LOG_WARNING, "Command already registered!\n");
02397 return 0;
02398 }
02399 }
02400
02401 int ast_agi_unregister(struct ast_module *mod, agi_command *cmd)
02402 {
02403 struct agi_command *e;
02404 int unregistered = 0;
02405 char fullcmd[80];
02406
02407 ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
02408
02409 AST_RWLIST_WRLOCK(&agi_commands);
02410 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&agi_commands, e, list) {
02411 if (cmd == e) {
02412 AST_RWLIST_REMOVE_CURRENT(list);
02413 if (mod != ast_module_info->self)
02414 ast_module_unref(ast_module_info->self);
02415 unregistered=1;
02416 break;
02417 }
02418 }
02419 AST_RWLIST_TRAVERSE_SAFE_END;
02420 AST_RWLIST_UNLOCK(&agi_commands);
02421 if (unregistered)
02422 ast_verb(2, "AGI Command '%s' unregistered\n",fullcmd);
02423 else
02424 ast_log(LOG_WARNING, "Unable to unregister command: '%s'!\n",fullcmd);
02425 return unregistered;
02426 }
02427
02428 int ast_agi_register_multiple(struct ast_module *mod, struct agi_command *cmd, unsigned int len)
02429 {
02430 unsigned int i, x = 0;
02431
02432 for (i = 0; i < len; i++) {
02433 if (ast_agi_register(mod, cmd + i) == 1) {
02434 x++;
02435 continue;
02436 }
02437
02438
02439
02440
02441 for (; x > 0; x--) {
02442
02443
02444
02445
02446
02447
02448
02449
02450 (void) ast_agi_unregister(mod, cmd + x - 1);
02451 }
02452 return -1;
02453 }
02454
02455 return 0;
02456 }
02457
02458 int ast_agi_unregister_multiple(struct ast_module *mod, struct agi_command *cmd, unsigned int len)
02459 {
02460 unsigned int i;
02461 int res = 0;
02462
02463 for (i = 0; i < len; i++) {
02464
02465
02466
02467
02468 res |= ast_agi_unregister(mod, cmd + i);
02469 }
02470
02471 return res;
02472 }
02473
02474 static agi_command *find_command(char *cmds[], int exact)
02475 {
02476 int y, match;
02477 struct agi_command *e;
02478
02479 AST_RWLIST_RDLOCK(&agi_commands);
02480 AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
02481 if (!e->cmda[0])
02482 break;
02483
02484 match = 1;
02485 for (y = 0; match && cmds[y]; y++) {
02486
02487
02488
02489 if (!e->cmda[y] && !exact)
02490 break;
02491
02492 if (!e->cmda[y]) {
02493 AST_RWLIST_UNLOCK(&agi_commands);
02494 return NULL;
02495 }
02496 if (strcasecmp(e->cmda[y], cmds[y]))
02497 match = 0;
02498 }
02499
02500
02501 if ((exact > -1) && e->cmda[y])
02502 match = 0;
02503 if (match) {
02504 AST_RWLIST_UNLOCK(&agi_commands);
02505 return e;
02506 }
02507 }
02508 AST_RWLIST_UNLOCK(&agi_commands);
02509 return NULL;
02510 }
02511
02512 static int parse_args(char *s, int *max, char *argv[])
02513 {
02514 int x = 0, quoted = 0, escaped = 0, whitespace = 1;
02515 char *cur;
02516
02517 cur = s;
02518 while(*s) {
02519 switch(*s) {
02520 case '"':
02521
02522 if (escaped)
02523 goto normal;
02524 else
02525 quoted = !quoted;
02526 if (quoted && whitespace) {
02527
02528 argv[x++] = cur;
02529 whitespace=0;
02530 }
02531 escaped = 0;
02532 break;
02533 case ' ':
02534 case '\t':
02535 if (!quoted && !escaped) {
02536
02537
02538 whitespace = 1;
02539 *(cur++) = '\0';
02540 } else
02541
02542 goto normal;
02543 break;
02544 case '\\':
02545
02546 if (escaped) {
02547 goto normal;
02548 } else {
02549 escaped=1;
02550 }
02551 break;
02552 default:
02553 normal:
02554 if (whitespace) {
02555 if (x >= MAX_ARGS -1) {
02556 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
02557 break;
02558 }
02559
02560 argv[x++] = cur;
02561 whitespace=0;
02562 }
02563 *(cur++) = *s;
02564 escaped=0;
02565 }
02566 s++;
02567 }
02568
02569 *(cur++) = '\0';
02570 argv[x] = NULL;
02571 *max = x;
02572 return 0;
02573 }
02574
02575 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead)
02576 {
02577 char *argv[MAX_ARGS];
02578 int argc = MAX_ARGS, res;
02579 agi_command *c;
02580 const char *ami_res = "Unknown Result";
02581 char *ami_cmd = ast_strdupa(buf);
02582 int command_id = ast_random(), resultcode = 200;
02583
02584 manager_event(EVENT_FLAG_AGI, "AGIExec",
02585 "SubEvent: Start\r\n"
02586 "Channel: %s\r\n"
02587 "CommandId: %d\r\n"
02588 "Command: %s\r\n", chan->name, command_id, ami_cmd);
02589 parse_args(buf, &argc, argv);
02590 if ((c = find_command(argv, 0)) && (!dead || (dead && c->dead))) {
02591
02592
02593 if (c->mod != ast_module_info->self)
02594 ast_module_ref(c->mod);
02595
02596
02597 if (chan->cdr && !ast_check_hangup(chan) && strcasecmp(argv[0], "EXEC"))
02598 ast_cdr_setapp(chan->cdr, "AGI", buf);
02599
02600 res = c->handler(chan, agi, argc, argv);
02601 if (c->mod != ast_module_info->self)
02602 ast_module_unref(c->mod);
02603 switch (res) {
02604 case RESULT_SHOWUSAGE: ami_res = "Usage"; resultcode = 520; break;
02605 case RESULT_FAILURE: ami_res = "Failure"; resultcode = -1; break;
02606 case RESULT_SUCCESS: ami_res = "Success"; resultcode = 200; break;
02607 }
02608 manager_event(EVENT_FLAG_AGI, "AGIExec",
02609 "SubEvent: End\r\n"
02610 "Channel: %s\r\n"
02611 "CommandId: %d\r\n"
02612 "Command: %s\r\n"
02613 "ResultCode: %d\r\n"
02614 "Result: %s\r\n", chan->name, command_id, ami_cmd, resultcode, ami_res);
02615 switch(res) {
02616 case RESULT_SHOWUSAGE:
02617 ast_agi_send(agi->fd, chan, "520-Invalid command syntax. Proper usage follows:\n");
02618 ast_agi_send(agi->fd, chan, "%s", c->usage);
02619 ast_agi_send(agi->fd, chan, "520 End of proper usage.\n");
02620 break;
02621 case RESULT_FAILURE:
02622
02623
02624 return -1;
02625 }
02626 } else if ((c = find_command(argv, 0))) {
02627 ast_agi_send(agi->fd, chan, "511 Command Not Permitted on a dead channel\n");
02628 manager_event(EVENT_FLAG_AGI, "AGIExec",
02629 "SubEvent: End\r\n"
02630 "Channel: %s\r\n"
02631 "CommandId: %d\r\n"
02632 "Command: %s\r\n"
02633 "ResultCode: 511\r\n"
02634 "Result: Command not permitted on a dead channel\r\n", chan->name, command_id, ami_cmd);
02635 } else {
02636 ast_agi_send(agi->fd, chan, "510 Invalid or unknown command\n");
02637 manager_event(EVENT_FLAG_AGI, "AGIExec",
02638 "SubEvent: End\r\n"
02639 "Channel: %s\r\n"
02640 "CommandId: %d\r\n"
02641 "Command: %s\r\n"
02642 "ResultCode: 510\r\n"
02643 "Result: Invalid or unknown command\r\n", chan->name, command_id, ami_cmd);
02644 }
02645 return 0;
02646 }
02647 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
02648 {
02649 struct ast_channel *c;
02650 int outfd, ms, needhup = 0;
02651 enum agi_result returnstatus = AGI_RESULT_SUCCESS;
02652 struct ast_frame *f;
02653 char buf[AGI_BUF_LEN];
02654 char *res = NULL;
02655 FILE *readf;
02656
02657
02658 int retry = AGI_NANDFS_RETRY;
02659 int send_sighup;
02660 const char *sighup_str;
02661
02662 ast_channel_lock(chan);
02663 sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
02664 send_sighup = ast_strlen_zero(sighup_str) || !ast_false(sighup_str);
02665 ast_channel_unlock(chan);
02666
02667 if (!(readf = fdopen(agi->ctrl, "r"))) {
02668 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
02669 if (send_sighup && pid > -1)
02670 kill(pid, SIGHUP);
02671 close(agi->ctrl);
02672 return AGI_RESULT_FAILURE;
02673 }
02674
02675 setlinebuf(readf);
02676 setup_env(chan, request, agi->fd, (agi->audio > -1), argc, argv);
02677 for (;;) {
02678 if (needhup) {
02679 needhup = 0;
02680 dead = 1;
02681 if (send_sighup) {
02682 if (pid > -1) {
02683 kill(pid, SIGHUP);
02684 } else if (agi->fast) {
02685 send(agi->ctrl, "HANGUP\n", 7, MSG_OOB);
02686 }
02687 }
02688 }
02689 ms = -1;
02690 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
02691 if (c) {
02692 retry = AGI_NANDFS_RETRY;
02693
02694 f = ast_read(c);
02695 if (!f) {
02696 ast_debug(1, "%s hungup\n", chan->name);
02697 returnstatus = AGI_RESULT_HANGUP;
02698 needhup = 1;
02699 continue;
02700 } else {
02701
02702 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
02703
02704 if (write(agi->audio, f->data.ptr, f->datalen) < 0) {
02705 }
02706 }
02707 ast_frfree(f);
02708 }
02709 } else if (outfd > -1) {
02710 size_t len = sizeof(buf);
02711 size_t buflen = 0;
02712
02713 retry = AGI_NANDFS_RETRY;
02714 buf[0] = '\0';
02715
02716 while (buflen < (len - 1)) {
02717 res = fgets(buf + buflen, len, readf);
02718 if (feof(readf))
02719 break;
02720 if (ferror(readf) && ((errno != EINTR) && (errno != EAGAIN)))
02721 break;
02722 if (res != NULL && !agi->fast)
02723 break;
02724 buflen = strlen(buf);
02725 if (buflen && buf[buflen - 1] == '\n')
02726 break;
02727 len -= buflen;
02728 if (agidebug)
02729 ast_verbose( "AGI Rx << temp buffer %s - errno %s\n", buf, strerror(errno));
02730 }
02731
02732 if (!buf[0]) {
02733
02734 if (returnstatus) {
02735 returnstatus = -1;
02736 }
02737 ast_verb(3, "<%s>AGI Script %s completed, returning %d\n", chan->name, request, returnstatus);
02738 if (pid > 0)
02739 waitpid(pid, status, 0);
02740
02741 pid = -1;
02742 break;
02743 }
02744
02745
02746 if (*buf && strncasecmp(buf, "failure", 7) == 0) {
02747 returnstatus = AGI_RESULT_FAILURE;
02748 break;
02749 }
02750
02751
02752 if (*buf && buf[strlen(buf) - 1] == '\n')
02753 buf[strlen(buf) - 1] = 0;
02754 if (agidebug)
02755 ast_verbose("<%s>AGI Rx << %s\n", chan->name, buf);
02756 returnstatus |= agi_handle_command(chan, agi, buf, dead);
02757
02758 if (returnstatus < 0) {
02759 needhup = 1;
02760 continue;
02761 }
02762 } else {
02763 if (--retry <= 0) {
02764 ast_log(LOG_WARNING, "No channel, no fd?\n");
02765 returnstatus = AGI_RESULT_FAILURE;
02766 break;
02767 }
02768 }
02769 }
02770 if (agi->speech) {
02771 ast_speech_destroy(agi->speech);
02772 }
02773
02774 if (send_sighup) {
02775 if (pid > -1) {
02776 if (kill(pid, SIGHUP)) {
02777 ast_log(LOG_WARNING, "unable to send SIGHUP to AGI process %d: %s\n", pid, strerror(errno));
02778 } else {
02779 usleep(1);
02780 }
02781 waitpid(pid, status, WNOHANG);
02782 } else if (agi->fast) {
02783 send(agi->ctrl, "HANGUP\n", 7, MSG_OOB);
02784 }
02785 }
02786 fclose(readf);
02787 return returnstatus;
02788 }
02789
02790 static char *handle_cli_agi_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02791 {
02792 struct agi_command *command;
02793 char fullcmd[80];
02794
02795 switch (cmd) {
02796 case CLI_INIT:
02797 e->command = "agi show";
02798 e->usage =
02799 "Usage: agi show [topic]\n"
02800 " When called with a topic as an argument, displays usage\n"
02801 " information on the given command. If called without a\n"
02802 " topic, it provides a list of AGI commands.\n";
02803 case CLI_GENERATE:
02804 return NULL;
02805 }
02806 if (a->argc < e->args)
02807 return CLI_SHOWUSAGE;
02808 if (a->argc > e->args) {
02809 command = find_command(a->argv + e->args, 1);
02810 if (command) {
02811 ast_cli(a->fd, "%s", command->usage);
02812 ast_cli(a->fd, " Runs Dead : %s\n", command->dead ? "Yes" : "No");
02813 } else {
02814 if (find_command(a->argv + e->args, -1)) {
02815 return help_workhorse(a->fd, a->argv + e->args);
02816 } else {
02817 ast_join(fullcmd, sizeof(fullcmd), a->argv + e->args);
02818 ast_cli(a->fd, "No such command '%s'.\n", fullcmd);
02819 }
02820 }
02821 } else {
02822 return help_workhorse(a->fd, NULL);
02823 }
02824 return CLI_SUCCESS;
02825 }
02826
02827
02828
02829
02830 static void write_html_escaped(FILE *htmlfile, char *str)
02831 {
02832 char *cur = str;
02833
02834 while(*cur) {
02835 switch (*cur) {
02836 case '<':
02837 fprintf(htmlfile, "%s", "<");
02838 break;
02839 case '>':
02840 fprintf(htmlfile, "%s", ">");
02841 break;
02842 case '&':
02843 fprintf(htmlfile, "%s", "&");
02844 break;
02845 case '"':
02846 fprintf(htmlfile, "%s", """);
02847 break;
02848 default:
02849 fprintf(htmlfile, "%c", *cur);
02850 break;
02851 }
02852 cur++;
02853 }
02854
02855 return;
02856 }
02857
02858 static int write_htmldump(char *filename)
02859 {
02860 struct agi_command *command;
02861 char fullcmd[80];
02862 FILE *htmlfile;
02863
02864 if (!(htmlfile = fopen(filename, "wt")))
02865 return -1;
02866
02867 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
02868 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
02869 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
02870
02871 AST_RWLIST_RDLOCK(&agi_commands);
02872 AST_RWLIST_TRAVERSE(&agi_commands, command, list) {
02873 char *stringp, *tempstr;
02874
02875 if (!command->cmda[0])
02876 break;
02877
02878 if ((command->cmda[0])[0] == '_')
02879 continue;
02880 ast_join(fullcmd, sizeof(fullcmd), command->cmda);
02881
02882 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
02883 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TH></TR>\n", fullcmd, command->summary);
02884
02885 stringp = command->usage;
02886 tempstr = strsep(&stringp, "\n");
02887
02888 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">");
02889 write_html_escaped(htmlfile, tempstr);
02890 fprintf(htmlfile, "</TD></TR>\n");
02891 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
02892
02893 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
02894 write_html_escaped(htmlfile, tempstr);
02895 fprintf(htmlfile, "<BR>\n");
02896 }
02897 fprintf(htmlfile, "</TD></TR>\n");
02898 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
02899 }
02900 AST_RWLIST_UNLOCK(&agi_commands);
02901 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
02902 fclose(htmlfile);
02903 return 0;
02904 }
02905
02906 static char *handle_cli_agi_dumphtml_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02907 {
02908 switch (cmd) {
02909 case CLI_INIT:
02910 e->command = "agi dumphtml";
02911 e->usage =
02912 "Usage: agi dumphtml <filename>\n"
02913 " Dumps the AGI command list in HTML format to the given\n"
02914 " file.\n";
02915 return NULL;
02916 case CLI_GENERATE:
02917 return NULL;
02918 }
02919 if (a->argc < e->args + 1)
02920 return CLI_SHOWUSAGE;
02921
02922 if (write_htmldump(a->argv[2]) < 0) {
02923 ast_cli(a->fd, "Could not create file '%s'\n", a->argv[2]);
02924 return CLI_SHOWUSAGE;
02925 }
02926 ast_cli(a->fd, "AGI HTML commands dumped to: %s\n", a->argv[2]);
02927 return CLI_SUCCESS;
02928 }
02929
02930 static char *handle_cli_agi_dump_html(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02931 {
02932 switch (cmd) {
02933 case CLI_INIT:
02934 e->command = "agi dump html";
02935 e->usage =
02936 "Usage: agi dump html <filename>\n"
02937 " Dumps the AGI command list in HTML format to the given\n"
02938 " file.\n";
02939 return NULL;
02940 case CLI_GENERATE:
02941 return NULL;
02942 }
02943 if (a->argc != e->args + 1)
02944 return CLI_SHOWUSAGE;
02945
02946 if (write_htmldump(a->argv[e->args]) < 0) {
02947 ast_cli(a->fd, "Could not create file '%s'\n", a->argv[e->args]);
02948 return CLI_SHOWUSAGE;
02949 }
02950 ast_cli(a->fd, "AGI HTML commands dumped to: %s\n", a->argv[e->args]);
02951 return CLI_SUCCESS;
02952 }
02953
02954 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
02955 {
02956 enum agi_result res;
02957 char buf[AGI_BUF_LEN] = "", *tmp = buf;
02958 int fds[2], efd = -1, pid;
02959 AST_DECLARE_APP_ARGS(args,
02960 AST_APP_ARG(arg)[MAX_ARGS];
02961 );
02962 AGI agi;
02963
02964 if (ast_strlen_zero(data)) {
02965 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
02966 return -1;
02967 }
02968 if (dead)
02969 ast_debug(3, "Hungup channel detected, running agi in dead mode.\n");
02970 ast_copy_string(buf, data, sizeof(buf));
02971 memset(&agi, 0, sizeof(agi));
02972 AST_STANDARD_APP_ARGS(args, tmp);
02973 args.argv[args.argc] = NULL;
02974 #if 0
02975
02976 if (chan->_state != AST_STATE_UP) {
02977 if (ast_answer(chan))
02978 return -1;
02979 }
02980 #endif
02981 res = launch_script(chan, args.argv[0], args.argv, fds, enhanced ? &efd : NULL, &pid);
02982
02983
02984 if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {
02985 int status = 0;
02986 agi.fd = fds[1];
02987 agi.ctrl = fds[0];
02988 agi.audio = efd;
02989 agi.fast = (res == AGI_RESULT_SUCCESS_FAST) ? 1 : 0;
02990 res = run_agi(chan, args.argv[0], &agi, pid, &status, dead, args.argc, args.argv);
02991
02992 if ((res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) && status)
02993 res = AGI_RESULT_FAILURE;
02994 if (fds[1] != fds[0])
02995 close(fds[1]);
02996 if (efd > -1)
02997 close(efd);
02998 }
02999 ast_safe_fork_cleanup();
03000
03001 switch (res) {
03002 case AGI_RESULT_SUCCESS:
03003 case AGI_RESULT_SUCCESS_FAST:
03004 case AGI_RESULT_SUCCESS_ASYNC:
03005 pbx_builtin_setvar_helper(chan, "AGISTATUS", "SUCCESS");
03006 break;
03007 case AGI_RESULT_FAILURE:
03008 pbx_builtin_setvar_helper(chan, "AGISTATUS", "FAILURE");
03009 break;
03010 case AGI_RESULT_NOTFOUND:
03011 pbx_builtin_setvar_helper(chan, "AGISTATUS", "NOTFOUND");
03012 break;
03013 case AGI_RESULT_HANGUP:
03014 pbx_builtin_setvar_helper(chan, "AGISTATUS", "HANGUP");
03015 return -1;
03016 }
03017
03018 return 0;
03019 }
03020
03021 static int agi_exec(struct ast_channel *chan, void *data)
03022 {
03023 if (!ast_check_hangup(chan))
03024 return agi_exec_full(chan, data, 0, 0);
03025 else
03026 return agi_exec_full(chan, data, 0, 1);
03027 }
03028
03029 static int eagi_exec(struct ast_channel *chan, void *data)
03030 {
03031 int readformat, res;
03032
03033 if (ast_check_hangup(chan)) {
03034 ast_log(LOG_ERROR, "EAGI cannot be run on a dead/hungup channel, please use AGI.\n");
03035 return 0;
03036 }
03037 readformat = chan->readformat;
03038 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
03039 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
03040 return -1;
03041 }
03042 res = agi_exec_full(chan, data, 1, 0);
03043 if (!res) {
03044 if (ast_set_read_format(chan, readformat)) {
03045 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
03046 }
03047 }
03048 return res;
03049 }
03050
03051 static int deadagi_exec(struct ast_channel *chan, void *data)
03052 {
03053 ast_log(LOG_WARNING, "DeadAGI has been deprecated, please use AGI in all cases!\n");
03054 return agi_exec(chan, data);
03055 }
03056
03057 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");
03058
03059 static struct ast_cli_entry cli_agi[] = {
03060 AST_CLI_DEFINE(handle_cli_agi_add_cmd, "Add AGI command to a channel in Async AGI"),
03061 AST_CLI_DEFINE(handle_cli_agi_debug, "Enable/Disable AGI debugging"),
03062 AST_CLI_DEFINE(handle_cli_agi_show, "List AGI commands or specific help"),
03063 AST_CLI_DEFINE(handle_cli_agi_dump_html, "Dumps a list of AGI commands in HTML format", .deprecate_cmd = &cli_agi_dumphtml_deprecated)
03064 };
03065
03066 static int unload_module(void)
03067 {
03068 ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
03069
03070
03071
03072 (void) ast_agi_unregister_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
03073 ast_unregister_application(eapp);
03074 ast_unregister_application(deadapp);
03075 ast_manager_unregister("AGI");
03076 return ast_unregister_application(app);
03077 }
03078
03079 static int load_module(void)
03080 {
03081 ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
03082
03083
03084
03085 (void) ast_agi_register_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
03086 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
03087 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
03088 ast_manager_register2("AGI", EVENT_FLAG_AGI, action_add_agi_cmd, "Add an AGI command to execute by Async AGI", mandescr_asyncagi);
03089 return ast_register_application(app, agi_exec, synopsis, descrip);
03090 }
03091
03092 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Asterisk Gateway Interface (AGI)",
03093 .load = load_module,
03094 .unload = unload_module,
03095 );