157 #include <sys/time.h>
158 #include <sys/stat.h>
159 #include <sys/mman.h>
181 #include "asterisk/stasis_channels.h"
531 #define MVM_REVIEW (1 << 0)
532 #define MVM_OPERATOR (1 << 1)
533 #define MVM_REALTIME (1 << 2)
534 #define MVM_SVMAIL (1 << 3)
535 #define MVM_ENVELOPE (1 << 4)
536 #define MVM_PBXSKIP (1 << 9)
537 #define MVM_ALLOCED (1 << 13)
541 #define SENDMAIL "/usr/sbin/sendmail -t"
543 #define SOUND_INTRO "vm-intro"
546 #define MAX_DATETIME_FORMAT 512
547 #define MAX_NUM_CID_CONTEXTS 10
549 #define ERROR_LOCK_PATH -100
550 #define VOICEMAIL_DIR_MODE 0700
552 #define VOICEMAIL_CONFIG "minivm.conf"
553 #define ASTERISK_USERNAME "asterisk"
562 static char MVM_SPOOL_DIR[PATH_MAX];
565 static char *app_minivm_record =
"MinivmRecord";
566 static char *app_minivm_greet =
"MinivmGreet";
567 static char *app_minivm_notify =
"MinivmNotify";
568 static char *app_minivm_delete =
"MinivmDelete";
569 static char *app_minivm_accmess =
"MinivmAccMess";
570 static char *app_minivm_mwi =
"MinivmMWI";
574 enum minivm_option_flags {
575 OPT_SILENT = (1 << 0),
576 OPT_BUSY_GREETING = (1 << 1),
577 OPT_UNAVAIL_GREETING = (1 << 2),
578 OPT_TEMP_GREETING = (1 << 3),
579 OPT_NAME_GREETING = (1 << 4),
580 OPT_RECORDGAIN = (1 << 5),
583 enum minivm_option_args {
584 OPT_ARG_RECORDGAIN = 0,
585 OPT_ARG_ARRAY_SIZE = 1,
640 char fromaddress[100];
641 char serveremail[80];
656 signed char record_gain;
663 char msg_format[BUFSIZ];
676 struct timeval reset;
678 struct timeval lastreceived;
693 static int global_silencethreshold = 128;
697 static char default_vmformat[80];
700 static int global_saydurationminfo;
706 #define DEFAULT_DATEFORMAT "%A, %B %d, %Y at %r"
707 #define DEFAULT_CHARSET "ISO-8859-1"
713 static struct minivm_account *
find_user_realtime(
const char *domain,
const char *username);
718 static struct minivm_template *message_template_create(
const char *name)
720 struct minivm_template *
template;
728 ast_copy_string(template->dateformat, DEFAULT_DATEFORMAT,
sizeof(template->dateformat));
729 ast_copy_string(template->charset, DEFAULT_CHARSET,
sizeof(template->charset));
730 ast_copy_string(template->subject,
"New message in mailbox ${MVM_USERNAME}@${MVM_DOMAIN}",
sizeof(template->subject));
731 template->attachment = TRUE;
738 static void message_template_free(
struct minivm_template *
template)
741 ast_free(template->body);
748 static int message_template_build(
const char *name,
struct ast_variable *var)
750 struct minivm_template *
template;
753 template = message_template_create(name);
755 ast_log(LOG_ERROR,
"Out of memory, can't allocate message template object %s.\n", name);
760 ast_debug(3,
"Configuring template option %s = \"%s\" for template %s\n", var->
name, var->
value, name);
761 if (!strcasecmp(var->
name,
"fromaddress")) {
763 }
else if (!strcasecmp(var->
name,
"fromemail")) {
765 }
else if (!strcasecmp(var->
name,
"subject")) {
767 }
else if (!strcasecmp(var->
name,
"locale")) {
769 }
else if (!strcasecmp(var->
name,
"attachmedia")) {
771 }
else if (!strcasecmp(var->
name,
"dateformat")) {
773 }
else if (!strcasecmp(var->
name,
"charset")) {
775 }
else if (!strcasecmp(var->
name,
"templatefile")) {
777 ast_free(template->body);
779 if (!template->body) {
780 ast_log(LOG_ERROR,
"Error reading message body definition file %s\n", var->
value);
783 }
else if (!strcasecmp(var->
name,
"messagebody")) {
785 ast_free(template->body);
787 if (!template->body) {
788 ast_log(LOG_ERROR,
"Error parsing message body definition:\n %s\n", var->
value);
792 ast_log(LOG_ERROR,
"Unknown message template configuration option \"%s=%s\"\n", var->
name, var->
value);
798 ast_log(LOG_ERROR,
"-- %d errors found parsing message template definition %s\n", error, name);
811 static struct minivm_template *message_template_find(
const char *name)
813 struct minivm_template *
this, *res = NULL;
815 if (ast_strlen_zero(name))
820 if (!strcasecmp(this->name, name)) {
833 static void message_destroy_list(
void)
835 struct minivm_template *
this;
838 message_template_free(
this);
844 static int get_date(
char *s,
int len)
856 static void free_user(
struct minivm_account *vmu)
869 static void prep_email_sub_vars(
struct ast_channel *channel,
const struct minivm_account *vmu,
const char *cidnum,
const char *cidname,
const char *dur,
const char *date,
const char *counter)
875 ast_log(LOG_ERROR,
"No allocated channel, giving up...\n");
888 pbx_builtin_setvar_helper(channel,
"MVM_CALLERID", ast_callerid_merge(callerid,
sizeof(callerid), cidname, cidnum,
"Unknown Caller"));
892 if (!ast_strlen_zero(counter))
900 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
907 static struct minivm_account *mvm_user_alloc(
void)
909 struct minivm_account *
new;
922 static void vmaccounts_destroy_list(
void)
924 struct minivm_account *
this;
934 static struct minivm_account *find_account(
const char *domain,
const char *username,
int createtemp)
936 struct minivm_account *vmu = NULL, *cur;
939 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
940 ast_log(LOG_NOTICE,
"No username or domain? \n");
943 ast_debug(3,
"Looking for voicemail user %s in domain %s\n", username, domain);
948 if (!strcasecmp(domain, cur->domain) && !strcasecmp(username, cur->username))
954 ast_debug(3,
"Found account for %s@%s\n", username, domain);
960 if (createtemp && !vmu) {
962 vmu = mvm_user_alloc();
963 ast_set2_flag(vmu, TRUE, MVM_ALLOCED);
967 ast_debug(1,
"Created temporary account\n");
978 static struct minivm_account *
find_user_realtime(
const char *domain,
const char *username)
981 struct minivm_account *retval;
982 char name[MAXHOSTNAMELEN];
984 retval = mvm_user_alloc();
992 var = ast_load_realtime(
"minivm",
"username", username,
"domain", domain, SENTINEL);
999 snprintf(name,
sizeof(name),
"%s@%s", username, domain);
1013 for (; *str; str++) {
1014 if (*str > 126 || *str < 32 || strchr(
"()<>@,:;/\"[]?.=", *str)) {
1040 static const char *
ast_str_encode_mime(
struct ast_str **end, ssize_t maxlen,
const char *charset,
const char *start,
size_t preamble,
size_t postamble)
1042 struct ast_str *tmp = ast_str_alloca(80);
1043 int first_section = 1;
1047 for (; *start; start++) {
1048 int need_encoding = 0;
1049 if (*start < 33 || *start > 126 || strchr(
"()<>@,:;/\"[]?.=_", *start)) {
1052 if ((first_section && need_encoding && preamble +
ast_str_strlen(tmp) > 70) ||
1053 (first_section && !need_encoding && preamble +
ast_str_strlen(tmp) > 72) ||
1055 (!first_section && !need_encoding &&
ast_str_strlen(tmp) > 72)) {
1061 if (need_encoding && *start ==
' ') {
1063 }
else if (need_encoding) {
1087 for (ptr = from; *ptr; ptr++) {
1088 if (*ptr ==
'"' || *ptr ==
'\\') {
1101 static int sendmail(
struct minivm_template *
template,
struct minivm_account *vmu,
char *cidnum,
char *cidname,
const char *filename,
char *format,
int duration,
int attach_user_voicemail,
enum mvm_messagetype type,
const char *counter)
1107 char email[256] =
"";
1111 char fname[PATH_MAX];
1113 char tmp[80] =
"/tmp/astmail-XXXXXX";
1114 char mail_cmd_buffer[PATH_MAX];
1115 char sox_gain_tmpdir[PATH_MAX] =
"";
1116 char *file_to_delete = NULL, *dir_to_delete = NULL;
1125 if (!str1 || !str2) {
1129 if (type == MVM_MESSAGE_EMAIL) {
1130 if (vmu && !ast_strlen_zero(vmu->
email)) {
1132 }
else if (!ast_strlen_zero(vmu->
username) && !ast_strlen_zero(vmu->
domain))
1133 snprintf(email,
sizeof(email),
"%s@%s", vmu->
username, vmu->
domain);
1134 }
else if (type == MVM_MESSAGE_PAGE) {
1138 if (ast_strlen_zero(email)) {
1139 ast_log(LOG_WARNING,
"No address to send message to.\n");
1145 if (!strcmp(format,
"wav49"))
1149 if (type == MVM_MESSAGE_EMAIL && (vmu->
volgain < -.001 || vmu->
volgain > .001) ) {
1150 char sox_gain_cmd[PATH_MAX];
1152 ast_copy_string(sox_gain_tmpdir,
"/tmp/minivm-gain-XXXXXX",
sizeof(sox_gain_tmpdir));
1153 ast_debug(3,
"sox_gain_tmpdir: %s\n", sox_gain_tmpdir);
1154 if (!mkdtemp(sox_gain_tmpdir)) {
1155 ast_log(LOG_WARNING,
"Failed to create temporary directory for volgain: %d\n", errno);
1158 snprintf(fname,
sizeof(fname),
"%s/output.%s", sox_gain_tmpdir, format);
1159 snprintf(sox_gain_cmd,
sizeof(sox_gain_cmd),
"sox -v %.4f %s.%s %s", vmu->
volgain, filename, format, fname);
1161 ast_debug(3,
"VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", filename, format, vmu->
volgain, vmu->
username);
1164 file_to_delete = fname;
1165 dir_to_delete = sox_gain_tmpdir;
1167 snprintf(fname,
sizeof(fname),
"%s.%s", filename, format);
1170 if (template->attachment)
1171 ast_debug(1,
"Attaching file '%s', format '%s', uservm is '%d'\n", fname, format, attach_user_voicemail);
1177 p = fdopen(pfd,
"w");
1182 ast_debug(1,
"Opening temp file for e-mail: %s\n", tmp);
1185 ast_log(LOG_WARNING,
"Unable to open temporary file '%s'\n", tmp);
1194 snprintf(dur,
sizeof(dur),
"%d:%02d", duration / 60, duration % 60);
1197 if (!ast_strlen_zero(vmu->
zonetag)) {
1211 ast_strftime(date,
sizeof(date),
"%a, %d %b %Y %H:%M:%S %z", &tm);
1214 fprintf(p,
"Date: %s\n", date);
1217 ast_strftime(date,
sizeof(date), template->dateformat, &tm);
1220 prep_email_sub_vars(chan, vmu, cidnum, cidname, dur, date, counter);
1227 fromaddress = ast_strlen_zero(template->fromaddress) ?
"" :
template->fromaddress;
1230 if (ast_strlen_zero(fromemail))
1231 fromemail =
"asterisk";
1233 if (strchr(fromemail,
'@'))
1236 char host[MAXHOSTNAMELEN];
1237 gethostname(host,
sizeof(host)-1);
1238 snprintf(who,
sizeof(who),
"%s@%s", fromemail, host);
1241 if (ast_strlen_zero(fromaddress)) {
1242 fprintf(p,
"From: Asterisk PBX <%s>\n", who);
1244 ast_debug(4,
"Fromaddress template: %s\n", fromaddress);
1252 fprintf(p,
"%s %s\n", first_line ?
"From:" :
"",
ast_str_buffer(str2));
1257 fprintf(p,
"%s %s <%s>\n", first_line ?
"From:" :
"",
ast_str_buffer(str2), who);
1263 fprintf(p,
"Message-ID: <Asterisk-%u-%s-%d-%s>\n", (
unsigned int)ast_random(), vmu->
username, (
int)getpid(), who);
1265 if (ast_strlen_zero(vmu->
email)) {
1266 snprintf(email,
sizeof(email),
"%s@%s", vmu->
username, vmu->
domain);
1277 fprintf(p,
"%s %s\n", first_line ?
"To:" :
"",
ast_str_buffer(str2));
1282 fprintf(p,
"%s %s <%s>\n", first_line ?
"To:" :
"",
ast_str_buffer(str2), email);
1287 if (!ast_strlen_zero(template->subject)) {
1295 fprintf(p,
"%s %s\n", first_line ?
"Subject:" :
"",
ast_str_buffer(str2));
1300 fprintf(p,
"%s %s\n", first_line ?
"Subject:" :
"",
ast_str_buffer(str2));
1305 fprintf(p,
"Subject: New message in mailbox %s@%s\n", vmu->
username, vmu->
domain);
1306 ast_debug(1,
"Using default subject for this email \n");
1309 if (DEBUG_ATLEAST(3))
1310 fprintf(p,
"X-Asterisk-debug: template %s user account %s@%s\n", template->name, vmu->
username, vmu->
domain);
1311 fprintf(p,
"MIME-Version: 1.0\n");
1314 snprintf(bound,
sizeof(bound),
"voicemail_%s%d%u", vmu->
username, (
int)getpid(), (
unsigned int)ast_random());
1316 fprintf(p,
"Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1318 fprintf(p,
"--%s\n", bound);
1319 fprintf(p,
"Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", template->charset);
1320 if (!ast_strlen_zero(template->body)) {
1325 fprintf(p,
"Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message \n"
1326 "in mailbox %s from %s, on %s so you might\n"
1327 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->
fullname,
1328 dur, vmu->
username, (cidname ? cidname : (cidnum ? cidnum :
"an unknown caller")), date);
1329 ast_debug(3,
"Using default message body (no template)\n-----\n");
1332 if (template->attachment) {
1333 char *ctype =
"audio/x-";
1334 ast_debug(3,
"Attaching file to message: %s\n", fname);
1335 if (!strcasecmp(format,
"ogg"))
1336 ctype =
"application/";
1338 fprintf(p,
"--%s\n", bound);
1339 fprintf(p,
"Content-Type: %s%s; name=\"voicemailmsg.%s\"\n", ctype, format, format);
1340 fprintf(p,
"Content-Transfer-Encoding: base64\n");
1341 fprintf(p,
"Content-Description: Voicemail sound attachment.\n");
1342 fprintf(p,
"Content-Disposition: attachment; filename=\"voicemail%s.%s\"\n\n", counter ? counter :
"", format);
1345 fprintf(p,
"\n\n--%s--\n.\n", bound);
1351 if (file_to_delete && dir_to_delete) {
1354 res = snprintf(mail_cmd_buffer,
sizeof(mail_cmd_buffer),
1355 "( %s < %s ; rm -f %s %s ; rmdir %s ) &",
1356 global_mailcmd, tmp, tmp, file_to_delete, dir_to_delete);
1358 res = snprintf(mail_cmd_buffer,
sizeof(mail_cmd_buffer),
1359 "( %s < %s ; rm -f %s ) &",
1360 global_mailcmd, tmp, tmp);
1363 if (res <
sizeof(mail_cmd_buffer)) {
1364 file_to_delete = dir_to_delete = NULL;
1366 ast_log(LOG_ERROR,
"Could not send message, command line too long\n");
1372 ast_debug(1,
"Sent message to %s with command '%s'%s\n", vmu->
email, global_mailcmd, template->attachment ?
" - (media attachment)" :
"");
1373 ast_debug(3,
"Actual command used: %s\n", mail_cmd_buffer);
1378 if (file_to_delete) {
1379 unlink(file_to_delete);
1382 if (dir_to_delete) {
1383 rmdir(dir_to_delete);
1391 static int make_dir(
char *dest,
int len,
const char *domain,
const char *username,
const char *folder)
1393 return snprintf(dest, len,
"%s%s/%s%s%s", MVM_SPOOL_DIR, domain, username, ast_strlen_zero(folder) ?
"" :
"/", folder ? folder :
"");
1405 static int check_dirpath(
char *dest,
int len,
char *domain,
char *username,
char *folder)
1407 struct stat filestat;
1408 make_dir(dest, len, domain, username, folder ? folder :
"");
1409 if (stat(dest, &filestat)== -1)
1424 static int create_dirpath(
char *dest,
int len,
char *domain,
char *username,
char *folder)
1427 make_dir(dest, len, domain, username, folder);
1429 ast_log(LOG_WARNING,
"ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1432 ast_debug(2,
"Creating directory for %s@%s folder %s : %s\n", username, domain, folder, dest);
1440 static int invent_message(
struct ast_channel *chan,
char *domain,
char *username,
int busy,
char *ecodes)
1445 ast_debug(2,
"Still preparing to play message ...\n");
1447 snprintf(fn,
sizeof(fn),
"%s%s/%s/greet", MVM_SPOOL_DIR, domain, username);
1457 int numericusername = 1;
1460 ast_debug(2,
"No personal prompts. Using default prompt set for language\n");
1463 ast_debug(2,
"Numeric? Checking %c\n", *i);
1465 numericusername = FALSE;
1471 if (numericusername) {
1472 if (
ast_streamfile(chan,
"vm-theperson", ast_channel_language(chan)))
1481 if (
ast_streamfile(chan,
"vm-theextensionis", ast_channel_language(chan)))
1488 res =
ast_streamfile(chan, busy ?
"vm-isonphone" :
"vm-isunavail", ast_channel_language(chan));
1501 ast_debug(1,
"Deleting voicemail file %s\n", file);
1511 static int play_record_review(
struct ast_channel *chan,
char *playfile,
char *recordfile,
int maxtime,
char *fmt,
1512 int outsidecaller,
struct minivm_account *vmu,
int *duration,
int *sound_duration,
const char *unlockdir,
1513 signed char record_gain)
1516 int max_attempts = 3;
1519 int message_exists = 0;
1520 signed char zero_gain = 0;
1521 char *acceptdtmf =
"#";
1522 char *canceldtmf =
"";
1527 if (duration == NULL) {
1528 ast_log(LOG_WARNING,
"Error play_record_review called without duration pointer\n");
1534 while ((cmd >= 0) && (cmd !=
't')) {
1537 ast_verb(3,
"Saving message as is\n");
1543 ast_verb(3,
"Reviewing the message\n");
1551 ast_verb(3,
"Re-recording the message\n");
1553 ast_verb(3,
"Recording the message\n");
1554 if (recorded && outsidecaller)
1562 cmd =
ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf, 0,
AST_RECORD_IF_EXISTS_OVERWRITE);
1569 else if (cmd ==
'*')
1592 if (message_exists || recorded) {
1610 if (outsidecaller && !ast_test_flag(vmu,
MVM_REVIEW))
1612 if (message_exists) {
1620 if (!cmd && outsidecaller && ast_test_flag(vmu,
MVM_OPERATOR)) {
1630 if (attempts > max_attempts) {
1646 char *argv[5] = { NULL };
1651 if (ast_strlen_zero(vmu->
externnotify) && ast_strlen_zero(global_externnotify)) {
1655 snprintf(fquser,
sizeof(fquser),
"%s@%s", vmu->
username, vmu->
domain);
1657 caller = ast_channel_caller(chan);
1660 argv[idx++] = fquser;
1671 ast_debug(1,
"Executing: %s %s %s %s\n",
1672 argv[0], argv[1], argv[2] ?:
"", argv[3] ?:
"");
1678 static int notify_new_message(
struct ast_channel *chan,
const char *templatename,
struct minivm_account *vmu,
const char *filename,
long duration,
const char *format,
char *cidnum,
char *cidname)
1685 char *messageformat;
1687 char oldlocale[100];
1688 const char *counter;
1694 ast_log(LOG_WARNING,
"Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->
attachfmt, format, vmu->
username, vmu->
domain);
1698 etemplate = message_template_find(vmu->
etemplate);
1700 etemplate = message_template_find(templatename);
1702 etemplate = message_template_find(
"email-default");
1706 strsep(&stringp,
"|");
1708 if (!ast_strlen_zero(etemplate->
locale)) {
1710 ast_copy_string(oldlocale, setlocale(LC_TIME, NULL),
sizeof(oldlocale));
1711 ast_debug(2,
"Changing locale from %s to %s\n", oldlocale, etemplate->
locale);
1712 new_locale = setlocale(LC_TIME, etemplate->
locale);
1713 if (new_locale == NULL) {
1714 ast_log(LOG_WARNING,
"-_-_- Changing to new locale did not work. Locale: %s\n", etemplate->
locale);
1721 ast_channel_lock(chan);
1725 ast_channel_unlock(chan);
1727 if (ast_strlen_zero(counter)) {
1728 ast_debug(2,
"MVM_COUNTER not found\n");
1730 ast_debug(2,
"MVM_COUNTER found - will use it with value %s\n", counter);
1733 res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->
attachment, MVM_MESSAGE_EMAIL, counter);
1735 if (res == 0 && !ast_strlen_zero(vmu->
pager)) {
1737 etemplate = message_template_find(vmu->
ptemplate);
1739 etemplate = message_template_find(
"pager-default");
1741 if (!ast_strlen_zero(etemplate->
locale)) {
1742 ast_copy_string(oldlocale, setlocale(LC_TIME,
""),
sizeof(oldlocale));
1743 setlocale(LC_TIME, etemplate->
locale);
1746 res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->
attachment, MVM_MESSAGE_PAGE, counter);
1751 goto notify_cleanup;
1756 "Event",
"MiniVoiceMail",
1757 "Action",
"SentNotification",
1758 "Counter", counter ?:
"");
1760 goto notify_cleanup;
1764 goto notify_cleanup;
1770 if (!ast_strlen_zero(etemplate->
locale)) {
1771 setlocale(LC_TIME, oldlocale);
1781 char tmptxtfile[PATH_MAX];
1784 int res = 0, txtdes;
1786 int sound_duration = 0;
1788 char tmpdir[PATH_MAX];
1789 char ext_context[256] =
"";
1793 struct minivm_account *vmu;
1798 domain = strchr(tmp,
'@');
1804 if (!(vmu = find_account(domain, username, TRUE))) {
1806 ast_log(LOG_ERROR,
"Can't allocate temporary account for '%s@%s'\n", username, domain);
1812 if (strcmp(vmu->
domain,
"localhost"))
1813 snprintf(ext_context,
sizeof(ext_context),
"%s@%s", username, vmu->
domain);
1823 if (ast_strlen_zero(fmt)) {
1824 ast_log(LOG_WARNING,
"No format for saving voicemail? Default %s\n", default_vmformat);
1829 userdir = check_dirpath(tmpdir,
sizeof(tmpdir), vmu->
domain, username,
"tmp");
1833 create_dirpath(tmpdir,
sizeof(tmpdir),
"0000_minivm_temp",
"mediafiles",
"");
1834 ast_debug(3,
"Creating temporary directory %s\n", tmpdir);
1838 snprintf(tmptxtfile,
sizeof(tmptxtfile),
"%s/XXXXXX", tmpdir);
1841 txtdes = mkstemp(tmptxtfile);
1843 ast_log(LOG_ERROR,
"Unable to create message file %s: %s\n", tmptxtfile, strerror(errno));
1844 res =
ast_streamfile(chan,
"vm-mailboxfull", ast_channel_language(chan));
1860 ast_debug(2,
"Open file for metadata: %s\n", tmptxtfile);
1862 res = play_record_review(chan, NULL, tmptxtfile, global_vmmaxmessage, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain);
1864 txt = fdopen(txtdes,
"w+");
1866 ast_log(LOG_WARNING,
"Error opening text file for output\n");
1871 char logbuf[BUFSIZ];
1874 ast_strftime(timebuf,
sizeof(timebuf),
"%H:%M:%S", &tm);
1876 ast_callerid_merge(callerid,
sizeof(callerid),
1877 S_COR(ast_channel_caller(chan)->
id.name.valid, ast_channel_caller(chan)->
id.name.str, NULL),
1878 S_COR(ast_channel_caller(chan)->
id.
number.valid, ast_channel_caller(chan)->
id.
number.str, NULL),
1880 snprintf(logbuf,
sizeof(logbuf),
1882 "%s:%s:%s:%d:%s:%s:%s:%s:%d:%s:%s\n",
1884 ast_channel_context(chan),
1885 ast_channel_exten(chan),
1886 ast_channel_priority(chan),
1887 ast_channel_name(chan),
1892 duration < global_vmminmessage ?
"IGNORED" :
"OK",
1895 fprintf(txt,
"%s", logbuf);
1896 if (minivmlogfile) {
1898 fprintf(minivmlogfile,
"%s", logbuf);
1902 if (sound_duration < global_vmminmessage) {
1903 ast_verb(3,
"Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, global_vmminmessage);
1912 ast_debug(1,
"The recorded media file is gone, so we should remove the .txt file too!\n");
1915 if(ast_test_flag(vmu, MVM_ALLOCED))
1922 snprintf(timebuf,
sizeof(timebuf),
"%d", duration);
1934 ast_debug(2,
"-_-_- Deleted audio file after notification :: %s \n", tmptxtfile);
1941 if(ast_test_flag(vmu, MVM_ALLOCED))
1950 static void queue_mwi_event(
const char *channel_id,
const char *mbx,
const char *ctx,
int urgent,
int new,
int old)
1952 char *mailbox, *context;
1956 if (ast_strlen_zero(context)) {
1957 context =
"default";
1965 static int minivm_mwi_exec(
struct ast_channel *chan,
const char *data)
1974 if (ast_strlen_zero(data)) {
1975 ast_log(LOG_ERROR,
"Minivm needs at least an account argument \n");
1979 argc = ast_app_separate_args(tmpptr,
',', argv, ARRAY_LEN(argv));
1981 ast_log(LOG_ERROR,
"%d arguments passed to MiniVM_MWI, need 4.\n", argc);
1986 domain = strchr(tmp,
'@');
1991 if (ast_strlen_zero(domain) || ast_strlen_zero(mailbox)) {
1992 ast_log(LOG_ERROR,
"Need mailbox@context as argument. Sorry. Argument 0 %s\n", argv[0]);
1995 queue_mwi_event(ast_channel_uniqueid(chan), mailbox, domain, atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
2003 static int minivm_notify_exec(
struct ast_channel *chan,
const char *data)
2011 struct minivm_account *vmu;
2013 const char *
template =
"";
2014 const char *filename;
2016 const char *duration_string;
2017 if (ast_strlen_zero(data)) {
2018 ast_log(LOG_ERROR,
"Minivm needs at least an account argument \n");
2022 argc = ast_app_separate_args(tmpptr,
',', argv, ARRAY_LEN(argv));
2024 if (argc == 2 && !ast_strlen_zero(argv[1]))
2029 domain = strchr(tmp,
'@');
2034 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
2035 ast_log(LOG_ERROR,
"Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
2039 if(!(vmu = find_account(domain, username, TRUE))) {
2041 ast_log(LOG_WARNING,
"Could not allocate temporary memory for '%s@%s'\n", username, domain);
2046 ast_channel_lock(chan);
2050 ast_channel_unlock(chan);
2052 if (!ast_strlen_zero(filename)) {
2053 ast_channel_lock(chan);
2060 ast_channel_unlock(chan);
2063 S_COR(ast_channel_caller(chan)->
id.
number.valid, ast_channel_caller(chan)->
id.
number.str, NULL),
2064 S_COR(ast_channel_caller(chan)->
id.name.valid, ast_channel_caller(chan)->
id.name.str, NULL));
2070 if(ast_test_flag(vmu, MVM_ALLOCED))
2081 static int minivm_record_exec(
struct ast_channel *chan,
const char *data)
2089 char *opts[OPT_ARG_ARRAY_SIZE];
2091 memset(&leave_options, 0,
sizeof(leave_options));
2097 if (ast_strlen_zero(data)) {
2098 ast_log(LOG_ERROR,
"Minivm needs at least an account argument \n");
2102 argc = ast_app_separate_args(tmp,
',', argv, ARRAY_LEN(argv));
2107 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
2108 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
2111 if (sscanf(opts[OPT_ARG_RECORDGAIN],
"%30d", &gain) != 1) {
2112 ast_log(LOG_WARNING,
"Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
2115 leave_options.record_gain = (
signed char) gain;
2122 if (res == ERROR_LOCK_PATH) {
2123 ast_log(LOG_ERROR,
"Could not leave voicemail. The path is already locked.\n");
2134 static int minivm_greet_exec(
struct ast_channel *chan,
const char *data)
2140 char *opts[OPT_ARG_ARRAY_SIZE];
2144 char dest[PATH_MAX];
2145 char prefile[PATH_MAX] =
"";
2146 char tempfile[PATH_MAX] =
"";
2147 char ext_context[256] =
"";
2149 char ecodes[16] =
"#";
2151 struct minivm_account *vmu;
2154 if (ast_strlen_zero(data)) {
2155 ast_log(LOG_ERROR,
"Minivm needs at least an account argument \n");
2159 argc = ast_app_separate_args(tmpptr,
',', argv, ARRAY_LEN(argv));
2164 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
2169 domain = strchr(tmp,
'@');
2174 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
2175 ast_log(LOG_ERROR,
"Need username@domain as argument. Sorry. Argument: %s\n", argv[0]);
2178 ast_debug(1,
"Trying to find configuration for user %s in domain %s\n", username, domain);
2180 if (!(vmu = find_account(domain, username, TRUE))) {
2181 ast_log(LOG_ERROR,
"Could not allocate memory. \n");
2190 if (strcmp(vmu->
domain,
"localhost"))
2191 snprintf(ext_context,
sizeof(ext_context),
"%s@%s", username, vmu->
domain);
2195 if (ast_test_flag(&leave_options, OPT_BUSY_GREETING)) {
2196 res = check_dirpath(dest,
sizeof(dest), vmu->
domain, username,
"busy");
2198 snprintf(prefile,
sizeof(prefile),
"%s%s/%s/busy", MVM_SPOOL_DIR, vmu->
domain, username);
2199 }
else if (ast_test_flag(&leave_options, OPT_UNAVAIL_GREETING)) {
2200 res = check_dirpath(dest,
sizeof(dest), vmu->
domain, username,
"unavail");
2202 snprintf(prefile,
sizeof(prefile),
"%s%s/%s/unavail", MVM_SPOOL_DIR, vmu->
domain, username);
2205 snprintf(tempfile,
sizeof(tempfile),
"%s%s/%s/temp", MVM_SPOOL_DIR, vmu->
domain, username);
2206 if (!(res = check_dirpath(dest,
sizeof(dest), vmu->
domain, username,
"temp"))) {
2207 ast_debug(2,
"Temporary message directory does not exist, using default (%s)\n", tempfile);
2210 ast_debug(2,
"Preparing to play message ...\n");
2214 if (!ast_strlen_zero(vmu->
exit)) {
2216 S_COR(ast_channel_caller(chan)->
id.
number.valid, ast_channel_caller(chan)->
id.
number.str, NULL))) {
2217 strncat(ecodes,
"0",
sizeof(ecodes) - strlen(ecodes) - 1);
2221 S_COR(ast_channel_caller(chan)->
id.
number.valid, ast_channel_caller(chan)->
id.
number.str, NULL))) {
2222 strncat(ecodes,
"0",
sizeof(ecodes) - strlen(ecodes) - 1);
2227 if (!ast_strlen_zero(vmu->
exit)) {
2229 S_COR(ast_channel_caller(chan)->
id.
number.valid, ast_channel_caller(chan)->
id.
number.str, NULL))) {
2230 strncat(ecodes,
"*",
sizeof(ecodes) - strlen(ecodes) - 1);
2233 S_COR(ast_channel_caller(chan)->
id.
number.valid, ast_channel_caller(chan)->
id.
number.str, NULL))) {
2234 strncat(ecodes,
"*",
sizeof(ecodes) - strlen(ecodes) - 1);
2239 if (!ast_strlen_zero(prefile)) {
2240 if (
ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1)
2243 ast_debug(2,
"%s doesn't exist, doing what we can\n", prefile);
2244 res = invent_message(chan, vmu->
domain, username, ast_test_flag(&leave_options, OPT_BUSY_GREETING), ecodes);
2247 ast_debug(2,
"Hang up during prefile playback\n");
2249 if(ast_test_flag(vmu, MVM_ALLOCED))
2255 ast_set_flag(&leave_options, OPT_SILENT);
2258 if (!res && !ast_test_flag(&leave_options, OPT_SILENT)) {
2259 res =
ast_streamfile(chan, SOUND_INTRO, ast_channel_language(chan));
2263 ast_set_flag(&leave_options, OPT_SILENT);
2272 ast_channel_exten_set(chan,
"a");
2273 if (!ast_strlen_zero(vmu->
exit)) {
2274 ast_channel_context_set(chan, vmu->
exit);
2276 ast_channel_priority_set(chan, 0);
2279 }
else if (res ==
'0') {
2281 ast_channel_exten_set(chan,
"o");
2282 if (!ast_strlen_zero(vmu->
exit)) {
2283 ast_channel_context_set(chan, vmu->
exit);
2286 ast_channel_priority_set(chan, 0);
2290 }
else if (res < 0) {
2296 if(ast_test_flag(vmu, MVM_ALLOCED))
2307 static int minivm_delete_exec(
struct ast_channel *chan,
const char *data)
2310 char filename[BUFSIZ];
2312 if (!ast_strlen_zero(data)) {
2315 ast_channel_lock(chan);
2317 ast_channel_unlock(chan);
2320 if (ast_strlen_zero(filename)) {
2321 ast_log(LOG_ERROR,
"No filename given in application arguments or channel variable MVM_FILENAME\n");
2330 ast_debug(2,
"Can't delete file: %s\n", filename);
2333 ast_debug(2,
"Deleted voicemail file :: %s \n", filename);
2337 ast_debug(2,
"Filename does not exist: %s\n", filename);
2349 char filename[PATH_MAX];
2352 char *tmpptr = NULL;
2353 struct minivm_account *vmu;
2356 char *opts[OPT_ARG_ARRAY_SIZE];
2359 char *prompt = NULL;
2362 if (ast_strlen_zero(data)) {
2363 ast_log(LOG_ERROR,
"MinivmAccmess needs at least two arguments: account and option\n");
2367 argc = ast_app_separate_args(tmpptr,
',', argv, ARRAY_LEN(argv));
2371 ast_log(LOG_ERROR,
"MinivmAccmess needs at least two arguments: account and option\n");
2374 if (!error && strlen(argv[1]) > 1) {
2375 ast_log(LOG_ERROR,
"MinivmAccmess can only handle one option at a time. Bad option string: %s\n", argv[1]);
2380 ast_log(LOG_ERROR,
"Can't parse option %s\n", argv[1]);
2391 domain = strchr(tmp,
'@');
2396 if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
2397 ast_log(LOG_ERROR,
"Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
2402 if(!(vmu = find_account(domain, username, TRUE))) {
2404 ast_log(LOG_WARNING,
"Could not allocate temporary memory for '%s@%s'\n", username, domain);
2414 if (ast_test_flag(&flags, OPT_BUSY_GREETING)) {
2416 prompt =
"vm-rec-busy";
2417 }
else if (ast_test_flag(&flags, OPT_UNAVAIL_GREETING)) {
2418 message =
"unavailable";
2419 prompt =
"vm-rec-unv";
2420 }
else if (ast_test_flag(&flags, OPT_TEMP_GREETING)) {
2422 prompt =
"vm-rec-temp";
2423 }
else if (ast_test_flag(&flags, OPT_NAME_GREETING)) {
2425 prompt =
"vm-rec-name";
2427 snprintf(filename,
sizeof(filename),
"%s%s/%s/%s", MVM_SPOOL_DIR, vmu->
domain, vmu->
username, message);
2429 play_record_review(chan, prompt, filename, global_maxgreet, default_vmformat, 0, vmu, &duration, NULL, NULL, FALSE);
2431 ast_debug(1,
"Recorded new %s message in %s (duration %d)\n", message, filename, duration);
2433 if(ast_test_flag(vmu, MVM_ALLOCED))
2445 struct minivm_account *vmu;
2448 char accbuf[BUFSIZ];
2450 ast_debug(3,
"Creating %s account for [%s]\n", realtime ?
"realtime" :
"static", name);
2454 domain = strchr(accbuf,
'@');
2459 if (ast_strlen_zero(domain)) {
2460 ast_log(LOG_ERROR,
"No domain given for mini-voicemail account %s. Not configured.\n", name);
2464 ast_debug(3,
"Creating static account for user %s domain %s\n", username, domain);
2476 ast_debug(3,
"...Configuring account %s\n", name);
2479 ast_debug(3,
"Configuring %s = \"%s\" for account %s\n", var->
name, var->
value, name);
2480 if (!strcasecmp(var->
name,
"serveremail")) {
2482 }
else if (!strcasecmp(var->
name,
"email")) {
2484 }
else if (!strcasecmp(var->
name,
"accountcode")) {
2486 }
else if (!strcasecmp(var->
name,
"pincode")) {
2488 }
else if (!strcasecmp(var->
name,
"domain")) {
2490 }
else if (!strcasecmp(var->
name,
"language")) {
2492 }
else if (!strcasecmp(var->
name,
"timezone")) {
2494 }
else if (!strcasecmp(var->
name,
"externnotify")) {
2496 }
else if (!strcasecmp(var->
name,
"etemplate")) {
2498 }
else if (!strcasecmp(var->
name,
"ptemplate")) {
2500 }
else if (!strcasecmp(var->
name,
"fullname")) {
2502 }
else if (!strcasecmp(var->
name,
"setvar")) {
2504 char varname[strlen(var->
value) + 1];
2507 strcpy(varname, var->
value);
2508 if ((varval = strchr(varname,
'='))) {
2511 if ((tmpvar = ast_variable_new(varname, varval,
""))) {
2516 }
else if (!strcasecmp(var->
name,
"pager")) {
2518 }
else if (!strcasecmp(var->
name,
"volgain")) {
2521 ast_log(LOG_ERROR,
"Unknown configuration option for minivm account %s : %s\n", name, var->
name);
2525 ast_debug(3,
"...Linking account %s\n", name);
2533 ast_debug(2,
"MVM :: Created account %s@%s - tz %s etemplate %s %s\n", username, domain, ast_strlen_zero(vmu->
zonetag) ?
"" : vmu->
zonetag, ast_strlen_zero(vmu->
etemplate) ?
"" : vmu->
etemplate, realtime ?
"(realtime)" :
"");
2559 char *msg_format, *timezone_str;
2562 if (newzone == NULL)
2567 timezone_str = strsep(&msg_format,
"|");
2569 ast_log(LOG_WARNING,
"Invalid timezone definition : %s\n", zonename);
2589 char buf[BUFSIZ * 6];
2590 char readbuf[BUFSIZ];
2591 char filenamebuf[BUFSIZ];
2597 if (ast_strlen_zero(filename))
2599 if (*filename ==
'/')
2602 snprintf(filenamebuf,
sizeof(filenamebuf),
"%s/%s", ast_config_AST_CONFIG_DIR, filename);
2604 if (!(fi = fopen(filenamebuf,
"r"))) {
2605 ast_log(LOG_ERROR,
"Can't read message template from file: %s\n", filenamebuf);
2609 while (fgets(readbuf,
sizeof(readbuf), fi)) {
2611 if (writepos != buf) {
2616 writepos += strlen(readbuf) - 1;
2619 messagebody =
ast_calloc(1, strlen(buf + 1));
2621 ast_debug(4,
"---> Size of allocation %d\n", (
int) strlen(buf + 1) );
2622 ast_debug(4,
"---> Done reading message template : \n%s\n---- END message template--- \n", messagebody);
2630 char *tmpread, *tmpwrite;
2634 tmpread = tmpwrite = emailbody;
2635 while ((tmpwrite = strchr(tmpread,
'\\'))) {
2636 int len = strlen(
"\n");
2637 switch (tmpwrite[1]) {
2639 memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
2643 memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
2647 ast_log(LOG_NOTICE,
"Substitution routine does not support this character: %c\n", tmpwrite[1]);
2649 tmpread = tmpwrite + len;
2661 if (!strcmp(var->
name,
"mailcmd")) {
2663 }
else if (!strcmp(var->
name,
"maxgreet")) {
2664 global_maxgreet = atoi(var->
value);
2665 }
else if (!strcmp(var->
name,
"maxsilence")) {
2666 global_maxsilence = atoi(var->
value);
2667 if (global_maxsilence > 0)
2668 global_maxsilence *= 1000;
2669 }
else if (!strcmp(var->
name,
"logfile")) {
2670 if (!ast_strlen_zero(var->
value) ) {
2671 if(*(var->
value) ==
'/')
2674 snprintf(global_logfile,
sizeof(global_logfile),
"%s/%s", ast_config_AST_LOG_DIR, var->
value);
2676 }
else if (!strcmp(var->
name,
"externnotify")) {
2679 }
else if (!strcmp(var->
name,
"silencethreshold") || !strcmp(var->
name,
"silencetreshold")) {
2681 global_silencethreshold = atoi(var->
value);
2682 }
else if (!strcmp(var->
name,
"maxmessage")) {
2684 if (sscanf(var->
value,
"%30d", &x) == 1) {
2685 global_vmmaxmessage = x;
2688 ast_log(LOG_WARNING,
"Invalid max message time length\n");
2690 }
else if (!strcmp(var->
name,
"minmessage")) {
2692 if (sscanf(var->
value,
"%30d", &x) == 1) {
2693 global_vmminmessage = x;
2694 if (global_maxsilence <= global_vmminmessage)
2695 ast_log(LOG_WARNING,
"maxsilence should be less than minmessage or you may get empty messages\n");
2698 ast_log(LOG_WARNING,
"Invalid min message time length\n");
2700 }
else if (!strcmp(var->
name,
"format")) {
2702 }
else if (!strcmp(var->
name,
"review")) {
2704 }
else if (!strcmp(var->
name,
"operator")) {
2720 struct minivm_template *
template;
2724 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
2726 }
else if (cfg == CONFIG_STATUS_FILEINVALID) {
2727 ast_log(LOG_ERROR,
"Config file " VOICEMAIL_CONFIG
" is in an invalid format. Aborting.\n");
2734 message_destroy_list();
2736 vmaccounts_destroy_list();
2737 ast_debug(2,
"Destroyed memory objects...\n");
2740 global_externnotify[0] =
'\0';
2741 global_logfile[0] =
'\0';
2742 global_vmmaxmessage = 2000;
2743 global_maxgreet = 2000;
2744 global_vmminmessage = 0;
2746 global_maxsilence = 0;
2747 global_saydurationminfo = 2;
2749 ast_set2_flag((&globalflags), FALSE,
MVM_REVIEW);
2759 ast_log(LOG_WARNING,
"Failed to load configuration file. Module activated with default settings.\n");
2764 ast_debug(2,
"Loaded configuration file, now parsing\n");
2770 ast_debug(3,
"Found configuration section [%s]\n", cat);
2771 if (!strcasecmp(cat,
"general")) {
2774 }
else if (!strncasecmp(cat,
"template-", 9)) {
2776 char *name = cat + 9;
2779 error += message_template_build(name, ast_variable_browse(cfg, cat));
2781 var = ast_variable_browse(cfg, cat);
2782 if (!strcasecmp(cat,
"zonemessages")) {
2798 message_template_build(
"email-default", NULL);
2799 template = message_template_find(
"email-default");
2802 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"emaildateformat")))
2803 ast_copy_string(template->dateformat, chanvar,
sizeof(template->dateformat));
2804 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"emailfromstring")))
2805 ast_copy_string(template->fromaddress, chanvar,
sizeof(template->fromaddress));
2806 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"emailaaddress")))
2807 ast_copy_string(template->serveremail, chanvar,
sizeof(template->serveremail));
2808 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"emailaddress")))
2809 ast_copy_string(template->serveremail, chanvar,
sizeof(template->serveremail));
2810 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"emailcharset")))
2811 ast_copy_string(template->charset, chanvar,
sizeof(template->charset));
2812 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"emailsubject")))
2813 ast_copy_string(template->subject, chanvar,
sizeof(template->subject));
2814 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"emailbody")))
2816 template->attachment = TRUE;
2818 message_template_build(
"pager-default", NULL);
2819 template = message_template_find(
"pager-default");
2820 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"pagerfromstring")))
2821 ast_copy_string(template->fromaddress, chanvar,
sizeof(template->fromaddress));
2822 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"pageraddress")))
2823 ast_copy_string(template->serveremail, chanvar,
sizeof(template->serveremail));
2824 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"pagercharset")))
2825 ast_copy_string(template->charset, chanvar,
sizeof(template->charset));
2826 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"pagersubject")))
2827 ast_copy_string(template->subject, chanvar,
sizeof(template->subject));
2828 if ((chanvar = ast_variable_retrieve(cfg,
"general",
"pagerbody")))
2830 template->attachment = FALSE;
2833 ast_log(LOG_ERROR,
"--- A total of %d errors found in mini-voicemail configuration\n", error);
2840 fclose(minivmlogfile);
2843 if(!ast_strlen_zero(global_logfile)) {
2844 minivmlogfile = fopen(global_logfile,
"a");
2846 ast_log(LOG_ERROR,
"Failed to open minivm log file %s : %s\n", global_logfile, strerror(errno));
2848 ast_debug(3,
"Opened log file %s \n", global_logfile);
2857 struct minivm_template *
this;
2858 #define HVLT_OUTPUT_FORMAT "%-15s %-10s %-10s %-15.15s %-50s\n"
2863 e->
command =
"minivm list templates";
2865 "Usage: minivm list templates\n"
2866 " Lists message templates for e-mail, paging and IM\n";
2873 return CLI_SHOWUSAGE;
2877 ast_cli(a->fd,
"There are no message templates defined\n");
2881 ast_cli(a->fd, HVLT_OUTPUT_FORMAT,
"Template name",
"Charset",
"Locale",
"Attach media",
"Subject");
2882 ast_cli(a->fd, HVLT_OUTPUT_FORMAT,
"-------------",
"-------",
"------",
"------------",
"-------");
2884 ast_cli(a->fd, HVLT_OUTPUT_FORMAT, this->name,
2885 S_OR(this->charset,
"-"),
2886 S_OR(this->locale,
"-"),
2887 this->attachment ?
"Yes" :
"No",
2888 S_OR(this->subject,
"-"));
2892 ast_cli(a->fd,
"\n * Total: %d minivoicemail message templates\n", count);
2896 static char *complete_minivm_show_users(
const char *line,
const char *word,
int pos,
int state)
2900 struct minivm_account *vmu;
2901 const char *domain =
"";
2906 wordlen = strlen(word);
2908 if (!strncasecmp(word, vmu->
domain, wordlen)) {
2909 if (domain && strcmp(domain, vmu->
domain) && ++which > state)
2921 struct minivm_account *vmu;
2922 #define HMSU_OUTPUT_FORMAT "%-23s %-15s %-15s %-10s %-10s %-50s\n"
2927 e->
command =
"minivm list accounts [for]";
2929 "Usage: minivm list accounts [for <domain>]\n"
2930 " Lists all mailboxes currently set up\n";
2933 return complete_minivm_show_users(a->line, a->word, a->pos, a->n);
2936 if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
2937 return CLI_SHOWUSAGE;
2938 if ((a->argc == 5) && strcmp(a->argv[3],
"for"))
2939 return CLI_SHOWUSAGE;
2943 ast_cli(a->fd,
"There are no voicemail users currently defined\n");
2947 ast_cli(a->fd, HMSU_OUTPUT_FORMAT,
"User",
"E-Template",
"P-template",
"Zone",
"Format",
"Full name");
2948 ast_cli(a->fd, HMSU_OUTPUT_FORMAT,
"----",
"----------",
"----------",
"----",
"------",
"---------");
2951 if ((a->argc == 3) || ((a->argc == 5) && !strcmp(a->argv[4], vmu->
domain))) {
2954 ast_cli(a->fd, HMSU_OUTPUT_FORMAT, tmp,
S_OR(vmu->
etemplate,
"-"),
2962 ast_cli(a->fd,
"\n * Total: %d minivoicemail accounts\n", count);
2970 #define HMSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
2971 char *res = CLI_SUCCESS;
2975 e->
command =
"minivm list zones";
2977 "Usage: minivm list zones\n"
2978 " Lists zone message formats\n";
2984 if (a->argc != e->
args)
2985 return CLI_SHOWUSAGE;
2989 ast_cli(a->fd, HMSZ_OUTPUT_FORMAT,
"Zone",
"Timezone",
"Message Format");
2990 ast_cli(a->fd, HMSZ_OUTPUT_FORMAT,
"----",
"--------",
"--------------");
2995 ast_cli(a->fd,
"There are no voicemail zones currently defined\n");
3008 e->
command =
"minivm show settings";
3010 "Usage: minivm show settings\n"
3011 " Display Mini-Voicemail general settings\n";
3017 ast_cli(a->fd,
"* Mini-Voicemail general settings\n");
3018 ast_cli(a->fd,
" -------------------------------\n");
3019 ast_cli(a->fd,
"\n");
3020 ast_cli(a->fd,
" Mail command (shell): %s\n", global_mailcmd);
3021 ast_cli(a->fd,
" Max silence: %d\n", global_maxsilence);
3022 ast_cli(a->fd,
" Silence threshold: %d\n", global_silencethreshold);
3023 ast_cli(a->fd,
" Max message length (secs): %d\n", global_vmmaxmessage);
3024 ast_cli(a->fd,
" Min message length (secs): %d\n", global_vmminmessage);
3025 ast_cli(a->fd,
" Default format: %s\n", default_vmformat);
3026 ast_cli(a->fd,
" Extern notify (shell): %s\n", global_externnotify);
3027 ast_cli(a->fd,
" Logfile: %s\n", global_logfile[0] ? global_logfile :
"<disabled>");
3028 ast_cli(a->fd,
" Operator exit: %s\n", ast_test_flag(&globalflags,
MVM_OPERATOR) ?
"Yes" :
"No");
3029 ast_cli(a->fd,
" Message review: %s\n", ast_test_flag(&globalflags,
MVM_REVIEW) ?
"Yes" :
"No");
3031 ast_cli(a->fd,
"\n");
3044 e->
command =
"minivm show stats";
3046 "Usage: minivm show stats\n"
3047 " Display Mini-Voicemail counters\n";
3053 ast_cli(a->fd,
"* Mini-Voicemail statistics\n");
3054 ast_cli(a->fd,
" -------------------------\n");
3055 ast_cli(a->fd,
"\n");
3060 ast_cli(a->fd,
" Received messages since last reset: <none>\n");
3064 ast_strftime(buf,
sizeof(buf),
"%a %b %e %r %Z %Y", &timebuf);
3065 ast_cli(a->fd,
" Last received voicemail: %s\n", buf);
3068 ast_strftime(buf,
sizeof(buf),
"%a %b %e %r %Z %Y", &timebuf);
3069 ast_cli(a->fd,
" Last reset: %s\n", buf);
3071 ast_cli(a->fd,
"\n");
3078 struct minivm_account *vmu;
3083 if ((colname = strchr(username,
':'))) {
3089 if ((domain = strchr(username,
'@'))) {
3093 if (ast_strlen_zero(username) || ast_strlen_zero(domain)) {
3094 ast_log(LOG_ERROR,
"This function needs a username and a domain: username@domain\n");
3098 if (!(vmu = find_account(domain, username, TRUE)))
3101 if (!strcasecmp(colname,
"hasaccount")) {
3102 ast_copy_string(buf, (ast_test_flag(vmu, MVM_ALLOCED) ?
"0" :
"1"), len);
3103 }
else if (!strcasecmp(colname,
"fullname")) {
3105 }
else if (!strcasecmp(colname,
"email")) {
3106 if (!ast_strlen_zero(vmu->
email))
3110 }
else if (!strcasecmp(colname,
"pager")) {
3112 }
else if (!strcasecmp(colname,
"etemplate")) {
3117 }
else if (!strcasecmp(colname,
"language")) {
3119 }
else if (!strcasecmp(colname,
"timezone")) {
3121 }
else if (!strcasecmp(colname,
"ptemplate")) {
3126 }
else if (!strcasecmp(colname,
"accountcode")) {
3128 }
else if (!strcasecmp(colname,
"pincode")) {
3130 }
else if (!strcasecmp(colname,
"path")) {
3136 if (!strcmp(var->
name, colname)) {
3142 if(ast_test_flag(vmu, MVM_ALLOCED))
3156 case AST_LOCK_TIMEOUT:
3172 char filename[BUFSIZ];
3173 char readbuf[BUFSIZ];
3175 int old = 0, counter = 0;
3181 snprintf(filename,
sizeof(filename),
"%s/%s.counter", directory, countername);
3183 counterfile = fopen(filename,
"r");
3185 if(fgets(readbuf,
sizeof(readbuf), counterfile)) {
3186 ast_debug(3,
"Read this string from counter file: %s\n", readbuf);
3187 old = counter = atoi(readbuf);
3189 fclose(counterfile);
3195 ast_debug(2,
"MINIVM Counter %s/%s: Value %d\n", directory, countername, counter);
3209 counterfile = fopen(filename,
"w");
3211 ast_log(LOG_ERROR,
"Could not open counter file for writing : %s - %s\n", filename, strerror(errno));
3215 fprintf(counterfile,
"%d\n\n", counter);
3216 fclose(counterfile);
3218 ast_debug(2,
"MINIVM Counter %s/%s: Old value %d New value %d\n", directory, countername, old, counter);
3226 char userpath[BUFSIZ];
3233 if ((countername = strchr(username,
':'))) {
3234 *countername =
'\0';
3238 if ((domain = strchr(username,
'@'))) {
3244 if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
3245 ast_log(LOG_ERROR,
"No account given\n");
3249 if (ast_strlen_zero(countername)) {
3250 ast_log(LOG_ERROR,
"This function needs two arguments: Account:countername\n");
3255 if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
3261 if (!ast_strlen_zero(username) && !find_account(domain, username, FALSE)) {
3262 ast_log(LOG_ERROR,
"Minivm account does not exist: %s@%s\n", username, domain);
3266 create_dirpath(userpath,
sizeof(userpath), domain, username, NULL);
3271 snprintf(buf, len,
"%d", res);
3279 char userpath[BUFSIZ];
3285 change = atoi(value);
3289 if ((countername = strchr(username,
':'))) {
3290 *countername =
'\0';
3293 if ((operand = strchr(countername,
':'))) {
3298 if ((domain = strchr(username,
'@'))) {
3304 if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
3305 ast_log(LOG_ERROR,
"No account given\n");
3310 if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
3315 if (ast_strlen_zero(operand) || ast_strlen_zero(countername)) {
3316 ast_log(LOG_ERROR,
"Writing to this function requires three arguments: Account:countername:operand\n");
3321 if (!ast_strlen_zero(username) && !find_account(domain, username, FALSE)) {
3322 ast_log(LOG_ERROR,
"Minivm account does not exist: %s@%s\n", username, domain);
3326 create_dirpath(userpath,
sizeof(userpath), domain, username, NULL);
3328 if (*operand ==
'i')
3330 else if (*operand ==
'd') {
3331 change = change * -1;
3333 }
else if (*operand ==
's')
3336 ast_log(LOG_ERROR,
"Unknown operator: %s\n", operand);
3357 .
name =
"MINIVMCOUNTER",
3363 .
name =
"MINIVMACCOUNT",
3390 snprintf(MVM_SPOOL_DIR,
sizeof(MVM_SPOOL_DIR),
"%s/voicemail/", ast_config_AST_SPOOL_DIR);
3409 "Usage: minivm reload\n"
3410 " Reload mini-voicemail configuration and reset statistics\n";
3417 ast_cli(a->fd,
"\n-- Mini voicemail re-configured \n");
3437 message_destroy_list();
3439 vmaccounts_destroy_list();
3445 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
"Mini VoiceMail (A minimal Voicemail e-mail System)",
3446 .support_level = AST_MODULE_SUPPORT_EXTENDED,
struct ast_variable * next
static int reload(void)
Reload mini voicemail module.
Main Channel structure associated with a channel.
char * str
Subscriber phone number (Malloced)
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
#define AST_LIST_LOCK(head)
Locks a list.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
static char * handle_minivm_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command to list voicemail accounts.
struct stasis_message * ast_mwi_blob_create(struct ast_mwi_state *mwi_state, struct stasis_message_type *message_type, struct ast_json *blob)
Creates a ast_mwi_blob message.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
static int create_vmaccount(char *name, struct ast_variable *var, int realtime)
Append new mailbox to mailbox list from configuration file.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
#define ast_channel_unref(c)
Decrease channel reference count.
static char * handle_minivm_list_templates(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI routine for listing templates.
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
basically mkdir -p $dest/$context/$ext/$folder
static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
Creates a file system path expression for a folder within the voicemail data folder and the appropria...
Time-related functions and macros.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
struct ast_party_name name
Subscriber name.
Convenient Signal Processing routines.
static int global_vmminmessage
static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag)
Sends email notification that a user has a new voicemail waiting for them.
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
struct ast_mwi_state * ast_mwi_create(const char *mailbox, const char *context)
Create a ast_mwi_state object.
descriptor for a cli entry.
static char * handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Reload cofiguration.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
static int minivm_counter_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
${MINIVMCOUNTER()} Dialplan function - read counters
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
char language[MAX_LANGUAGE]
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
mvm_messagetype
Message types for notification.
Structure for variables, used for configurations and for channel variables.
char username[AST_MAX_CONTEXT]
static int timezone_add(const char *zonename, const char *config)
Add time zone to memory list.
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
int ast_say_digit_str(struct ast_channel *chan, const char *num, const char *ints, const char *lang)
says digits of a string
ast_channel_state
ast_channel states
char * str
Subscriber name (Malloced)
static void free_zone(struct minivm_zone *z)
Free Mini Voicemail timezone.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
static int global_vmmaxmessage
#define ast_strdup(str)
A wrapper for strdup()
static char global_mailcmd[160]
char accountcode[AST_MAX_ACCOUNT_CODE]
static int check_mime(const char *str)
Check if the string would need encoding within the MIME standard, to avoid confusing certain mail sof...
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
int ast_unlock_path(const char *path)
Unlock a path.
static char * handle_minivm_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail zones in the CLI.
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
int ast_channel_setoption(struct ast_channel *channel, int option, void *data, int datalen, int block)
Sets an option on a channel.
struct timeval lastreceived
int ast_unregister_application(const char *app)
Unregister an application.
char domain[AST_MAX_CONTEXT]
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
static int get_date(char *s, int len)
Gets the current date and time, as formatted string.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int args
This gets set in ast_cli_register()
int ast_base64_encode_file_path(const char *filename, FILE *outputfile, const char *endl)
Performs a base 64 encode algorithm on the contents of a File.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_MAX_ACCOUNT_CODE
Custom localtime functions for multiple timezones.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
static struct minivm_stats global_stats
Statistics for voicemail.
struct ast_party_id id
Caller party ID.
enum AST_LOCK_RESULT ast_lock_path(const char *path)
Lock a filesystem path.
Configuration File Parser.
static void timezone_destroy_list(void)
Clear list of timezones.
#define ast_config_load(filename, flags)
Load a config file.
General Asterisk PBX channel definitions.
Asterisk JSON abstraction layer.
Asterisk file paths, configured in asterisk.conf.
static int unload_module(void)
Unload mini voicemail module.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Data structure associated with a custom dialplan function.
static int global_maxgreet
Caller Party information.
int ast_play_and_wait(struct ast_channel *chan, const char *fn)
Play a stream and wait for a digit, returning the digit that was pressed.
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
static struct ast_cli_entry cli_minivm[]
CLI commands for Mini-voicemail.
int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists)
Record a file based on input from a channel This function will play "auth-thankyou" upon successful r...
A set of macros to manage forward-linked lists.
#define ast_debug(level,...)
Log a DEBUG message.
static const char * ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
Wraps a character sequence in double quotes, escaping occurences of quotes within the string...
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
static char * handle_minivm_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI Show settings.
static char * message_template_parse_filebody(const char *filename)
Read message template from file.
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
static int vm_delete(char *file)
Removes the voicemail sound and information file.
struct stasis_topic * ast_mwi_topic(const char *uniqueid)
Get the Stasis Message Bus API topic for MWI messages on a unique ID.
Core PBX routines and definitions.
static int load_module(void)
Load mini voicemail module.
static int global_maxsilence
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
static FILE * minivmlogfile
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
struct ast_variable * chanvars
static struct ast_vm_user * find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
Finds a voicemail user from the realtime engine.
static char * handle_minivm_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show stats.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Support for dynamic strings.
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
#define AST_OPTION_RXGAIN
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
Prompts the user and records a voicemail to a mailbox.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
static ast_mutex_t minivmloglock
#define ast_calloc(num, len)
A wrapper for calloc()
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
static char * message_template_parse_emailbody(const char *body)
Parse emailbody template from configuration file.
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
#define ast_publish_mwi_state_channel(mailbox, context, new_msgs, old_msgs, channel_id)
Publish a MWI state update associated with some channel.
static char global_externnotify[160]
static double global_volgain
Structure used to handle boolean flags.
The list of e-mail time zones.
static int vm_lock_path(const char *path)
lock directory
Options for leaving voicemail with the voicemail() application.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
#define SENDMAIL
Default mail command to mail voicemail. Change it with the mailcmd= command in voicemail.conf.
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
The list of e-mail templates.
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
static int minivm_counter_func_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
${MINIVMCOUNTER()} Dialplan function - changes counter data
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object...
Standard Command Line Interface.
static int load_config(int reload)
Load minivoicemail configuration.
structure for queuing ARI channel variable setting
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
static int minivm_account_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
${MINIVMACCOUNT()} Dialplan function - reads account data
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
int ast_answer(struct ast_channel *chan)
Answer a channel.
static int minivm_accmess_exec(struct ast_channel *chan, const char *data)
Record specific messages for voicemail account.
Abstract JSON element (object, array, string, int, ...).
static int access_counter_file(char *directory, char *countername, int value, int operand)
Access counter file, lock directory, read and possibly write it again changed.
unsigned char valid
TRUE if the name information is valid/present.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
struct stasis_message_type * ast_mwi_vm_app_type(void)
Get the Stasis Message Bus API message type for voicemail application specific messages.
Structure for gathering statistics.
static void run_externnotify(struct ast_channel *chan, struct minivm_account *vmu)
Run external notification for voicemail message.
static ast_mutex_t minivmlock
The structure that contains MWI state.
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
static struct ast_flags globalflags
int ast_safe_execvp(int dualfork, const char *file, char *const argv[])
Safely spawn an external program while closing file descriptors.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
unsigned char valid
TRUE if the number information is valid/present.
static const char * ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
Encode a string according to the MIME rules for encoding strings that are not 7-bit clean or contain ...
#define ast_custom_function_register(acf)
Register a custom function.
int ast_stopstream(struct ast_channel *c)
Stops a stream.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
static int apply_general_options(struct ast_variable *var)
Apply general configuration options.
static char global_logfile[PATH_MAX]
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
static void populate_defaults(struct ast_vm_user *vmu)
Sets default voicemail system options to a voicemail user.
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
struct ast_party_number number
Subscriber phone number.