2 * Asterisk -- A telephony toolkit for Linux.
6 * Copyright (C) 2003-2004, Digium Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/channel_pvt.h>
19 #include <asterisk/pbx.h>
20 #include <asterisk/options.h>
21 #include <asterisk/config.h>
22 #include <asterisk/say.h>
23 #include <asterisk/module.h>
24 #include <asterisk/adsi.h>
25 #include <asterisk/app.h>
26 #include <asterisk/manager.h>
27 #include <asterisk/dsp.h>
28 #include <asterisk/localtime.h>
29 #include <asterisk/cli.h>
30 #include <asterisk/utils.h>
31 #ifdef USE_ODBC_STORAGE
32 #include <asterisk/res_odbc.h>
42 #include <sys/types.h>
47 #include "../asterisk.h"
48 #include "../astconf.h"
50 #define COMMAND_TIMEOUT 5000
52 #define VOICEMAIL_CONFIG "voicemail.conf"
53 #define ASTERISK_USERNAME "asterisk"
55 /* Default mail command to mail voicemail. Change it with the
56 mailcmd= command in voicemail.conf */
57 #define SENDMAIL "/usr/sbin/sendmail -t"
59 #define INTRO "vm-intro"
63 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
65 #define BASEMAXINLINE 256
66 #define BASELINELEN 72
67 #define BASEMAXINLINE 256
70 #define MAX_DATETIME_FORMAT 512
71 #define MAX_NUM_CID_CONTEXTS 10
73 static int load_config(void);
75 /* Syntaxes supported, not really language codes.
84 German requires the following additional soundfile:
87 Spanish requires the following additional soundfile:
90 Dutch, Portuguese & Spanish require the following additional soundfiles:
91 vm-INBOXs singular of 'new'
92 vm-Olds singular of 'old/heard/read'
105 Italian requires the following additional soundfile:
112 Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folderS, spelled among others when you have to change folder.
113 For the above reasons, vm-INBOX and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
122 unsigned char iobuf[BASEMAXINLINE];
125 /* Structure for linked list of users */
127 char context[80]; /* Voicemail context */
128 char mailbox[80]; /* Mailbox id, unique within vm context */
129 char password[80]; /* Secret pin code, numbers only */
130 char fullname[80]; /* Full name, for directory app */
131 char email[80]; /* E-mail address */
132 char pager[80]; /* E-mail address to pager (no attachment) */
133 char serveremail[80]; /* From: Mail address */
134 char mailcmd[160]; /* Configurable mail command */
135 char language[MAX_LANGUAGE]; /* Config: Language setting */
136 char zonetag[80]; /* Time zone */
139 char uniqueid[20]; /* Unique integer identifier */
153 struct ast_vm_user *next;
159 char msg_format[512];
160 struct vm_zone *next;
179 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
180 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
181 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration);
182 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc);
183 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
185 #ifdef USE_ODBC_STORAGE
186 static char odbc_database[80];
187 #define RETRIEVE(a,b) retrieve_file(a,b)
188 #define DISPOSE(a,b) remove_file(a,b)
189 #define STORE(a,b) store_file(a,b)
190 #define EXISTS(a,b,c,d) (message_exists(a,b))
191 #define RENAME(a,b,c,d,e,f) (rename_file(a,b,c,d))
192 #define COPY(a,b,c,d,e,f) (copy_file(a,b,c,d))
193 #define DELETE(a,b,c) (delete_file(a,b))
195 #define RETRIEVE(a,b)
198 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
199 #define RENAME(a,b,c,d,e,f) (rename_file(e,f));
200 #define COPY(a,b,c,d,e,f) (copy_file(e,f));
201 #define DELETE(a,b,c) (vm_delete(c))
204 static char ext_pass_cmd[128];
206 static char *tdesc = "Comedian Mail (Voicemail System)";
208 static char *addesc = "Comedian Mail";
210 static char *synopsis_vm =
211 "Leave a voicemail message";
213 static char *descrip_vm =
214 " VoiceMail([s|u|b]extension[@context][&extension[@context]][...]): Leaves"
215 "voicemail for a given extension (must be configured in voicemail.conf).\n"
216 " If the extension is preceded by \n"
217 "* 's' then instructions for leaving the message will be skipped.\n"
218 "* 'u' then the \"unavailable\" message will be played.\n"
219 " (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
220 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
221 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
222 "extension 'o' in the current context.\n"
223 "If the caller presses '*' during the prompt, the call jumps to\n"
224 "extension 'a' in the current context.\n"
225 "If the requested mailbox does not exist, and there exists a priority\n"
226 "n + 101, then that priority will be taken next.\n"
227 "When multiple mailboxes are specified, the unavailable or busy message\n"
228 "will be taken from the first mailbox specified.\n"
229 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
230 "Otherwise, it returns 0.\n";
232 static char *synopsis_vmain =
233 "Enter voicemail system";
235 static char *descrip_vmain =
236 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
237 "for the checking of voicemail. The mailbox can be passed as the option,\n"
238 "which will stop the voicemail system from prompting the user for the mailbox.\n"
239 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
240 "the mailbox is preceded by 'p' then the supplied mailbox is prepended to the\n"
241 "user's entry and the resulting string is used as the mailbox number. This is\n"
242 "useful for virtual hosting of voicemail boxes. If a context is specified,\n"
243 "logins are considered in that voicemail context only.\n"
244 "Returns -1 if the user hangs up or 0 otherwise.\n";
246 static char *synopsis_vm_box_exists =
247 "Check if vmbox exists";
249 static char *descrip_vm_box_exists =
250 " MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
251 "if the specified voice mailbox exists.\n";
253 static char *synopsis_vmauthenticate =
254 "Authenticate off voicemail passwords";
256 static char *descrip_vmauthenticate =
257 " VMAuthenticate([mailbox][@context]): Behaves identically to the Authenticate\n"
258 "application, with the exception that the passwords are taken from\n"
260 " If the mailbox is specified, only that mailbox's password will be considered\n"
261 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
262 "be set with the authenticated mailbox.\n";
264 /* Leave a message */
265 static char *app = "VoiceMail";
267 /* Check mail, control, etc */
268 static char *app2 = "VoiceMailMain";
270 static char *app3 = "MailboxExists";
271 static char *app4 = "VMAuthenticate";
273 AST_MUTEX_DEFINE_STATIC(vmlock);
274 struct ast_vm_user *users;
275 struct ast_vm_user *usersl;
276 struct vm_zone *zones = NULL;
277 struct vm_zone *zonesl = NULL;
278 static int attach_voicemail;
279 static int maxsilence;
280 static int silencethreshold = 128;
281 static char serveremail[80];
282 static char mailcmd[160]; /* Configurable mail cmd */
283 static char externnotify[160];
285 static char vmfmts[80];
286 static int vmminmessage;
287 static int vmmaxmessage;
290 static int maxlogins;
294 static int saycidinfo;
295 static int svmailinfo;
297 static int saydurationinfo;
298 static int saydurationminfo;
299 static int skipaftercmd;
302 static char dialcontext[80];
303 static char callcontext[80];
304 static char exitcontext[80];
306 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
309 static char *emailbody = NULL;
310 static int pbxskip = 0;
311 static char *emailsubject = NULL;
312 static char fromstring[100];
313 static char pagerfromstring[100];
314 static char emailtitle[100];
315 static char charset[32] = "ISO-8859-1";
317 static int directory_forward;
318 static char adsifdn[4] = "\x00\x00\x00\x0F";
319 static char adsisec[4] = "\x9B\xDB\xF7\xAC";
320 static int adsiver = 1;
326 static void populate_defaults(struct ast_vm_user *vmu)
340 vmu->sayduration = 1;
341 if (saydurationminfo>0)
342 vmu->saydurationm = saydurationminfo;
346 vmu->forcegreetings = 1;
348 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
350 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
352 strncpy(vmu->exit, exitcontext, sizeof(vmu->exit) -1);
355 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
358 if (!strcasecmp(var, "attach")) {
363 } else if (!strcasecmp(var, "serveremail")) {
364 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
365 } else if (!strcasecmp(var, "language")) {
366 strncpy(vmu->language, value, sizeof(vmu->language) - 1);
367 } else if (!strcasecmp(var, "tz")) {
368 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
369 } else if (!strcasecmp(var, "delete")) {
370 vmu->delete = ast_true(value);
371 } else if (!strcasecmp(var, "saycid")){
376 } else if (!strcasecmp(var,"sendvoicemail")){
381 } else if (!strcasecmp(var, "review")){
386 } else if (!strcasecmp(var, "operator")){
391 } else if (!strcasecmp(var, "envelope")){
396 } else if (!strcasecmp(var, "sayduration")){
398 vmu->sayduration = 1;
400 vmu->sayduration = 0;
401 } else if (!strcasecmp(var, "saydurationm")){
402 if (sscanf(value, "%d", &x) == 1) {
403 vmu->saydurationm = x;
405 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
407 } else if (!strcasecmp(var, "forcename")){
412 } else if (!strcasecmp(var, "forcegreetings")){
414 vmu->forcegreetings = 1;
416 vmu->forcegreetings = 0;
417 } else if (!strcasecmp(var, "callback")) {
418 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
419 } else if (!strcasecmp(var, "dialout")) {
420 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
421 } else if (!strcasecmp(var, "exitcontext")) {
422 strncpy(vmu->exit, value, sizeof(vmu->exit) -1);
426 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
429 if (!ast_strlen_zero(vmu->uniqueid)) {
430 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password);
432 strncpy(vmu->password, password, sizeof(vmu->password) - 1);
438 static void apply_options(struct ast_vm_user *vmu, const char *options)
439 { /* Destructively Parse options and apply */
443 stringp = ast_strdupa(options);
444 while ((s = strsep(&stringp, "|"))) {
446 if ((var = strsep(&value, "=")) && value) {
447 apply_option(vmu, var, value);
452 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
454 struct ast_variable *var, *tmp;
455 struct ast_vm_user *retval;
460 retval=malloc(sizeof(struct ast_vm_user));
463 memset(retval, 0, sizeof(struct ast_vm_user));
467 strncpy(retval->mailbox, mailbox, sizeof(retval->mailbox) - 1);
469 strncpy(retval->context, context, sizeof(retval->context) - 1);
471 strncpy(retval->context, "default", sizeof(retval->context) - 1);
472 populate_defaults(retval);
473 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", retval->context, NULL);
477 printf("%s => %s\n", tmp->name, tmp->value);
478 if (!strcasecmp(tmp->name, "password")) {
479 strncpy(retval->password, tmp->value, sizeof(retval->password) - 1);
480 } else if (!strcasecmp(tmp->name, "uniqueid")) {
481 strncpy(retval->uniqueid, tmp->value, sizeof(retval->uniqueid) - 1);
482 } else if (!strcasecmp(tmp->name, "pager")) {
483 strncpy(retval->pager, tmp->value, sizeof(retval->pager) - 1);
484 } else if (!strcasecmp(tmp->name, "email")) {
485 strncpy(retval->email, tmp->value, sizeof(retval->email) - 1);
486 } else if (!strcasecmp(tmp->name, "fullname")) {
487 strncpy(retval->fullname, tmp->value, sizeof(retval->fullname) - 1);
489 apply_option(retval, tmp->name, tmp->value);
501 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
503 /* This function could be made to generate one from a database, too */
504 struct ast_vm_user *vmu=NULL, *cur;
505 ast_mutex_lock(&vmlock);
508 if ((!context || !strcasecmp(context, cur->context)) &&
509 (!strcasecmp(mailbox, cur->mailbox)))
517 /* Make a copy, so that on a reload, we have no race */
518 vmu = malloc(sizeof(struct ast_vm_user));
520 memcpy(vmu, cur, sizeof(struct ast_vm_user));
528 vmu = find_user_realtime(ivm, context, mailbox);
529 ast_mutex_unlock(&vmlock);
533 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
535 /* This function could be made to generate one from a database, too */
536 struct ast_vm_user *cur;
538 ast_mutex_lock(&vmlock);
541 if ((!context || !strcasecmp(context, cur->context)) &&
542 (!strcasecmp(mailbox, cur->mailbox)))
547 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
550 ast_mutex_unlock(&vmlock);
554 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
556 /* There's probably a better way of doing this. */
557 /* That's why I've put the password change in a separate function. */
558 /* This could also be done with a database function */
565 char currcontext[256] ="";
566 char tmpin[AST_CONFIG_MAX_PATH];
567 char tmpout[AST_CONFIG_MAX_PATH];
568 char *user, *pass, *rest, *trim, *tempcontext;
571 if (!change_password_realtime(vmu, newpassword))
575 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
576 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
577 configin = fopen(tmpin,"r");
579 configout = fopen(tmpout,"w+");
582 if (!configin || !configout) {
586 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
590 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
594 while (!feof(configin)) {
595 /* Read in the line */
596 fgets(inbuf, sizeof(inbuf), configin);
598 if (!feof(configin)) {
599 /* Make a backup of it */
600 memcpy(orig, inbuf, sizeof(orig));
601 /* Strip trailing \n and comment */
602 inbuf[strlen(inbuf) - 1] = '\0';
603 user = strchr(inbuf, ';');
609 /* check for '[' (opening of context name ) */
610 tempcontext = strchr(user, '[');
612 strncpy(currcontext, tempcontext +1, sizeof(currcontext) - 1);
613 /* now check for ']' */
614 tempcontext = strchr(currcontext, ']');
618 currcontext[0] = '\0';
620 pass = strchr(user, '=');
623 while (*trim && *trim < 33) {
633 while (*pass && *pass < 33)
637 rest = strchr(pass,',');
645 /* Compare user, pass AND context */
646 if (user && *user && !strcmp(user, vmu->mailbox) &&
647 pass && !strcmp(pass, vmu->password) &&
648 currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
649 /* This is the line */
651 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
653 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
656 /* Put it back like it was */
657 fprintf(configout, orig);
664 stat((char *)tmpin, &statbuf);
665 chmod((char *)tmpout, statbuf.st_mode);
666 chown((char *)tmpout, statbuf.st_uid, statbuf.st_gid);
667 unlink((char *)tmpin);
668 rename((char *)tmpout,(char *)tmpin);
669 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
670 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
673 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
676 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
677 ast_safe_system(buf);
680 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
682 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
685 static int make_file(char *dest, int len, char *dir, int num)
687 return snprintf(dest, len, "%s/msg%04d", dir, num);
691 #ifdef USE_ODBC_STORAGE
692 static int retrieve_file(char *dir, int msgnum)
700 SQLSMALLINT colcount=0;
708 SQLSMALLINT datatype;
709 SQLSMALLINT decimaldigits;
710 SQLSMALLINT nullable;
719 obj = fetch_odbc_obj(odbc_database);
722 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
725 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
726 c = strchr(fmt, '|');
729 if (!strcasecmp(fmt, "wav49"))
730 strncpy(fmt, "WAV", sizeof(fmt));
731 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
733 make_file(fn, sizeof(fn), dir, msgnum);
735 strncpy(fn, dir, sizeof(fn) - 1);
736 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
737 f = fopen(full_fn, "w+");
738 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
739 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
740 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
741 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
744 snprintf(sql, sizeof(sql), "SELECT * FROM voicemessages WHERE dir=? AND msgnum=?");
745 res = SQLPrepare(stmt, sql, SQL_NTS);
746 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
747 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
748 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
751 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
752 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
753 res = SQLExecute(stmt);
754 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
755 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
756 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
759 res = SQLRowCount(stmt, &rowcount);
760 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO))) {
761 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
762 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
766 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
768 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
769 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
772 res = SQLNumResultCols(stmt, &colcount);
773 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
774 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
775 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
778 res = SQLFetch(stmt);
779 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
780 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
781 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
785 fprintf(f, "[message]\n");
786 for (x=0;x<colcount;x++) {
788 collen = sizeof(coltitle);
789 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
790 &datatype, &colsize, &decimaldigits, &nullable);
791 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
792 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
793 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
796 if (!strcmp(coltitle, "recording")) {
798 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
800 /* Ugh, gotta fill it so we can mmap */
802 size_t left = 0, bytes = 0;
806 if (bytes > sizeof(tmp))
808 if (write(fd, tmp, bytes) != bytes) {
816 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
819 memset(fdm, 0, fdlen);
820 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
821 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
822 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
823 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
828 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
829 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
830 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
831 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
834 printf("Got field '%s'\n", coltitle);
835 if (strcmp(coltitle, "msgnum") && strcmp(coltitle, "dir") && f)
836 fprintf(f, "%s=%s\n", coltitle, rowdata);
840 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
842 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
853 static int remove_file(char *dir, int msgnum)
861 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
865 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
866 make_file(fn, sizeof(fn), dir, msgnum);
868 strncpy(fn, dir, sizeof(fn) - 1);
869 ast_filedelete(fn, NULL);
870 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
875 static int last_message_index(char *dir)
886 obj = fetch_odbc_obj(odbc_database);
889 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
892 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
893 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
894 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
897 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM voicemessages WHERE dir=?");
898 res = SQLPrepare(stmt, sql, SQL_NTS);
899 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
900 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
901 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
904 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
905 res = SQLExecute(stmt);
906 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
907 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
908 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
911 res = SQLRowCount(stmt, &rowcount);
912 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
913 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
914 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
917 res = SQLFetch(stmt);
918 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
919 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
920 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
923 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
924 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
925 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
926 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
929 if (sscanf(rowdata, "%i", &x) != 1)
930 ast_log(LOG_WARNING, "Failed to read message count!\n");
931 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
933 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
938 static int message_exists(char *dir, int msgnum)
950 obj = fetch_odbc_obj(odbc_database);
953 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
956 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
957 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
958 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
959 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
962 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM voicemessages WHERE dir=? AND msgnum=?");
963 res = SQLPrepare(stmt, sql, SQL_NTS);
964 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
965 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
966 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
969 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
970 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
971 res = SQLExecute(stmt);
972 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
973 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
974 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
977 res = SQLRowCount(stmt, &rowcount);
978 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
979 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
980 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
983 res = SQLFetch(stmt);
984 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
985 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
986 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
989 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
990 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
991 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
992 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
995 if (sscanf(rowdata, "%i", &x) != 1)
996 ast_log(LOG_WARNING, "Failed to read message count!\n");
997 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
999 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1004 static int count_messages(char *dir)
1006 return last_message_index(dir) + 1;
1008 static void delete_file(char *sdir, int smsg)
1018 obj = fetch_odbc_obj(odbc_database);
1020 if (sdir[0] != '/') {
1021 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, sdir);
1024 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1025 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1026 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1027 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1030 snprintf(sql, sizeof(sql), "DELETE FROM voicemessages WHERE dir=? AND msgnum=?");
1031 res = SQLPrepare(stmt, sql, SQL_NTS);
1032 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1033 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1034 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1037 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1038 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1039 res = SQLExecute(stmt);
1040 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1041 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1042 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1045 res = SQLRowCount(stmt, &rowcount);
1046 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO))) {
1047 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
1048 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1051 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1053 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1058 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg)
1070 delete_file(ddir, dmsg);
1071 obj = fetch_odbc_obj(odbc_database);
1073 if (ddir[0] != '/') {
1074 snprintf(rddir, sizeof(rddir), "%s/%s", ast_config_AST_SPOOL_DIR, ddir);
1077 if (sdir[0] != '/') {
1078 snprintf(rsdir, sizeof(rsdir), "%s/%s", ast_config_AST_SPOOL_DIR, sdir);
1081 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1082 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1083 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1084 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1085 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1088 snprintf(sql, sizeof(sql), "INSERT INTO voicemessages (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording FROM voicemessages WHERE dir=? AND msgnum=?");
1089 res = SQLPrepare(stmt, sql, SQL_NTS);
1090 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1091 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1092 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1095 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1096 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1097 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1098 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1099 res = SQLExecute(stmt);
1100 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1101 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1102 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1105 res = SQLRowCount(stmt, &rowcount);
1106 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1107 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1108 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1111 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1113 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1118 static int store_file(char *dir, int msgnum)
1135 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1136 char *category = "";
1137 struct ast_config *cfg=NULL;
1140 delete_file(dir, msgnum);
1141 obj = fetch_odbc_obj(odbc_database);
1143 if (dir[0] != '/') {
1144 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
1147 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1148 c = strchr(fmt, '|');
1151 if (!strcasecmp(fmt, "wav49"))
1152 strncpy(fmt, "WAV", sizeof(fmt));
1153 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1155 make_file(fn, sizeof(fn), dir, msgnum);
1157 strncpy(fn, dir, sizeof(fn) - 1);
1158 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1159 cfg = ast_load(full_fn);
1160 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1161 fd = open(full_fn, O_RDWR);
1163 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1167 context = ast_variable_retrieve(cfg, "message", "context");
1168 if (!context) context = "";
1169 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1170 if (!macrocontext) macrocontext = "";
1171 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1172 if (!callerid) callerid = "";
1173 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1174 if (!origtime) origtime = "";
1175 duration = ast_variable_retrieve(cfg, "message", "duration");
1176 if (!duration) duration = "";
1177 category = ast_variable_retrieve(cfg, "message", "category");
1178 if (!category) category = "";
1180 fdlen = lseek(fd, 0, SEEK_END);
1181 lseek(fd, 0, SEEK_SET);
1182 printf("Length is %d\n", fdlen);
1183 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1185 ast_log(LOG_WARNING, "Memory map failed!\n");
1188 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1189 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1190 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1193 if (!ast_strlen_zero(category))
1194 snprintf(sql, sizeof(sql), "INSERT INTO voicemessages (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)");
1196 snprintf(sql, sizeof(sql), "INSERT INTO voicemessages (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)");
1197 res = SQLPrepare(stmt, sql, SQL_NTS);
1198 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1199 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1200 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1203 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1204 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1205 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1206 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1207 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1208 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1209 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1210 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1211 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1212 if (!ast_strlen_zero(category))
1213 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1214 res = SQLExecute(stmt);
1215 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1216 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1217 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1220 res = SQLRowCount(stmt, &rowcount);
1221 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1222 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
1223 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1226 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1228 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1239 static void rename_file(char *sdir, int smsg, char *ddir, int dmsg)
1251 delete_file(ddir, dmsg);
1252 obj = fetch_odbc_obj(odbc_database);
1254 if (ddir[0] != '/') {
1255 snprintf(rddir, sizeof(rddir), "%s/%s", ast_config_AST_SPOOL_DIR, ddir);
1258 if (sdir[0] != '/') {
1259 snprintf(rsdir, sizeof(rsdir), "%s/%s", ast_config_AST_SPOOL_DIR, sdir);
1262 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1263 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1264 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1265 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1266 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1269 snprintf(sql, sizeof(sql), "UPDATE voicemessages SET dir=?, msgnum=? WHERE dir=? AND msgnum=?");
1270 res = SQLPrepare(stmt, sql, SQL_NTS);
1271 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1272 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1273 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1276 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1277 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1278 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1279 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1280 res = SQLExecute(stmt);
1281 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1282 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1283 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1286 res = SQLRowCount(stmt, &rowcount);
1287 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1288 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
1289 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1292 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1294 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1300 static int count_messages(char *dir)
1302 // Find all .txt files - even if they are not in sequence from 0000
1306 struct dirent *vment = NULL;
1308 if ((vmdir = opendir(dir))) {
1309 while ((vment = readdir(vmdir)))
1311 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
1322 static void rename_file(char *sfn, char *dfn)
1326 ast_filerename(sfn,dfn,NULL);
1327 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1328 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1332 static int copy(char *infile, char *outfile)
1340 #ifdef HARDLINK_WHEN_POSSIBLE
1341 /* Hard link if possible; saves disk space & is faster */
1342 if (link(infile, outfile)) {
1344 if ((ifd = open(infile, O_RDONLY)) < 0) {
1345 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1348 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1349 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1354 len = read(ifd, buf, sizeof(buf));
1356 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1362 res = write(ofd, buf, len);
1364 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1374 #ifdef HARDLINK_WHEN_POSSIBLE
1376 /* Hard link succeeded */
1382 static void copy_file(char *frompath, char *topath)
1384 char frompath2[256],topath2[256];
1385 ast_filecopy(frompath, topath, NULL);
1386 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1387 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1388 copy(frompath2, topath2);
1391 static int last_message_index(char *dir)
1395 for (x=0;x<MAXMSG;x++) {
1396 make_file(fn, sizeof(fn), dir, x);
1397 if (ast_fileexists(fn, NULL, NULL) < 1)
1403 static int vm_delete(char *file)
1408 txtsize = (strlen(file) + 5)*sizeof(char);
1409 txt = (char *)alloca(txtsize);
1410 /* Sprintf here would safe because we alloca'd exactly the right length,
1411 * but trying to eliminate all sprintf's anyhow
1413 snprintf(txt, txtsize, "%s.txt", file);
1415 return ast_filedelete(file, NULL);
1421 inbuf(struct baseio *bio, FILE *fi)
1428 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1443 inchar(struct baseio *bio, FILE *fi)
1445 if (bio->iocp>=bio->iolen) {
1446 if (!inbuf(bio, fi))
1450 return bio->iobuf[bio->iocp++];
1454 ochar(struct baseio *bio, int c, FILE *so)
1456 if (bio->linelength>=BASELINELEN) {
1457 if (fputs(eol,so)==EOF)
1463 if (putc(((unsigned char)c),so)==EOF)
1471 static int base_encode(char *filename, FILE *so)
1473 unsigned char dtable[BASEMAXINLINE];
1478 memset(&bio, 0, sizeof(bio));
1479 bio.iocp = BASEMAXINLINE;
1481 if (!(fi = fopen(filename, "rb"))) {
1482 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1486 for (i= 0;i<9;i++) {
1489 dtable[26+i]= 'a'+i;
1490 dtable[26+i+9]= 'j'+i;
1492 for (i= 0;i<8;i++) {
1493 dtable[i+18]= 'S'+i;
1494 dtable[26+i+18]= 's'+i;
1496 for (i= 0;i<10;i++) {
1497 dtable[52+i]= '0'+i;
1503 unsigned char igroup[3],ogroup[4];
1506 igroup[0]= igroup[1]= igroup[2]= 0;
1508 for (n= 0;n<3;n++) {
1509 if ((c = inchar(&bio, fi)) == EOF) {
1514 igroup[n]= (unsigned char)c;
1518 ogroup[0]= dtable[igroup[0]>>2];
1519 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1520 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1521 ogroup[3]= dtable[igroup[2]&0x3F];
1531 ochar(&bio, ogroup[i], so);
1535 if (fputs(eol,so)==EOF)
1543 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize)
1546 /* Prepare variables for substition in email body and subject */
1547 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1548 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1549 snprintf(passdata, passdatasize, "%d", msgnum);
1550 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1551 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1552 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1553 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1554 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1555 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1558 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail)
1568 char tmp[80] = "/tmp/astmail-XXXXXX";
1572 struct vm_zone *the_zone = NULL;
1573 if (vmu && ast_strlen_zero(vmu->email)) {
1574 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1577 if (!strcmp(format, "wav49"))
1579 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
1580 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1584 p = fdopen(pfd, "w");
1591 gethostname(host, sizeof(host));
1592 if (strchr(srcemail, '@'))
1593 strncpy(who, srcemail, sizeof(who)-1);
1595 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1597 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1600 /* Does this user have a timezone specified? */
1601 if (!ast_strlen_zero(vmu->zonetag)) {
1602 /* Find the zone in the list */
1606 if (!strcmp(z->name, vmu->zonetag)) {
1615 ast_localtime(&t,&tm,the_zone->timezone);
1617 ast_localtime(&t,&tm,NULL);
1618 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1619 fprintf(p, "Date: %s\n", date);
1622 struct ast_channel *ast = ast_channel_alloc(0);
1625 int vmlen = strlen(fromstring)*3 + 200;
1626 if ((passdata = alloca(vmlen))) {
1627 memset(passdata, 0, vmlen);
1628 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1629 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1630 fprintf(p, "From: %s <%s>\n",passdata,who);
1631 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1632 ast_channel_free(ast);
1633 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1635 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1636 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1639 struct ast_channel *ast = ast_channel_alloc(0);
1642 int vmlen = strlen(emailsubject)*3 + 200;
1643 if ((passdata = alloca(vmlen))) {
1644 memset(passdata, 0, vmlen);
1645 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1646 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1647 fprintf(p, "Subject: %s\n",passdata);
1648 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1649 ast_channel_free(ast);
1650 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1653 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1656 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1658 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1659 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
1660 fprintf(p, "MIME-Version: 1.0\n");
1661 if (attach_user_voicemail) {
1662 /* Something unique. */
1663 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
1665 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1667 fprintf(p, "--%s\n", bound);
1669 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1670 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1672 struct ast_channel *ast = ast_channel_alloc(0);
1675 int vmlen = strlen(emailbody)*3 + 200;
1676 if ((passdata = alloca(vmlen))) {
1677 memset(passdata, 0, vmlen);
1678 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1679 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1680 fprintf(p, "%s\n",passdata);
1681 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1682 ast_channel_free(ast);
1683 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1685 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1687 "in mailbox %s from %s, on %s so you might\n"
1688 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1689 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1691 if (attach_user_voicemail) {
1692 fprintf(p, "--%s\n", bound);
1693 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
1694 fprintf(p, "Content-Transfer-Encoding: base64\n");
1695 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1696 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1698 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1699 base_encode(fname, p);
1700 fprintf(p, "\n\n--%s--\n.\n", bound);
1703 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1704 ast_safe_system(tmp2);
1705 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1707 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1713 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu)
1721 char tmp[80] = "/tmp/astmail-XXXXXX";
1725 struct vm_zone *the_zone = NULL;
1729 p = fdopen(pfd, "w");
1737 gethostname(host, sizeof(host));
1738 if (strchr(srcemail, '@'))
1739 strncpy(who, srcemail, sizeof(who)-1);
1741 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1743 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1746 /* Does this user have a timezone specified? */
1747 if (!ast_strlen_zero(vmu->zonetag)) {
1748 /* Find the zone in the list */
1752 if (!strcmp(z->name, vmu->zonetag)) {
1761 ast_localtime(&t,&tm,the_zone->timezone);
1763 ast_localtime(&t,&tm,NULL);
1765 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1766 fprintf(p, "Date: %s\n", date);
1768 if (*pagerfromstring) {
1769 struct ast_channel *ast = ast_channel_alloc(0);
1772 int vmlen = strlen(fromstring)*3 + 200;
1773 if ((passdata = alloca(vmlen))) {
1774 memset(passdata, 0, vmlen);
1775 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1776 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1777 fprintf(p, "From: %s <%s>\n",passdata,who);
1779 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1780 ast_channel_free(ast);
1781 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1783 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1784 fprintf(p, "To: %s\n", pager);
1785 fprintf(p, "Subject: New VM\n\n");
1786 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1787 fprintf(p, "New %s long msg in box %s\n"
1788 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1790 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1791 ast_safe_system(tmp2);
1792 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1794 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1800 static int get_date(char *s, int len)
1805 localtime_r(&t,&tm);
1806 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1809 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1813 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
1815 if (ast_fileexists(fn, NULL, NULL) > 0) {
1816 res = ast_streamfile(chan, fn, chan->language);
1821 res = ast_waitstream(chan, ecodes);
1827 /* Dispose just in case */
1829 res = ast_streamfile(chan, "vm-theperson", chan->language);
1832 res = ast_waitstream(chan, ecodes);
1835 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1840 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1842 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1845 res = ast_waitstream(chan, ecodes);
1849 static void free_user(struct ast_vm_user *vmu)
1855 static void free_zone(struct vm_zone *z)
1860 static char *mbox(int id)
1888 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
1890 static void copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt)
1892 char fromdir[256], todir[256], frompath[256], topath[256];
1893 char *frombox = mbox(imbox);
1896 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
1898 make_dir(todir, sizeof(todir), recip->context, "", "");
1899 /* It's easier just to try to make it than to check for its existence */
1900 if (mkdir(todir, 0700) && (errno != EEXIST))
1901 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1902 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "");
1903 /* It's easier just to try to make it than to check for its existence */
1904 if (mkdir(todir, 0700) && (errno != EEXIST))
1905 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1906 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
1907 if (mkdir(todir, 0700) && (errno != EEXIST))
1908 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1910 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
1911 make_file(frompath, sizeof(frompath), fromdir, msgnum);
1914 make_file(topath, sizeof(topath), todir, recipmsgnum);
1915 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
1918 } while (recipmsgnum < MAXMSG);
1919 if (recipmsgnum < MAXMSG) {
1920 COPY(fromdir, msgnum, todir, recipmsgnum, frompath, topath);
1922 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
1925 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
1928 static void run_externnotify(char *context, char *extension)
1930 char arguments[255];
1931 int newvoicemails = 0, oldvoicemails = 0;
1933 if (!ast_strlen_zero(externnotify)) {
1934 if (ast_app_messagecount(extension, &newvoicemails, &oldvoicemails)) {
1935 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
1937 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
1938 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
1939 ast_safe_system(arguments);
1945 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1959 char prefile[256]="";
1960 char tempfile[256]="";
1961 char ext_context[256] = "";
1964 char ecodes[16] = "#";
1965 char tmp[256] = "", *tmpptr;
1966 struct ast_vm_user *vmu;
1967 struct ast_vm_user svm;
1968 char *category = NULL;
1970 strncpy(tmp, ext, sizeof(tmp) - 1);
1972 context = strchr(tmp, '@');
1976 tmpptr = strchr(context, '&');
1978 tmpptr = strchr(ext, '&');
1986 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
1988 if ((vmu = find_user(&svm, context, ext))) {
1989 /* Setup pre-file if appropriate */
1990 if (strcmp(vmu->context, "default"))
1991 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
1993 strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);
1995 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1997 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1998 snprintf(tempfile, sizeof(tempfile), "voicemail/%s/%s/temp", vmu->context, ext);
1999 RETRIEVE(tempfile, -1);
2000 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2001 strncpy(prefile, tempfile, sizeof(prefile) - 1);
2002 DISPOSE(tempfile, -1);
2003 make_dir(dir, sizeof(dir), vmu->context, "", "");
2004 /* It's easier just to try to make it than to check for its existence */
2005 if (mkdir(dir, 0700) && (errno != EEXIST))
2006 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2007 make_dir(dir, sizeof(dir), vmu->context, ext, "");
2008 /* It's easier just to try to make it than to check for its existence */
2009 if (mkdir(dir, 0700) && (errno != EEXIST))
2010 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2011 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
2012 if (mkdir(dir, 0700) && (errno != EEXIST))
2013 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2015 /* Check current or macro-calling context for special extensions */
2016 if (!ast_strlen_zero(vmu->exit)) {
2017 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2018 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2019 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
2020 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2021 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2022 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2026 if (!ast_strlen_zero(vmu->exit)) {
2027 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2028 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2029 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2030 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2031 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2032 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2037 /* Play the beginning intro if desired */
2038 if (!ast_strlen_zero(prefile)) {
2039 RETRIEVE(prefile, -1);
2040 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2041 if (ast_streamfile(chan, prefile, chan->language) > -1)
2042 res = ast_waitstream(chan, ecodes);
2044 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2045 res = invent_message(chan, vmu->context, ext, busy, ecodes);
2047 DISPOSE(prefile, -1);
2049 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2055 /* On a '#' we skip the instructions */
2059 if (!res && !silent) {
2060 res = ast_streamfile(chan, INTRO, chan->language);
2062 res = ast_waitstream(chan, ecodes);
2069 ast_stopstream(chan);
2070 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2071 other than the operator -- an automated attendant or mailbox login for example */
2073 strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
2074 if (!ast_strlen_zero(vmu->exit)) {
2075 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
2076 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2077 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
2083 /* Check for a '0' here */
2086 if (vmu->operator) {
2087 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
2088 if (!ast_strlen_zero(vmu->exit)) {
2089 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
2090 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2091 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
2093 ast_play_and_wait(chan, "transfer");
2098 ast_play_and_wait(chan, "vm-sorry");
2106 /* The meat of recording the message... All the announcements and beeps have been played*/
2107 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
2108 if (!ast_strlen_zero(fmt)) {
2111 make_file(fn, sizeof(fn), dir, msgnum);
2112 if (!EXISTS(dir,msgnum,fn,chan->language))
2115 } while (msgnum < MAXMSG);
2117 /* Unless we're *really* silent, try to send the beep */
2118 res = ast_streamfile(chan, "beep", chan->language);
2120 res = ast_waitstream(chan, "");
2122 if (msgnum < MAXMSG) {
2123 /* Store information */
2124 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2125 txt = fopen(txtfile, "w+");
2127 get_date(date, sizeof(date));
2130 "; Message Information file\n"
2149 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2150 date, (long)time(NULL),
2151 category ? category : "");
2154 ast_log(LOG_WARNING, "Error opening text file for output\n");
2155 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
2160 fd = open(txtfile, O_APPEND | O_WRONLY);
2162 txt = fdopen(fd, "a");
2164 fprintf(txt, "duration=%d\n", duration);
2169 if (duration < vmminmessage) {
2170 if (option_verbose > 2)
2171 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2172 DELETE(dir,msgnum,fn);
2173 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2176 /* Are there to be more recipients of this message? */
2178 struct ast_vm_user recipu, *recip;
2179 char *exten, *context;
2181 exten = strsep(&tmpptr, "&");
2182 context = strchr(exten, '@');
2187 if ((recip = find_user(&recipu, context, exten))) {
2188 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2192 if (ast_fileexists(fn, NULL, NULL)) {
2193 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2195 DISPOSE(dir, msgnum);
2198 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2200 res = ast_waitstream(chan, "");
2201 ast_log(LOG_WARNING, "No more messages possible\n");
2204 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2208 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2209 /*Send the call to n+101 priority, where n is the current priority*/
2210 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num))
2211 chan->priority+=100;
2217 #ifdef USE_ODBC_STORAGE
2220 static void resequence_mailbox(char * dir)
2222 // we know max messages, so stop process when number is hit
2228 for (x=0,dest=0;x<MAXMSG;x++) {
2229 make_file(sfn, sizeof(sfn), dir, x);
2230 if (EXISTS(dir, x, sfn, NULL)) {
2233 make_file(dfn, sizeof(dfn), dir, dest);
2234 RENAME(dir, x, dir, dest, sfn, dfn);
2243 static int say_and_wait(struct ast_channel *chan, int num, char *language)
2246 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2250 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
2255 char *dbox = mbox(box);
2257 make_file(sfn, sizeof(sfn), dir, msg);
2258 make_dir(ddir, sizeof(ddir), context, username, dbox);
2260 for (x=0;x<MAXMSG;x++) {
2261 make_file(dfn, sizeof(dfn), ddir, x);
2262 if (!EXISTS(ddir, x, dfn, NULL))
2267 if (strcmp(sfn, dfn)) {
2268 COPY(dir, msg, ddir, x, sfn, dfn);
2273 static int adsi_logo(unsigned char *buf)
2276 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2277 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2281 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2289 bytes += adsi_data_mode(buf + bytes);
2290 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2293 bytes += adsi_logo(buf);
2294 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2296 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
2298 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2299 bytes += adsi_data_mode(buf + bytes);
2300 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2302 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2304 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2305 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2306 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2307 bytes += adsi_voice_mode(buf + bytes, 0);
2308 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2315 bytes += adsi_logo(buf);
2316 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2317 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
2318 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2319 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2322 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2323 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2324 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2325 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2326 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2327 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2328 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2331 /* Add another dot */
2333 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
2334 bytes += adsi_voice_mode(buf + bytes, 0);
2336 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2337 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2341 /* These buttons we load but don't use yet */
2342 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2343 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2344 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2345 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2346 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2347 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2348 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2351 /* Add another dot */
2353 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2354 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2355 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2360 snprintf(num, sizeof(num), "%d", x);
2361 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2363 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2364 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2367 /* Add another dot */
2369 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2370 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2371 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2374 if (adsi_end_download(chan)) {
2376 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2377 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2378 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2379 bytes += adsi_voice_mode(buf + bytes, 0);
2380 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2384 bytes += adsi_download_disconnect(buf + bytes);
2385 bytes += adsi_voice_mode(buf + bytes, 0);
2386 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2388 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2393 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2394 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2396 ast_log(LOG_DEBUG, "Restarting session...\n");
2399 /* Load the session now */
2400 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2402 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2404 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2406 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2410 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2413 if (!adsi_available(chan))
2415 x = adsi_load_session(chan, adsifdn, adsiver, 1);
2419 if (adsi_load_vmail(chan, useadsi)) {
2420 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2427 static void adsi_login(struct ast_channel *chan)
2431 unsigned char keys[8];
2433 if (!adsi_available(chan))
2438 /* Set one key for next */
2439 keys[3] = ADSI_KEY_APPS + 3;
2441 bytes += adsi_logo(buf + bytes);
2442 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2443 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2444 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2445 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2446 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2447 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2448 bytes += adsi_set_keys(buf + bytes, keys);
2449 bytes += adsi_voice_mode(buf + bytes, 0);
2450 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2453 static void adsi_password(struct ast_channel *chan)
2457 unsigned char keys[8];
2459 if (!adsi_available(chan))
2464 /* Set one key for next */
2465 keys[3] = ADSI_KEY_APPS + 3;
2467 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2468 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2469 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2470 bytes += adsi_set_keys(buf + bytes, keys);
2471 bytes += adsi_voice_mode(buf + bytes, 0);
2472 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2475 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2479 unsigned char keys[8];
2482 if (!adsi_available(chan))
2486 y = ADSI_KEY_APPS + 12 + start + x;
2487 if (y > ADSI_KEY_APPS + 12 + 4)
2489 keys[x] = ADSI_KEY_SKT | y;
2491 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2495 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2496 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2497 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2498 bytes += adsi_set_keys(buf + bytes, keys);
2499 bytes += adsi_voice_mode(buf + bytes, 0);
2501 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2504 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2507 char buf[256], buf1[256], buf2[256];
2513 char datetime[21]="";
2516 unsigned char keys[8];
2520 if (!adsi_available(chan))
2523 /* Retrieve important info */
2524 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
2525 f = fopen(fn2, "r");
2528 fgets(buf, sizeof(buf), f);
2532 strsep(&stringp, "=");
2533 val = strsep(&stringp, "=");
2534 if (val && !ast_strlen_zero(val)) {
2535 if (!strcmp(buf, "callerid"))
2536 strncpy(cid, val, sizeof(cid) - 1);
2537 if (!strcmp(buf, "origdate"))
2538 strncpy(datetime, val, sizeof(datetime) - 1);
2544 /* New meaning for keys */
2546 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2551 /* No prev key, provide "Folder" instead */
2552 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2554 if (vms->curmsg >= vms->lastmsg) {
2555 /* If last message ... */
2557 /* but not only message, provide "Folder" instead */
2558 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2559 bytes += adsi_voice_mode(buf + bytes, 0);
2562 /* Otherwise if only message, leave blank */
2567 if (!ast_strlen_zero(cid)) {
2568 ast_callerid_parse(cid, &name, &num);
2572 name = "Unknown Caller";
2574 /* If deleted, show "undeleted" */
2576 if (vms->deleted[vms->curmsg])
2577 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2580 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2581 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
2582 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
2583 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
2585 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2586 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2587 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2588 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2589 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2590 bytes += adsi_set_keys(buf + bytes, keys);
2591 bytes += adsi_voice_mode(buf + bytes, 0);
2593 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2596 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
2600 unsigned char keys[8];
2604 if (!adsi_available(chan))
2607 /* New meaning for keys */
2609 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2615 /* No prev key, provide "Folder" instead */
2616 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2618 if (vms->curmsg >= vms->lastmsg) {
2619 /* If last message ... */
2621 /* but not only message, provide "Folder" instead */
2622 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2624 /* Otherwise if only message, leave blank */
2629 /* If deleted, show "undeleted" */
2630 if (vms->deleted[vms->curmsg])
2631 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2634 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2635 bytes += adsi_set_keys(buf + bytes, keys);
2636 bytes += adsi_voice_mode(buf + bytes, 0);
2638 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2641 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
2643 char buf[256] = "", buf1[256] = "", buf2[256] = "";
2645 unsigned char keys[8];
2648 char *newm = (vms->newmessages == 1) ? "message" : "messages";
2649 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
2650 if (!adsi_available(chan))
2652 if (vms->newmessages) {
2653 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
2654 if (vms->oldmessages) {
2655 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
2656 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
2658 snprintf(buf2, sizeof(buf2), "%s.", newm);
2660 } else if (vms->oldmessages) {
2661 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
2662 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2664 strncpy(buf1, "You have no messages.", sizeof(buf1) - 1);
2668 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2669 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2670 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2673 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2677 /* Don't let them listen if there are none */
2678 if (vms->lastmsg < 0)
2680 bytes += adsi_set_keys(buf + bytes, keys);
2682 bytes += adsi_voice_mode(buf + bytes, 0);
2684 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2687 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
2689 char buf[256] = "", buf1[256] = "", buf2[256] = "";
2691 unsigned char keys[8];
2694 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
2696 if (!adsi_available(chan))
2699 /* Original command keys */
2701 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2706 if ((vms->lastmsg + 1) < 1)
2709 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
2710 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
2712 if (vms->lastmsg + 1)
2713 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
2715 strncpy(buf2, "no messages.", sizeof(buf2) - 1);
2716 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2717 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2718 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2719 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2720 bytes += adsi_set_keys(buf + bytes, keys);
2722 bytes += adsi_voice_mode(buf + bytes, 0);
2724 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2729 static void adsi_clear(struct ast_channel *chan)
2733 if (!adsi_available(chan))
2735 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2736 bytes += adsi_voice_mode(buf + bytes, 0);
2738 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2742 static void adsi_goodbye(struct ast_channel *chan)
2747 if (!adsi_available(chan))
2749 bytes += adsi_logo(buf + bytes);
2750 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2751 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2752 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2753 bytes += adsi_voice_mode(buf + bytes, 0);
2755 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2758 /*--- get_folder: Folder menu ---*/
2759 /* Plays "press 1 for INBOX messages" etc
2760 Should possibly be internationalized
2762 static int get_folder(struct ast_channel *chan, int start)
2767 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
2770 for (x = start; x< 5; x++) { /* For all folders */
2771 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2773 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
2776 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
2777 d = vm_play_folder_name(chan, fn);
2780 d = ast_waitfordigit(chan, 500);
2784 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
2787 d = ast_waitfordigit(chan, 4000);
2791 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2794 res = ast_play_and_wait(chan, fn); /* Folder name */
2795 while (((res < '0') || (res > '9')) &&
2796 (res != '#') && (res >= 0)) {
2797 res = get_folder(chan, 0);
2802 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2808 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2813 /* prepend a message to the current message and return */
2816 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2817 cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
2827 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
2828 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
2830 cmd = ast_play_and_wait(chan,"vm-starmain");
2831 /* "press star to return to the main menu" */
2833 cmd = ast_waitfordigit(chan,6000);
2845 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
2847 char todir[256], fn[256], ext_context[256], *stringp;
2849 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
2850 make_file(fn, sizeof(fn), todir, msgnum);
2851 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
2853 /* Attach only the first format */
2854 fmt = ast_strdupa(fmt);
2857 strsep(&stringp, "|");
2859 if (!ast_strlen_zero(vmu->email)) {
2860 int attach_user_voicemail = attach_voicemail;
2861 char *myserveremail = serveremail;
2862 if (vmu->attach > -1)
2863 attach_user_voicemail = vmu->attach;
2864 if (!ast_strlen_zero(vmu->serveremail))
2865 myserveremail = vmu->serveremail;
2866 sendmail(myserveremail, vmu, msgnum, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail);
2869 if (!ast_strlen_zero(vmu->pager)) {
2870 char *myserveremail = serveremail;
2871 if (!ast_strlen_zero(vmu->serveremail))
2872 myserveremail = vmu->serveremail;
2873 sendpage(myserveremail, vmu->pager, msgnum, vmu->mailbox, cidnum, cidname, duration, vmu);
2876 ast_log(LOG_ERROR, "Out of memory\n");
2880 DELETE(todir, msgnum, fn);
2883 /* Leave voicemail for someone */
2884 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL));
2885 run_externnotify(chan->context, ext_context);
2889 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt,int flag)
2891 char username[70]="";
2896 struct ast_config *mif;
2900 char ext_context[256]="";
2901 int res = 0, cmd = 0;
2902 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2905 int saved_messages = 0, found = 0;
2906 int valid_extensions = 0;
2908 while (!res && !valid_extensions) {
2910 int use_directory = 0;
2911 if( directory_forward ) {
2915 while((cmd >= 0) && !done ){
2932 /* Press 1 to enter an extension press 2 to use the directory */
2933 cmd = ast_play_and_wait(chan,"vm-forward");
2935 cmd = ast_waitfordigit(chan,3000);
2946 if( cmd<0 || cmd=='t' )
2950 if( use_directory ) {
2951 /* use app_directory */
2953 char old_context[sizeof(chan->context)];
2954 char old_exten[sizeof(chan->exten)];
2956 struct ast_app* app;
2959 app = pbx_findapp("Directory");
2961 /* make mackup copies */
2962 memcpy(old_context, chan->context, sizeof(chan->context));
2963 memcpy(old_exten, chan->exten, sizeof(chan->exten));
2964 old_priority = chan->priority;
2966 /* call the the Directory, changes the channel */
2967 res = pbx_exec(chan, app, ((context)?context:chan->context), 1);
2969 strncpy(username, chan->exten, sizeof(username)-1);
2971 /* restore the old context, exten, and priority */
2972 memcpy(chan->context, old_context, sizeof(chan->context));
2973 memcpy(chan->exten, old_exten, sizeof(chan->exten));
2974 chan->priority = old_priority;
2977 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
2978 directory_forward = 0;
2981 /* Ask for an extension */
2982 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
2985 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2989 /* start all over if no username */
2990 if (ast_strlen_zero(username))