diff -urdpN asterisk-1.0.3-orig/res/res_agi.c asterisk-1.0.3-patched/res/res_agi.c > asterisk-1.0.3-patch.diff --- asterisk-1.0.3-orig/res/res_agi.c 2004-12-31 13:16:54.285932344 +0200 +++ asterisk-1.0.3-patched/res/res_agi.c 2004-12-31 13:17:03.658507496 +0200 @@ -93,6 +93,28 @@ LOCAL_USER_DECL; #define AGI_PORT 4573 +char base64_encode_nibble(unsigned char nibble); + +char base64_encode_nibble(unsigned char nibble) { + if (nibble == 0) return '0'; + else if (nibble == 1) return '1'; + else if (nibble == 2) return '2'; + else if (nibble == 3) return '3'; + else if (nibble == 4) return '4'; + else if (nibble == 5) return '5'; + else if (nibble == 6) return '6'; + else if (nibble == 7) return '7'; + else if (nibble == 8) return '8'; + else if (nibble == 9) return '9'; + else if (nibble == 10) return 'A'; + else if (nibble == 11) return 'B'; + else if (nibble == 12) return 'C'; + else if (nibble == 13) return 'D'; + else if (nibble == 14) return 'E'; + else if (nibble == 15) return 'F'; + else return '0'; +} + static void agi_debug_cli(int fd, char *fmt, ...) { char *stuff; @@ -763,6 +785,216 @@ static int handle_recordfile(struct ast_ return RESULT_SUCCESS; } +static int handle_recordstream(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) +{ + struct ast_frame *f; + struct timeval tv, start; + int res = 0; + int ms; + unsigned long int packets=0; + + 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; + + char* tempbuf = NULL; + unsigned long int tempbuflen = 0; + unsigned char tempchar; + unsigned char tempchar2; + unsigned short int tempshort1; + unsigned short int tempshort2; + char tempbuf2[3]; + + + if (argc < 3) + return RESULT_SHOWUSAGE; + if (sscanf(argv[3], "%i", &ms) != 1) + return RESULT_SHOWUSAGE; + + if (argc > 4) + silencestr = strchr(argv[4],'s'); + if ((argc > 5) && (!silencestr)) + silencestr = strchr(argv[5],'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; + } + } + } + + 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; + } + if (silence > 0) { + sildet = ast_dsp_new(); + if (!sildet) { + ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); + + res = ast_set_read_format(chan, rfmt); + if (res) + ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); + + return -1; + } + ast_dsp_set_threshold(sildet, 256); + } + + if ((argc > 4) && (!strchr(argv[4], '='))) + res = ast_streamfile(chan, "beep", chan->language); + + if (!res) + res = ast_waitstream(chan, argv[2]); + if (!res) { + 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) { + fdprintf(agi->fd, "200 result=%d (waitfor) endpos=%lu\n", res, packets); + if (sildet) + ast_dsp_free(sildet); + + res = ast_set_read_format(chan, rfmt); + if (res) + ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); + + return RESULT_FAILURE; + } + f = ast_read(chan); + if (!f) { + fdprintf(agi->fd, "200 result=%d (hangup) endpos=%lu\n", 0, packets); + if (sildet) + ast_dsp_free(sildet); + + res = ast_set_read_format(chan, rfmt); + if (res) + ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); + + return RESULT_FAILURE; + } + switch(f->frametype) { + case AST_FRAME_DTMF: + if (strchr(argv[2], f->subclass)) { + /* This is an interrupting chracter */ + fdprintf(agi->fd, "200 result=%d (dtmf) endpos=%lu\n", f->subclass, packets); + ast_frfree(f); + if (sildet) + ast_dsp_free(sildet); + + res = ast_set_read_format(chan, rfmt); + if (res) + ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); + + return RESULT_SUCCESS; + } + break; + case AST_FRAME_VOICE: + /* 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 */ + 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; + } + + int i = 0; + + if (f->datalen > 0) { + tempbuflen = f->datalen*2 + 1; + if ((tempbuf = (char*)malloc(sizeof(char) * tempbuflen)) != NULL) { + tempbuf[0] = '\0'; + + for (i = 1; i < f->datalen; i = i + 2) { + tempchar = (unsigned char)(((unsigned char*)f->data)[i - 1]); + tempchar2 = (unsigned char)(((unsigned char*)f->data)[i]); + + tempshort1 = (unsigned short int)(((tempchar & 0xff) << 8) | (tempchar2 & 0xff)); + tempshort2 = (unsigned short int)(htons(tempshort1)); + + tempchar = (unsigned char)((tempshort2 & 0xff00) >> 8); + tempchar2 = (unsigned char)((tempshort2 & 0x00ff)); + + tempbuf2[0] = base64_encode_nibble((unsigned char)((tempchar & 0xf0) >> 4)); + tempbuf2[1] = base64_encode_nibble((unsigned char)((tempchar & 0x0f))); + tempbuf2[2] = '\0'; + strcat(tempbuf, tempbuf2); + + tempbuf2[0] = base64_encode_nibble((unsigned char)((tempchar2 & 0xf0) >> 4)); + tempbuf2[1] = base64_encode_nibble((unsigned char)((tempchar2 & 0x0f))); + tempbuf2[2] = '\0'; + strcat(tempbuf, tempbuf2); + } + + packets = packets + 1; + fdprintf(agi->fd, "%lu\n%s\n", tempbuflen - 1, tempbuf); + } else { + ast_log(LOG_WARNING, "Failed to allocate memory.\n"); + fdprintf(agi->fd, "200 result=%d (writefile) endpos=%lu\n", 0, packets); + if (sildet) + ast_dsp_free(sildet); + + if (tempbuf != NULL) { + free(tempbuf); + tempbuf = NULL; + } + + res = ast_set_read_format(chan, rfmt); + if (res) + ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); + + return RESULT_FAILURE; + } + + if (tempbuf != NULL) { + free(tempbuf); + tempbuf = NULL; + } + } + + ast_frfree(f); + gettimeofday(&tv, NULL); + if (gotsilence) + break; + } + + fdprintf(agi->fd, "200 result=%d (timeout) endpos=%lu\n", res, packets); + } else + fdprintf(agi->fd, "200 result=%d (randomerror) endpos=%lu\n", res, packets); + + res = ast_set_read_format(chan, rfmt); + if (res) + ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name); + + return RESULT_SUCCESS; +} + static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int timeout; @@ -1223,6 +1455,17 @@ static char usage_recordfile[] = " lack of dtmf digits or reaching timeout. Silence value must be\n" " preceeded by \"s=\" and is optional.\n"; +static char usage_recordstream[] = +" Usage: RECORD STREAM [BEEP] [s=silence]\n" +" Record to a file until a given dtmf digit in the sequence is received\n" +" Returns -1 on hangup. The timeout is the maximum record time in milliseconds, or\n" +" -1 for no timeout. \"silence\" is the number of seconds of silence allowed before the\n" +" function returns despite the lack of dtmf digits or reaching timeout. Silence value must\n" +" be preceeded by \"s=\" and is optional. Data returned follows the structure of multiple\n" +" packets which consists of the length (in bytes) of the packet, followed by newline, followed\n" +" by the packet data followed by a newline. Packet data is base 64 encoded binary. After all\n" +" packets have been streamed, the result string is returned with an indication in brackets of\n" +" the reason for record termination and endpos equal to the number of packets transmitted.\n"; static char usage_autohangup[] = " Usage: SET AUTOHANGUP