/* * Asterisk -- A telephony toolkit for Linux. * * Asterisk Gateway Interface * * Copyright (C) 1999, Mark Spencer * * Mark Spencer * * This program is free software, distributed under the terms of * the GNU General Public License */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../asterisk.h" #include "../astconf.h" #define MAX_ARGS 128 /* Recycle some stuff from the CLI interface */ #define fdprintf ast_cli typedef struct agi_state { int fd; /* FD for general output */ int audio; /* FD for audio output */ int ctrl; /* FD for input control */ } AGI; typedef struct agi_command { /* Null terminated list of the words of the command */ char *cmda[AST_MAX_CMD_LEN]; /* Handler for the command (channel, AGI state, # of arguments, argument list). Returns RESULT_SHOWUSAGE for improper arguments */ int (*handler)(struct ast_channel *chan, AGI *agi, int argc, char *argv[]); /* Summary of the command (< 60 characters) */ char *summary; /* Detailed usage information */ char *usage; } agi_command; static char *tdesc = "Asterisk Gateway Interface (AGI)"; static char *app = "AGI"; static char *eapp = "EAGI"; static char *deadapp = "DeadAGI"; static char *synopsis = "Executes an AGI compliant application"; static char *esynopsis = "Executes an EAGI compliant application"; static char *deadsynopsis = "Executes AGI on a hungup channel"; static char *descrip = " [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n" "program on a channel. AGI allows Asterisk to launch external programs\n" "written in any language to control a telephony channel, play audio,\n" "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n" "and stdout.\n" "Returns -1 on hangup (except for DeadAGI) or if application requested\n" " hangup, or 0 on non-hangup exit. \n" "Using 'EAGI' provides enhanced AGI, with incoming audio available out of band" "on file descriptor 3\n\n" "Use the CLI command 'show agi' to list available agi commands\n"; STANDARD_LOCAL_USER; LOCAL_USER_DECL; #define TONE_BLOCK_SIZE 200 static int launch_script(char *script, char *argv[], int *fds, int *efd, int *opid) { char tmp[256]; int pid; int toast[2]; int fromast[2]; int audio[2]; int x; int res; if (script[0] != '/') { snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script); script = tmp; } if (pipe(toast)) { ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno)); return -1; } if (pipe(fromast)) { ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno)); close(toast[0]); close(toast[1]); return -1; } if (efd) { if (pipe(audio)) { ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno)); close(fromast[0]); close(fromast[1]); close(toast[0]); close(toast[1]); return -1; } res = fcntl(audio[1], F_GETFL); if (res > -1) res = fcntl(audio[1], F_SETFL, res | O_NONBLOCK); if (res < 0) { ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno)); close(fromast[0]); close(fromast[1]); close(toast[0]); close(toast[1]); close(audio[0]); close(audio[1]); return -1; } } pid = fork(); if (pid < 0) { ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); return -1; } if (!pid) { /* Redirect stdin and out, provide enhanced audio channel if desired */ dup2(fromast[0], STDIN_FILENO); dup2(toast[1], STDOUT_FILENO); if (efd) { dup2(audio[0], STDERR_FILENO + 1); } else { close(STDERR_FILENO + 1); } /* Close everything but stdin/out/error */ for (x=STDERR_FILENO + 2;x<1024;x++) close(x); /* Execute script */ execv(script, argv); /* Can't use ast_log since FD's are closed */ fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno)); exit(1); } if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script); fds[0] = toast[0]; fds[1] = fromast[1]; if (efd) { *efd = audio[1]; } /* close what we're not using in the parent */ close(toast[1]); close(fromast[0]); if (efd) { // [PHM 12/18/03] close(audio[0]); } *opid = pid; return 0; } static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced) { /* Print initial environment, with agi_request always being the first thing */ fdprintf(fd, "agi_request: %s\n", request); fdprintf(fd, "agi_channel: %s\n", chan->name); fdprintf(fd, "agi_language: %s\n", chan->language); fdprintf(fd, "agi_type: %s\n", chan->type); fdprintf(fd, "agi_uniqueid: %s\n", chan->uniqueid); /* ANI/DNIS */ fdprintf(fd, "agi_callerid: %s\n", chan->callerid ? chan->callerid : "unknown"); fdprintf(fd, "agi_dnid: %s\n", chan->dnid ? chan->dnid : "unknown"); fdprintf(fd, "agi_rdnis: %s\n", chan->rdnis ? chan->rdnis : "unknown"); /* Context information */ fdprintf(fd, "agi_context: %s\n", chan->context); fdprintf(fd, "agi_extension: %s\n", chan->exten); fdprintf(fd, "agi_priority: %d\n", chan->priority); fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0"); /* User information */ fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : ""); /* End with empty return */ fdprintf(fd, "\n"); } static int handle_answer(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; res = 0; if (chan->_state != AST_STATE_UP) { /* Answer the chan */ res = ast_answer(chan); } fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } static int handle_waitfordigit(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int to; if (argc != 4) return RESULT_SHOWUSAGE; if (sscanf(argv[3], "%i", &to) != 1) return RESULT_SHOWUSAGE; res = ast_waitfordigit_full(chan, to, agi->audio, agi->ctrl); fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } static int handle_sendtext(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 3) return RESULT_SHOWUSAGE; /* At the moment, the parser (perhaps broken) returns with the last argument PLUS the newline at the end of the input buffer. This probably needs to be fixed, but I wont do that because other stuff may break as a result. The right way would probably be to strip off the trailing newline before parsing, then here, add a newline at the end of the string before sending it to ast_sendtext --DUDE */ res = ast_sendtext(chan, argv[2]); fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } static int handle_recvchar(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 3) return RESULT_SHOWUSAGE; res = ast_recvchar(chan,atoi(argv[2])); if (res == 0) { fdprintf(agi->fd, "200 result=%d (timeout)\n", res); return RESULT_SUCCESS; } if (res > 0) { fdprintf(agi->fd, "200 result=%d\n", res); return RESULT_SUCCESS; } else { fdprintf(agi->fd, "200 result=%d (hangup)\n", res); return RESULT_FAILURE; } } static int handle_tddmode(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res,x; if (argc != 3) return RESULT_SHOWUSAGE; if (!strncasecmp(argv[2],"on",2)) x = 1; else x = 0; if (!strncasecmp(argv[2],"mate",4)) x = 2; if (!strncasecmp(argv[2],"tdd",3)) x = 1; res = ast_channel_setoption(chan,AST_OPTION_TDD,&x,sizeof(char),0); fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } static int handle_sendimage(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 3) return RESULT_SHOWUSAGE; res = ast_send_image(chan, argv[2]); if (!ast_check_hangup(chan)) res = 0; fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } static int handle_streamfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; struct ast_filestream *fs; long sample_offset = 0; long max_length; if (argc < 4) return RESULT_SHOWUSAGE; if (argc > 5) return RESULT_SHOWUSAGE; if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1)) return RESULT_SHOWUSAGE; fs = ast_openstream(chan, argv[2], chan->language); if(!fs){ fdprintf(agi->fd, "200 result=%d endpos=%ld\n", 0, sample_offset); ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]); return RESULT_FAILURE; } ast_seekstream(fs, 0, SEEK_END); max_length = ast_tellstream(fs); ast_seekstream(fs, sample_offset, SEEK_SET); res = ast_applystream(chan, fs); res = ast_playstream(fs); if (res) { fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset); if (res >= 0) return RESULT_SHOWUSAGE; else return RESULT_FAILURE; } res = ast_waitstream_full(chan, argv[3], agi->audio, agi->ctrl); /* this is to check for if ast_waitstream closed the stream, we probably are at * the end of the stream, return that amount, else check for the amount */ sample_offset = (chan->stream)?ast_tellstream(fs):max_length; ast_stopstream(chan); if (res == 1) { /* Stop this command, don't print a result line, as there is a new command */ return RESULT_SUCCESS; } fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } /*--- handle_saynumber: Say number in various language syntaxes ---*/ /* Need to add option for gender here as well. Coders wanted */ /* While waiting, we're sending a (char *) NULL. */ static int handle_saynumber(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int num; if (argc != 4) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%i", &num) != 1) return RESULT_SHOWUSAGE; res = ast_say_number_full(chan, num, argv[3], chan->language, (char *) NULL, agi->audio, agi->ctrl); if (res == 1) return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } static int handle_saydigits(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int num; if (argc != 4) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%i", &num) != 1) return RESULT_SHOWUSAGE; res = ast_say_digit_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } static int handle_saytime(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; int num; if (argc != 4) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%i", &num) != 1) return RESULT_SHOWUSAGE; res = ast_say_time(chan, num, argv[3], chan->language); if (res == 1) return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } static int handle_sayphonetic(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; if (argc != 4) return RESULT_SHOWUSAGE; res = ast_say_phonetic_str_full(chan, argv[2], argv[3], chan->language, agi->audio, agi->ctrl); if (res == 1) /* New command */ return RESULT_SUCCESS; fdprintf(agi->fd, "200 result=%d\n", res); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } static int handle_getdata(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int res; char data[1024]; int max; int timeout; if (argc < 3) return RESULT_SHOWUSAGE; if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0; if (argc >= 5) max = atoi(argv[4]); else max = 1024; res = ast_app_getdata_full(chan, argv[2], data, max, timeout, agi->audio, agi->ctrl); if (res == 2) /* New command */ return RESULT_SUCCESS; else if (res == 1) fdprintf(agi->fd, "200 result=%s (timeout)\n", data); else if (res < 0 ) fdprintf(agi->fd, "200 result=-1\n"); else fdprintf(agi->fd, "200 result=%s\n", data); if (res >= 0) return RESULT_SUCCESS; else return RESULT_FAILURE; } static int handle_setcontext(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { if (argc != 3) return RESULT_SHOWUSAGE; strncpy(chan->context, argv[2], sizeof(chan->context)-1); fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_setextension(struct ast_channel *chan, AGI *agi, int argc, char **argv) { if (argc != 3) return RESULT_SHOWUSAGE; strncpy(chan->exten, argv[2], sizeof(chan->exten)-1); fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_setpriority(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int pri; if (argc != 3) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%i", &pri) != 1) return RESULT_SHOWUSAGE; chan->priority = pri - 1; fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { struct ast_filestream *fs; struct ast_frame *f; struct timeval tv, start; long sample_offset = 0; int res = 0; int ms; struct ast_dsp *sildet=NULL; /* silence detector dsp */ int totalsilence = 0; int dspsilence = 0; int silence = 0; /* amount of silence to allow */ int gotsilence = 0; /* did we timeout for silence? */ char *silencestr=NULL; int rfmt=0; /* XXX EAGI FIXME XXX */ if (argc < 6) return RESULT_SHOWUSAGE; if (sscanf(argv[5], "%i", &ms) != 1) return RESULT_SHOWUSAGE; if (argc > 6) silencestr = strchr(argv[6],'s'); if ((argc > 7) && (!silencestr)) silencestr = strchr(argv[7],'s'); if ((argc > 8) && (!silencestr)) silencestr = strchr(argv[8],'s'); if (silencestr) { if (strlen(silencestr) > 2) { if ((silencestr[0] == 's') && (silencestr[1] == '=')) { silencestr++; silencestr++; if (silencestr) silence = atoi(silencestr); if (silence > 0) silence *= 1000; } } } if (silence > 0) { rfmt = chan->readformat; res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); return -1; } sildet = ast_dsp_new(); if (!sildet) { ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); return -1; } ast_dsp_set_threshold(sildet, 256); } /* backward compatibility, if no offset given, arg[6] would have been * caught below and taken to be a beep, else if it is a digit then it is a * offset */ if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1) && (!strchr(argv[6], '='))) res = ast_streamfile(chan, "beep", chan->language); if ((argc > 7) && (!strchr(argv[7], '='))) res = ast_streamfile(chan, "beep", chan->language); if (!res) res = ast_waitstream(chan, argv[4]); if (!res) { fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, 0644); if (!fs) { res = -1; fdprintf(agi->fd, "200 result=%d (writefile)\n", res); if (sildet) ast_dsp_free(sildet); return RESULT_FAILURE; } chan->stream = fs; ast_applystream(chan,fs); /* really should have checks */ ast_seekstream(fs, sample_offset, SEEK_SET); ast_truncstream(fs); gettimeofday(&start, NULL); gettimeofday(&tv, NULL); while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) { res = ast_waitfor(chan, -1); if (res < 0) { ast_closestream(fs); fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset); if (sildet) ast_dsp_free(sildet); return RESULT_FAILURE; } f = ast_read(chan); if (!f) { fdprintf(agi->fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset); ast_closestream(fs); if (sildet) ast_dsp_free(sildet); return RESULT_FAILURE; } switch(f->frametype) { case AST_FRAME_DTMF: if (strchr(argv[4], f->subclass)) { /* This is an interrupting chracter */ sample_offset = ast_tellstream(fs); fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset); ast_closestream(fs); ast_frfree(f); if (sildet) ast_dsp_free(sildet); return RESULT_SUCCESS; } break; case AST_FRAME_VOICE: ast_writestream(fs, f); /* this is a safe place to check progress since we know that fs * is valid after a write, and it will then have our current * location */ sample_offset = ast_tellstream(fs); if (silence > 0) { dspsilence = 0; ast_dsp_silence(sildet, f, &dspsilence); if (dspsilence) { totalsilence = dspsilence; } else { totalsilence = 0; } if (totalsilence > silence) { /* Ended happily with silence */ ast_frfree(f); gotsilence = 1; break; } } break; } ast_frfree(f); gettimeofday(&tv, NULL); if (gotsilence) break; } if (gotsilence) { ast_stream_rewind(fs, silence-1000); ast_truncstream(fs); sample_offset = ast_tellstream(fs); } fdprintf(agi->fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset); ast_closestream(fs); } else fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset); if (silence > 0) { res = ast_set_read_format(chan, rfmt); if (res) ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); ast_dsp_free(sildet); } return RESULT_SUCCESS; } static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int timeout; if (argc != 3) return RESULT_SHOWUSAGE; if (sscanf(argv[2], "%d", &timeout) != 1) return RESULT_SHOWUSAGE; if (timeout < 0) timeout = 0; if (timeout) chan->whentohangup = time(NULL) + timeout; else chan->whentohangup = 0; fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **argv) { struct ast_channel *c; if (argc==1) { /* no argument: hangup the current channel */ ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT); fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } else if (argc==2) { /* one argument: look for info on the specified channel */ c = ast_channel_walk_locked(NULL); while (c) { if (strcasecmp(argv[1],c->name)==0) { /* we have a matching channel */ ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT); fdprintf(agi->fd, "200 result=1\n"); ast_mutex_unlock(&c->lock); return RESULT_SUCCESS; } ast_mutex_unlock(&c->lock); c = ast_channel_walk_locked(c); } /* if we get this far no channel name matched the argument given */ fdprintf(agi->fd, "200 result=-1\n"); return RESULT_SUCCESS; } else { return RESULT_SHOWUSAGE; } } static int handle_exec(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; struct ast_app *app; if (argc < 2) return RESULT_SHOWUSAGE; if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "AGI Script Executing Application: (%s) Options: (%s)\n", argv[1], argv[2]); app = pbx_findapp(argv[1]); if (app) { res = pbx_exec(chan, app, argv[2], 1); } else { ast_log(LOG_WARNING, "Could not find application (%s)\n", argv[1]); res = -2; } fdprintf(agi->fd, "200 result=%d\n", res); return res; } static int handle_setcallerid(struct ast_channel *chan, AGI *agi, int argc, char **argv) { if (argv[2]) ast_set_callerid(chan, argv[2], 0); fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, char **argv) { struct ast_channel *c; if (argc==2) { /* no argument: supply info on the current channel */ fdprintf(agi->fd, "200 result=%d\n", chan->_state); return RESULT_SUCCESS; } else if (argc==3) { /* one argument: look for info on the specified channel */ c = ast_channel_walk_locked(NULL); while (c) { if (strcasecmp(argv[2],c->name)==0) { fdprintf(agi->fd, "200 result=%d\n", c->_state); ast_mutex_unlock(&c->lock); return RESULT_SUCCESS; } ast_mutex_unlock(&c->lock); c = ast_channel_walk_locked(c); } /* if we get this far no channel name matched the argument given */ fdprintf(agi->fd, "200 result=-1\n"); return RESULT_SUCCESS; } else { return RESULT_SHOWUSAGE; } } static int handle_setvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv) { if (argv[3]) pbx_builtin_setvar_helper(chan, argv[2], argv[3]); fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } static int handle_getvariable(struct ast_channel *chan, AGI *agi, int argc, char **argv) { char *tempstr; if ((tempstr = pbx_builtin_getvar_helper(chan, argv[2]))) fdprintf(agi->fd, "200 result=1 (%s)\n", tempstr); else fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_verbose(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int level = 0; char *prefix; if (argc < 2) return RESULT_SHOWUSAGE; if (argv[2]) sscanf(argv[2], "%d", &level); switch (level) { case 4: prefix = VERBOSE_PREFIX_4; break; case 3: prefix = VERBOSE_PREFIX_3; break; case 2: prefix = VERBOSE_PREFIX_2; break; case 1: default: prefix = VERBOSE_PREFIX_1; break; } if (level <= option_verbose) ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]); fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } static int handle_dbget(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; char tmp[256]; if (argc != 4) return RESULT_SHOWUSAGE; res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp)); if (res) fdprintf(agi->fd, "200 result=0\n"); else fdprintf(agi->fd, "200 result=1 (%s)\n", tmp); return RESULT_SUCCESS; } static int handle_dbput(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; if (argc != 5) return RESULT_SHOWUSAGE; res = ast_db_put(argv[2], argv[3], argv[4]); if (res) fdprintf(agi->fd, "200 result=0\n"); else fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } static int handle_dbdel(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; if (argc != 4) return RESULT_SHOWUSAGE; res = ast_db_del(argv[2], argv[3]); if (res) fdprintf(agi->fd, "200 result=0\n"); else fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } static int handle_dbdeltree(struct ast_channel *chan, AGI *agi, int argc, char **argv) { int res; if ((argc < 3) || (argc > 4)) return RESULT_SHOWUSAGE; if (argc == 4) res = ast_db_deltree(argv[2], argv[3]); else res = ast_db_deltree(argv[2], NULL); if (res) fdprintf(agi->fd, "200 result=0\n"); else fdprintf(agi->fd, "200 result=1\n"); return RESULT_SUCCESS; } static int handle_noop(struct ast_channel *chan, AGI *agi, int arg, char *argv[]) { fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static int handle_setmusic(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { if (!strncasecmp(argv[2],"on",2)) { if (argc > 3) ast_moh_start(chan, argv[3]); else ast_moh_start(chan, NULL); } if (!strncasecmp(argv[2],"off",3)) { ast_moh_stop(chan); } fdprintf(agi->fd, "200 result=0\n"); return RESULT_SUCCESS; } static char usage_setmusic[] = " Usage: SET MUSIC ON \n" " Enables/Disables the music on hold generator. If is\n" " not specified then the default music on hold class will be used.\n" " Always returns 0\n"; static char usage_dbput[] = " Usage: DATABASE PUT \n" " Adds or updates an entry in the Asterisk database for a\n" " given family, key, and value.\n" " Returns 1 if succesful, 0 otherwise\n"; static char usage_dbget[] = " Usage: DATABASE GET \n" " Retrieves an entry in the Asterisk database for a\n" " given family and key.\n" " Returns 0 if is not set. Returns 1 if \n" " is set and returns the variable in parenthesis\n" " example return code: 200 result=1 (testvariable)\n"; static char usage_dbdel[] = " Usage: DATABASE DEL \n" " Deletes an entry in the Asterisk database for a\n" " given family and key.\n" " Returns 1 if succesful, 0 otherwise\n"; static char usage_dbdeltree[] = " Usage: DATABASE DELTREE [keytree]\n" " Deletes a family or specific keytree withing a family\n" " in the Asterisk database.\n" " Returns 1 if succesful, 0 otherwise\n"; static char usage_verbose[] = " Usage: VERBOSE \n" " Sends to the console via verbose message system.\n" " is the the verbose level (1-4)\n" " Always returns 1\n"; static char usage_getvariable[] = " Usage: GET VARIABLE \n" " Returns 0 if is not set. Returns 1 if \n" " is set and returns the variable in parenthesis\n" " example return code: 200 result=1 (testvariable)\n"; static char usage_setvariable[] = " Usage: SET VARIABLE \n"; static char usage_channelstatus[] = " Usage: CHANNEL STATUS []\n" " Returns the status of the specified channel.\n" " If no channel name is given the returns the status of the\n" " current channel.\n" " Return values:\n" " 0 Channel is down and available\n" " 1 Channel is down, but reserved\n" " 2 Channel is off hook\n" " 3 Digits (or equivalent) have been dialed\n" " 4 Line is ringing\n" " 5 Remote end is ringing\n" " 6 Line is up\n" " 7 Line is busy\n"; static char usage_setcallerid[] = " Usage: SET CALLERID \n" " Changes the callerid of the current channel.\n"; static char usage_exec[] = " Usage: EXEC \n" " Executes with given .\n" " Returns whatever the application returns, or -2 on failure to find application\n"; static char usage_hangup[] = " Usage: HANGUP []\n" " Hangs up the specified channel.\n" " If no channel name is given, hangs up the current channel\n"; static char usage_answer[] = " Usage: ANSWER\n" " Answers channel if not already in answer state. Returns -1 on\n" " channel failure, or 0 if successful.\n"; static char usage_waitfordigit[] = " Usage: WAIT FOR DIGIT \n" " Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n" " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n" " the numerical value of the ascii of the digit if one is received. Use -1\n" " for the timeout value if you desire the call to block indefinitely.\n"; static char usage_sendtext[] = " Usage: SEND TEXT \"\"\n" " Sends the given text on a channel. Most channels do not support the\n" " transmission of text. Returns 0 if text is sent, or if the channel does not\n" " support text transmission. Returns -1 only on error/hangup. Text\n" " consisting of greater than one word should be placed in quotes since the\n" " command only accepts a single argument.\n"; static char usage_recvchar[] = " Usage: RECEIVE CHAR \n" " Receives a character of text on a channel. Specify timeout to be the\n" " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n" " do not support the reception of text. Returns the decimal value of the character\n" " if one is received, or 0 if the channel does not support text reception. Returns\n" " -1 only on error/hangup.\n"; static char usage_tddmode[] = " Usage: TDD MODE \n" " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n" " successful, or 0 if channel is not TDD-capable.\n"; static char usage_sendimage[] = " Usage: SEND IMAGE \n" " Sends the given image on a channel. Most channels do not support the\n" " transmission of images. Returns 0 if image is sent, or if the channel does not\n" " support image transmission. Returns -1 only on error/hangup. Image names\n" " should not include extensions.\n"; static char usage_streamfile[] = " Usage: STREAM FILE [sample offset]\n" " Send the given file, allowing playback to be interrupted by the given\n" " digits, if any. Use double quotes for the digits if you wish none to be\n" " permitted. If sample offset is provided then the audio will seek to sample\n" " offset before play starts. Returns 0 if playback completes without a digit\n" " being pressed, or the ASCII numerical value of the digit if one was pressed,\n" " or -1 on error or if the channel was disconnected. Remember, the file\n" " extension must not be included in the filename.\n"; static char usage_saynumber[] = " Usage: SAY NUMBER \n" " Say a given number, returning early if any of the given DTMF digits\n" " are received on the channel. Returns 0 if playback completes without a digit\n" " being pressed, or the ASCII numerical value of the digit if one was pressed or\n" " -1 on error/hangup.\n"; static char usage_saydigits[] = " Usage: SAY DIGITS \n" " Say a given digit string, returning early if any of the given DTMF digits\n" " are received on the channel. Returns 0 if playback completes without a digit\n" " being pressed, or the ASCII numerical value of the digit if one was pressed or\n" " -1 on error/hangup.\n"; static char usage_saytime[] = " Usage: SAY TIME