Index: apps/app_voicemail.c =================================================================== RCS file: /usr/cvsroot/asterisk/apps/app_voicemail.c,v retrieving revision 1.13 diff -u -r1.13 app_voicemail.c --- apps/app_voicemail.c 3 May 2003 20:46:19 -0000 1.13 +++ apps/app_voicemail.c 16 Jul 2003 15:44:48 -0000 @@ -65,6 +65,7 @@ #define BASELINELEN 72 #define eol "\r\n" +#define MAX_DATETIME_FORMAT 512 static int iocp; static int iolen; static int linelength; @@ -114,6 +115,8 @@ STANDARD_LOCAL_USER; LOCAL_USER_DECL; +static int play_message_datetime(struct ast_channel *chan, char *filename, char *ext); +static int play_datetime_format(struct ast_channel *chan, time_t time, const char *dateformat, const char *timezone); static int make_dir(char *dest, int len, char *ext, char *mailbox) { @@ -1573,6 +1576,12 @@ goto cmd; \ } while(0) +#define WAITFILE3(file) do { \ + if (ast_streamfile(chan, file, chan->language)) \ + ast_log(LOG_WARNING, "Unable to play message %s\n", file); \ + d = ast_waitstream(chan, AST_DIGIT_ANY); \ +} while(0) + #define WAITFILE2(file) do { \ if (ast_streamfile(chan, file, chan->language)) \ ast_log(LOG_WARNING, "Unable to play message %s\n", file); \ @@ -1614,6 +1623,7 @@ } \ make_file(fn, sizeof(fn), curdir, a); \ heard[a] = 1; \ + play_message_datetime(chan, fn, username); \ WAITFILE(fn); \ } while(0) @@ -2301,3 +2311,344 @@ { return ASTERISK_GPL_KEY; } + +static int play_message_datetime(struct ast_channel *chan, char *file, char *ext) +{ + char filename[256], *astdtformat = NULL, *origtime, *ext_var, zonetag[256], timezone[MAX_DATETIME_FORMAT]; + struct ast_config *cfg, *msg_cfg; + time_t t; + struct tm time_now, time_then; + struct timeval tv_now; + char temp[256]; + int retval; + + cfg = ast_load(VOICEMAIL_CONFIG); + if (!cfg) { + ast_log(LOG_WARNING, "No voicemail.conf?!!\n"); + return 0; + } + + snprintf(filename,sizeof(filename), "%s.txt", file); + msg_cfg = ast_load(filename); + if (!msg_cfg) { + ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename); + return 0; + } + + if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) + return 0; + if (sscanf(origtime,"%ld",&t) < 1) { + ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename); + return 0; + } + + if (!(ext_var = ast_variable_retrieve(cfg, "default", ext))) { + ast_log(LOG_WARNING, "Ooops, can't find the user in voicemail.conf?!!\n"); + return 0; + } else { + /* Does this user have a timezone other than the default? */ + char *userzone, *next; + ext_var = strdupa(ext_var); + userzone = strstr(ext_var, "tz="); + if (userzone != NULL) { + /* User-defined zone used */ + userzone += 3; + if ((next = index(userzone, ','))) + *next = '\0'; + else if ((next = index(userzone, '|'))) + *next = '\0'; + strncpy(zonetag,userzone,sizeof(zonetag) - 1); + } else { + /* Use the system defined timezone */ + char *system_tz; + if (!(system_tz = ast_variable_retrieve(cfg, "general", "tz"))) { + /* No zone defined */ + zonetag[0] = '\0'; + } else { + /* Default zone used */ + strncpy(zonetag, system_tz, sizeof(zonetag) - 1); + } + } + } + + /* At this point, zonetag contains a label + * for a zone to be looked up in voicemail.conf */ + if (zonetag[0] != '\0') { + char *catg; + int found = 0; + for ( catg = ast_category_browse(cfg, NULL) + ; catg != NULL + ; catg = ast_category_browse(cfg, catg) ) { + if (!strcmp(catg, "timezone")) { + /* Good, have timezones to play with */ + struct ast_variable *av_zone = ast_variable_browse(cfg, catg); + while (1) { + if (!strcmp(av_zone->name, zonetag)) { + found = 1; + strncpy(timezone, av_zone->value, sizeof(timezone) - 1); + break; + } + if (av_zone->next != NULL) + av_zone = av_zone->next; + else + break; + } + break; + } + } + + /* Safety case for when a timezone specified is not defined */ + if (! found) { + timezone[0] = '\0'; + } + } + + /* Set the default format */ + if (!(astdtformat = ast_variable_retrieve(cfg, "general", "datetime"))) + astdtformat = "'vm-received' Q 'digits/at' IMp"; + + /* Set the DIFF_* variables */ + localtime_r(&t,&time_now); + gettimeofday(&tv_now,NULL); + localtime_r(&tv_now.tv_sec,&time_then); + + /* Day difference */ + if (time_now.tm_year == time_then.tm_year) + sprintf(temp,"%d",time_now.tm_yday); + else + sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday)); + pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp); + + /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */ + + /* Separate the datetime format from the zone name */ + if (timezone[0] != '\0') { + char *tmp = index(timezone, '|'); + /* astdtformat stays the default unless specified otherwise */ + if (tmp != NULL) { + *tmp = '\0'; + astdtformat = tmp + 1; + } + } + + if (timezone[0] == '\0') + retval = play_datetime_format(chan, t, astdtformat, NULL); + else + retval = play_datetime_format(chan, t, astdtformat, timezone); + pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL); + return retval; +} + +static int play_datetime_format(struct ast_channel *chan, time_t time, const char *dateformat, const char *timezone) +{ + int d = 0, offset = 0, sndoffset = 0; + char sndfile[256], nextmsg[256], *tzenv; + struct tm tm; + char current_tz[256] = ""; + + tzenv = getenv("TZ"); + if (tzenv != NULL) + strncpy(current_tz, getenv("TZ"), sizeof(current_tz) - 1); + if ((timezone != NULL) && strcmp(current_tz,timezone)) { + setenv("TZ", timezone, 1); + tzset(); + localtime_r(&time, &tm); + if (tzenv != NULL) + setenv("TZ", current_tz, 1); + else + unsetenv("TZ"); + } else { + /* No need to change the timezone */ + localtime_r(&time, &tm); + } + + for (offset=0 ; dateformat[offset] != '\0' ; offset++) { + ast_log(LOG_NOTICE, "Parsing %c in %s\n", dateformat[offset], dateformat); + switch (dateformat[offset]) { + /* NOTE: if you add more options here, please try to be consistent with strftime(3) */ + case '\'': + /* Literal name of a sound file */ + sndoffset=0; + for (sndoffset=0 ; dateformat[++offset] != '\'' ; sndoffset++) { + sndfile[sndoffset] = dateformat[offset]; + if (dateformat[offset] == '\0') { + ast_log(LOG_WARNING, "Unmatched ' in date format (%s)\n", dateformat); + return 0; + } + } + sndfile[sndoffset] = '\0'; + snprintf(nextmsg,sizeof(nextmsg),"%s/%s", AST_SOUNDS, sndfile); + WAITFILE3(nextmsg); + break; + case '$': + /* Ooooh, variables and/or expressions */ + do { + char buffer[4096]; + pbx_substitute_variables_helper(chan, dateformat + offset, buffer, sizeof(buffer)); + d = play_datetime_format(chan, time, buffer, timezone); + /* Subtract one, so that when the for loop increments, we point at the nil */ + offset = strlen(dateformat) - 1; + } while (0); + break; + case 'A': + case 'a': + /* Sunday - Saturday */ + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/day-%d", AST_SOUNDS, tm.tm_wday); + WAITFILE3(nextmsg); + break; + case 'B': + case 'b': + case 'h': + /* January - December */ + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/mon-%d", AST_SOUNDS, tm.tm_mon); + WAITFILE3(nextmsg); + break; + case 'd': + case 'e': + /* First - Thirtyfirst */ + if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) { + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/h-%d", AST_SOUNDS, tm.tm_mday); + WAITFILE3(nextmsg); + } else if (tm.tm_mday == 31) { + /* "Thirty" and "first" */ + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/30", AST_SOUNDS); + WAITFILE3(nextmsg); + if (!d) { + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/h-1", AST_SOUNDS); + WAITFILE3(nextmsg); + } + } else { + /* Between 21 and 29 - two sounds */ + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/20", AST_SOUNDS); + WAITFILE3(nextmsg); + if (!d) { + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/h-%d", AST_SOUNDS, tm.tm_mday - 20); + WAITFILE3(nextmsg); + } + } + break; + case 'Y': + /* Year */ + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/2", AST_SOUNDS); + WAITFILE3(nextmsg); + if (!d) { + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/thousand", AST_SOUNDS); + WAITFILE3(nextmsg); + } + if (!d) { + /* This works until the end of 2020 */ + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/%d", AST_SOUNDS, tm.tm_year - 100); + WAITFILE3(nextmsg); + } + break; + case 'I': + case 'l': + /* 12-Hour */ + if (tm.tm_hour == 0) + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/12", AST_SOUNDS); + else if (tm.tm_hour > 12) + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/%d", AST_SOUNDS, tm.tm_hour - 12); + else + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/%d", AST_SOUNDS, tm.tm_hour); + WAITFILE3(nextmsg); + break; + case 'H': + case 'k': + /* 24-Hour */ + if (dateformat[offset] == 'H') { + /* e.g. oh-eight */ + if (tm.tm_hour < 10) { + WAITFILE3(AST_SOUNDS "/digits/oh"); + } + } else { + /* e.g. eight */ + if (tm.tm_hour == 0) { + WAITFILE3(AST_SOUNDS "/digits/oh"); + } + } + if (!d) { + if (tm.tm_hour != 0) { + snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/digits/%d", tm.tm_hour); + WAITFILE3(nextmsg); + } + } + break; + case 'M': + /* Minute */ + if (tm.tm_min == 0) { + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/oclock", AST_SOUNDS); + WAITFILE3(nextmsg); + } else if (tm.tm_min < 10) { + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/oh", AST_SOUNDS); + WAITFILE3(nextmsg); + if (!d) { + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/%d", AST_SOUNDS, tm.tm_min); + WAITFILE3(nextmsg); + } + } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) { + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/%d", AST_SOUNDS, tm.tm_min); + WAITFILE3(nextmsg); + } else { + int ten, one; + ten = (tm.tm_min / 10) * 10; + one = (tm.tm_min % 10); + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/%d", AST_SOUNDS, ten); + WAITFILE3(nextmsg); + if (!d) { + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/%d", AST_SOUNDS, one); + WAITFILE3(nextmsg); + } + } + break; + case 'P': + case 'p': + /* AM/PM */ + if ((tm.tm_hour == 0) || (tm.tm_hour > 12)) + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/p-m", AST_SOUNDS); + else + snprintf(nextmsg,sizeof(nextmsg),"%s/digits/a-m", AST_SOUNDS); + WAITFILE3(nextmsg); + break; + case 'Q': + /* Shorthand for "Today", "Yesterday", or ABdY */ + { + struct timeval now; + struct tm tmnow; + time_t beg_today; + + gettimeofday(&now,NULL); + localtime_r(&now.tv_sec,&tmnow); + tmnow.tm_hour = 0; + tmnow.tm_min = 0; + tmnow.tm_sec = 0; + beg_today = mktime(&tmnow); + if (beg_today < time) { + /* Today */ + WAITFILE3(AST_SOUNDS "/digits/today"); + } else if (beg_today - 86400 < time) { + /* Yesterday */ + WAITFILE3(AST_SOUNDS "/digits/yesterday"); + } else { + d = play_datetime_format(chan, time, "ABdY", timezone); + } + } + break; + case 'R': + d = play_datetime_format(chan, time, "HM", timezone); + break; + case ' ': + case ' ': + /* Just ignore spaces and tabs */ + break; + default: + /* Unknown character */ + ast_log(LOG_WARNING, "Unknown character in datetime format: %c at offset %d\n", dateformat[offset], offset); + } + /* Jump out on DTMF */ + if (d) { + break; + } + } + return d; +} +