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