![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
res_agi.c
Go to the documentation of this file.
00001 /* 00002 * Asterisk -- An open source telephony toolkit. 00003 * 00004 * Copyright (C) 1999 - 2006, Digium, Inc. 00005 * 00006 * Mark Spencer <markster@digium.com> 00007 * 00008 * See http://www.asterisk.org for more information about 00009 * the Asterisk project. Please do not directly contact 00010 * any of the maintainers of this project for assistance; 00011 * the project provides a web site, mailing lists and IRC 00012 * channels for your use. 00013 * 00014 * This program is free software, distributed under the terms of 00015 * the GNU General Public License Version 2. See the LICENSE file 00016 * at the top of the source tree. 00017 */ 00018 00019 /*! \file 00020 * 00021 * \brief AGI - the Asterisk Gateway Interface 00022 * 00023 * \author Mark Spencer <markster@digium.com> 00024 */ 00025 00026 #include "asterisk.h" 00027 00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 48776 $") 00029 00030 #include <sys/types.h> 00031 #include <netdb.h> 00032 #include <sys/socket.h> 00033 #include <netinet/in.h> 00034 #include <netinet/tcp.h> 00035 #include <arpa/inet.h> 00036 #include <math.h> 00037 #include <stdlib.h> 00038 #include <unistd.h> 00039 #include <string.h> 00040 #include <stdlib.h> 00041 #include <signal.h> 00042 #include <sys/time.h> 00043 #include <stdio.h> 00044 #include <fcntl.h> 00045 #include <errno.h> 00046 #include <sys/wait.h> 00047 00048 #include "asterisk/file.h" 00049 #include "asterisk/logger.h" 00050 #include "asterisk/channel.h" 00051 #include "asterisk/pbx.h" 00052 #include "asterisk/module.h" 00053 #include "asterisk/astdb.h" 00054 #include "asterisk/callerid.h" 00055 #include "asterisk/cli.h" 00056 #include "asterisk/logger.h" 00057 #include "asterisk/options.h" 00058 #include "asterisk/image.h" 00059 #include "asterisk/say.h" 00060 #include "asterisk/app.h" 00061 #include "asterisk/dsp.h" 00062 #include "asterisk/musiconhold.h" 00063 #include "asterisk/utils.h" 00064 #include "asterisk/lock.h" 00065 #include "asterisk/strings.h" 00066 #include "asterisk/agi.h" 00067 00068 #define MAX_ARGS 128 00069 #define MAX_COMMANDS 128 00070 00071 /* Recycle some stuff from the CLI interface */ 00072 #define fdprintf agi_debug_cli 00073 00074 static char *app = "AGI"; 00075 00076 static char *eapp = "EAGI"; 00077 00078 static char *deadapp = "DeadAGI"; 00079 00080 static char *synopsis = "Executes an AGI compliant application"; 00081 static char *esynopsis = "Executes an EAGI compliant application"; 00082 static char *deadsynopsis = "Executes AGI on a hungup channel"; 00083 00084 static char *descrip = 00085 " [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n" 00086 "program on a channel. AGI allows Asterisk to launch external programs\n" 00087 "written in any language to control a telephony channel, play audio,\n" 00088 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n" 00089 "and stdout.\n" 00090 " This channel will stop dialplan execution on hangup inside of this\n" 00091 "application, except when using DeadAGI. Otherwise, dialplan execution\n" 00092 "will continue normally.\n" 00093 " A locally executed AGI script will receive SIGHUP on hangup from the channel\n" 00094 "except when using DeadAGI. This can be disabled by setting the AGISIGHUP channel\n" 00095 "variable to \"no\" before executing the AGI application.\n" 00096 " Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n" 00097 "on file descriptor 3\n\n" 00098 " Use the CLI command 'agi show' to list available agi commands\n" 00099 " This application sets the following channel variable upon completion:\n" 00100 " AGISTATUS The status of the attempt to the run the AGI script\n" 00101 " text string, one of SUCCESS | FAILED | HANGUP\n"; 00102 00103 static int agidebug = 0; 00104 00105 #define TONE_BLOCK_SIZE 200 00106 00107 /* Max time to connect to an AGI remote host */ 00108 #define MAX_AGI_CONNECT 2000 00109 00110 #define AGI_PORT 4573 00111 00112 enum agi_result { 00113 AGI_RESULT_SUCCESS, 00114 AGI_RESULT_FAILURE, 00115 AGI_RESULT_HANGUP 00116 }; 00117 00118 static void agi_debug_cli(int fd, char *fmt, ...) 00119 { 00120 char *stuff; 00121 int res = 0; 00122 00123 va_list ap; 00124 va_start(ap, fmt); 00125 res = vasprintf(&stuff, fmt, ap); 00126 va_end(ap); 00127 if (res == -1) { 00128 ast_log(LOG_ERROR, "Out of memory\n"); 00129 } else { 00130 if (agidebug) 00131 ast_verbose("AGI Tx >> %s\n", stuff); 00132 ast_carefulwrite(fd, stuff, strlen(stuff), 100); 00133 free(stuff); 00134 } 00135 } 00136 00137 /* launch_netscript: The fastagi handler. 00138 FastAGI defaults to port 4573 */ 00139 static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, int *efd, int *opid) 00140 { 00141 int s; 00142 int flags; 00143 struct pollfd pfds[1]; 00144 char *host; 00145 char *c; int port = AGI_PORT; 00146 char *script=""; 00147 struct sockaddr_in sin; 00148 struct hostent *hp; 00149 struct ast_hostent ahp; 00150 int res; 00151 00152 /* agiusl is "agi://host.domain[:port][/script/name]" */ 00153 host = ast_strdupa(agiurl + 6); /* Remove agi:// */ 00154 /* Strip off any script name */ 00155 if ((c = strchr(host, '/'))) { 00156 *c = '\0'; 00157 c++; 00158 script = c; 00159 } 00160 if ((c = strchr(host, ':'))) { 00161 *c = '\0'; 00162 c++; 00163 port = atoi(c); 00164 } 00165 if (efd) { 00166 ast_log(LOG_WARNING, "AGI URI's don't support Enhanced AGI yet\n"); 00167 return -1; 00168 } 00169 hp = ast_gethostbyname(host, &ahp); 00170 if (!hp) { 00171 ast_log(LOG_WARNING, "Unable to locate host '%s'\n", host); 00172 return -1; 00173 } 00174 s = socket(AF_INET, SOCK_STREAM, 0); 00175 if (s < 0) { 00176 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno)); 00177 return -1; 00178 } 00179 flags = fcntl(s, F_GETFL); 00180 if (flags < 0) { 00181 ast_log(LOG_WARNING, "Fcntl(F_GETFL) failed: %s\n", strerror(errno)); 00182 close(s); 00183 return -1; 00184 } 00185 if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) { 00186 ast_log(LOG_WARNING, "Fnctl(F_SETFL) failed: %s\n", strerror(errno)); 00187 close(s); 00188 return -1; 00189 } 00190 memset(&sin, 0, sizeof(sin)); 00191 sin.sin_family = AF_INET; 00192 sin.sin_port = htons(port); 00193 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); 00194 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) && (errno != EINPROGRESS)) { 00195 ast_log(LOG_WARNING, "Connect failed with unexpected error: %s\n", strerror(errno)); 00196 close(s); 00197 return AGI_RESULT_FAILURE; 00198 } 00199 00200 pfds[0].fd = s; 00201 pfds[0].events = POLLOUT; 00202 while ((res = poll(pfds, 1, MAX_AGI_CONNECT)) != 1) { 00203 if (errno != EINTR) { 00204 if (!res) { 00205 ast_log(LOG_WARNING, "FastAGI connection to '%s' timed out after MAX_AGI_CONNECT (%d) milliseconds.\n", 00206 agiurl, MAX_AGI_CONNECT); 00207 } else 00208 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno)); 00209 close(s); 00210 return AGI_RESULT_FAILURE; 00211 } 00212 } 00213 /* XXX in theory should check for partial writes... */ 00214 while (write(s, "agi_network: yes\n", strlen("agi_network: yes\n")) < 0) { 00215 if (errno != EINTR) { 00216 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", agiurl, strerror(errno)); 00217 close(s); 00218 return AGI_RESULT_FAILURE; 00219 } 00220 } 00221 00222 /* If we have a script parameter, relay it to the fastagi server */ 00223 /* Script parameters take the form of: AGI(agi://my.example.com/?extension=${EXTEN}) */ 00224 if (!ast_strlen_zero(script)) 00225 fdprintf(s, "agi_network_script: %s\n", script); 00226 00227 if (option_debug > 3) 00228 ast_log(LOG_DEBUG, "Wow, connected!\n"); 00229 fds[0] = s; 00230 fds[1] = s; 00231 *opid = -1; 00232 return AGI_RESULT_SUCCESS; 00233 } 00234 00235 static enum agi_result launch_script(char *script, char *argv[], int *fds, int *efd, int *opid) 00236 { 00237 char tmp[256]; 00238 int pid; 00239 int toast[2]; 00240 int fromast[2]; 00241 int audio[2]; 00242 int x; 00243 int res; 00244 sigset_t signal_set, old_set; 00245 00246 if (!strncasecmp(script, "agi://", 6)) 00247 return launch_netscript(script, argv, fds, efd, opid); 00248 00249 if (script[0] != '/') { 00250 snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_AGI_DIR, script); 00251 script = tmp; 00252 } 00253 if (pipe(toast)) { 00254 ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno)); 00255 return AGI_RESULT_FAILURE; 00256 } 00257 if (pipe(fromast)) { 00258 ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno)); 00259 close(toast[0]); 00260 close(toast[1]); 00261 return AGI_RESULT_FAILURE; 00262 } 00263 if (efd) { 00264 if (pipe(audio)) { 00265 ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno)); 00266 close(fromast[0]); 00267 close(fromast[1]); 00268 close(toast[0]); 00269 close(toast[1]); 00270 return AGI_RESULT_FAILURE; 00271 } 00272 res = fcntl(audio[1], F_GETFL); 00273 if (res > -1) 00274 res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK); 00275 if (res < 0) { 00276 ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno)); 00277 close(fromast[0]); 00278 close(fromast[1]); 00279 close(toast[0]); 00280 close(toast[1]); 00281 close(audio[0]); 00282 close(audio[1]); 00283 return AGI_RESULT_FAILURE; 00284 } 00285 } 00286 00287 /* Block SIGHUP during the fork - prevents a race */ 00288 sigfillset(&signal_set); 00289 pthread_sigmask(SIG_BLOCK, &signal_set, &old_set); 00290 pid = fork(); 00291 if (pid < 0) { 00292 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); 00293 pthread_sigmask(SIG_SETMASK, &old_set, NULL); 00294 return AGI_RESULT_FAILURE; 00295 } 00296 if (!pid) { 00297 /* Pass paths to AGI via environmental variables */ 00298 setenv("AST_CONFIG_DIR", ast_config_AST_CONFIG_DIR, 1); 00299 setenv("AST_CONFIG_FILE", ast_config_AST_CONFIG_FILE, 1); 00300 setenv("AST_MODULE_DIR", ast_config_AST_MODULE_DIR, 1); 00301 setenv("AST_SPOOL_DIR", ast_config_AST_SPOOL_DIR, 1); 00302 setenv("AST_MONITOR_DIR", ast_config_AST_MONITOR_DIR, 1); 00303 setenv("AST_VAR_DIR", ast_config_AST_VAR_DIR, 1); 00304 setenv("AST_DATA_DIR", ast_config_AST_DATA_DIR, 1); 00305 setenv("AST_LOG_DIR", ast_config_AST_LOG_DIR, 1); 00306 setenv("AST_AGI_DIR", ast_config_AST_AGI_DIR, 1); 00307 setenv("AST_KEY_DIR", ast_config_AST_KEY_DIR, 1); 00308 setenv("AST_RUN_DIR", ast_config_AST_RUN_DIR, 1); 00309 00310 /* Don't run AGI scripts with realtime priority -- it causes audio stutter */ 00311 ast_set_priority(0); 00312 00313 /* Redirect stdin and out, provide enhanced audio channel if desired */ 00314 dup2(fromast[0], STDIN_FILENO); 00315 dup2(toast[1], STDOUT_FILENO); 00316 if (efd) { 00317 dup2(audio[0], STDERR_FILENO + 1); 00318 } else { 00319 close(STDERR_FILENO + 1); 00320 } 00321 00322 /* Before we unblock our signals, return our trapped signals back to the defaults */ 00323 signal(SIGHUP, SIG_DFL); 00324 signal(SIGCHLD, SIG_DFL); 00325 signal(SIGINT, SIG_DFL); 00326 signal(SIGURG, SIG_DFL); 00327 signal(SIGTERM, SIG_DFL); 00328 signal(SIGPIPE, SIG_DFL); 00329 signal(SIGXFSZ, SIG_DFL); 00330 00331 /* unblock important signal handlers */ 00332 if (pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)) { 00333 ast_log(LOG_WARNING, "unable to unblock signals for AGI script: %s\n", strerror(errno)); 00334 _exit(1); 00335 } 00336 00337 /* Close everything but stdin/out/error */ 00338 for (x=STDERR_FILENO + 2;x<1024;x++) 00339 close(x); 00340 00341 /* Execute script */ 00342 /* XXX argv should be deprecated in favor of passing agi_argX paramaters */ 00343 execv(script, argv); 00344 /* Can't use ast_log since FD's are closed */ 00345 fprintf(stdout, "verbose \"Failed to execute '%s': %s\" 2\n", script, strerror(errno)); 00346 fflush(stdout); 00347 _exit(1); 00348 } 00349 pthread_sigmask(SIG_SETMASK, &old_set, NULL); 00350 if (option_verbose > 2) 00351 ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script); 00352 fds[0] = toast[0]; 00353 fds[1] = fromast[1]; 00354 if (efd) { 00355 *efd = audio[1]; 00356 } 00357 /* close what we're not using in the parent */ 00358 close(toast[1]); 00359 close(fromast[0]); 00360 00361 if (efd) 00362 close(audio[0]); 00363 00364 *opid = pid; 00365 return AGI_RESULT_SUCCESS; 00366 } 00367 00368 static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced, int argc, char *argv[]) 00369 { 00370 int count; 00371 00372 /* Print initial environment, with agi_request always being the first 00373 thing */ 00374 fdprintf(fd, "agi_request: %s\n", request); 00375 fdprintf(fd, "agi_channel: %s\n", chan->name); 00376 fdprintf(fd, "agi_language: %s\n", chan->language); 00377 fdprintf(fd, "agi_type: %s\n", chan->tech->type); 00378 fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid); 00379 00380 /* ANI/DNIS */ 00381 fdprintf(fd, "agi_callerid: %s\n", S_OR(chan->cid.cid_num, "unknown")); 00382 fdprintf(fd, "agi_calleridname: %s\n", S_OR(chan->cid.cid_name, "unknown")); 00383 fdprintf(fd, "agi_callingpres: %d\n", chan->cid.cid_pres); 00384 fdprintf(fd, "agi_callingani2: %d\n", chan->cid.cid_ani2); 00385 fdprintf(fd, "agi_callington: %d\n", chan->cid.cid_ton); 00386 fdprintf(fd, "agi_callingtns: %d\n", chan->cid.cid_tns); 00387 fdprintf(fd, "agi_dnid: %s\n", S_OR(chan->cid.cid_dnid, "unknown")); 00388 fdprintf(fd, "agi_rdnis: %s\n", S_OR(chan->cid.cid_rdnis, "unknown")); 00389 00390 /* Context information */ 00391 fdprintf(fd, "agi_context: %s\n", chan->context); 00392 fdprintf(fd, "agi_extension: %s\n", chan->exten); 00393 fdprintf(fd, "agi_priority: %d\n", chan->priority); 00394 fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0"); 00395 00396 /* User information */ 00397 fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : ""); 00398 00399 /* Send any parameters to the fastagi server that have been passed via the agi application */ 00400 /* Agi application paramaters take the form of: AGI(/path/to/example/script|${EXTEN}) */ 00401 for(count = 1; count < argc; count++) 00402 fdprintf(fd, "agi_arg_%d: %s\n", count, argv[count]); 00403 00404 /* End with empty return */ 00405 fdprintf(fd, "\n"); 00406 } 00407 00408 static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00409 { 00410 int res; 00411 res = 0; 00412 if (chan->_state != AST_STATE_UP) { 00413 /* Answer the chan */ 00414 res = ast_answer(chan); 00415 } 00416 fdprintf(agi->fd, "200 result=%d\n", res); 00417 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00418 } 00419 00420 static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00421 { 00422 int res; 00423 int to; 00424 if (argc != 4) 00425 return RESULT_SHOWUSAGE; 00426 if (sscanf(argv[3], "%d", &to) != 1) 00427 return RESULT_SHOWUSAGE; 00428 res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl); 00429 fdprintf(agi->fd, "200 result=%d\n", res); 00430 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00431 } 00432 00433 static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00434 { 00435 int res; 00436 if (argc != 3) 00437 return RESULT_SHOWUSAGE; 00438 /* At the moment, the parser (perhaps broken) returns with 00439 the last argument PLUS the newline at the end of the input 00440 buffer. This probably needs to be fixed, but I wont do that 00441 because other stuff may break as a result. The right way 00442 would probably be to strip off the trailing newline before 00443 parsing, then here, add a newline at the end of the string 00444 before sending it to ast_sendtext --DUDE */ 00445 res = ast_sendtext(chan, argv[2]); 00446 fdprintf(agi->fd, "200 result=%d\n", res); 00447 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00448 } 00449 00450 static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00451 { 00452 int res; 00453 if (argc != 3) 00454 return RESULT_SHOWUSAGE; 00455 res = ast_recvchar(chan,atoi(argv[2])); 00456 if (res == 0) { 00457 fdprintf(agi->fd, "200 result=%d (timeout)\n", res); 00458 return RESULT_SUCCESS; 00459 } 00460 if (res > 0) { 00461 fdprintf(agi->fd, "200 result=%d\n", res); 00462 return RESULT_SUCCESS; 00463 } 00464 else { 00465 fdprintf(agi->fd, "200 result=%d (hangup)\n", res); 00466 return RESULT_FAILURE; 00467 } 00468 } 00469 00470 static int handle_recvtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00471 { 00472 char *buf; 00473 00474 if (argc != 3) 00475 return RESULT_SHOWUSAGE; 00476 buf = ast_recvtext(chan,atoi(argv[2])); 00477 if (buf) { 00478 fdprintf(agi->fd, "200 result=1 (%s)\n", buf); 00479 free(buf); 00480 } else { 00481 fdprintf(agi->fd, "200 result=-1\n"); 00482 } 00483 return RESULT_SUCCESS; 00484 } 00485 00486 static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00487 { 00488 int res,x; 00489 if (argc != 3) 00490 return RESULT_SHOWUSAGE; 00491 if (!strncasecmp(argv[2],"on",2)) 00492 x = 1; 00493 else 00494 x = 0; 00495 if (!strncasecmp(argv[2],"mate",4)) 00496 x = 2; 00497 if (!strncasecmp(argv[2],"tdd",3)) 00498 x = 1; 00499 res = ast_channel_setoption(chan, AST_OPTION_TDD, &x, sizeof(char), 0); 00500 if (res != RESULT_SUCCESS) 00501 fdprintf(agi->fd, "200 result=0\n"); 00502 else 00503 fdprintf(agi->fd, "200 result=1\n"); 00504 return RESULT_SUCCESS; 00505 } 00506 00507 static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00508 { 00509 int res; 00510 if (argc != 3) 00511 return RESULT_SHOWUSAGE; 00512 res = ast_send_image(chan, argv[2]); 00513 if (!ast_check_hangup(chan)) 00514 res = 0; 00515 fdprintf(agi->fd, "200 result=%d\n", res); 00516 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00517 } 00518 00519 static int handle_controlstreamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00520 { 00521 int res = 0; 00522 int skipms = 3000; 00523 char *fwd = NULL; 00524 char *rev = NULL; 00525 char *pause = NULL; 00526 char *stop = NULL; 00527 00528 if (argc < 5 || argc > 9) 00529 return RESULT_SHOWUSAGE; 00530 00531 if (!ast_strlen_zero(argv[4])) 00532 stop = argv[4]; 00533 else 00534 stop = NULL; 00535 00536 if ((argc > 5) && (sscanf(argv[5], "%d", &skipms) != 1)) 00537 return RESULT_SHOWUSAGE; 00538 00539 if (argc > 6 && !ast_strlen_zero(argv[6])) 00540 fwd = argv[6]; 00541 else 00542 fwd = "#"; 00543 00544 if (argc > 7 && !ast_strlen_zero(argv[7])) 00545 rev = argv[7]; 00546 else 00547 rev = "*"; 00548 00549 if (argc > 8 && !ast_strlen_zero(argv[8])) 00550 pause = argv[8]; 00551 else 00552 pause = NULL; 00553 00554 res = ast_control_streamfile(chan, argv[3], fwd, rev, stop, pause, NULL, skipms); 00555 00556 fdprintf(agi->fd, "200 result=%d\n", res); 00557 00558 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00559 } 00560 00561 static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00562 { 00563 int res; 00564 int vres; 00565 struct ast_filestream *fs; 00566 struct ast_filestream *vfs; 00567 long sample_offset = 0; 00568 long max_length; 00569 char *edigits = ""; 00570 00571 if (argc < 4 || argc > 5) 00572 return RESULT_SHOWUSAGE; 00573 00574 if (argv[3]) 00575 edigits = argv[3]; 00576 00577 if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1)) 00578 return RESULT_SHOWUSAGE; 00579 00580 fs = ast_openstream(chan, argv[2], chan->language); 00581 00582 if (!fs) { 00583 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset); 00584 return RESULT_SUCCESS; 00585 } 00586 vfs = ast_openvstream(chan, argv[2], chan->language); 00587 if (vfs && option_debug) 00588 ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n"); 00589 00590 if (option_verbose > 2) 00591 ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (sample_offset %ld)\n", argv[2], edigits, sample_offset); 00592 00593 ast_seekstream(fs, 0, SEEK_END); 00594 max_length = ast_tellstream(fs); 00595 ast_seekstream(fs, sample_offset, SEEK_SET); 00596 res = ast_applystream(chan, fs); 00597 if (vfs) 00598 vres = ast_applystream(chan, vfs); 00599 res = ast_playstream(fs); 00600 if (vfs) 00601 vres = ast_playstream(vfs); 00602 00603 if (res) { 00604 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset); 00605 return (res >= 0) ? RESULT_SHOWUSAGE : RESULT_FAILURE; 00606 } 00607 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl); 00608 /* this is to check for if ast_waitstream closed the stream, we probably are at 00609 * the end of the stream, return that amount, else check for the amount */ 00610 sample_offset = (chan->stream) ? ast_tellstream(fs) : max_length; 00611 ast_stopstream(chan); 00612 if (res == 1) { 00613 /* Stop this command, don't print a result line, as there is a new command */ 00614 return RESULT_SUCCESS; 00615 } 00616 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset); 00617 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00618 } 00619 00620 /* get option - really similar to the handle_streamfile, but with a timeout */ 00621 static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00622 { 00623 int res; 00624 int vres; 00625 struct ast_filestream *fs; 00626 struct ast_filestream *vfs; 00627 long sample_offset = 0; 00628 long max_length; 00629 int timeout = 0; 00630 char *edigits = ""; 00631 00632 if ( argc < 4 || argc > 5 ) 00633 return RESULT_SHOWUSAGE; 00634 00635 if ( argv[3] ) 00636 edigits = argv[3]; 00637 00638 if ( argc == 5 ) 00639 timeout = atoi(argv[4]); 00640 else if (chan->pbx->dtimeout) { 00641 /* by default dtimeout is set to 5sec */ 00642 timeout = chan->pbx->dtimeout * 1000; /* in msec */ 00643 } 00644 00645 fs = ast_openstream(chan, argv[2], chan->language); 00646 if (!fs) { 00647 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset); 00648 ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]); 00649 return RESULT_SUCCESS; 00650 } 00651 vfs = ast_openvstream(chan, argv[2], chan->language); 00652 if (vfs && option_debug) 00653 ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n"); 00654 00655 if (option_verbose > 2) 00656 ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (escape_digits=%s) (timeout %d)\n", argv[2], edigits, timeout); 00657 00658 ast_seekstream(fs, 0, SEEK_END); 00659 max_length = ast_tellstream(fs); 00660 ast_seekstream(fs, sample_offset, SEEK_SET); 00661 res = ast_applystream(chan, fs); 00662 if (vfs) 00663 vres = ast_applystream(chan, vfs); 00664 res = ast_playstream(fs); 00665 if (vfs) 00666 vres = ast_playstream(vfs); 00667 if (res) { 00668 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset); 00669 if (res >= 0) 00670 return RESULT_SHOWUSAGE; 00671 else 00672 return RESULT_FAILURE; 00673 } 00674 res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl); 00675 /* this is to check for if ast_waitstream closed the stream, we probably are at 00676 * the end of the stream, return that amount, else check for the amount */ 00677 sample_offset = (chan->stream)?ast_tellstream(fs):max_length; 00678 ast_stopstream(chan); 00679 if (res == 1) { 00680 /* Stop this command, don't print a result line, as there is a new command */ 00681 return RESULT_SUCCESS; 00682 } 00683 00684 /* If the user didnt press a key, wait for digitTimeout*/ 00685 if (res == 0 ) { 00686 res = ast_waitfordigit_full(chan, timeout, agi->audio, agi->ctrl); 00687 /* Make sure the new result is in the escape digits of the GET OPTION */ 00688 if ( !strchr(edigits,res) ) 00689 res=0; 00690 } 00691 00692 fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset); 00693 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00694 } 00695 00696 00697 00698 00699 /*--- handle_saynumber: Say number in various language syntaxes ---*/ 00700 /* Need to add option for gender here as well. Coders wanted */ 00701 /* While waiting, we're sending a NULL. */ 00702 static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00703 { 00704 int res; 00705 int num; 00706 if (argc != 4) 00707 return RESULT_SHOWUSAGE; 00708 if (sscanf(argv[2], "%d", &num) != 1) 00709 return RESULT_SHOWUSAGE; 00710 res = ast_say_number_full(chan, num, argv[3], chan->language, NULL, agi->audio, agi->ctrl); 00711 if (res == 1) 00712 return RESULT_SUCCESS; 00713 fdprintf(agi->fd, "200 result=%d\n", res); 00714 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00715 } 00716 00717 static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00718 { 00719 int res; 00720 int num; 00721 00722 if (argc != 4) 00723 return RESULT_SHOWUSAGE; 00724 if (sscanf(argv[2], "%d", &num) != 1) 00725 return RESULT_SHOWUSAGE; 00726 00727 res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); 00728 if (res == 1) /* New command */ 00729 return RESULT_SUCCESS; 00730 fdprintf(agi->fd, "200 result=%d\n", res); 00731 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00732 } 00733 00734 static int handle_sayalpha(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00735 { 00736 int res; 00737 00738 if (argc != 4) 00739 return RESULT_SHOWUSAGE; 00740 00741 res = ast_say_character_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); 00742 if (res == 1) /* New command */ 00743 return RESULT_SUCCESS; 00744 fdprintf(agi->fd, "200 result=%d\n", res); 00745 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00746 } 00747 00748 static int handle_saydate(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00749 { 00750 int res; 00751 int num; 00752 if (argc != 4) 00753 return RESULT_SHOWUSAGE; 00754 if (sscanf(argv[2], "%d", &num) != 1) 00755 return RESULT_SHOWUSAGE; 00756 res = ast_say_date(chan, num, argv[3], chan->language); 00757 if (res == 1) 00758 return RESULT_SUCCESS; 00759 fdprintf(agi->fd, "200 result=%d\n", res); 00760 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00761 } 00762 00763 static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00764 { 00765 int res; 00766 int num; 00767 if (argc != 4) 00768 return RESULT_SHOWUSAGE; 00769 if (sscanf(argv[2], "%d", &num) != 1) 00770 return RESULT_SHOWUSAGE; 00771 res = ast_say_time(chan, num, argv[3], chan->language); 00772 if (res == 1) 00773 return RESULT_SUCCESS; 00774 fdprintf(agi->fd, "200 result=%d\n", res); 00775 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00776 } 00777 00778 static int handle_saydatetime(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00779 { 00780 int res=0; 00781 time_t unixtime; 00782 char *format, *zone=NULL; 00783 00784 if (argc < 4) 00785 return RESULT_SHOWUSAGE; 00786 00787 if (argc > 4) { 00788 format = argv[4]; 00789 } else { 00790 /* XXX this doesn't belong here, but in the 'say' module */ 00791 if (!strcasecmp(chan->language, "de")) { 00792 format = "A dBY HMS"; 00793 } else { 00794 format = "ABdY 'digits/at' IMp"; 00795 } 00796 } 00797 00798 if (argc > 5 && !ast_strlen_zero(argv[5])) 00799 zone = argv[5]; 00800 00801 if (ast_get_time_t(argv[2], &unixtime, 0, NULL)) 00802 return RESULT_SHOWUSAGE; 00803 00804 res = ast_say_date_with_format(chan, unixtime, argv[3], chan->language, format, zone); 00805 if (res == 1) 00806 return RESULT_SUCCESS; 00807 00808 fdprintf(agi->fd, "200 result=%d\n", res); 00809 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00810 } 00811 00812 static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00813 { 00814 int res; 00815 00816 if (argc != 4) 00817 return RESULT_SHOWUSAGE; 00818 00819 res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); 00820 if (res == 1) /* New command */ 00821 return RESULT_SUCCESS; 00822 fdprintf(agi->fd, "200 result=%d\n", res); 00823 return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE; 00824 } 00825 00826 static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00827 { 00828 int res; 00829 char data[1024]; 00830 int max; 00831 int timeout; 00832 00833 if (argc < 3) 00834 return RESULT_SHOWUSAGE; 00835 if (argc >= 4) 00836 timeout = atoi(argv[3]); 00837 else 00838 timeout = 0; 00839 if (argc >= 5) 00840 max = atoi(argv[4]); 00841 else 00842 max = 1024; 00843 res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl); 00844 if (res == 2) /* New command */ 00845 return RESULT_SUCCESS; 00846 else if (res == 1) 00847 fdprintf(agi->fd, "200 result=%s (timeout)\n", data); 00848 else if (res < 0 ) 00849 fdprintf(agi->fd, "200 result=-1\n"); 00850 else 00851 fdprintf(agi->fd, "200 result=%s\n", data); 00852 return RESULT_SUCCESS; 00853 } 00854 00855 static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00856 { 00857 00858 if (argc != 3) 00859 return RESULT_SHOWUSAGE; 00860 ast_copy_string(chan->context, argv[2], sizeof(chan->context)); 00861 fdprintf(agi->fd, "200 result=0\n"); 00862 return RESULT_SUCCESS; 00863 } 00864 00865 static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv) 00866 { 00867 if (argc != 3) 00868 return RESULT_SHOWUSAGE; 00869 ast_copy_string(chan->exten, argv[2], sizeof(chan->exten)); 00870 fdprintf(agi->fd, "200 result=0\n"); 00871 return RESULT_SUCCESS; 00872 } 00873 00874 static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv) 00875 { 00876 int pri; 00877 if (argc != 3) 00878 return RESULT_SHOWUSAGE; 00879 00880 if (sscanf(argv[2], "%d", &pri) != 1) { 00881 if ((pri = ast_findlabel_extension(chan, chan->context, chan->exten, argv[2], chan->cid.cid_num)) < 1) 00882 return RESULT_SHOWUSAGE; 00883 } 00884 00885 ast_explicit_goto(chan, NULL, NULL, pri); 00886 fdprintf(agi->fd, "200 result=0\n"); 00887 return RESULT_SUCCESS; 00888 } 00889 00890 static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 00891 { 00892 struct ast_filestream *fs; 00893 struct ast_frame *f; 00894 struct timeval start; 00895 long sample_offset = 0; 00896 int res = 0; 00897 int ms; 00898 00899 struct ast_dsp *sildet=NULL; /* silence detector dsp */ 00900 int totalsilence = 0; 00901 int dspsilence = 0; 00902 int silence = 0; /* amount of silence to allow */ 00903 int gotsilence = 0; /* did we timeout for silence? */ 00904 char *silencestr=NULL; 00905 int rfmt=0; 00906 00907 00908 /* XXX EAGI FIXME XXX */ 00909 00910 if (argc < 6) 00911 return RESULT_SHOWUSAGE; 00912 if (sscanf(argv[5], "%d", &ms) != 1) 00913 return RESULT_SHOWUSAGE; 00914 00915 if (argc > 6) 00916 silencestr = strchr(argv[6],'s'); 00917 if ((argc > 7) && (!silencestr)) 00918 silencestr = strchr(argv[7],'s'); 00919 if ((argc > 8) && (!silencestr)) 00920 silencestr = strchr(argv[8],'s'); 00921 00922 if (silencestr) { 00923 if (strlen(silencestr) > 2) { 00924 if ((silencestr[0] == 's') && (silencestr[1] == '=')) { 00925 silencestr++; 00926 silencestr++; 00927 if (silencestr) 00928 silence = atoi(silencestr); 00929 if (silence > 0) 00930 silence *= 1000; 00931 } 00932 } 00933 } 00934 00935 if (silence > 0) { 00936 rfmt = chan->readformat; 00937 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); 00938 if (res < 0) { 00939 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); 00940 return -1; 00941 } 00942 sildet = ast_dsp_new(); 00943 if (!sildet) { 00944 ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); 00945 return -1; 00946 } 00947 ast_dsp_set_threshold(sildet, 256); 00948 } 00949 00950 /* backward compatibility, if no offset given, arg[6] would have been 00951 * caught below and taken to be a beep, else if it is a digit then it is a 00952 * offset */ 00953 if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '='))) 00954 res = ast_streamfile(chan, "beep", chan->language); 00955 00956 if ((argc > 7) && (!strchr(argv[7], '='))) 00957 res = ast_streamfile(chan, "beep", chan->language); 00958 00959 if (!res) 00960 res = ast_waitstream(chan, argv[4]); 00961 if (res) { 00962 fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset); 00963 } else { 00964 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, AST_FILE_MODE); 00965 if (!fs) { 00966 res = -1; 00967 fdprintf(agi->fd, "200 result=%d (writefile)\n", res); 00968 if (sildet) 00969 ast_dsp_free(sildet); 00970 return RESULT_FAILURE; 00971 } 00972 00973 /* Request a video update */ 00974 ast_indicate(chan, AST_CONTROL_VIDUPDATE); 00975 00976 chan->stream = fs; 00977 ast_applystream(chan,fs); 00978 /* really should have checks */ 00979 ast_seekstream(fs, sample_offset, SEEK_SET); 00980 ast_truncstream(fs); 00981 00982 start = ast_tvnow(); 00983 while ((ms < 0) || ast_tvdiff_ms(ast_tvnow(), start) < ms) { 00984 res = ast_waitfor(chan, -1); 00985 if (res < 0) { 00986 ast_closestream(fs); 00987 fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset); 00988 if (sildet) 00989 ast_dsp_free(sildet); 00990 return RESULT_FAILURE; 00991 } 00992 f = ast_read(chan); 00993 if (!f) { 00994 fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset); 00995 ast_closestream(fs); 00996 if (sildet) 00997 ast_dsp_free(sildet); 00998 return RESULT_FAILURE; 00999 } 01000 switch(f->frametype) { 01001 case AST_FRAME_DTMF: 01002 if (strchr(argv[4], f->subclass)) { 01003 /* This is an interrupting chracter, so rewind to chop off any small 01004 amount of DTMF that may have been recorded 01005 */ 01006 ast_stream_rewind(fs, 200); 01007 ast_truncstream(fs); 01008 sample_offset = ast_tellstream(fs); 01009 fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset); 01010 ast_closestream(fs); 01011 ast_frfree(f); 01012 if (sildet) 01013 ast_dsp_free(sildet); 01014 return RESULT_SUCCESS; 01015 } 01016 break; 01017 case AST_FRAME_VOICE: 01018 ast_writestream(fs, f); 01019 /* this is a safe place to check progress since we know that fs 01020 * is valid after a write, and it will then have our current 01021 * location */ 01022 sample_offset = ast_tellstream(fs); 01023 if (silence > 0) { 01024 dspsilence = 0; 01025 ast_dsp_silence(sildet, f, &dspsilence); 01026 if (dspsilence) { 01027 totalsilence = dspsilence; 01028 } else { 01029 totalsilence = 0; 01030 } 01031 if (totalsilence > silence) { 01032 /* Ended happily with silence */ 01033 gotsilence = 1; 01034 break; 01035 } 01036 } 01037 break; 01038 case AST_FRAME_VIDEO: 01039 ast_writestream(fs, f); 01040 default: 01041 /* Ignore all other frames */ 01042 break; 01043 } 01044 ast_frfree(f); 01045 if (gotsilence) 01046 break; 01047 } 01048 01049 if (gotsilence) { 01050 ast_stream_rewind(fs, silence-1000); 01051 ast_truncstream(fs); 01052 sample_offset = ast_tellstream(fs); 01053 } 01054 fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset); 01055 ast_closestream(fs); 01056 } 01057 01058 if (silence > 0) { 01059 res = ast_set_read_format(chan, rfmt); 01060 if (res) 01061 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); 01062 ast_dsp_free(sildet); 01063 } 01064 return RESULT_SUCCESS; 01065 } 01066 01067 static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 01068 { 01069 int timeout; 01070 01071 if (argc != 3) 01072 return RESULT_SHOWUSAGE; 01073 if (sscanf(argv[2], "%d", &timeout) != 1) 01074 return RESULT_SHOWUSAGE; 01075 if (timeout < 0) 01076 timeout = 0; 01077 if (timeout) 01078 chan->whentohangup = time(NULL) + timeout; 01079 else 01080 chan->whentohangup = 0; 01081 fdprintf(agi->fd, "200 result=0\n"); 01082 return RESULT_SUCCESS; 01083 } 01084 01085 static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01086 { 01087 struct ast_channel *c; 01088 if (argc == 1) { 01089 /* no argument: hangup the current channel */ 01090 ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT); 01091 fdprintf(agi->fd, "200 result=1\n"); 01092 return RESULT_SUCCESS; 01093 } else if (argc == 2) { 01094 /* one argument: look for info on the specified channel */ 01095 c = ast_get_channel_by_name_locked(argv[1]); 01096 if (c) { 01097 /* we have a matching channel */ 01098 ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT); 01099 fdprintf(agi->fd, "200 result=1\n"); 01100 ast_channel_unlock(c); 01101 return RESULT_SUCCESS; 01102 } 01103 /* if we get this far no channel name matched the argument given */ 01104 fdprintf(agi->fd, "200 result=-1\n"); 01105 return RESULT_SUCCESS; 01106 } else { 01107 return RESULT_SHOWUSAGE; 01108 } 01109 } 01110 01111 static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01112 { 01113 int res; 01114 struct ast_app *app; 01115 01116 if (argc < 2) 01117 return RESULT_SHOWUSAGE; 01118 01119 if (option_verbose > 2) 01120 ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]); 01121 01122 app = pbx_findapp(argv[1]); 01123 01124 if (app) { 01125 res = pbx_exec(chan, app, argv[2]); 01126 } else { 01127 ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]); 01128 res = -2; 01129 } 01130 fdprintf(agi->fd, "200 result=%d\n", res); 01131 01132 return res; 01133 } 01134 01135 static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01136 { 01137 char tmp[256]=""; 01138 char *l = NULL, *n = NULL; 01139 01140 if (argv[2]) { 01141 ast_copy_string(tmp, argv[2], sizeof(tmp)); 01142 ast_callerid_parse(tmp, &n, &l); 01143 if (l) 01144 ast_shrink_phone_number(l); 01145 else 01146 l = ""; 01147 if (!n) 01148 n = ""; 01149 ast_set_callerid(chan, l, n, NULL); 01150 } 01151 01152 fdprintf(agi->fd, "200 result=1\n"); 01153 return RESULT_SUCCESS; 01154 } 01155 01156 static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01157 { 01158 struct ast_channel *c; 01159 if (argc == 2) { 01160 /* no argument: supply info on the current channel */ 01161 fdprintf(agi->fd, "200 result=%d\n", chan->_state); 01162 return RESULT_SUCCESS; 01163 } else if (argc == 3) { 01164 /* one argument: look for info on the specified channel */ 01165 c = ast_get_channel_by_name_locked(argv[2]); 01166 if (c) { 01167 fdprintf(agi->fd, "200 result=%d\n", c->_state); 01168 ast_channel_unlock(c); 01169 return RESULT_SUCCESS; 01170 } 01171 /* if we get this far no channel name matched the argument given */ 01172 fdprintf(agi->fd, "200 result=-1\n"); 01173 return RESULT_SUCCESS; 01174 } else { 01175 return RESULT_SHOWUSAGE; 01176 } 01177 } 01178 01179 static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01180 { 01181 if (argv[3]) 01182 pbx_builtin_setvar_helper(chan, argv[2], argv[3]); 01183 01184 fdprintf(agi->fd, "200 result=1\n"); 01185 return RESULT_SUCCESS; 01186 } 01187 01188 static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01189 { 01190 char *ret; 01191 char tempstr[1024]; 01192 01193 if (argc != 3) 01194 return RESULT_SHOWUSAGE; 01195 01196 /* check if we want to execute an ast_custom_function */ 01197 if (!ast_strlen_zero(argv[2]) && (argv[2][strlen(argv[2]) - 1] == ')')) { 01198 ret = ast_func_read(chan, argv[2], tempstr, sizeof(tempstr)) ? NULL : tempstr; 01199 } else { 01200 pbx_retrieve_variable(chan, argv[2], &ret, tempstr, sizeof(tempstr), NULL); 01201 } 01202 01203 if (ret) 01204 fdprintf(agi->fd, "200 result=1 (%s)\n", ret); 01205 else 01206 fdprintf(agi->fd, "200 result=0\n"); 01207 01208 return RESULT_SUCCESS; 01209 } 01210 01211 static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01212 { 01213 char tmp[4096] = ""; 01214 struct ast_channel *chan2=NULL; 01215 01216 if ((argc != 4) && (argc != 5)) 01217 return RESULT_SHOWUSAGE; 01218 if (argc == 5) { 01219 chan2 = ast_get_channel_by_name_locked(argv[4]); 01220 } else { 01221 chan2 = chan; 01222 } 01223 if (chan) { /* XXX isn't this chan2 ? */ 01224 pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1); 01225 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp); 01226 } else { 01227 fdprintf(agi->fd, "200 result=0\n"); 01228 } 01229 if (chan2 && (chan2 != chan)) 01230 ast_channel_unlock(chan2); 01231 return RESULT_SUCCESS; 01232 } 01233 01234 static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01235 { 01236 int level = 0; 01237 char *prefix; 01238 01239 if (argc < 2) 01240 return RESULT_SHOWUSAGE; 01241 01242 if (argv[2]) 01243 sscanf(argv[2], "%d", &level); 01244 01245 switch (level) { 01246 case 4: 01247 prefix = VERBOSE_PREFIX_4; 01248 break; 01249 case 3: 01250 prefix = VERBOSE_PREFIX_3; 01251 break; 01252 case 2: 01253 prefix = VERBOSE_PREFIX_2; 01254 break; 01255 case 1: 01256 default: 01257 prefix = VERBOSE_PREFIX_1; 01258 break; 01259 } 01260 01261 if (level <= option_verbose) 01262 ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]); 01263 01264 fdprintf(agi->fd, "200 result=1\n"); 01265 01266 return RESULT_SUCCESS; 01267 } 01268 01269 static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01270 { 01271 int res; 01272 char tmp[256]; 01273 01274 if (argc != 4) 01275 return RESULT_SHOWUSAGE; 01276 res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp)); 01277 if (res) 01278 fdprintf(agi->fd, "200 result=0\n"); 01279 else 01280 fdprintf(agi->fd, "200 result=1 (%s)\n", tmp); 01281 01282 return RESULT_SUCCESS; 01283 } 01284 01285 static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01286 { 01287 int res; 01288 01289 if (argc != 5) 01290 return RESULT_SHOWUSAGE; 01291 res = ast_db_put(argv[2], argv[3], argv[4]); 01292 fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1'); 01293 return RESULT_SUCCESS; 01294 } 01295 01296 static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01297 { 01298 int res; 01299 01300 if (argc != 4) 01301 return RESULT_SHOWUSAGE; 01302 res = ast_db_del(argv[2], argv[3]); 01303 fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1'); 01304 return RESULT_SUCCESS; 01305 } 01306 01307 static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv) 01308 { 01309 int res; 01310 if ((argc < 3) || (argc > 4)) 01311 return RESULT_SHOWUSAGE; 01312 if (argc == 4) 01313 res = ast_db_deltree(argv[2], argv[3]); 01314 else 01315 res = ast_db_deltree(argv[2], NULL); 01316 01317 fdprintf(agi->fd, "200 result=%c\n", res ? '0' : '1'); 01318 return RESULT_SUCCESS; 01319 } 01320 01321 static const char debug_usage[] = 01322 "Usage: agi debug\n" 01323 " Enables dumping of AGI transactions for debugging purposes\n"; 01324 01325 static const char no_debug_usage[] = 01326 "Usage: agi nodebug\n" 01327 " Disables dumping of AGI transactions for debugging purposes\n"; 01328 01329 static int agi_do_debug(int fd, int argc, char *argv[]) 01330 { 01331 if (argc != 2) 01332 return RESULT_SHOWUSAGE; 01333 agidebug = 1; 01334 ast_cli(fd, "AGI Debugging Enabled\n"); 01335 return RESULT_SUCCESS; 01336 } 01337 01338 static int agi_no_debug(int fd, int argc, char *argv[]) 01339 { 01340 if (argc != 2) 01341 return RESULT_SHOWUSAGE; 01342 agidebug = 0; 01343 ast_cli(fd, "AGI Debugging Disabled\n"); 01344 return RESULT_SUCCESS; 01345 } 01346 01347 static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[]) 01348 { 01349 fdprintf(agi->fd, "200 result=0\n"); 01350 return RESULT_SUCCESS; 01351 } 01352 01353 static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) 01354 { 01355 if (!strncasecmp(argv[2], "on", 2)) 01356 ast_moh_start(chan, argc > 3 ? argv[3] : NULL, NULL); 01357 else if (!strncasecmp(argv[2], "off", 3)) 01358 ast_moh_stop(chan); 01359 fdprintf(agi->fd, "200 result=0\n"); 01360 return RESULT_SUCCESS; 01361 } 01362 01363 static char usage_setmusic[] = 01364 " Usage: SET MUSIC ON <on|off> <class>\n" 01365 " Enables/Disables the music on hold generator. If <class> is\n" 01366 " not specified, then the default music on hold class will be used.\n" 01367 " Always returns 0.\n"; 01368 01369 static char usage_dbput[] = 01370 " Usage: DATABASE PUT <family> <key> <value>\n" 01371 " Adds or updates an entry in the Asterisk database for a\n" 01372 " given family, key, and value.\n" 01373 " Returns 1 if successful, 0 otherwise.\n"; 01374 01375 static char usage_dbget[] = 01376 " Usage: DATABASE GET <family> <key>\n" 01377 " Retrieves an entry in the Asterisk database for a\n" 01378 " given family and key.\n" 01379 " Returns 0 if <key> is not set. Returns 1 if <key>\n" 01380 " is set and returns the variable in parentheses.\n" 01381 " Example return code: 200 result=1 (testvariable)\n"; 01382 01383 static char usage_dbdel[] = 01384 " Usage: DATABASE DEL <family> <key>\n" 01385 " Deletes an entry in the Asterisk database for a\n" 01386 " given family and key.\n" 01387 " Returns 1 if successful, 0 otherwise.\n"; 01388 01389 static char usage_dbdeltree[] = 01390 " Usage: DATABASE DELTREE <family> [keytree]\n" 01391 " Deletes a family or specific keytree within a family\n" 01392 " in the Asterisk database.\n" 01393 " Returns 1 if successful, 0 otherwise.\n"; 01394 01395 static char usage_verbose[] = 01396 " Usage: VERBOSE <message> <level>\n" 01397 " Sends <message> to the console via verbose message system.\n" 01398 " <level> is the the verbose level (1-4)\n" 01399 " Always returns 1.\n"; 01400 01401 static char usage_getvariable[] = 01402 " Usage: GET VARIABLE <variablename>\n" 01403 " Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n" 01404 " is set and returns the variable in parentheses.\n" 01405 " example return code: 200 result=1 (testvariable)\n"; 01406 01407 static char usage_getvariablefull[] = 01408 " Usage: GET FULL VARIABLE <variablename> [<channel name>]\n" 01409 " Returns 0 if <variablename> is not set or channel does not exist. Returns 1\n" 01410 "if <variablename> is set and returns the variable in parenthesis. Understands\n" 01411 "complex variable names and builtin variables, unlike GET VARIABLE.\n" 01412 " example return code: 200 result=1 (testvariable)\n"; 01413 01414 static char usage_setvariable[] = 01415 " Usage: SET VARIABLE <variablename> <value>\n"; 01416 01417 static char usage_channelstatus[] = 01418 " Usage: CHANNEL STATUS [<channelname>]\n" 01419 " Returns the status of the specified channel.\n" 01420 " If no channel name is given the returns the status of the\n" 01421 " current channel. Return values:\n" 01422 " 0 Channel is down and available\n" 01423 " 1 Channel is down, but reserved\n" 01424 " 2 Channel is off hook\n" 01425 " 3 Digits (or equivalent) have been dialed\n" 01426 " 4 Line is ringing\n" 01427 " 5 Remote end is ringing\n" 01428 " 6 Line is up\n" 01429 " 7 Line is busy\n"; 01430 01431 static char usage_setcallerid[] = 01432 " Usage: SET CALLERID <number>\n" 01433 " Changes the callerid of the current channel.\n"; 01434 01435 static char usage_exec[] = 01436 " Usage: EXEC <application> <options>\n" 01437 " Executes <application> with given <options>.\n" 01438 " Returns whatever the application returns, or -2 on failure to find application\n"; 01439 01440 static char usage_hangup[] = 01441 " Usage: HANGUP [<channelname>]\n" 01442 " Hangs up the specified channel.\n" 01443 " If no channel name is given, hangs up the current channel\n"; 01444 01445 static char usage_answer[] = 01446 " Usage: ANSWER\n" 01447 " Answers channel if not already in answer state. Returns -1 on\n" 01448 " channel failure, or 0 if successful.\n"; 01449 01450 static char usage_waitfordigit[] = 01451 " Usage: WAIT FOR DIGIT <timeout>\n" 01452 " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n" 01453 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n" 01454 " the numerical value of the ascii of the digit if one is received. Use -1\n" 01455 " for the timeout value if you desire the call to block indefinitely.\n"; 01456 01457 static char usage_sendtext[] = 01458 " Usage: SEND TEXT \"<text to send>\"\n" 01459 " Sends the given text on a channel. Most channels do not support the\n" 01460 " transmission of text. Returns 0 if text is sent, or if the channel does not\n" 01461 " support text transmission. Returns -1 only on error/hangup. Text\n" 01462 " consisting of greater than one word should be placed in quotes since the\n" 01463 " command only accepts a single argument.\n"; 01464 01465 static char usage_recvchar[] = 01466 " Usage: RECEIVE CHAR <timeout>\n" 01467 " Receives a character of text on a channel. Specify timeout to be the\n" 01468 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n" 01469 " do not support the reception of text. Returns the decimal value of the character\n" 01470 " if one is received, or 0 if the channel does not support text reception. Returns\n" 01471 " -1 only on error/hangup.\n"; 01472 01473 static char usage_recvtext[] = 01474 " Usage: RECEIVE TEXT <timeout>\n" 01475 " Receives a string of text on a channel. Specify timeout to be the\n" 01476 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n" 01477 " do not support the reception of text. Returns -1 for failure or 1 for success, and the string in parentheses.\n"; 01478 01479 static char usage_tddmode[] = 01480 " Usage: TDD MODE <on|off>\n" 01481 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n" 01482 " successful, or 0 if channel is not TDD-capable.\n"; 01483 01484 static char usage_sendimage[] = 01485 " Usage: SEND IMAGE <image>\n" 01486 " Sends the given image on a channel. Most channels do not support the\n" 01487 " transmission of images. Returns 0 if image is sent, or if the channel does not\n" 01488 " support image transmission. Returns -1 only on error/hangup. Image names\n" 01489 " should not include extensions.\n"; 01490 01491 static char usage_streamfile[] = 01492 " Usage: STREAM FILE <filename> <escape digits> [sample offset]\n" 01493 " Send the given file, allowing playback to be interrupted by the given\n" 01494 " digits, if any. Use double quotes for the digits if you wish none to be\n" 01495 " permitted. If sample offset is provided then the audio will seek to sample\n" 01496 " offset before play starts. Returns 0 if playback completes without a digit\n" 01497 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n" 01498 " or -1 on error or if the channel was disconnected. Remember, the file\n" 01499 " extension must not be included in the filename.\n"; 01500 01501 static char usage_controlstreamfile[] = 01502 " Usage: CONTROL STREAM FILE <filename> <escape digits> [skipms] [ffchar] [rewchr] [pausechr]\n" 01503 " Send the given file, allowing playback to be controled by the given\n" 01504 " digits, if any. Use double quotes for the digits if you wish none to be\n" 01505 " permitted. Returns 0 if playback completes without a digit\n" 01506 " being pressed, or the ASCII numerical value of the digit if one was pressed,\n" 01507 " or -1 on error or if the channel was disconnected. Remember, the file\n" 01508 " extension must not be included in the filename.\n\n" 01509 " Note: ffchar and rewchar default to * and # respectively.\n"; 01510 01511 static char usage_getoption[] = 01512 " Usage: GET OPTION <filename> <escape_digits> [timeout]\n" 01513 " Behaves similar to STREAM FILE but used with a timeout option.\n"; 01514 01515 static char usage_saynumber[] = 01516 " Usage: SAY NUMBER <number> <escape digits>\n" 01517 " Say a given number, returning early if any of the given DTMF digits\n" 01518 " are received on the channel. Returns 0 if playback completes without a digit\n" 01519 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n" 01520 " -1 on error/hangup.\n"; 01521 01522 static char usage_saydigits[] = 01523 " Usage: SAY DIGITS <number> <escape digits>\n" 01524 " Say a given digit string, returning early if any of the given DTMF digits\n" 01525 " are received on the channel. Returns 0 if playback completes without a digit\n" 01526 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n" 01527 " -1 on error/hangup.\n"; 01528 01529 static char usage_sayalpha[] = 01530 " Usage: SAY ALPHA <number> <escape digits>\n" 01531 " Say a given character string, returning early if any of the given DTMF digits\n" 01532 " are received on the channel. Returns 0 if playback completes without a digit\n" 01533 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n" 01534 " -1 on error/hangup.\n"; 01535 01536 static char usage_saydate[] = 01537 " Usage: SAY DATE <date> <escape digits>\n" 01538 " Say a given date, returning early if any of the given DTMF digits are\n" 01539 " received on the channel. <date> is number of seconds elapsed since 00:00:00\n" 01540 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n" 01541 " completes without a digit being pressed, or the ASCII numerical value of the\n" 01542 " digit if one was pressed or -1 on error/hangup.\n"; 01543 01544 static char usage_saytime[] = 01545 " Usage: SAY TIME <time> <escape digits>\n" 01546 " Say a given time, returning early if any of the given DTMF digits are\n" 01547 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n" 01548 " on January 1, 1970, Coordinated Universal Time (UTC). Returns 0 if playback\n" 01549 " completes without a digit being pressed, or the ASCII numerical value of the\n" 01550 " digit if one was pressed or -1 on error/hangup.\n"; 01551 01552 static char usage_saydatetime[] = 01553 " Usage: SAY DATETIME <time> <escape digits> [format] [timezone]\n" 01554 " Say a given time, returning early if any of the given DTMF digits are\n" 01555 " received on the channel. <time> is number of seconds elapsed since 00:00:00\n" 01556 " on January 1, 1970, Coordinated Universal Time (UTC). [format] is the format\n" 01557 " the time should be said in. See voicemail.conf (defaults to \"ABdY\n" 01558 " 'digits/at' IMp\"). Acceptable values for [timezone] can be found in\n" 01559 " /usr/share/zoneinfo. Defaults to machine default. Returns 0 if playback\n" 01560 " completes without a digit being pressed, or the ASCII numerical value of the\n" 01561 " digit if one was pressed or -1 on error/hangup.\n"; 01562 01563 static char usage_sayphonetic[] = 01564 " Usage: SAY PHONETIC <string> <escape digits>\n" 01565 " Say a given character string with phonetics, returning early if any of the\n" 01566 " given DTMF digits are received on the channel. Returns 0 if playback\n" 01567 " completes without a digit pressed, the ASCII numerical value of the digit\n" 01568 " if one was pressed, or -1 on error/hangup.\n"; 01569 01570 static char usage_getdata[] = 01571 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n" 01572 " Stream the given file, and recieve DTMF data. Returns the digits received\n" 01573 "from the channel at the other end.\n"; 01574 01575 static char usage_setcontext[] = 01576 " Usage: SET CONTEXT <desired context>\n" 01577 " Sets the context for continuation upon exiting the application.\n"; 01578 01579 static char usage_setextension[] = 01580 " Usage: SET EXTENSION <new extension>\n" 01581 " Changes the extension for continuation upon exiting the application.\n"; 01582 01583 static char usage_setpriority[] = 01584 " Usage: SET PRIORITY <priority>\n" 01585 " Changes the priority for continuation upon exiting the application.\n" 01586 " The priority must be a valid priority or label.\n"; 01587 01588 static char usage_recordfile[] = 01589 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> \\\n" 01590 " [offset samples] [BEEP] [s=silence]\n" 01591 " Record to a file until a given dtmf digit in the sequence is received\n" 01592 " Returns -1 on hangup or error. The format will specify what kind of file\n" 01593 " will be recorded. The timeout is the maximum record time in milliseconds, or\n" 01594 " -1 for no timeout. \"Offset samples\" is optional, and, if provided, will seek\n" 01595 " to the offset without exceeding the end of the file. \"silence\" is the number\n" 01596 " of seconds of silence allowed before the function returns despite the\n" 01597 " lack of dtmf digits or reaching timeout. Silence value must be\n" 01598 " preceeded by \"s=\" and is also optional.\n"; 01599 01600 static char usage_autohangup[] = 01601 " Usage: SET AUTOHANGUP <time>\n" 01602 " Cause the channel to automatically hangup at <time> seconds in the\n" 01603 " future. Of course it can be hungup before then as well. Setting to 0 will\n" 01604 " cause the autohangup feature to be disabled on this channel.\n"; 01605 01606 static char usage_noop[] = 01607 " Usage: NoOp\n" 01608 " Does nothing.\n"; 01609 01610 static agi_command commands[MAX_COMMANDS] = { 01611 { { "answer", NULL }, handle_answer, "Answer channel", usage_answer }, 01612 { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus }, 01613 { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel }, 01614 { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree }, 01615 { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget }, 01616 { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput }, 01617 { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec }, 01618 { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata }, 01619 { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull }, 01620 { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption }, 01621 { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable }, 01622 { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup }, 01623 { { "noop", NULL }, handle_noop, "Does nothing", usage_noop }, 01624 { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar }, 01625 { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext }, 01626 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile }, 01627 { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha }, 01628 { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits }, 01629 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber }, 01630 { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic }, 01631 { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate }, 01632 { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime }, 01633 { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime }, 01634 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage }, 01635 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext }, 01636 { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup }, 01637 { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid }, 01638 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext }, 01639 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension }, 01640 { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }, 01641 { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority }, 01642 { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable }, 01643 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile }, 01644 { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile }, 01645 { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode }, 01646 { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose }, 01647 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit }, 01648 }; 01649 01650 static int help_workhorse(int fd, char *match[]) 01651 { 01652 char fullcmd[80]; 01653 char matchstr[80]; 01654 int x; 01655 struct agi_command *e; 01656 if (match) 01657 ast_join(matchstr, sizeof(matchstr), match); 01658 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) { 01659 e = &commands[x]; 01660 if (!e->cmda[0]) 01661 break; 01662 /* Hide commands that start with '_' */ 01663 if ((e->cmda[0])[0] == '_') 01664 continue; 01665 ast_join(fullcmd, sizeof(fullcmd), e->cmda); 01666 if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr))) 01667 continue; 01668 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary); 01669 } 01670 return 0; 01671 } 01672 01673 int ast_agi_register(agi_command *agi) 01674 { 01675 int x; 01676 for (x=0; x<MAX_COMMANDS - 1; x++) { 01677 if (commands[x].cmda[0] == agi->cmda[0]) { 01678 ast_log(LOG_WARNING, "Command already registered!\n"); 01679 return -1; 01680 } 01681 } 01682 for (x=0; x<MAX_COMMANDS - 1; x++) { 01683 if (!commands[x].cmda[0]) { 01684 commands[x] = *agi; 01685 return 0; 01686 } 01687 } 01688 ast_log(LOG_WARNING, "No more room for new commands!\n"); 01689 return -1; 01690 } 01691 01692 void ast_agi_unregister(agi_command *agi) 01693 { 01694 int x; 01695 for (x=0; x<MAX_COMMANDS - 1; x++) { 01696 if (commands[x].cmda[0] == agi->cmda[0]) { 01697 memset(&commands[x], 0, sizeof(agi_command)); 01698 } 01699 } 01700 } 01701 01702 static agi_command *find_command(char *cmds[], int exact) 01703 { 01704 int x; 01705 int y; 01706 int match; 01707 01708 for (x=0; x < sizeof(commands) / sizeof(commands[0]); x++) { 01709 if (!commands[x].cmda[0]) 01710 break; 01711 /* start optimistic */ 01712 match = 1; 01713 for (y=0; match && cmds[y]; y++) { 01714 /* If there are no more words in the command (and we're looking for 01715 an exact match) or there is a difference between the two words, 01716 then this is not a match */ 01717 if (!commands[x].cmda[y] && !exact) 01718 break; 01719 /* don't segfault if the next part of a command doesn't exist */ 01720 if (!commands[x].cmda[y]) 01721 return NULL; 01722 if (strcasecmp(commands[x].cmda[y], cmds[y])) 01723 match = 0; 01724 } 01725 /* If more words are needed to complete the command then this is not 01726 a candidate (unless we're looking for a really inexact answer */ 01727 if ((exact > -1) && commands[x].cmda[y]) 01728 match = 0; 01729 if (match) 01730 return &commands[x]; 01731 } 01732 return NULL; 01733 } 01734 01735 01736 static int parse_args(char *s, int *max, char *argv[]) 01737 { 01738 int x=0; 01739 int quoted=0; 01740 int escaped=0; 01741 int whitespace=1; 01742 char *cur; 01743 01744 cur = s; 01745 while(*s) { 01746 switch(*s) { 01747 case '"': 01748 /* If it's escaped, put a literal quote */ 01749 if (escaped) 01750 goto normal; 01751 else 01752 quoted = !quoted; 01753 if (quoted && whitespace) { 01754 /* If we're starting a quote, coming off white space start a new word, too */ 01755 argv[x++] = cur; 01756 whitespace=0; 01757 } 01758 escaped = 0; 01759 break; 01760 case ' ': 01761 case '\t': 01762 if (!quoted && !escaped) { 01763 /* If we're not quoted, mark this as whitespace, and 01764 end the previous argument */ 01765 whitespace = 1; 01766 *(cur++) = '\0'; 01767 } else 01768 /* Otherwise, just treat it as anything else */ 01769 goto normal; 01770 break; 01771 case '\\': 01772 /* If we're escaped, print a literal, otherwise enable escaping */ 01773 if (escaped) { 01774 goto normal; 01775 } else { 01776 escaped=1; 01777 } 01778 break; 01779 default: 01780 normal: 01781 if (whitespace) { 01782 if (x >= MAX_ARGS -1) { 01783 ast_log(LOG_WARNING, "Too many arguments, truncating\n"); 01784 break; 01785 } 01786 /* Coming off of whitespace, start the next argument */ 01787 argv[x++] = cur; 01788 whitespace=0; 01789 } 01790 *(cur++) = *s; 01791 escaped=0; 01792 } 01793 s++; 01794 } 01795 /* Null terminate */ 01796 *(cur++) = '\0'; 01797 argv[x] = NULL; 01798 *max = x; 01799 return 0; 01800 } 01801 01802 static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf) 01803 { 01804 char *argv[MAX_ARGS]; 01805 int argc = MAX_ARGS; 01806 int res; 01807 agi_command *c; 01808 01809 parse_args(buf, &argc, argv); 01810 c = find_command(argv, 0); 01811 if (c) { 01812 res = c->handler(chan, agi, argc, argv); 01813 switch(res) { 01814 case RESULT_SHOWUSAGE: 01815 fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n"); 01816 fdprintf(agi->fd, c->usage); 01817 fdprintf(agi->fd, "520 End of proper usage.\n"); 01818 break; 01819 case AST_PBX_KEEPALIVE: 01820 /* We've been asked to keep alive, so do so */ 01821 return AST_PBX_KEEPALIVE; 01822 break; 01823 case RESULT_FAILURE: 01824 /* They've already given the failure. We've been hung up on so handle this 01825 appropriately */ 01826 return -1; 01827 } 01828 } else { 01829 fdprintf(agi->fd, "510 Invalid or unknown command\n"); 01830 } 01831 return 0; 01832 } 01833 #define RETRY 3 01834 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[]) 01835 { 01836 struct ast_channel *c; 01837 int outfd; 01838 int ms; 01839 enum agi_result returnstatus = AGI_RESULT_SUCCESS; 01840 struct ast_frame *f; 01841 char buf[2048]; 01842 FILE *readf; 01843 /* how many times we'll retry if ast_waitfor_nandfs will return without either 01844 channel or file descriptor in case select is interrupted by a system call (EINTR) */ 01845 int retry = RETRY; 01846 01847 if (!(readf = fdopen(agi->ctrl, "r"))) { 01848 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n"); 01849 if (pid > -1) 01850 kill(pid, SIGHUP); 01851 close(agi->ctrl); 01852 return AGI_RESULT_FAILURE; 01853 } 01854 setlinebuf(readf); 01855 setup_env(chan, request, agi->fd, (agi->audio > -1), argc, argv); 01856 for (;;) { 01857 ms = -1; 01858 c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms); 01859 if (c) { 01860 retry = RETRY; 01861 /* Idle the channel until we get a command */ 01862 f = ast_read(c); 01863 if (!f) { 01864 if (option_debug) 01865 ast_log(LOG_DEBUG, "%s hungup\n", chan->name); 01866 returnstatus = AGI_RESULT_HANGUP; 01867 break; 01868 } else { 01869 /* If it's voice, write it to the audio pipe */ 01870 if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) { 01871 /* Write, ignoring errors */ 01872 write(agi->audio, f->data, f->datalen); 01873 } 01874 ast_frfree(f); 01875 } 01876 } else if (outfd > -1) { 01877 retry = RETRY; 01878 if (!fgets(buf, sizeof(buf), readf)) { 01879 /* Program terminated */ 01880 if (returnstatus) 01881 returnstatus = -1; 01882 if (option_verbose > 2) 01883 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus); 01884 if (pid > 0) 01885 waitpid(pid, status, 0); 01886 /* No need to kill the pid anymore, since they closed us */ 01887 pid = -1; 01888 break; 01889 } 01890 /* get rid of trailing newline, if any */ 01891 if (*buf && buf[strlen(buf) - 1] == '\n') 01892 buf[strlen(buf) - 1] = 0; 01893 if (agidebug) 01894 ast_verbose("AGI Rx << %s\n", buf); 01895 returnstatus |= agi_handle_command(chan, agi, buf); 01896 /* If the handle_command returns -1, we need to stop */ 01897 if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) { 01898 break; 01899 } 01900 } else { 01901 if (--retry <= 0) { 01902 ast_log(LOG_WARNING, "No channel, no fd?\n"); 01903 returnstatus = AGI_RESULT_FAILURE; 01904 break; 01905 } 01906 } 01907 } 01908 /* Notify process */ 01909 if (pid > -1) { 01910 const char *sighup = pbx_builtin_getvar_helper(chan, "AGISIGHUP"); 01911 if (ast_strlen_zero(sighup) || !ast_false(sighup)) { 01912 if (kill(pid, SIGHUP)) 01913 ast_log(LOG_WARNING, "unable to send SIGHUP to AGI process %d: %s\n", pid, strerror(errno)); 01914 } 01915 } 01916 fclose(readf); 01917 return returnstatus; 01918 } 01919 01920 static int handle_showagi(int fd, int argc, char *argv[]) 01921 { 01922 struct agi_command *e; 01923 char fullcmd[80]; 01924 if ((argc < 2)) 01925 return RESULT_SHOWUSAGE; 01926 if (argc > 2) { 01927 e = find_command(argv + 2, 1); 01928 if (e) 01929 ast_cli(fd, e->usage); 01930 else { 01931 if (find_command(argv + 2, -1)) { 01932 return help_workhorse(fd, argv + 1); 01933 } else { 01934 ast_join(fullcmd, sizeof(fullcmd), argv+1); 01935 ast_cli(fd, "No such command '%s'.\n", fullcmd); 01936 } 01937 } 01938 } else { 01939 return help_workhorse(fd, NULL); 01940 } 01941 return RESULT_SUCCESS; 01942 } 01943 01944 static int handle_agidumphtml(int fd, int argc, char *argv[]) 01945 { 01946 struct agi_command *e; 01947 char fullcmd[80]; 01948 int x; 01949 FILE *htmlfile; 01950 01951 if ((argc < 3)) 01952 return RESULT_SHOWUSAGE; 01953 01954 if (!(htmlfile = fopen(argv[2], "wt"))) { 01955 ast_cli(fd, "Could not create file '%s'\n", argv[2]); 01956 return RESULT_SHOWUSAGE; 01957 } 01958 01959 fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n"); 01960 fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n"); 01961 01962 01963 fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n"); 01964 01965 for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) { 01966 char *stringp, *tempstr; 01967 01968 e = &commands[x]; 01969 if (!e->cmda[0]) /* end ? */ 01970 break; 01971 /* Hide commands that start with '_' */ 01972 if ((e->cmda[0])[0] == '_') 01973 continue; 01974 ast_join(fullcmd, sizeof(fullcmd), e->cmda); 01975 01976 fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n"); 01977 fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary); 01978 01979 stringp=e->usage; 01980 tempstr = strsep(&stringp, "\n"); 01981 01982 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr); 01983 01984 fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n"); 01985 while ((tempstr = strsep(&stringp, "\n")) != NULL) 01986 fprintf(htmlfile, "%s<BR>\n",tempstr); 01987 fprintf(htmlfile, "</TD></TR>\n"); 01988 fprintf(htmlfile, "</TABLE></TD></TR>\n\n"); 01989 01990 } 01991 01992 fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n"); 01993 fclose(htmlfile); 01994 ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]); 01995 return RESULT_SUCCESS; 01996 } 01997 01998 static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead) 01999 { 02000 enum agi_result res; 02001 struct ast_module_user *u; 02002 char *argv[MAX_ARGS]; 02003 char buf[2048]=""; 02004 char *tmp = buf; 02005 int argc = 0; 02006 int fds[2]; 02007 int efd = -1; 02008 int pid; 02009 char *stringp; 02010 AGI agi; 02011 02012 if (ast_strlen_zero(data)) { 02013 ast_log(LOG_WARNING, "AGI requires an argument (script)\n"); 02014 return -1; 02015 } 02016 ast_copy_string(buf, data, sizeof(buf)); 02017 02018 memset(&agi, 0, sizeof(agi)); 02019 while ((stringp = strsep(&tmp, "|")) && argc < MAX_ARGS-1) 02020 argv[argc++] = stringp; 02021 argv[argc] = NULL; 02022 02023 u = ast_module_user_add(chan); 02024 #if 0 02025 /* Answer if need be */ 02026 if (chan->_state != AST_STATE_UP) { 02027 if (ast_answer(chan)) { 02028 LOCAL_USER_REMOVE(u); 02029 return -1; 02030 } 02031 } 02032 #endif 02033 res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid); 02034 if (res == AGI_RESULT_SUCCESS) { 02035 int status = 0; 02036 agi.fd = fds[1]; 02037 agi.ctrl = fds[0]; 02038 agi.audio = efd; 02039 res = run_agi(chan, argv[0], &agi, pid, &status, dead, argc, argv); 02040 /* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */ 02041 if (res == AGI_RESULT_SUCCESS && status) 02042 res = AGI_RESULT_FAILURE; 02043 if (fds[1] != fds[0]) 02044 close(fds[1]); 02045 if (efd > -1) 02046 close(efd); 02047 ast_unreplace_sigchld(); 02048 } 02049 ast_module_user_remove(u); 02050 02051 switch (res) { 02052 case AGI_RESULT_SUCCESS: 02053 pbx_builtin_setvar_helper(chan, "AGISTATUS", "SUCCESS"); 02054 break; 02055 case AGI_RESULT_FAILURE: 02056 pbx_builtin_setvar_helper(chan, "AGISTATUS", "FAILURE"); 02057 break; 02058 case AGI_RESULT_HANGUP: 02059 pbx_builtin_setvar_helper(chan, "AGISTATUS", "HANGUP"); 02060 return -1; 02061 } 02062 02063 return 0; 02064 } 02065 02066 static int agi_exec(struct ast_channel *chan, void *data) 02067 { 02068 if (chan->_softhangup) 02069 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n"); 02070 return agi_exec_full(chan, data, 0, 0); 02071 } 02072 02073 static int eagi_exec(struct ast_channel *chan, void *data) 02074 { 02075 int readformat; 02076 int res; 02077 02078 if (chan->_softhangup) 02079 ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n"); 02080 readformat = chan->readformat; 02081 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) { 02082 ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name); 02083 return -1; 02084 } 02085 res = agi_exec_full(chan, data, 1, 0); 02086 if (!res) { 02087 if (ast_set_read_format(chan, readformat)) { 02088 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat)); 02089 } 02090 } 02091 return res; 02092 } 02093 02094 static int deadagi_exec(struct ast_channel *chan, void *data) 02095 { 02096 return agi_exec_full(chan, data, 0, 1); 02097 } 02098 02099 static char showagi_help[] = 02100 "Usage: agi show [topic]\n" 02101 " When called with a topic as an argument, displays usage\n" 02102 " information on the given command. If called without a\n" 02103 " topic, it provides a list of AGI commands.\n"; 02104 02105 02106 static char dumpagihtml_help[] = 02107 "Usage: agi dumphtml <filename>\n" 02108 " Dumps the agi command list in html format to given filename\n"; 02109 02110 static struct ast_cli_entry cli_agi[] = { 02111 { { "agi", "debug", NULL }, 02112 agi_do_debug, "Enable AGI debugging", 02113 debug_usage }, 02114 02115 { { "agi", "debug", "off", NULL }, 02116 agi_no_debug, "Disable AGI debugging", 02117 no_debug_usage }, 02118 02119 { { "agi", "show", NULL }, 02120 handle_showagi, "List AGI commands or specific help", 02121 showagi_help }, 02122 02123 { { "agi", "dumphtml", NULL }, 02124 handle_agidumphtml, "Dumps a list of agi commands in html format", 02125 dumpagihtml_help }, 02126 }; 02127 02128 static int unload_module(void) 02129 { 02130 ast_module_user_hangup_all(); 02131 ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry)); 02132 ast_unregister_application(eapp); 02133 ast_unregister_application(deadapp); 02134 return ast_unregister_application(app); 02135 } 02136 02137 static int load_module(void) 02138 { 02139 ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry)); 02140 ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip); 02141 ast_register_application(eapp, eagi_exec, esynopsis, descrip); 02142 return ast_register_application(app, agi_exec, synopsis, descrip); 02143 } 02144 02145 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk Gateway Interface (AGI)");