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: 237408 $")
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, workaround;
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], argc >= 3 ? 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 (!(workaround = ast_test_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS))) {
01475 ast_set_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS);
01476 }
01477 if (ast_compat_res_agi && argc >= 3 && !ast_strlen_zero(argv[2])) {
01478 char *compat = alloca(strlen(argv[2]) * 2 + 1), *cptr, *vptr;
01479 for (cptr = compat, vptr = argv[2]; *vptr; vptr++) {
01480 if (*vptr == ',') {
01481 *cptr++ = '\\';
01482 *cptr++ = ',';
01483 } else if (*vptr == '|') {
01484 *cptr++ = ',';
01485 } else {
01486 *cptr++ = *vptr;
01487 }
01488 }
01489 *cptr = '\0';
01490 res = pbx_exec(chan, app_to_exec, compat);
01491 } else {
01492 res = pbx_exec(chan, app_to_exec, argc == 2 ? "" : argv[2]);
01493 }
01494 if (!workaround) {
01495 ast_clear_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS);
01496 }
01497 } else {
01498 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]);
01499 res = -2;
01500 }
01501 ast_agi_send(agi->fd, chan, "200 result=%d\n", res);
01502
01503
01504 return res;
01505 }
01506
01507 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01508 {
01509 char tmp[256]="";
01510 char *l = NULL, *n = NULL;
01511
01512 if (argv[2]) {
01513 ast_copy_string(tmp, argv[2], sizeof(tmp));
01514 ast_callerid_parse(tmp, &n, &l);
01515 if (l)
01516 ast_shrink_phone_number(l);
01517 else
01518 l = "";
01519 if (!n)
01520 n = "";
01521 ast_set_callerid(chan, l, n, NULL);
01522 }
01523
01524 ast_agi_send(agi->fd, chan, "200 result=1\n");
01525 return RESULT_SUCCESS;
01526 }
01527
01528 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01529 {
01530 struct ast_channel *c;
01531 if (argc == 2) {
01532
01533 ast_agi_send(agi->fd, chan, "200 result=%d\n", chan->_state);
01534 return RESULT_SUCCESS;
01535 } else if (argc == 3) {
01536
01537 c = ast_get_channel_by_name_locked(argv[2]);
01538 if (c) {
01539 ast_agi_send(agi->fd, chan, "200 result=%d\n", c->_state);
01540 ast_channel_unlock(c);
01541 return RESULT_SUCCESS;
01542 }
01543
01544 ast_agi_send(agi->fd, chan, "200 result=-1\n");
01545 return RESULT_SUCCESS;
01546 } else {
01547 return RESULT_SHOWUSAGE;
01548 }
01549 }
01550
01551 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01552 {
01553 if (argv[3])
01554 pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
01555
01556 ast_agi_send(agi->fd, chan, "200 result=1\n");
01557 return RESULT_SUCCESS;
01558 }
01559
01560 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01561 {
01562 char *ret;
01563 char tempstr[1024];
01564
01565 if (argc != 3)
01566 return RESULT_SHOWUSAGE;
01567
01568
01569 if (!ast_strlen_zero(argv[2]) && (argv[2][strlen(argv[2]) - 1] == ')')) {
01570 ret = ast_func_read(chan, argv[2], tempstr, sizeof(tempstr)) ? NULL : tempstr;
01571 } else {
01572 pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL);
01573 }
01574
01575 if (ret)
01576 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", ret);
01577 else
01578 ast_agi_send(agi->fd, chan, "200 result=0\n");
01579
01580 return RESULT_SUCCESS;
01581 }
01582
01583 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01584 {
01585 char tmp[4096];
01586 struct ast_channel *chan2=NULL;
01587
01588 if ((argc != 4) && (argc != 5))
01589 return RESULT_SHOWUSAGE;
01590 if (argc == 5) {
01591 chan2 = ast_get_channel_by_name_locked(argv[4]);
01592 } else {
01593 chan2 = chan;
01594 }
01595 if (chan2) {
01596 pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
01597 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", tmp);
01598 } else {
01599 ast_agi_send(agi->fd, chan, "200 result=0\n");
01600 }
01601 if (chan2 && (chan2 != chan))
01602 ast_channel_unlock(chan2);
01603 return RESULT_SUCCESS;
01604 }
01605
01606 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01607 {
01608 int level = 0;
01609
01610 if (argc < 2)
01611 return RESULT_SHOWUSAGE;
01612
01613 if (argv[2])
01614 sscanf(argv[2], "%30d", &level);
01615
01616 ast_verb(level, "%s: %s\n", chan->data, argv[1]);
01617
01618 ast_agi_send(agi->fd, chan, "200 result=1\n");
01619
01620 return RESULT_SUCCESS;
01621 }
01622
01623 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01624 {
01625 int res;
01626 struct ast_str *buf;
01627
01628 if (argc != 4)
01629 return RESULT_SHOWUSAGE;
01630
01631 if (!(buf = ast_str_create(16))) {
01632 ast_agi_send(agi->fd, chan, "200 result=-1\n");
01633 return RESULT_SUCCESS;
01634 }
01635
01636 do {
01637 res = ast_db_get(argv[2], argv[3], buf->str, buf->len);
01638 buf->used = strlen(buf->str);
01639 if (buf->used < buf->len - 1) {
01640 break;
01641 }
01642 if (ast_str_make_space(&buf, buf->len * 2)) {
01643 break;
01644 }
01645 } while (1);
01646
01647 if (res)
01648 ast_agi_send(agi->fd, chan, "200 result=0\n");
01649 else
01650 ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", buf->str);
01651
01652 ast_free(buf);
01653 return RESULT_SUCCESS;
01654 }
01655
01656 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01657 {
01658 int res;
01659
01660 if (argc != 5)
01661 return RESULT_SHOWUSAGE;
01662 res = ast_db_put(argv[2], argv[3], argv[4]);
01663 ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01664 return RESULT_SUCCESS;
01665 }
01666
01667 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01668 {
01669 int res;
01670
01671 if (argc != 4)
01672 return RESULT_SHOWUSAGE;
01673 res = ast_db_del(argv[2], argv[3]);
01674 ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01675 return RESULT_SUCCESS;
01676 }
01677
01678 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01679 {
01680 int res;
01681
01682 if ((argc < 3) || (argc > 4))
01683 return RESULT_SHOWUSAGE;
01684 if (argc == 4)
01685 res = ast_db_deltree(argv[2], argv[3]);
01686 else
01687 res = ast_db_deltree(argv[2], NULL);
01688
01689 ast_agi_send(agi->fd, chan, "200 result=%c\n", res ? '0' : '1');
01690 return RESULT_SUCCESS;
01691 }
01692
01693 static char *handle_cli_agi_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01694 {
01695 switch (cmd) {
01696 case CLI_INIT:
01697 e->command = "agi set debug [on|off]";
01698 e->usage =
01699 "Usage: agi set debug [on|off]\n"
01700 " Enables/disables dumping of AGI transactions for\n"
01701 " debugging purposes.\n";
01702 return NULL;
01703
01704 case CLI_GENERATE:
01705 return NULL;
01706 }
01707
01708 if (a->argc != e->args)
01709 return CLI_SHOWUSAGE;
01710
01711 if (strncasecmp(a->argv[3], "off", 3) == 0) {
01712 agidebug = 0;
01713 } else if (strncasecmp(a->argv[3], "on", 2) == 0) {
01714 agidebug = 1;
01715 } else {
01716 return CLI_SHOWUSAGE;
01717 }
01718 ast_cli(a->fd, "AGI Debugging %sabled\n", agidebug ? "En" : "Dis");
01719 return CLI_SUCCESS;
01720 }
01721
01722 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[])
01723 {
01724 ast_agi_send(agi->fd, chan, "200 result=0\n");
01725 return RESULT_SUCCESS;
01726 }
01727
01728 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[])
01729 {
01730 if (argc < 3) {
01731 return RESULT_SHOWUSAGE;
01732 }
01733 if (!strncasecmp(argv[2], "on", 2))
01734 ast_moh_start(chan, argc > 3 ? argv[3] : NULL, NULL);
01735 else if (!strncasecmp(argv[2], "off", 3))
01736 ast_moh_stop(chan);
01737 ast_agi_send(agi->fd, chan, "200 result=0\n");
01738 return RESULT_SUCCESS;
01739 }
01740
01741 static int handle_speechcreate(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01742 {
01743
01744 if (agi->speech) {
01745 ast_agi_send(agi->fd, chan, "200 result=0\n");
01746 return RESULT_SUCCESS;
01747 }
01748
01749 if ((agi->speech = ast_speech_new(argv[2], AST_FORMAT_SLINEAR)))
01750 ast_agi_send(agi->fd, chan, "200 result=1\n");
01751 else
01752 ast_agi_send(agi->fd, chan, "200 result=0\n");
01753
01754 return RESULT_SUCCESS;
01755 }
01756
01757 static int handle_speechset(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01758 {
01759
01760 if (argc != 3)
01761 return RESULT_SHOWUSAGE;
01762
01763
01764 if (!agi->speech) {
01765 ast_agi_send(agi->fd, chan, "200 result=0\n");
01766 return RESULT_SUCCESS;
01767 }
01768
01769 ast_speech_change(agi->speech, argv[2], argv[3]);
01770 ast_agi_send(agi->fd, chan, "200 result=1\n");
01771
01772 return RESULT_SUCCESS;
01773 }
01774
01775 static int handle_speechdestroy(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01776 {
01777 if (agi->speech) {
01778 ast_speech_destroy(agi->speech);
01779 agi->speech = NULL;
01780 ast_agi_send(agi->fd, chan, "200 result=1\n");
01781 } else {
01782 ast_agi_send(agi->fd, chan, "200 result=0\n");
01783 }
01784
01785 return RESULT_SUCCESS;
01786 }
01787
01788 static int handle_speechloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01789 {
01790 if (argc != 5)
01791 return RESULT_SHOWUSAGE;
01792
01793 if (!agi->speech) {
01794 ast_agi_send(agi->fd, chan, "200 result=0\n");
01795 return RESULT_SUCCESS;
01796 }
01797
01798 if (ast_speech_grammar_load(agi->speech, argv[3], argv[4]))
01799 ast_agi_send(agi->fd, chan, "200 result=0\n");
01800 else
01801 ast_agi_send(agi->fd, chan, "200 result=1\n");
01802
01803 return RESULT_SUCCESS;
01804 }
01805
01806 static int handle_speechunloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01807 {
01808 if (argc != 4)
01809 return RESULT_SHOWUSAGE;
01810
01811 if (!agi->speech) {
01812 ast_agi_send(agi->fd, chan, "200 result=0\n");
01813 return RESULT_SUCCESS;
01814 }
01815
01816 if (ast_speech_grammar_unload(agi->speech, argv[3]))
01817 ast_agi_send(agi->fd, chan, "200 result=0\n");
01818 else
01819 ast_agi_send(agi->fd, chan, "200 result=1\n");
01820
01821 return RESULT_SUCCESS;
01822 }
01823
01824 static int handle_speechactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01825 {
01826 if (argc != 4)
01827 return RESULT_SHOWUSAGE;
01828
01829 if (!agi->speech) {
01830 ast_agi_send(agi->fd, chan, "200 result=0\n");
01831 return RESULT_SUCCESS;
01832 }
01833
01834 if (ast_speech_grammar_activate(agi->speech, argv[3]))
01835 ast_agi_send(agi->fd, chan, "200 result=0\n");
01836 else
01837 ast_agi_send(agi->fd, chan, "200 result=1\n");
01838
01839 return RESULT_SUCCESS;
01840 }
01841
01842 static int handle_speechdeactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01843 {
01844 if (argc != 4)
01845 return RESULT_SHOWUSAGE;
01846
01847 if (!agi->speech) {
01848 ast_agi_send(agi->fd, chan, "200 result=0\n");
01849 return RESULT_SUCCESS;
01850 }
01851
01852 if (ast_speech_grammar_deactivate(agi->speech, argv[3]))
01853 ast_agi_send(agi->fd, chan, "200 result=0\n");
01854 else
01855 ast_agi_send(agi->fd, chan, "200 result=1\n");
01856
01857 return RESULT_SUCCESS;
01858 }
01859
01860 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang, int offset)
01861 {
01862 struct ast_filestream *fs = NULL;
01863
01864 if (!(fs = ast_openstream(chan, filename, preflang)))
01865 return -1;
01866
01867 if (offset)
01868 ast_seekstream(fs, offset, SEEK_SET);
01869
01870 if (ast_applystream(chan, fs))
01871 return -1;
01872
01873 if (ast_playstream(fs))
01874 return -1;
01875
01876 return 0;
01877 }
01878
01879 static int handle_speechrecognize(struct ast_channel *chan, AGI *agi, int argc, char **argv)
01880 {
01881 struct ast_speech *speech = agi->speech;
01882 char *prompt, dtmf = 0, tmp[4096] = "", *buf = tmp;
01883 int timeout = 0, offset = 0, old_read_format = 0, res = 0, i = 0;
01884 long current_offset = 0;
01885 const char *reason = NULL;
01886 struct ast_frame *fr = NULL;
01887 struct ast_speech_result *result = NULL;
01888 size_t left = sizeof(tmp);
01889 time_t start = 0, current;
01890
01891 if (argc < 4)
01892 return RESULT_SHOWUSAGE;
01893
01894 if (!speech) {
01895 ast_agi_send(agi->fd, chan, "200 result=0\n");
01896 return RESULT_SUCCESS;
01897 }
01898
01899 prompt = argv[2];
01900 timeout = atoi(argv[3]);
01901
01902
01903 if (argc == 5)
01904 offset = atoi(argv[4]);
01905
01906
01907 old_read_format = chan->readformat;
01908 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
01909 ast_agi_send(agi->fd, chan, "200 result=0\n");
01910 return RESULT_SUCCESS;
01911 }
01912
01913
01914 if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
01915 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
01916 ast_speech_start(speech);
01917 }
01918
01919
01920 speech_streamfile(chan, prompt, chan->language, offset);
01921
01922
01923 while (ast_strlen_zero(reason)) {
01924
01925 ast_sched_runq(chan->sched);
01926
01927
01928 if ((res = ast_sched_wait(chan->sched)) < 0)
01929 res = 1000;
01930
01931
01932 if (ast_waitfor(chan, res) > 0) {
01933 if (!(fr = ast_read(chan))) {
01934 reason = "hangup";
01935 break;
01936 }
01937 }
01938
01939
01940 if ((timeout > 0) && (start > 0)) {
01941 time(¤t);
01942 if ((current - start) >= timeout) {
01943 reason = "timeout";
01944 if (fr)
01945 ast_frfree(fr);
01946 break;
01947 }
01948 }
01949
01950
01951 ast_mutex_lock(&speech->lock);
01952
01953
01954 if (ast_test_flag(speech, AST_SPEECH_QUIET) && chan->stream) {
01955 current_offset = ast_tellstream(chan->stream);
01956 ast_stopstream(chan);
01957 ast_clear_flag(speech, AST_SPEECH_QUIET);
01958 }
01959
01960
01961 switch (speech->state) {
01962 case AST_SPEECH_STATE_READY:
01963
01964 if ((timeout > 0) && start == 0 && ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL))) {
01965 ast_stopstream(chan);
01966 time(&start);
01967 }
01968
01969 if (fr && fr->frametype == AST_FRAME_VOICE)
01970 ast_speech_write(speech, fr->data.ptr, fr->datalen);
01971 break;
01972 case AST_SPEECH_STATE_WAIT:
01973
01974 if ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL)) {
01975 ast_stopstream(chan);
01976
01977 if (!ast_strlen_zero(speech->processing_sound) && strcasecmp(speech->processing_sound, "none"))
01978 speech_streamfile(chan, speech->processing_sound, chan->language, 0);
01979 }
01980 break;
01981 case AST_SPEECH_STATE_DONE:
01982
01983 speech->results = ast_speech_results_get(speech);
01984
01985 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
01986 reason = "speech";
01987 break;
01988 default:
01989 break;
01990 }
01991 ast_mutex_unlock(&speech->lock);
01992
01993
01994 if (fr) {
01995 if (fr->frametype == AST_FRAME_DTMF) {
01996 reason = "dtmf";
01997 dtmf = fr->subclass;
01998 } else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass == AST_CONTROL_HANGUP) {
01999 reason = "hangup";
02000 }
02001 ast_frfree(fr);
02002 }
02003 }
02004
02005 if (!strcasecmp(reason, "speech")) {
02006
02007 for (result = speech->results; result; result = AST_LIST_NEXT(result, list)) {
02008
02009 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);
02010
02011 i++;
02012 }
02013
02014 ast_agi_send(agi->fd, chan, "200 result=1 (speech) endpos=%ld results=%d %s\n", current_offset, i, tmp);
02015 } else if (!strcasecmp(reason, "dtmf")) {
02016 ast_agi_send(agi->fd, chan, "200 result=1 (digit) digit=%c endpos=%ld\n", dtmf, current_offset);
02017 } else if (!strcasecmp(reason, "hangup") || !strcasecmp(reason, "timeout")) {
02018 ast_agi_send(agi->fd, chan, "200 result=1 (%s) endpos=%ld\n", reason, current_offset);
02019 } else {
02020 ast_agi_send(agi->fd, chan, "200 result=0 endpos=%ld\n", current_offset);
02021 }
02022
02023 return RESULT_SUCCESS;
02024 }
02025
02026 static char usage_setmusic[] =
02027 " Usage: SET MUSIC ON <on|off> <class>\n"
02028 " Enables/Disables the music on hold generator. If <class> is\n"
02029 " not specified, then the default music on hold class will be used.\n"
02030 " Always returns 0.\n";
02031
02032 static char usage_dbput[] =
02033 " Usage: DATABASE PUT <family> <key> <value>\n"
02034 " Adds or updates an entry in the Asterisk database for a\n"
02035 " given family, key, and value.\n"
02036 " Returns 1 if successful, 0 otherwise.\n";
02037
02038 static char usage_dbget[] =
02039 " Usage: DATABASE GET <family> <key>\n"
02040 " Retrieves an entry in the Asterisk database for a\n"
02041 " given family and key.\n"
02042 " Returns 0 if <key> is not set. Returns 1 if <key>\n"
02043 " is set and returns the variable in parentheses.\n"
02044 " Example return code: 200 result=1 (testvariable)\n";
02045
02046 static char usage_dbdel[] =
02047 " Usage: DATABASE DEL <family> <key>\n"
02048 " Deletes an entry in the Asterisk database for a\n"
02049 " given family and key.\n"
02050 " Returns 1 if successful, 0 otherwise.\n";
02051
02052 static char usage_dbdeltree[] =
02053 " Usage: DATABASE DELTREE <family> [keytree]\n"
02054 " Deletes a family or specific keytree within a family\n"
02055 " in the Asterisk database.\n"
02056 " Returns 1 if successful, 0 otherwise.\n";
02057
02058 static char usage_verbose[] =
02059 " Usage: VERBOSE <message> <level>\n"
02060 " Sends <message> to the console via verbose message system.\n"
02061 " <level> is the the verbose level (1-4)\n"
02062 " Always returns 1.\n";
02063
02064 static char usage_getvariable[] =
02065 " Usage: GET VARIABLE <variablename>\n"
02066 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
02067 " is set and returns the variable in parentheses.\n"
02068 " example return code: 200 result=1 (testvariable)\n";
02069
02070 static char usage_getvariablefull[] =
02071 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n"
02072 " Returns 0 if <variablename> is not set or channel does not exist. Returns 1\n"
02073 "if <variablename> is set and returns the variable in parenthesis. Understands\n"
02074 "complex variable names and builtin variables, unlike GET VARIABLE.\n"
02075 " example return code: 200 result=1 (testvariable)\n";
02076
02077 static char usage_setvariable[] =
02078 " Usage: SET VARIABLE <variablename> <value>\n";
02079
02080 static char usage_channelstatus[] =
02081 " Usage: CHANNEL STATUS [<channelname>]\n"
02082 " Returns the status of the specified channel.\n"
02083 " If no channel name is given the returns the status of the\n"
02084 " current channel. Return values:\n"
02085 " 0 Channel is down and available\n"
02086 " 1 Channel is down, but reserved\n"
02087 " 2 Channel is off hook\n"
02088 " 3 Digits (or equivalent) have been dialed\n"
02089 " 4 Line is ringing\n"
02090 " 5 Remote end is ringing\n"
02091 " 6 Line is up\n"
02092 " 7 Line is busy\n";
02093
02094 static char usage_setcallerid[] =
02095 " Usage: SET CALLERID <number>\n"
02096 " Changes the callerid of the current channel.\n";
02097
02098 static char usage_exec[] =
02099 " Usage: EXEC <application> <options>\n"
02100 " Executes <application> with given <options>.\n"
02101 " Returns whatever the application returns, or -2 on failure to find application\n";
02102
02103 static char usage_hangup[] =
02104 " Usage: HANGUP [<channelname>]\n"
02105 " Hangs up the specified channel.\n"
02106 " If no channel name is given, hangs up the current channel\n";
02107
02108 static char usage_answer[] =
02109 " Usage: ANSWER\n"
02110 " Answers channel if not already in answer state. Returns -1 on\n"
02111 " channel failure, or 0 if successful.\n";
02112
02113 static char usage_waitfordigit[] =
02114 " Usage: WAIT FOR DIGIT <timeout>\n"
02115 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
02116 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
02117 " the numerical value of the ascii of the digit if one is received. Use -1\n"
02118 " for the timeout value if you desire the call to block indefinitely.\n";
02119
02120 static char usage_sendtext[] =
02121 " Usage: SEND TEXT \"<text to send>\"\n"
02122 " Sends the given text on a channel. Most channels do not support the\n"
02123 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
02124 " support text transmission. Returns -1 only on error/hangup. Text\n"
02125 " consisting of greater than one word should be placed in quotes since the\n"
02126 " command only accepts a single argument.\n";
02127
02128 static char usage_recvchar[] =
02129 " Usage: RECEIVE CHAR <timeout>\n"
02130 " Receives a character of text on a channel. Specify timeout to be the\n"
02131 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
02132 " do not support the reception of text. Returns the decimal value of the character\n"
02133 " if one is received, or 0 if the channel does not support text reception. Returns\n"
02134 " -1 only on error/hangup.\n";
02135
02136 static char usage_recvtext[] =
02137 " Usage: RECEIVE TEXT <timeout>\n"
02138 " Receives a string of text on a channel. Specify timeout to be the\n"
02139 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
02140 " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n";
02141
02142 static char usage_tddmode[] =
02143 " Usage: TDD MODE <on|off>\n"
02144 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
02145 " successful, or 0 if channel is not TDD-capable.\n";
02146
02147 static char usage_sendimage[] =
02148 " Usage: SEND IMAGE <image>\n"
02149 " Sends the given image on a channel. Most channels do not support the\n"
02150 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
02151 " support image transmission. Returns -1 only on error/hangup. Image names\n"
02152 " should not include extensions.\n";
02153
02154 static char usage_streamfile[] =
02155 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
02156 " Send the given file, allowing playback to be interrupted by the given\n"
02157 " digits, if any. Use double quotes for the digits if you wish none to be\n"
02158 " permitted. If sample offset is provided then the audio will seek to sample\n"
02159 " offset before play starts. 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";
02163
02164 static char usage_controlstreamfile[] =
02165 " Usage: CONTROL STREAM FILE <filename> <escape digits> [skipms] [ffchar] [rewchr] [pausechr]\n"
02166 " Send the given file, allowing playback to be controled by the given\n"
02167 " digits, if any. Use double quotes for the digits if you wish none to be\n"
02168 " permitted. Returns 0 if playback completes without a digit\n"
02169 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
02170 " or -1 on error or if the channel was disconnected. Remember, the file\n"
02171 " extension must not be included in the filename.\n\n"
02172 " Note: ffchar and rewchar default to * and # respectively.\n";
02173
02174 static char usage_getoption[] =
02175 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n"
02176 " Behaves similar to STREAM FILE but used with a timeout option.\n";
02177
02178 static char usage_saynumber[] =
02179 " Usage: SAY NUMBER <number> <escape digits> [gender]\n"
02180 " Say a given number, returning early if any of the given DTMF digits\n"
02181 " are received on the channel. Returns 0 if playback completes without a digit\n"
02182 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02183 " -1 on error/hangup.\n";
02184
02185 static char usage_saydigits[] =
02186 " Usage: SAY DIGITS <number> <escape digits>\n"
02187 " Say a given digit string, returning early if any of the given DTMF digits\n"
02188 " are received on the channel. Returns 0 if playback completes without a digit\n"
02189 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02190 " -1 on error/hangup.\n";
02191
02192 static char usage_sayalpha[] =
02193 " Usage: SAY ALPHA <number> <escape digits>\n"
02194 " Say a given character string, returning early if any of the given DTMF digits\n"
02195 " are received on the channel. Returns 0 if playback completes without a digit\n"
02196 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
02197 " -1 on error/hangup.\n";
02198
02199 static char usage_saydate[] =
02200 " Usage: SAY DATE <date> <escape digits>\n"
02201 " Say a given date, returning early if any of the given DTMF digits are\n"
02202 " received on the channel. <date> is number of seconds elapsed since 00:00:00\n"
02203 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
02204 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02205 " digit if one was pressed or -1 on error/hangup.\n";
02206
02207 static char usage_saytime[] =
02208 " Usage: SAY TIME <time> <escape digits>\n"
02209 " Say a given time, returning early if any of the given DTMF digits are\n"
02210 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
02211 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n"
02212 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02213 " digit if one was pressed or -1 on error/hangup.\n";
02214
02215 static char usage_saydatetime[] =
02216 " Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n"
02217 " Say a given time, returning early if any of the given DTMF digits are\n"
02218 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n"
02219 " on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n"
02220 " the time should be said in. See voicemail.conf (defaults to \"ABdY\n"
02221 " 'digits/at' IMp\"). Acceptable values for [timezone] can be found in\n"
02222 " /usr/share/zoneinfo. Defaults to machine default. Returns 0 if playback\n"
02223 " completes without a digit being pressed, or the ASCII numerical value of the\n"
02224 " digit if one was pressed or -1 on error/hangup.\n";
02225
02226 static char usage_sayphonetic[] =
02227 " Usage: SAY PHONETIC <string> <escape digits>\n"
02228 " Say a given character string with phonetics, returning early if any of the\n"
02229 " given DTMF digits are received on the channel. Returns 0 if playback\n"
02230 " completes without a digit pressed, the ASCII numerical value of the digit\n"
02231 " if one was pressed, or -1 on error/hangup.\n";
02232
02233 static char usage_getdata[] =
02234 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
02235 " Stream the given file, and receive DTMF data. Returns the digits received\n"
02236 "from the channel at the other end.\n";
02237
02238 static char usage_setcontext[] =
02239 " Usage: SET CONTEXT <desired context>\n"
02240 " Sets the context for continuation upon exiting the application.\n";
02241
02242 static char usage_setextension[] =
02243 " Usage: SET EXTENSION <new extension>\n"
02244 " Changes the extension for continuation upon exiting the application.\n";
02245
02246 static char usage_setpriority[] =
02247 " Usage: SET PRIORITY <priority>\n"
02248 " Changes the priority for continuation upon exiting the application.\n"
02249 " The priority must be a valid priority or label.\n";
02250
02251 static char usage_recordfile[] =
02252 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n"
02253 " [offset samples] [BEEP] [s=silence]\n"
02254 " Record to a file until a given dtmf digit in the sequence is received\n"
02255 " Returns -1 on hangup or error. The format will specify what kind of file\n"
02256 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
02257 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n"
02258 " to the offset without exceeding the end of the file. \"silence\" is the number\n"
02259 " of seconds of silence allowed before the function returns despite the\n"
02260 " lack of dtmf digits or reaching timeout. Silence value must be\n"
02261 " preceeded by \"s=\" and is also optional.\n";
02262
02263 static char usage_autohangup[] =
02264 " Usage: SET AUTOHANGUP <time>\n"
02265 " Cause the channel to automatically hangup at <time> seconds in the\n"
02266 " future. Of course it can be hungup before then as well. Setting to 0 will\n"
02267 " cause the autohangup feature to be disabled on this channel.\n";
02268
02269 static char usage_noop[] =
02270 " Usage: NoOp\n"
02271 " Does nothing.\n";
02272
02273 static char usage_speechcreate[] =
02274 " Usage: SPEECH CREATE <engine>\n"
02275 " Create a speech object to be used by the other Speech AGI commands.\n";
02276
02277 static char usage_speechset[] =
02278 " Usage: SPEECH SET <name> <value>\n"
02279 " Set an engine-specific setting.\n";
02280
02281 static char usage_speechdestroy[] =
02282 " Usage: SPEECH DESTROY\n"
02283 " Destroy the speech object created by SPEECH CREATE.\n";
02284
02285 static char usage_speechloadgrammar[] =
02286 " Usage: SPEECH LOAD GRAMMAR <grammar name> <path to grammar>\n"
02287 " Loads the specified grammar as the specified name.\n";
02288
02289 static char usage_speechunloadgrammar[] =
02290 " Usage: SPEECH UNLOAD GRAMMAR <grammar name>\n"
02291 " Unloads the specified grammar.\n";
02292
02293 static char usage_speechactivategrammar[] =
02294 " Usage: SPEECH ACTIVATE GRAMMAR <grammar name>\n"
02295 " Activates the specified grammar on the speech object.\n";
02296
02297 static char usage_speechdeactivategrammar[] =
02298 " Usage: SPEECH DEACTIVATE GRAMMAR <grammar name>\n"
02299 " Deactivates the specified grammar on the speech object.\n";
02300
02301 static char usage_speechrecognize[] =
02302 " Usage: SPEECH RECOGNIZE <prompt> <timeout> [<offset>]\n"
02303 " Plays back given prompt while listening for speech and dtmf.\n";
02304
02305 static char usage_asyncagi_break[] =
02306 " Usage: ASYNCAGI BREAK\n"
02307 " Returns control to the dialplan\n";
02308
02309
02310
02311
02312 static struct agi_command commands[] = {
02313 { { "answer", NULL }, handle_answer, "Answer channel", usage_answer , 0 },
02314 { { "asyncagi", "break", NULL }, handle_asyncagi_break, "Exit AsyncAGI processing", usage_asyncagi_break, 1 },
02315 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus , 0 },
02316 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel , 1 },
02317 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree , 1 },
02318 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget , 1 },
02319 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput , 1 },
02320 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec , 1 },
02321 { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata , 0 },
02322 { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull , 1 },
02323 { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption , 0 },
02324 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable , 1 },
02325 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup , 0 },
02326 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop , 1 },
02327 { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar , 0 },
02328 { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext , 0 },
02329 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile , 0 },
02330 { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha , 0 },
02331 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits , 0 },
02332 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber , 0 },
02333 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic , 0 },
02334 { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate , 0 },
02335 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime , 0 },
02336 { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime , 0 },
02337 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage , 0 },
02338 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext , 0 },
02339 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup , 0 },
02340 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid , 0 },
02341 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext , 0 },
02342 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension , 0 },
02343 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic , 0 },
02344 { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority , 0 },
02345 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable , 1 },
02346 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile , 0 },
02347 { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile , 0 },
02348 { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode , 0 },
02349 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose , 1 },
02350 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit , 0 },
02351 { { "speech", "create", NULL }, handle_speechcreate, "Creates a speech object", usage_speechcreate, 0 },
02352 { { "speech", "set", NULL }, handle_speechset, "Sets a speech engine setting", usage_speechset, 0 },
02353 { { "speech", "destroy", NULL }, handle_speechdestroy, "Destroys a speech object", usage_speechdestroy, 1 },
02354 { { "speech", "load", "grammar", NULL }, handle_speechloadgrammar, "Loads a grammar", usage_speechloadgrammar, 0 },
02355 { { "speech", "unload", "grammar", NULL }, handle_speechunloadgrammar, "Unloads a grammar", usage_speechunloadgrammar, 1 },
02356 { { "speech", "activate", "grammar", NULL }, handle_speechactivategrammar, "Activates a grammar", usage_speechactivategrammar, 0 },
02357 { { "speech", "deactivate", "grammar", NULL }, handle_speechdeactivategrammar, "Deactivates a grammar", usage_speechdeactivategrammar, 0 },
02358 { { "speech", "recognize", NULL }, handle_speechrecognize, "Recognizes speech", usage_speechrecognize, 0 },
02359 };
02360
02361 static AST_RWLIST_HEAD_STATIC(agi_commands, agi_command);
02362
02363 static char *help_workhorse(int fd, char *match[])
02364 {
02365 char fullcmd[80], matchstr[80];
02366 struct agi_command *e;
02367
02368 if (match)
02369 ast_join(matchstr, sizeof(matchstr), match);
02370
02371 ast_cli(fd, "%5.5s %30.30s %s\n","Dead","Command","Description");
02372 AST_RWLIST_RDLOCK(&agi_commands);
02373 AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
02374 if (!e->cmda[0])
02375 break;
02376
02377 if ((e->cmda[0])[0] == '_')
02378 continue;
02379 ast_join(fullcmd, sizeof(fullcmd), e->cmda);
02380 if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr)))
02381 continue;
02382 ast_cli(fd, "%5.5s %30.30s %s\n", e->dead ? "Yes" : "No" , fullcmd, e->summary);
02383 }
02384 AST_RWLIST_UNLOCK(&agi_commands);
02385
02386 return CLI_SUCCESS;
02387 }
02388
02389 int ast_agi_register(struct ast_module *mod, agi_command *cmd)
02390 {
02391 char fullcmd[80];
02392
02393 ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
02394
02395 if (!find_command(cmd->cmda,1)) {
02396 cmd->mod = mod;
02397 AST_RWLIST_WRLOCK(&agi_commands);
02398 AST_LIST_INSERT_TAIL(&agi_commands, cmd, list);
02399 AST_RWLIST_UNLOCK(&agi_commands);
02400 if (mod != ast_module_info->self)
02401 ast_module_ref(ast_module_info->self);
02402 ast_verb(2, "AGI Command '%s' registered\n",fullcmd);
02403 return 1;
02404 } else {
02405 ast_log(LOG_WARNING, "Command already registered!\n");
02406 return 0;
02407 }
02408 }
02409
02410 int ast_agi_unregister(struct ast_module *mod, agi_command *cmd)
02411 {
02412 struct agi_command *e;
02413 int unregistered = 0;
02414 char fullcmd[80];
02415
02416 ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
02417
02418 AST_RWLIST_WRLOCK(&agi_commands);
02419 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&agi_commands, e, list) {
02420 if (cmd == e) {
02421 AST_RWLIST_REMOVE_CURRENT(list);
02422 if (mod != ast_module_info->self)
02423 ast_module_unref(ast_module_info->self);
02424 unregistered=1;
02425 break;
02426 }
02427 }
02428 AST_RWLIST_TRAVERSE_SAFE_END;
02429 AST_RWLIST_UNLOCK(&agi_commands);
02430 if (unregistered)
02431 ast_verb(2, "AGI Command '%s' unregistered\n",fullcmd);
02432 else
02433 ast_log(LOG_WARNING, "Unable to unregister command: '%s'!\n",fullcmd);
02434 return unregistered;
02435 }
02436
02437 int ast_agi_register_multiple(struct ast_module *mod, struct agi_command *cmd, unsigned int len)
02438 {
02439 unsigned int i, x = 0;
02440
02441 for (i = 0; i < len; i++) {
02442 if (ast_agi_register(mod, cmd + i) == 1) {
02443 x++;
02444 continue;
02445 }
02446
02447
02448
02449
02450 for (; x > 0; x--) {
02451
02452
02453
02454
02455
02456
02457
02458
02459 (void) ast_agi_unregister(mod, cmd + x - 1);
02460 }
02461 return -1;
02462 }
02463
02464 return 0;
02465 }
02466
02467 int ast_agi_unregister_multiple(struct ast_module *mod, struct agi_command *cmd, unsigned int len)
02468 {
02469 unsigned int i;
02470 int res = 0;
02471
02472 for (i = 0; i < len; i++) {
02473
02474
02475
02476
02477 res |= ast_agi_unregister(mod, cmd + i);
02478 }
02479
02480 return res;
02481 }
02482
02483 static agi_command *find_command(char *cmds[], int exact)
02484 {
02485 int y, match;
02486 struct agi_command *e;
02487
02488 AST_RWLIST_RDLOCK(&agi_commands);
02489 AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
02490 if (!e->cmda[0])
02491 break;
02492
02493 match = 1;
02494 for (y = 0; match && cmds[y]; y++) {
02495
02496
02497
02498 if (!e->cmda[y] && !exact)
02499 break;
02500
02501 if (!e->cmda[y]) {
02502 AST_RWLIST_UNLOCK(&agi_commands);
02503 return NULL;
02504 }
02505 if (strcasecmp(e->cmda[y], cmds[y]))
02506 match = 0;
02507 }
02508
02509
02510 if ((exact > -1) && e->cmda[y])
02511 match = 0;
02512 if (match) {
02513 AST_RWLIST_UNLOCK(&agi_commands);
02514 return e;
02515 }
02516 }
02517 AST_RWLIST_UNLOCK(&agi_commands);
02518 return NULL;
02519 }
02520
02521 static int parse_args(char *s, int *max, char *argv[])
02522 {
02523 int x = 0, quoted = 0, escaped = 0, whitespace = 1;
02524 char *cur;
02525
02526 cur = s;
02527 while(*s) {
02528 switch(*s) {
02529 case '"':
02530
02531 if (escaped)
02532 goto normal;
02533 else
02534 quoted = !quoted;
02535 if (quoted && whitespace) {
02536
02537 argv[x++] = cur;
02538 whitespace=0;
02539 }
02540 escaped = 0;
02541 break;
02542 case ' ':
02543 case '\t':
02544 if (!quoted && !escaped) {
02545
02546
02547 whitespace = 1;
02548 *(cur++) = '\0';
02549 } else
02550
02551 goto normal;
02552 break;
02553 case '\\':
02554
02555 if (escaped) {
02556 goto normal;
02557 } else {
02558 escaped=1;
02559 }
02560 break;
02561 default:
02562 normal:
02563 if (whitespace) {
02564 if (x >= MAX_ARGS -1) {
02565 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
02566 break;
02567 }
02568
02569 argv[x++] = cur;
02570 whitespace=0;
02571 }
02572 *(cur++) = *s;
02573 escaped=0;
02574 }
02575 s++;
02576 }
02577
02578 *(cur++) = '\0';
02579 argv[x] = NULL;
02580 *max = x;
02581 return 0;
02582 }
02583
02584 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead)
02585 {
02586 char *argv[MAX_ARGS];
02587 int argc = MAX_ARGS, res;
02588 agi_command *c;
02589 const char *ami_res = "Unknown Result";
02590 char *ami_cmd = ast_strdupa(buf);
02591 int command_id = ast_random(), resultcode = 200;
02592
02593 manager_event(EVENT_FLAG_AGI, "AGIExec",
02594 "SubEvent: Start\r\n"
02595 "Channel: %s\r\n"
02596 "CommandId: %d\r\n"
02597 "Command: %s\r\n", chan->name, command_id, ami_cmd);
02598 parse_args(buf, &argc, argv);
02599 if ((c = find_command(argv, 0)) && (!dead || (dead && c->dead))) {
02600
02601
02602 if (c->mod != ast_module_info->self)
02603 ast_module_ref(c->mod);
02604
02605
02606 if (chan->cdr && !ast_check_hangup(chan) && strcasecmp(argv[0], "EXEC"))
02607 ast_cdr_setapp(chan->cdr, "AGI", buf);
02608
02609 res = c->handler(chan, agi, argc, argv);
02610 if (c->mod != ast_module_info->self)
02611 ast_module_unref(c->mod);
02612 switch (res) {
02613 case RESULT_SHOWUSAGE: ami_res = "Usage"; resultcode = 520; break;
02614 case RESULT_FAILURE: ami_res = "Failure"; resultcode = -1; break;
02615 case RESULT_SUCCESS: ami_res = "Success"; resultcode = 200; break;
02616 }
02617 manager_event(EVENT_FLAG_AGI, "AGIExec",
02618 "SubEvent: End\r\n"
02619 "Channel: %s\r\n"
02620 "CommandId: %d\r\n"
02621 "Command: %s\r\n"
02622 "ResultCode: %d\r\n"
02623 "Result: %s\r\n", chan->name, command_id, ami_cmd, resultcode, ami_res);
02624 switch(res) {
02625 case RESULT_SHOWUSAGE:
02626 ast_agi_send(agi->fd, chan, "520-Invalid command syntax. Proper usage follows:\n");
02627 ast_agi_send(agi->fd, chan, "%s", c->usage);
02628 ast_agi_send(agi->fd, chan, "520 End of proper usage.\n");
02629 break;
02630 case RESULT_FAILURE:
02631
02632
02633 return -1;
02634 }
02635 } else if ((c = find_command(argv, 0))) {
02636 ast_agi_send(agi->fd, chan, "511 Command Not Permitted on a dead channel\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: 511\r\n"
02643 "Result: Command not permitted on a dead channel\r\n", chan->name, command_id, ami_cmd);
02644 } else {
02645 ast_agi_send(agi->fd, chan, "510 Invalid or unknown command\n");
02646 manager_event(EVENT_FLAG_AGI, "AGIExec",
02647 "SubEvent: End\r\n"
02648 "Channel: %s\r\n"
02649 "CommandId: %d\r\n"
02650 "Command: %s\r\n"
02651 "ResultCode: 510\r\n"
02652 "Result: Invalid or unknown command\r\n", chan->name, command_id, ami_cmd);
02653 }
02654 return 0;
02655 }
02656 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
02657 {
02658 struct ast_channel *c;
02659 int outfd, ms, needhup = 0;
02660 enum agi_result returnstatus = AGI_RESULT_SUCCESS;
02661 struct ast_frame *f;
02662 char buf[AGI_BUF_LEN];
02663 char *res = NULL;
02664 FILE *readf;
02665
02666
02667 int retry = AGI_NANDFS_RETRY;
02668 int send_sighup;
02669 const char *sighup_str;
02670
02671 ast_channel_lock(chan);
02672 sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
02673 send_sighup = ast_strlen_zero(sighup_str) || !ast_false(sighup_str);
02674 ast_channel_unlock(chan);
02675
02676 if (!(readf = fdopen(agi->ctrl, "r"))) {
02677 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
02678 if (send_sighup && pid > -1)
02679 kill(pid, SIGHUP);
02680 close(agi->ctrl);
02681 return AGI_RESULT_FAILURE;
02682 }
02683
02684 setlinebuf(readf);
02685 setup_env(chan, request, agi->fd, (agi->audio > -1), argc, argv);
02686 for (;;) {
02687 if (needhup) {
02688 needhup = 0;
02689 dead = 1;
02690 if (send_sighup) {
02691 if (pid > -1) {
02692 kill(pid, SIGHUP);
02693 } else if (agi->fast) {
02694 send(agi->ctrl, "HANGUP\n", 7, MSG_OOB);
02695 }
02696 }
02697 }
02698 ms = -1;
02699 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
02700 if (c) {
02701 retry = AGI_NANDFS_RETRY;
02702
02703 f = ast_read(c);
02704 if (!f) {
02705 ast_debug(1, "%s hungup\n", chan->name);
02706 returnstatus = AGI_RESULT_HANGUP;
02707 needhup = 1;
02708 continue;
02709 } else {
02710
02711 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
02712
02713 if (write(agi->audio, f->data.ptr, f->datalen) < 0) {
02714 }
02715 }
02716 ast_frfree(f);
02717 }
02718 } else if (outfd > -1) {
02719 size_t len = sizeof(buf);
02720 size_t buflen = 0;
02721
02722 retry = AGI_NANDFS_RETRY;
02723 buf[0] = '\0';
02724
02725 while (buflen < (len - 1)) {
02726 res = fgets(buf + buflen, len, readf);
02727 if (feof(readf))
02728 break;
02729 if (ferror(readf) && ((errno != EINTR) && (errno != EAGAIN)))
02730 break;
02731 if (res != NULL && !agi->fast)
02732 break;
02733 buflen = strlen(buf);
02734 if (buflen && buf[buflen - 1] == '\n')
02735 break;
02736 len -= buflen;
02737 if (agidebug)
02738 ast_verbose( "AGI Rx << temp buffer %s - errno %s\n", buf, strerror(errno));
02739 }
02740
02741 if (!buf[0]) {
02742
02743 if (returnstatus) {
02744 returnstatus = -1;
02745 }
02746 ast_verb(3, "<%s>AGI Script %s completed, returning %d\n", chan->name, request, returnstatus);
02747 if (pid > 0)
02748 waitpid(pid, status, 0);
02749
02750 pid = -1;
02751 break;
02752 }
02753
02754
02755 if (*buf && strncasecmp(buf, "failure", 7) == 0) {
02756 returnstatus = AGI_RESULT_FAILURE;
02757 break;
02758 }
02759
02760
02761 if (*buf && buf[strlen(buf) - 1] == '\n')
02762 buf[strlen(buf) - 1] = 0;
02763 if (agidebug)
02764 ast_verbose("<%s>AGI Rx << %s\n", chan->name, buf);
02765 returnstatus |= agi_handle_command(chan, agi, buf, dead);
02766
02767 if (returnstatus < 0) {
02768 needhup = 1;
02769 continue;
02770 }
02771 } else {
02772 if (--retry <= 0) {
02773 ast_log(LOG_WARNING, "No channel, no fd?\n");
02774 returnstatus = AGI_RESULT_FAILURE;
02775 break;
02776 }
02777 }
02778 }
02779 if (agi->speech) {
02780 ast_speech_destroy(agi->speech);
02781 }
02782
02783 if (send_sighup) {
02784 if (pid > -1) {
02785 if (kill(pid, SIGHUP)) {
02786 ast_log(LOG_WARNING, "unable to send SIGHUP to AGI process %d: %s\n", pid, strerror(errno));
02787 } else {
02788 usleep(1);
02789 }
02790 waitpid(pid, status, WNOHANG);
02791 } else if (agi->fast) {
02792 send(agi->ctrl, "HANGUP\n", 7, MSG_OOB);
02793 }
02794 }
02795 fclose(readf);
02796 return returnstatus;
02797 }
02798
02799 static char *handle_cli_agi_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02800 {
02801 struct agi_command *command;
02802 char fullcmd[80];
02803
02804 switch (cmd) {
02805 case CLI_INIT:
02806 e->command = "agi show";
02807 e->usage =
02808 "Usage: agi show [topic]\n"
02809 " When called with a topic as an argument, displays usage\n"
02810 " information on the given command. If called without a\n"
02811 " topic, it provides a list of AGI commands.\n";
02812 case CLI_GENERATE:
02813 return NULL;
02814 }
02815 if (a->argc < e->args)
02816 return CLI_SHOWUSAGE;
02817 if (a->argc > e->args) {
02818 command = find_command(a->argv + e->args, 1);
02819 if (command) {
02820 ast_cli(a->fd, "%s", command->usage);
02821 ast_cli(a->fd, " Runs Dead : %s\n", command->dead ? "Yes" : "No");
02822 } else {
02823 if (find_command(a->argv + e->args, -1)) {
02824 return help_workhorse(a->fd, a->argv + e->args);
02825 } else {
02826 ast_join(fullcmd, sizeof(fullcmd), a->argv + e->args);
02827 ast_cli(a->fd, "No such command '%s'.\n", fullcmd);
02828 }
02829 }
02830 } else {
02831 return help_workhorse(a->fd, NULL);
02832 }
02833 return CLI_SUCCESS;
02834 }
02835
02836
02837
02838
02839 static void write_html_escaped(FILE *htmlfile, char *str)
02840 {
02841 char *cur = str;
02842
02843 while(*cur) {
02844 switch (*cur) {
02845 case '<':
02846 fprintf(htmlfile, "%s", "<");
02847 break;
02848 case '>':
02849 fprintf(htmlfile, "%s", ">");
02850 break;
02851 case '&':
02852 fprintf(htmlfile, "%s", "&");
02853 break;
02854 case '"':
02855 fprintf(htmlfile, "%s", """);
02856 break;
02857 default:
02858 fprintf(htmlfile, "%c", *cur);
02859 break;
02860 }
02861 cur++;
02862 }
02863
02864 return;
02865 }
02866
02867 static int write_htmldump(char *filename)
02868 {
02869 struct agi_command *command;
02870 char fullcmd[80];
02871 FILE *htmlfile;
02872
02873 if (!(htmlfile = fopen(filename, "wt")))
02874 return -1;
02875
02876 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
02877 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
02878 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
02879
02880 AST_RWLIST_RDLOCK(&agi_commands);
02881 AST_RWLIST_TRAVERSE(&agi_commands, command, list) {
02882 char *stringp, *tempstr;
02883
02884 if (!command->cmda[0])
02885 break;
02886
02887 if ((command->cmda[0])[0] == '_')
02888 continue;
02889 ast_join(fullcmd, sizeof(fullcmd), command->cmda);
02890
02891 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
02892 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TH></TR>\n", fullcmd, command->summary);
02893
02894 stringp = command->usage;
02895 tempstr = strsep(&stringp, "\n");
02896
02897 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">");
02898 write_html_escaped(htmlfile, tempstr);
02899 fprintf(htmlfile, "</TD></TR>\n");
02900 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
02901
02902 while ((tempstr = strsep(&stringp, "\n")) != NULL) {
02903 write_html_escaped(htmlfile, tempstr);
02904 fprintf(htmlfile, "<BR>\n");
02905 }
02906 fprintf(htmlfile, "</TD></TR>\n");
02907 fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
02908 }
02909 AST_RWLIST_UNLOCK(&agi_commands);
02910 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
02911 fclose(htmlfile);
02912 return 0;
02913 }
02914
02915 static char *handle_cli_agi_dumphtml_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02916 {
02917 switch (cmd) {
02918 case CLI_INIT:
02919 e->command = "agi dumphtml";
02920 e->usage =
02921 "Usage: agi dumphtml <filename>\n"
02922 " Dumps the AGI command list in HTML format to the given\n"
02923 " file.\n";
02924 return NULL;
02925 case CLI_GENERATE:
02926 return NULL;
02927 }
02928 if (a->argc < e->args + 1)
02929 return CLI_SHOWUSAGE;
02930
02931 if (write_htmldump(a->argv[2]) < 0) {
02932 ast_cli(a->fd, "Could not create file '%s'\n", a->argv[2]);
02933 return CLI_SHOWUSAGE;
02934 }
02935 ast_cli(a->fd, "AGI HTML commands dumped to: %s\n", a->argv[2]);
02936 return CLI_SUCCESS;
02937 }
02938
02939 static char *handle_cli_agi_dump_html(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02940 {
02941 switch (cmd) {
02942 case CLI_INIT:
02943 e->command = "agi dump html";
02944 e->usage =
02945 "Usage: agi dump html <filename>\n"
02946 " Dumps the AGI command list in HTML format to the given\n"
02947 " file.\n";
02948 return NULL;
02949 case CLI_GENERATE:
02950 return NULL;
02951 }
02952 if (a->argc != e->args + 1)
02953 return CLI_SHOWUSAGE;
02954
02955 if (write_htmldump(a->argv[e->args]) < 0) {
02956 ast_cli(a->fd, "Could not create file '%s'\n", a->argv[e->args]);
02957 return CLI_SHOWUSAGE;
02958 }
02959 ast_cli(a->fd, "AGI HTML commands dumped to: %s\n", a->argv[e->args]);
02960 return CLI_SUCCESS;
02961 }
02962
02963 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead)
02964 {
02965 enum agi_result res;
02966 char buf[AGI_BUF_LEN] = "", *tmp = buf;
02967 int fds[2], efd = -1, pid;
02968 AST_DECLARE_APP_ARGS(args,
02969 AST_APP_ARG(arg)[MAX_ARGS];
02970 );
02971 AGI agi;
02972
02973 if (ast_strlen_zero(data)) {
02974 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
02975 return -1;
02976 }
02977 if (dead)
02978 ast_debug(3, "Hungup channel detected, running agi in dead mode.\n");
02979 ast_copy_string(buf, data, sizeof(buf));
02980 memset(&agi, 0, sizeof(agi));
02981 AST_STANDARD_APP_ARGS(args, tmp);
02982 args.argv[args.argc] = NULL;
02983 #if 0
02984
02985 if (chan->_state != AST_STATE_UP) {
02986 if (ast_answer(chan))
02987 return -1;
02988 }
02989 #endif
02990 res = launch_script(chan, args.argv[0], args.argv, fds, enhanced ? &efd : NULL, &pid);
02991
02992
02993 if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {
02994 int status = 0;
02995 agi.fd = fds[1];
02996 agi.ctrl = fds[0];
02997 agi.audio = efd;
02998 agi.fast = (res == AGI_RESULT_SUCCESS_FAST) ? 1 : 0;
02999 res = run_agi(chan, args.argv[0], &agi, pid, &status, dead, args.argc, args.argv);
03000
03001 if ((res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) && status)
03002 res = AGI_RESULT_FAILURE;
03003 if (fds[1] != fds[0])
03004 close(fds[1]);
03005 if (efd > -1)
03006 close(efd);
03007 }
03008 ast_safe_fork_cleanup();
03009
03010 switch (res) {
03011 case AGI_RESULT_SUCCESS:
03012 case AGI_RESULT_SUCCESS_FAST:
03013 case AGI_RESULT_SUCCESS_ASYNC:
03014 pbx_builtin_setvar_helper(chan, "AGISTATUS", "SUCCESS");
03015 break;
03016 case AGI_RESULT_FAILURE:
03017 pbx_builtin_setvar_helper(chan, "AGISTATUS", "FAILURE");
03018 break;
03019 case AGI_RESULT_NOTFOUND:
03020 pbx_builtin_setvar_helper(chan, "AGISTATUS", "NOTFOUND");
03021 break;
03022 case AGI_RESULT_HANGUP:
03023 pbx_builtin_setvar_helper(chan, "AGISTATUS", "HANGUP");
03024 return -1;
03025 }
03026
03027 return 0;
03028 }
03029
03030 static int agi_exec(struct ast_channel *chan, void *data)
03031 {
03032 if (!ast_check_hangup(chan))
03033 return agi_exec_full(chan, data, 0, 0);
03034 else
03035 return agi_exec_full(chan, data, 0, 1);
03036 }
03037
03038 static int eagi_exec(struct ast_channel *chan, void *data)
03039 {
03040 int readformat, res;
03041
03042 if (ast_check_hangup(chan)) {
03043 ast_log(LOG_ERROR, "EAGI cannot be run on a dead/hungup channel, please use AGI.\n");
03044 return 0;
03045 }
03046 readformat = chan->readformat;
03047 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
03048 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
03049 return -1;
03050 }
03051 res = agi_exec_full(chan, data, 1, 0);
03052 if (!res) {
03053 if (ast_set_read_format(chan, readformat)) {
03054 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
03055 }
03056 }
03057 return res;
03058 }
03059
03060 static int deadagi_exec(struct ast_channel *chan, void *data)
03061 {
03062 ast_log(LOG_WARNING, "DeadAGI has been deprecated, please use AGI in all cases!\n");
03063 return agi_exec(chan, data);
03064 }
03065
03066 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");
03067
03068 static struct ast_cli_entry cli_agi[] = {
03069 AST_CLI_DEFINE(handle_cli_agi_add_cmd, "Add AGI command to a channel in Async AGI"),
03070 AST_CLI_DEFINE(handle_cli_agi_debug, "Enable/Disable AGI debugging"),
03071 AST_CLI_DEFINE(handle_cli_agi_show, "List AGI commands or specific help"),
03072 AST_CLI_DEFINE(handle_cli_agi_dump_html, "Dumps a list of AGI commands in HTML format", .deprecate_cmd = &cli_agi_dumphtml_deprecated)
03073 };
03074
03075 static int unload_module(void)
03076 {
03077 ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
03078
03079
03080
03081 (void) ast_agi_unregister_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
03082 ast_unregister_application(eapp);
03083 ast_unregister_application(deadapp);
03084 ast_manager_unregister("AGI");
03085 return ast_unregister_application(app);
03086 }
03087
03088 static int load_module(void)
03089 {
03090 ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
03091
03092
03093
03094 (void) ast_agi_register_multiple(ast_module_info->self, commands, ARRAY_LEN(commands));
03095 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
03096 ast_register_application(eapp, eagi_exec, esynopsis, descrip);
03097 ast_manager_register2("AGI", EVENT_FLAG_AGI, action_add_agi_cmd, "Add an AGI command to execute by Async AGI", mandescr_asyncagi);
03098 return ast_register_application(app, agi_exec, synopsis, descrip);
03099 }
03100
03101 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Asterisk Gateway Interface (AGI)",
03102 .load = load_module,
03103 .unload = unload_module,
03104 );