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