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