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