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
13 * 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr)
14 * George Konstantoulakis <gkon@inaccessnetworks.com>
17 #include <asterisk/lock.h>
18 #include <asterisk/file.h>
19 #include <asterisk/logger.h>
20 #include <asterisk/channel.h>
21 #include <asterisk/channel_pvt.h>
22 #include <asterisk/pbx.h>
23 #include <asterisk/options.h>
24 #include <asterisk/config.h>
25 #include <asterisk/say.h>
26 #include <asterisk/module.h>
27 #include <asterisk/adsi.h>
28 #include <asterisk/app.h>
29 #include <asterisk/manager.h>
30 #include <asterisk/dsp.h>
31 #include <asterisk/localtime.h>
32 #include <asterisk/cli.h>
33 #include <asterisk/utils.h>
34 #ifdef USE_ODBC_STORAGE
35 #include <asterisk/res_odbc.h>
45 #include <sys/types.h>
50 #include "../asterisk.h"
51 #include "../astconf.h"
53 #define COMMAND_TIMEOUT 5000
55 #define VOICEMAIL_CONFIG "voicemail.conf"
56 #define ASTERISK_USERNAME "asterisk"
58 /* Default mail command to mail voicemail. Change it with the
59 mailcmd= command in voicemail.conf */
60 #define SENDMAIL "/usr/sbin/sendmail -t"
62 #define INTRO "vm-intro"
66 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
68 #define BASEMAXINLINE 256
69 #define BASELINELEN 72
70 #define BASEMAXINLINE 256
73 #define MAX_DATETIME_FORMAT 512
74 #define MAX_NUM_CID_CONTEXTS 10
76 static int load_config(void);
78 /* Syntaxes supported, not really language codes.
88 German requires the following additional soundfile:
91 Spanish requires the following additional soundfile:
94 Dutch, Portuguese & Spanish require the following additional soundfiles:
95 vm-INBOXs singular of 'new'
96 vm-Olds singular of 'old/heard/read'
109 Italian requires the following additional soundfile:
116 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.
117 For the above reasons, vm-INBOX and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
126 unsigned char iobuf[BASEMAXINLINE];
129 /* Structure for linked list of users */
131 char context[80]; /* Voicemail context */
132 char mailbox[80]; /* Mailbox id, unique within vm context */
133 char password[80]; /* Secret pin code, numbers only */
134 char fullname[80]; /* Full name, for directory app */
135 char email[80]; /* E-mail address */
136 char pager[80]; /* E-mail address to pager (no attachment) */
137 char serveremail[80]; /* From: Mail address */
138 char mailcmd[160]; /* Configurable mail command */
139 char language[MAX_LANGUAGE]; /* Config: Language setting */
140 char zonetag[80]; /* Time zone */
143 char uniqueid[20]; /* Unique integer identifier */
157 struct ast_vm_user *next;
163 char msg_format[512];
164 struct vm_zone *next;
183 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
184 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
185 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);
186 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc);
187 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
189 #ifdef USE_ODBC_STORAGE
190 static char odbc_database[80];
191 #define RETRIEVE(a,b) retrieve_file(a,b)
192 #define DISPOSE(a,b) remove_file(a,b)
193 #define STORE(a,b) store_file(a,b)
194 #define EXISTS(a,b,c,d) (message_exists(a,b))
195 #define RENAME(a,b,c,d,e,f) (rename_file(a,b,c,d))
196 #define COPY(a,b,c,d,e,f) (copy_file(a,b,c,d))
197 #define DELETE(a,b,c) (delete_file(a,b))
199 #define RETRIEVE(a,b)
202 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
203 #define RENAME(a,b,c,d,e,f) (rename_file(e,f));
204 #define COPY(a,b,c,d,e,f) (copy_file(e,f));
205 #define DELETE(a,b,c) (vm_delete(c))
208 static char ext_pass_cmd[128];
210 static char *tdesc = "Comedian Mail (Voicemail System)";
212 static char *addesc = "Comedian Mail";
214 static char *synopsis_vm =
215 "Leave a voicemail message";
217 static char *descrip_vm =
218 " VoiceMail([s|u|b]extension[@context][&extension[@context]][...]): Leaves"
219 "voicemail for a given extension (must be configured in voicemail.conf).\n"
220 " If the extension is preceded by \n"
221 "* 's' then instructions for leaving the message will be skipped.\n"
222 "* 'u' then the \"unavailable\" message will be played.\n"
223 " (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
224 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
225 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
226 "extension 'o' in the current context.\n"
227 "If the caller presses '*' during the prompt, the call jumps to\n"
228 "extension 'a' in the current context.\n"
229 "If the requested mailbox does not exist, and there exists a priority\n"
230 "n + 101, then that priority will be taken next.\n"
231 "When multiple mailboxes are specified, the unavailable or busy message\n"
232 "will be taken from the first mailbox specified.\n"
233 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
234 "Otherwise, it returns 0.\n";
236 static char *synopsis_vmain =
237 "Enter voicemail system";
239 static char *descrip_vmain =
240 " VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
241 "for the checking of voicemail. The mailbox can be passed as the option,\n"
242 "which will stop the voicemail system from prompting the user for the mailbox.\n"
243 "If the mailbox is preceded by 's' then the password check will be skipped. If\n"
244 "the mailbox is preceded by 'p' then the supplied mailbox is prepended to the\n"
245 "user's entry and the resulting string is used as the mailbox number. This is\n"
246 "useful for virtual hosting of voicemail boxes. If a context is specified,\n"
247 "logins are considered in that voicemail context only.\n"
248 "Returns -1 if the user hangs up or 0 otherwise.\n";
250 static char *synopsis_vm_box_exists =
251 "Check if vmbox exists";
253 static char *descrip_vm_box_exists =
254 " MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
255 "if the specified voice mailbox exists.\n";
257 static char *synopsis_vmauthenticate =
258 "Authenticate off voicemail passwords";
260 static char *descrip_vmauthenticate =
261 " VMAuthenticate([mailbox][@context]): Behaves identically to the Authenticate\n"
262 "application, with the exception that the passwords are taken from\n"
264 " If the mailbox is specified, only that mailbox's password will be considered\n"
265 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
266 "be set with the authenticated mailbox.\n";
268 /* Leave a message */
269 static char *app = "VoiceMail";
271 /* Check mail, control, etc */
272 static char *app2 = "VoiceMailMain";
274 static char *app3 = "MailboxExists";
275 static char *app4 = "VMAuthenticate";
277 AST_MUTEX_DEFINE_STATIC(vmlock);
278 struct ast_vm_user *users;
279 struct ast_vm_user *usersl;
280 struct vm_zone *zones = NULL;
281 struct vm_zone *zonesl = NULL;
282 static int attach_voicemail;
283 static int maxsilence;
284 static int silencethreshold = 128;
285 static char serveremail[80];
286 static char mailcmd[160]; /* Configurable mail cmd */
287 static char externnotify[160];
289 static char vmfmts[80];
290 static int vmminmessage;
291 static int vmmaxmessage;
294 static int maxlogins;
298 static int saycidinfo;
299 static int svmailinfo;
301 static int saydurationinfo;
302 static int saydurationminfo;
303 static int skipaftercmd;
306 static char dialcontext[80];
307 static char callcontext[80];
308 static char exitcontext[80];
310 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
313 static char *emailbody = NULL;
314 static int pbxskip = 0;
315 static char *emailsubject = NULL;
316 static char fromstring[100];
317 static char pagerfromstring[100];
318 static char emailtitle[100];
319 static char charset[32] = "ISO-8859-1";
321 static int directory_forward;
322 static char adsifdn[4] = "\x00\x00\x00\x0F";
323 static char adsisec[4] = "\x9B\xDB\xF7\xAC";
324 static int adsiver = 1;
330 static void populate_defaults(struct ast_vm_user *vmu)
344 vmu->sayduration = 1;
345 if (saydurationminfo>0)
346 vmu->saydurationm = saydurationminfo;
350 vmu->forcegreetings = 1;
352 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
354 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
356 strncpy(vmu->exit, exitcontext, sizeof(vmu->exit) -1);
359 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
362 if (!strcasecmp(var, "attach")) {
367 } else if (!strcasecmp(var, "serveremail")) {
368 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
369 } else if (!strcasecmp(var, "language")) {
370 strncpy(vmu->language, value, sizeof(vmu->language) - 1);
371 } else if (!strcasecmp(var, "tz")) {
372 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
373 } else if (!strcasecmp(var, "delete")) {
374 vmu->delete = ast_true(value);
375 } else if (!strcasecmp(var, "saycid")){
380 } else if (!strcasecmp(var,"sendvoicemail")){
385 } else if (!strcasecmp(var, "review")){
390 } else if (!strcasecmp(var, "operator")){
395 } else if (!strcasecmp(var, "envelope")){
400 } else if (!strcasecmp(var, "sayduration")){
402 vmu->sayduration = 1;
404 vmu->sayduration = 0;
405 } else if (!strcasecmp(var, "saydurationm")){
406 if (sscanf(value, "%d", &x) == 1) {
407 vmu->saydurationm = x;
409 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
411 } else if (!strcasecmp(var, "forcename")){
416 } else if (!strcasecmp(var, "forcegreetings")){
418 vmu->forcegreetings = 1;
420 vmu->forcegreetings = 0;
421 } else if (!strcasecmp(var, "callback")) {
422 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
423 } else if (!strcasecmp(var, "dialout")) {
424 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
425 } else if (!strcasecmp(var, "exitcontext")) {
426 strncpy(vmu->exit, value, sizeof(vmu->exit) -1);
430 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
433 if (!ast_strlen_zero(vmu->uniqueid)) {
434 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
436 strncpy(vmu->password, password, sizeof(vmu->password) - 1);
442 static void apply_options(struct ast_vm_user *vmu, const char *options)
443 { /* Destructively Parse options and apply */
447 stringp = ast_strdupa(options);
448 while ((s = strsep(&stringp, "|"))) {
450 if ((var = strsep(&value, "=")) && value) {
451 apply_option(vmu, var, value);
456 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
458 struct ast_variable *var, *tmp;
459 struct ast_vm_user *retval;
464 retval=malloc(sizeof(struct ast_vm_user));
467 memset(retval, 0, sizeof(struct ast_vm_user));
471 strncpy(retval->mailbox, mailbox, sizeof(retval->mailbox) - 1);
473 strncpy(retval->context, context, sizeof(retval->context) - 1);
475 strncpy(retval->context, "default", sizeof(retval->context) - 1);
476 populate_defaults(retval);
477 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", retval->context, NULL);
481 printf("%s => %s\n", tmp->name, tmp->value);
482 if (!strcasecmp(tmp->name, "password")) {
483 strncpy(retval->password, tmp->value, sizeof(retval->password) - 1);
484 } else if (!strcasecmp(tmp->name, "uniqueid")) {
485 strncpy(retval->uniqueid, tmp->value, sizeof(retval->uniqueid) - 1);
486 } else if (!strcasecmp(tmp->name, "pager")) {
487 strncpy(retval->pager, tmp->value, sizeof(retval->pager) - 1);
488 } else if (!strcasecmp(tmp->name, "email")) {
489 strncpy(retval->email, tmp->value, sizeof(retval->email) - 1);
490 } else if (!strcasecmp(tmp->name, "fullname")) {
491 strncpy(retval->fullname, tmp->value, sizeof(retval->fullname) - 1);
493 apply_option(retval, tmp->name, tmp->value);
505 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
507 /* This function could be made to generate one from a database, too */
508 struct ast_vm_user *vmu=NULL, *cur;
509 ast_mutex_lock(&vmlock);
512 if ((!context || !strcasecmp(context, cur->context)) &&
513 (!strcasecmp(mailbox, cur->mailbox)))
521 /* Make a copy, so that on a reload, we have no race */
522 vmu = malloc(sizeof(struct ast_vm_user));
524 memcpy(vmu, cur, sizeof(struct ast_vm_user));
532 vmu = find_user_realtime(ivm, context, mailbox);
533 ast_mutex_unlock(&vmlock);
537 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
539 /* This function could be made to generate one from a database, too */
540 struct ast_vm_user *cur;
542 ast_mutex_lock(&vmlock);
545 if ((!context || !strcasecmp(context, cur->context)) &&
546 (!strcasecmp(mailbox, cur->mailbox)))
551 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
554 ast_mutex_unlock(&vmlock);
558 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
560 /* There's probably a better way of doing this. */
561 /* That's why I've put the password change in a separate function. */
562 /* This could also be done with a database function */
569 char currcontext[256] ="";
570 char tmpin[AST_CONFIG_MAX_PATH];
571 char tmpout[AST_CONFIG_MAX_PATH];
572 char *user, *pass, *rest, *trim, *tempcontext;
575 if (!change_password_realtime(vmu, newpassword))
579 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
580 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
581 configin = fopen(tmpin,"r");
583 configout = fopen(tmpout,"w+");
586 if (!configin || !configout) {
590 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
594 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
598 while (!feof(configin)) {
599 /* Read in the line */
600 fgets(inbuf, sizeof(inbuf), configin);
602 if (!feof(configin)) {
603 /* Make a backup of it */
604 memcpy(orig, inbuf, sizeof(orig));
605 /* Strip trailing \n and comment */
606 inbuf[strlen(inbuf) - 1] = '\0';
607 user = strchr(inbuf, ';');
613 /* check for '[' (opening of context name ) */
614 tempcontext = strchr(user, '[');
616 strncpy(currcontext, tempcontext +1, sizeof(currcontext) - 1);
617 /* now check for ']' */
618 tempcontext = strchr(currcontext, ']');
622 currcontext[0] = '\0';
624 pass = strchr(user, '=');
627 while (*trim && *trim < 33) {
637 while (*pass && *pass < 33)
641 rest = strchr(pass,',');
649 /* Compare user, pass AND context */
650 if (user && *user && !strcmp(user, vmu->mailbox) &&
651 pass && !strcmp(pass, vmu->password) &&
652 currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
653 /* This is the line */
655 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
657 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
660 /* Put it back like it was */
661 fprintf(configout, orig);
668 stat((char *)tmpin, &statbuf);
669 chmod((char *)tmpout, statbuf.st_mode);
670 chown((char *)tmpout, statbuf.st_uid, statbuf.st_gid);
671 unlink((char *)tmpin);
672 rename((char *)tmpout,(char *)tmpin);
673 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
674 strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
677 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
680 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
681 ast_safe_system(buf);
684 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
686 return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
689 static int make_file(char *dest, int len, char *dir, int num)
691 return snprintf(dest, len, "%s/msg%04d", dir, num);
695 #ifdef USE_ODBC_STORAGE
696 static int retrieve_file(char *dir, int msgnum)
704 SQLSMALLINT colcount=0;
712 SQLSMALLINT datatype;
713 SQLSMALLINT decimaldigits;
714 SQLSMALLINT nullable;
723 obj = fetch_odbc_obj(odbc_database);
726 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
729 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
730 c = strchr(fmt, '|');
733 if (!strcasecmp(fmt, "wav49"))
734 strncpy(fmt, "WAV", sizeof(fmt));
735 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
737 make_file(fn, sizeof(fn), dir, msgnum);
739 strncpy(fn, dir, sizeof(fn) - 1);
740 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
741 f = fopen(full_fn, "w+");
742 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
743 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
744 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
745 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
748 snprintf(sql, sizeof(sql), "SELECT * FROM voicemessages WHERE dir=? AND msgnum=?");
749 res = SQLPrepare(stmt, sql, SQL_NTS);
750 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
751 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
752 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
755 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
756 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
757 res = SQLExecute(stmt);
758 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
759 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
760 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
763 res = SQLRowCount(stmt, &rowcount);
764 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO))) {
765 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
766 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
770 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
772 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
773 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
776 res = SQLNumResultCols(stmt, &colcount);
777 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
778 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
779 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
782 res = SQLFetch(stmt);
783 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
784 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
785 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
789 fprintf(f, "[message]\n");
790 for (x=0;x<colcount;x++) {
792 collen = sizeof(coltitle);
793 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
794 &datatype, &colsize, &decimaldigits, &nullable);
795 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
796 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
797 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
800 if (!strcmp(coltitle, "recording")) {
802 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
804 /* Ugh, gotta fill it so we can mmap */
806 size_t left = 0, bytes = 0;
810 if (bytes > sizeof(tmp))
812 if (write(fd, tmp, bytes) != bytes) {
820 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
823 memset(fdm, 0, fdlen);
824 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
825 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
826 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
827 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
832 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
833 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
834 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
835 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
838 printf("Got field '%s'\n", coltitle);
839 if (strcmp(coltitle, "msgnum") && strcmp(coltitle, "dir") && f)
840 fprintf(f, "%s=%s\n", coltitle, rowdata);
844 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
846 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
857 static int remove_file(char *dir, int msgnum)
865 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
869 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
870 make_file(fn, sizeof(fn), dir, msgnum);
872 strncpy(fn, dir, sizeof(fn) - 1);
873 ast_filedelete(fn, NULL);
874 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
879 static int last_message_index(char *dir)
890 obj = fetch_odbc_obj(odbc_database);
893 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
896 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
897 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
898 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
901 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM voicemessages WHERE dir=?");
902 res = SQLPrepare(stmt, sql, SQL_NTS);
903 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
904 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
905 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
908 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
909 res = SQLExecute(stmt);
910 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
911 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
912 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
915 res = SQLRowCount(stmt, &rowcount);
916 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
917 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
918 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
921 res = SQLFetch(stmt);
922 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
923 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
924 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
927 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
928 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
929 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
930 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
933 if (sscanf(rowdata, "%i", &x) != 1)
934 ast_log(LOG_WARNING, "Failed to read message count!\n");
935 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
937 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
942 static int message_exists(char *dir, int msgnum)
954 obj = fetch_odbc_obj(odbc_database);
957 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
960 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
961 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
962 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
963 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
966 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM voicemessages WHERE dir=? AND msgnum=?");
967 res = SQLPrepare(stmt, sql, SQL_NTS);
968 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
969 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
970 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
973 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
974 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
975 res = SQLExecute(stmt);
976 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
977 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
978 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
981 res = SQLRowCount(stmt, &rowcount);
982 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
983 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
984 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
987 res = SQLFetch(stmt);
988 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
989 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
990 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
993 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
994 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
995 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
996 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
999 if (sscanf(rowdata, "%i", &x) != 1)
1000 ast_log(LOG_WARNING, "Failed to read message count!\n");
1001 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1003 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1008 static int count_messages(char *dir)
1010 return last_message_index(dir) + 1;
1012 static void delete_file(char *sdir, int smsg)
1022 obj = fetch_odbc_obj(odbc_database);
1024 if (sdir[0] != '/') {
1025 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, sdir);
1028 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1029 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1030 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1031 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1034 snprintf(sql, sizeof(sql), "DELETE FROM voicemessages WHERE dir=? AND msgnum=?");
1035 res = SQLPrepare(stmt, sql, SQL_NTS);
1036 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1037 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1038 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1041 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1042 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1043 res = SQLExecute(stmt);
1044 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1045 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1046 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1049 res = SQLRowCount(stmt, &rowcount);
1050 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO))) {
1051 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
1052 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1055 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1057 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1062 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg)
1074 delete_file(ddir, dmsg);
1075 obj = fetch_odbc_obj(odbc_database);
1077 if (ddir[0] != '/') {
1078 snprintf(rddir, sizeof(rddir), "%s/%s", ast_config_AST_SPOOL_DIR, ddir);
1081 if (sdir[0] != '/') {
1082 snprintf(rsdir, sizeof(rsdir), "%s/%s", ast_config_AST_SPOOL_DIR, sdir);
1085 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1086 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1087 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1088 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1089 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1092 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=?");
1093 res = SQLPrepare(stmt, sql, SQL_NTS);
1094 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1095 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1096 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1099 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1100 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1101 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1102 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1103 res = SQLExecute(stmt);
1104 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1105 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1106 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1109 res = SQLRowCount(stmt, &rowcount);
1110 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1111 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1112 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1115 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1117 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1122 static int store_file(char *dir, int msgnum)
1139 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1140 char *category = "";
1141 struct ast_config *cfg=NULL;
1144 delete_file(dir, msgnum);
1145 obj = fetch_odbc_obj(odbc_database);
1147 if (dir[0] != '/') {
1148 snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
1151 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1152 c = strchr(fmt, '|');
1155 if (!strcasecmp(fmt, "wav49"))
1156 strncpy(fmt, "WAV", sizeof(fmt));
1157 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1159 make_file(fn, sizeof(fn), dir, msgnum);
1161 strncpy(fn, dir, sizeof(fn) - 1);
1162 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1163 cfg = ast_load(full_fn);
1164 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1165 fd = open(full_fn, O_RDWR);
1167 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1171 context = ast_variable_retrieve(cfg, "message", "context");
1172 if (!context) context = "";
1173 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1174 if (!macrocontext) macrocontext = "";
1175 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1176 if (!callerid) callerid = "";
1177 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1178 if (!origtime) origtime = "";
1179 duration = ast_variable_retrieve(cfg, "message", "duration");
1180 if (!duration) duration = "";
1181 category = ast_variable_retrieve(cfg, "message", "category");
1182 if (!category) category = "";
1184 fdlen = lseek(fd, 0, SEEK_END);
1185 lseek(fd, 0, SEEK_SET);
1186 printf("Length is %d\n", fdlen);
1187 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1189 ast_log(LOG_WARNING, "Memory map failed!\n");
1192 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1193 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1194 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1197 if (!ast_strlen_zero(category))
1198 snprintf(sql, sizeof(sql), "INSERT INTO voicemessages (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)");
1200 snprintf(sql, sizeof(sql), "INSERT INTO voicemessages (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)");
1201 res = SQLPrepare(stmt, sql, SQL_NTS);
1202 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1203 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1204 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1207 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1208 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1209 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1210 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1211 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1212 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1213 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1214 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1215 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1216 if (!ast_strlen_zero(category))
1217 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1218 res = SQLExecute(stmt);
1219 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1220 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1221 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1224 res = SQLRowCount(stmt, &rowcount);
1225 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1226 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
1227 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1230 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1232 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1243 static void rename_file(char *sdir, int smsg, char *ddir, int dmsg)
1255 delete_file(ddir, dmsg);
1256 obj = fetch_odbc_obj(odbc_database);
1258 if (ddir[0] != '/') {
1259 snprintf(rddir, sizeof(rddir), "%s/%s", ast_config_AST_SPOOL_DIR, ddir);
1262 if (sdir[0] != '/') {
1263 snprintf(rsdir, sizeof(rsdir), "%s/%s", ast_config_AST_SPOOL_DIR, sdir);
1266 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1267 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1268 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1269 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1270 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1273 snprintf(sql, sizeof(sql), "UPDATE voicemessages SET dir=?, msgnum=? WHERE dir=? AND msgnum=?");
1274 res = SQLPrepare(stmt, sql, SQL_NTS);
1275 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1276 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1277 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1280 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1281 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1282 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1283 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1284 res = SQLExecute(stmt);
1285 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1286 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1287 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1290 res = SQLRowCount(stmt, &rowcount);
1291 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1292 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
1293 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1296 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1298 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1304 static int count_messages(char *dir)
1306 /* Find all .txt files - even if they are not in sequence from 0000 */
1310 struct dirent *vment = NULL;
1312 if ((vmdir = opendir(dir))) {
1313 while ((vment = readdir(vmdir)))
1315 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
1326 static void rename_file(char *sfn, char *dfn)
1330 ast_filerename(sfn,dfn,NULL);
1331 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1332 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1336 static int copy(char *infile, char *outfile)
1344 #ifdef HARDLINK_WHEN_POSSIBLE
1345 /* Hard link if possible; saves disk space & is faster */
1346 if (link(infile, outfile)) {
1348 if ((ifd = open(infile, O_RDONLY)) < 0) {
1349 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1352 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1353 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1358 len = read(ifd, buf, sizeof(buf));
1360 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1366 res = write(ofd, buf, len);
1368 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1378 #ifdef HARDLINK_WHEN_POSSIBLE
1380 /* Hard link succeeded */
1386 static void copy_file(char *frompath, char *topath)
1388 char frompath2[256],topath2[256];
1389 ast_filecopy(frompath, topath, NULL);
1390 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1391 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1392 copy(frompath2, topath2);
1395 static int last_message_index(char *dir)
1399 for (x=0;x<MAXMSG;x++) {
1400 make_file(fn, sizeof(fn), dir, x);
1401 if (ast_fileexists(fn, NULL, NULL) < 1)
1407 static int vm_delete(char *file)
1412 txtsize = (strlen(file) + 5)*sizeof(char);
1413 txt = (char *)alloca(txtsize);
1414 /* Sprintf here would safe because we alloca'd exactly the right length,
1415 * but trying to eliminate all sprintf's anyhow
1417 snprintf(txt, txtsize, "%s.txt", file);
1419 return ast_filedelete(file, NULL);
1425 inbuf(struct baseio *bio, FILE *fi)
1432 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1447 inchar(struct baseio *bio, FILE *fi)
1449 if (bio->iocp>=bio->iolen) {
1450 if (!inbuf(bio, fi))
1454 return bio->iobuf[bio->iocp++];
1458 ochar(struct baseio *bio, int c, FILE *so)
1460 if (bio->linelength>=BASELINELEN) {
1461 if (fputs(eol,so)==EOF)
1467 if (putc(((unsigned char)c),so)==EOF)
1475 static int base_encode(char *filename, FILE *so)
1477 unsigned char dtable[BASEMAXINLINE];
1482 memset(&bio, 0, sizeof(bio));
1483 bio.iocp = BASEMAXINLINE;
1485 if (!(fi = fopen(filename, "rb"))) {
1486 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1490 for (i= 0;i<9;i++) {
1493 dtable[26+i]= 'a'+i;
1494 dtable[26+i+9]= 'j'+i;
1496 for (i= 0;i<8;i++) {
1497 dtable[i+18]= 'S'+i;
1498 dtable[26+i+18]= 's'+i;
1500 for (i= 0;i<10;i++) {
1501 dtable[52+i]= '0'+i;
1507 unsigned char igroup[3],ogroup[4];
1510 igroup[0]= igroup[1]= igroup[2]= 0;
1512 for (n= 0;n<3;n++) {
1513 if ((c = inchar(&bio, fi)) == EOF) {
1518 igroup[n]= (unsigned char)c;
1522 ogroup[0]= dtable[igroup[0]>>2];
1523 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1524 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1525 ogroup[3]= dtable[igroup[2]&0x3F];
1535 ochar(&bio, ogroup[i], so);
1539 if (fputs(eol,so)==EOF)
1547 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)
1550 /* Prepare variables for substition in email body and subject */
1551 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1552 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1553 snprintf(passdata, passdatasize, "%d", msgnum);
1554 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1555 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1556 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1557 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1558 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1559 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1562 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)
1572 char tmp[80] = "/tmp/astmail-XXXXXX";
1576 struct vm_zone *the_zone = NULL;
1577 if (vmu && ast_strlen_zero(vmu->email)) {
1578 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1581 if (!strcmp(format, "wav49"))
1583 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
1584 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1588 p = fdopen(pfd, "w");
1595 gethostname(host, sizeof(host));
1596 if (strchr(srcemail, '@'))
1597 strncpy(who, srcemail, sizeof(who)-1);
1599 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1601 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1604 /* Does this user have a timezone specified? */
1605 if (!ast_strlen_zero(vmu->zonetag)) {
1606 /* Find the zone in the list */
1610 if (!strcmp(z->name, vmu->zonetag)) {
1619 ast_localtime(&t,&tm,the_zone->timezone);
1621 ast_localtime(&t,&tm,NULL);
1622 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1623 fprintf(p, "Date: %s\n", date);
1626 struct ast_channel *ast = ast_channel_alloc(0);
1629 int vmlen = strlen(fromstring)*3 + 200;
1630 if ((passdata = alloca(vmlen))) {
1631 memset(passdata, 0, vmlen);
1632 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1633 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1634 fprintf(p, "From: %s <%s>\n",passdata,who);
1635 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1636 ast_channel_free(ast);
1637 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1639 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1640 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1643 struct ast_channel *ast = ast_channel_alloc(0);
1646 int vmlen = strlen(emailsubject)*3 + 200;
1647 if ((passdata = alloca(vmlen))) {
1648 memset(passdata, 0, vmlen);
1649 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1650 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1651 fprintf(p, "Subject: %s\n",passdata);
1652 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1653 ast_channel_free(ast);
1654 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1657 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1660 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1662 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1663 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
1664 fprintf(p, "MIME-Version: 1.0\n");
1665 if (attach_user_voicemail) {
1666 /* Something unique. */
1667 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
1669 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1671 fprintf(p, "--%s\n", bound);
1673 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1674 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1676 struct ast_channel *ast = ast_channel_alloc(0);
1679 int vmlen = strlen(emailbody)*3 + 200;
1680 if ((passdata = alloca(vmlen))) {
1681 memset(passdata, 0, vmlen);
1682 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1683 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1684 fprintf(p, "%s\n",passdata);
1685 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1686 ast_channel_free(ast);
1687 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1689 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1691 "in mailbox %s from %s, on %s so you might\n"
1692 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1693 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1695 if (attach_user_voicemail) {
1696 fprintf(p, "--%s\n", bound);
1697 fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
1698 fprintf(p, "Content-Transfer-Encoding: base64\n");
1699 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1700 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1702 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1703 base_encode(fname, p);
1704 fprintf(p, "\n\n--%s--\n.\n", bound);
1707 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1708 ast_safe_system(tmp2);
1709 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1711 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1717 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu)
1725 char tmp[80] = "/tmp/astmail-XXXXXX";
1729 struct vm_zone *the_zone = NULL;
1733 p = fdopen(pfd, "w");
1741 gethostname(host, sizeof(host));
1742 if (strchr(srcemail, '@'))
1743 strncpy(who, srcemail, sizeof(who)-1);
1745 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1747 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1750 /* Does this user have a timezone specified? */
1751 if (!ast_strlen_zero(vmu->zonetag)) {
1752 /* Find the zone in the list */
1756 if (!strcmp(z->name, vmu->zonetag)) {
1765 ast_localtime(&t,&tm,the_zone->timezone);
1767 ast_localtime(&t,&tm,NULL);
1769 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1770 fprintf(p, "Date: %s\n", date);
1772 if (*pagerfromstring) {
1773 struct ast_channel *ast = ast_channel_alloc(0);
1776 int vmlen = strlen(fromstring)*3 + 200;
1777 if ((passdata = alloca(vmlen))) {
1778 memset(passdata, 0, vmlen);
1779 prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1780 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1781 fprintf(p, "From: %s <%s>\n",passdata,who);
1783 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1784 ast_channel_free(ast);
1785 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1787 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1788 fprintf(p, "To: %s\n", pager);
1789 fprintf(p, "Subject: New VM\n\n");
1790 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1791 fprintf(p, "New %s long msg in box %s\n"
1792 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1794 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1795 ast_safe_system(tmp2);
1796 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1798 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1804 static int get_date(char *s, int len)
1809 localtime_r(&t,&tm);
1810 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1813 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1817 snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
1819 if (ast_fileexists(fn, NULL, NULL) > 0) {
1820 res = ast_streamfile(chan, fn, chan->language);
1825 res = ast_waitstream(chan, ecodes);
1831 /* Dispose just in case */
1833 res = ast_streamfile(chan, "vm-theperson", chan->language);
1836 res = ast_waitstream(chan, ecodes);
1839 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1844 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1846 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1849 res = ast_waitstream(chan, ecodes);
1853 static void free_user(struct ast_vm_user *vmu)
1859 static void free_zone(struct vm_zone *z)
1864 static char *mbox(int id)
1892 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
1894 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)
1896 char fromdir[256], todir[256], frompath[256], topath[256];
1897 char *frombox = mbox(imbox);
1900 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
1902 make_dir(todir, sizeof(todir), recip->context, "", "");
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, "");
1907 /* It's easier just to try to make it than to check for its existence */
1908 if (mkdir(todir, 0700) && (errno != EEXIST))
1909 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1910 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
1911 if (mkdir(todir, 0700) && (errno != EEXIST))
1912 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1914 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
1915 make_file(frompath, sizeof(frompath), fromdir, msgnum);
1918 make_file(topath, sizeof(topath), todir, recipmsgnum);
1919 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
1922 } while (recipmsgnum < MAXMSG);
1923 if (recipmsgnum < MAXMSG) {
1924 COPY(fromdir, msgnum, todir, recipmsgnum, frompath, topath);
1926 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
1929 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
1932 static void run_externnotify(char *context, char *extension)
1934 char arguments[255];
1935 int newvoicemails = 0, oldvoicemails = 0;
1937 if (!ast_strlen_zero(externnotify)) {
1938 if (ast_app_messagecount(extension, &newvoicemails, &oldvoicemails)) {
1939 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
1941 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
1942 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
1943 ast_safe_system(arguments);
1949 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1963 char prefile[256]="";
1964 char tempfile[256]="";
1965 char ext_context[256] = "";
1968 char ecodes[16] = "#";
1969 char tmp[256] = "", *tmpptr;
1970 struct ast_vm_user *vmu;
1971 struct ast_vm_user svm;
1972 char *category = NULL;
1974 strncpy(tmp, ext, sizeof(tmp) - 1);
1976 context = strchr(tmp, '@');
1980 tmpptr = strchr(context, '&');
1982 tmpptr = strchr(ext, '&');
1990 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
1992 if ((vmu = find_user(&svm, context, ext))) {
1993 /* Setup pre-file if appropriate */
1994 if (strcmp(vmu->context, "default"))
1995 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
1997 strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);
1999 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
2001 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
2002 snprintf(tempfile, sizeof(tempfile), "voicemail/%s/%s/temp", vmu->context, ext);
2003 RETRIEVE(tempfile, -1);
2004 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2005 strncpy(prefile, tempfile, sizeof(prefile) - 1);
2006 DISPOSE(tempfile, -1);
2007 make_dir(dir, sizeof(dir), vmu->context, "", "");
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, "");
2012 /* It's easier just to try to make it than to check for its existence */
2013 if (mkdir(dir, 0700) && (errno != EEXIST))
2014 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2015 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
2016 if (mkdir(dir, 0700) && (errno != EEXIST))
2017 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2019 /* Check current or macro-calling context for special extensions */
2020 if (!ast_strlen_zero(vmu->exit)) {
2021 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2022 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2023 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
2024 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2025 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2026 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2030 if (!ast_strlen_zero(vmu->exit)) {
2031 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2032 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2033 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2034 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2035 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2036 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2041 /* Play the beginning intro if desired */
2042 if (!ast_strlen_zero(prefile)) {
2043 RETRIEVE(prefile, -1);
2044 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2045 if (ast_streamfile(chan, prefile, chan->language) > -1)
2046 res = ast_waitstream(chan, ecodes);
2048 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2049 res = invent_message(chan, vmu->context, ext, busy, ecodes);
2051 DISPOSE(prefile, -1);
2053 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2059 /* On a '#' we skip the instructions */
2063 if (!res && !silent) {
2064 res = ast_streamfile(chan, INTRO, chan->language);
2066 res = ast_waitstream(chan, ecodes);
2073 ast_stopstream(chan);
2074 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2075 other than the operator -- an automated attendant or mailbox login for example */
2077 strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
2078 if (!ast_strlen_zero(vmu->exit)) {
2079 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
2080 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2081 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
2087 /* Check for a '0' here */
2090 if (vmu->operator) {
2091 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
2092 if (!ast_strlen_zero(vmu->exit)) {
2093 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
2094 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2095 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
2097 ast_play_and_wait(chan, "transfer");
2102 ast_play_and_wait(chan, "vm-sorry");
2110 /* The meat of recording the message... All the announcements and beeps have been played*/
2111 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
2112 if (!ast_strlen_zero(fmt)) {
2115 make_file(fn, sizeof(fn), dir, msgnum);
2116 if (!EXISTS(dir,msgnum,fn,chan->language))
2119 } while (msgnum < MAXMSG);
2121 /* Unless we're *really* silent, try to send the beep */
2122 res = ast_streamfile(chan, "beep", chan->language);
2124 res = ast_waitstream(chan, "");
2126 if (msgnum < MAXMSG) {
2127 /* Store information */
2128 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2129 txt = fopen(txtfile, "w+");
2131 get_date(date, sizeof(date));
2134 "; Message Information file\n"
2153 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2154 date, (long)time(NULL),
2155 category ? category : "");
2158 ast_log(LOG_WARNING, "Error opening text file for output\n");
2159 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
2164 fd = open(txtfile, O_APPEND | O_WRONLY);
2166 txt = fdopen(fd, "a");
2168 fprintf(txt, "duration=%d\n", duration);
2173 if (duration < vmminmessage) {
2174 if (option_verbose > 2)
2175 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2176 DELETE(dir,msgnum,fn);
2177 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2180 /* Are there to be more recipients of this message? */
2182 struct ast_vm_user recipu, *recip;
2183 char *exten, *context;
2185 exten = strsep(&tmpptr, "&");
2186 context = strchr(exten, '@');
2191 if ((recip = find_user(&recipu, context, exten))) {
2192 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2196 if (ast_fileexists(fn, NULL, NULL)) {
2197 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2199 DISPOSE(dir, msgnum);
2202 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2204 res = ast_waitstream(chan, "");
2205 ast_log(LOG_WARNING, "No more messages possible\n");
2208 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2212 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2213 /*Send the call to n+101 priority, where n is the current priority*/
2214 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num))
2215 chan->priority+=100;
2222 static void resequence_mailbox(char * dir)
2224 /* we know max messages, so stop process when number is hit */
2230 for (x=0,dest=0;x<MAXMSG;x++) {
2231 make_file(sfn, sizeof(sfn), dir, x);
2232 if (EXISTS(dir, x, sfn, NULL)) {
2235 make_file(dfn, sizeof(dfn), dir, dest);
2236 RENAME(dir, x, dir, dest, sfn, dfn);
2245 static int say_and_wait(struct ast_channel *chan, int num, char *language)
2248 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2252 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
2257 char *dbox = mbox(box);
2259 make_file(sfn, sizeof(sfn), dir, msg);
2260 make_dir(ddir, sizeof(ddir), context, username, dbox);
2262 for (x=0;x<MAXMSG;x++) {
2263 make_file(dfn, sizeof(dfn), ddir, x);
2264 if (!EXISTS(ddir, x, dfn, NULL))
2269 if (strcmp(sfn, dfn)) {
2270 COPY(dir, msg, ddir, x, sfn, dfn);
2275 static int adsi_logo(unsigned char *buf)
2278 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2279 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2283 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2291 bytes += adsi_data_mode(buf + bytes);
2292 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2295 bytes += adsi_logo(buf);
2296 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2298 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
2300 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2301 bytes += adsi_data_mode(buf + bytes);
2302 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2304 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2306 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2307 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2308 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2309 bytes += adsi_voice_mode(buf + bytes, 0);
2310 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2317 bytes += adsi_logo(buf);
2318 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2319 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
2320 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2321 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2324 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2325 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2326 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2327 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2328 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2329 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2330 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2333 /* Add another dot */
2335 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
2336 bytes += adsi_voice_mode(buf + bytes, 0);
2338 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2339 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2343 /* These buttons we load but don't use yet */
2344 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2345 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2346 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2347 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2348 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2349 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2350 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2353 /* Add another dot */
2355 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2356 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2357 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2362 snprintf(num, sizeof(num), "%d", x);
2363 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2365 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2366 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2369 /* Add another dot */
2371 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2372 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2373 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2376 if (adsi_end_download(chan)) {
2378 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2379 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2380 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2381 bytes += adsi_voice_mode(buf + bytes, 0);
2382 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2386 bytes += adsi_download_disconnect(buf + bytes);
2387 bytes += adsi_voice_mode(buf + bytes, 0);
2388 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2390 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2395 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2396 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2398 ast_log(LOG_DEBUG, "Restarting session...\n");
2401 /* Load the session now */
2402 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2404 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2406 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2408 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2412 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2415 if (!adsi_available(chan))
2417 x = adsi_load_session(chan, adsifdn, adsiver, 1);
2421 if (adsi_load_vmail(chan, useadsi)) {
2422 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2429 static void adsi_login(struct ast_channel *chan)
2433 unsigned char keys[8];
2435 if (!adsi_available(chan))
2440 /* Set one key for next */
2441 keys[3] = ADSI_KEY_APPS + 3;
2443 bytes += adsi_logo(buf + bytes);
2444 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2445 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2446 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2447 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2448 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2449 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2450 bytes += adsi_set_keys(buf + bytes, keys);
2451 bytes += adsi_voice_mode(buf + bytes, 0);
2452 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2455 static void adsi_password(struct ast_channel *chan)
2459 unsigned char keys[8];
2461 if (!adsi_available(chan))
2466 /* Set one key for next */
2467 keys[3] = ADSI_KEY_APPS + 3;
2469 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2470 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2471 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2472 bytes += adsi_set_keys(buf + bytes, keys);
2473 bytes += adsi_voice_mode(buf + bytes, 0);
2474 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2477 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2481 unsigned char keys[8];
2484 if (!adsi_available(chan))
2488 y = ADSI_KEY_APPS + 12 + start + x;
2489 if (y > ADSI_KEY_APPS + 12 + 4)
2491 keys[x] = ADSI_KEY_SKT | y;
2493 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2497 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2498 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2499 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2500 bytes += adsi_set_keys(buf + bytes, keys);
2501 bytes += adsi_voice_mode(buf + bytes, 0);
2503 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2506 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2509 char buf[256], buf1[256], buf2[256];
2515 char datetime[21]="";
2518 unsigned char keys[8];
2522 if (!adsi_available(chan))
2525 /* Retrieve important info */
2526 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
2527 f = fopen(fn2, "r");
2530 fgets(buf, sizeof(buf), f);
2534 strsep(&stringp, "=");
2535 val = strsep(&stringp, "=");
2536 if (val && !ast_strlen_zero(val)) {
2537 if (!strcmp(buf, "callerid"))
2538 strncpy(cid, val, sizeof(cid) - 1);
2539 if (!strcmp(buf, "origdate"))
2540 strncpy(datetime, val, sizeof(datetime) - 1);
2546 /* New meaning for keys */
2548 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2553 /* No prev key, provide "Folder" instead */
2554 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2556 if (vms->curmsg >= vms->lastmsg) {
2557 /* If last message ... */
2559 /* but not only message, provide "Folder" instead */
2560 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2561 bytes += adsi_voice_mode(buf + bytes, 0);
2564 /* Otherwise if only message, leave blank */
2569 if (!ast_strlen_zero(cid)) {
2570 ast_callerid_parse(cid, &name, &num);
2574 name = "Unknown Caller";
2576 /* If deleted, show "undeleted" */
2578 if (vms->deleted[vms->curmsg])
2579 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2582 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2583 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
2584 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
2585 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
2587 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2588 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2589 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2590 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2591 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2592 bytes += adsi_set_keys(buf + bytes, keys);
2593 bytes += adsi_voice_mode(buf + bytes, 0);
2595 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2598 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
2602 unsigned char keys[8];
2606 if (!adsi_available(chan))
2609 /* New meaning for keys */
2611 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2617 /* No prev key, provide "Folder" instead */
2618 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2620 if (vms->curmsg >= vms->lastmsg) {
2621 /* If last message ... */
2623 /* but not only message, provide "Folder" instead */
2624 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2626 /* Otherwise if only message, leave blank */
2631 /* If deleted, show "undeleted" */
2632 if (vms->deleted[vms->curmsg])
2633 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2636 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2637 bytes += adsi_set_keys(buf + bytes, keys);
2638 bytes += adsi_voice_mode(buf + bytes, 0);
2640 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2643 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
2645 char buf[256] = "", buf1[256] = "", buf2[256] = "";
2647 unsigned char keys[8];
2650 char *newm = (vms->newmessages == 1) ? "message" : "messages";
2651 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
2652 if (!adsi_available(chan))
2654 if (vms->newmessages) {
2655 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
2656 if (vms->oldmessages) {
2657 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
2658 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
2660 snprintf(buf2, sizeof(buf2), "%s.", newm);
2662 } else if (vms->oldmessages) {
2663 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
2664 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2666 strncpy(buf1, "You have no messages.", sizeof(buf1) - 1);
2670 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2671 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2672 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2675 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2679 /* Don't let them listen if there are none */
2680 if (vms->lastmsg < 0)
2682 bytes += adsi_set_keys(buf + bytes, keys);
2684 bytes += adsi_voice_mode(buf + bytes, 0);
2686 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2689 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
2691 char buf[256] = "", buf1[256] = "", buf2[256] = "";
2693 unsigned char keys[8];
2696 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
2698 if (!adsi_available(chan))
2701 /* Original command keys */
2703 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2708 if ((vms->lastmsg + 1) < 1)
2711 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
2712 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
2714 if (vms->lastmsg + 1)
2715 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
2717 strncpy(buf2, "no messages.", sizeof(buf2) - 1);
2718 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2719 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2720 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2721 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2722 bytes += adsi_set_keys(buf + bytes, keys);
2724 bytes += adsi_voice_mode(buf + bytes, 0);
2726 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2731 static void adsi_clear(struct ast_channel *chan)
2735 if (!adsi_available(chan))
2737 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2738 bytes += adsi_voice_mode(buf + bytes, 0);
2740 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2744 static void adsi_goodbye(struct ast_channel *chan)
2749 if (!adsi_available(chan))
2751 bytes += adsi_logo(buf + bytes);
2752 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2753 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2754 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2755 bytes += adsi_voice_mode(buf + bytes, 0);
2757 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2760 /*--- get_folder: Folder menu ---*/
2761 /* Plays "press 1 for INBOX messages" etc
2762 Should possibly be internationalized
2764 static int get_folder(struct ast_channel *chan, int start)
2769 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
2772 for (x = start; x< 5; x++) { /* For all folders */
2773 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2775 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
2778 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
2779 d = vm_play_folder_name(chan, fn);
2782 d = ast_waitfordigit(chan, 500);
2786 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
2789 d = ast_waitfordigit(chan, 4000);
2793 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2796 res = ast_play_and_wait(chan, fn); /* Folder name */
2797 while (((res < '0') || (res > '9')) &&
2798 (res != '#') && (res >= 0)) {
2799 res = get_folder(chan, 0);
2804 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2810 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2815 /* prepend a message to the current message and return */
2818 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2819 cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
2829 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
2830 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
2832 cmd = ast_play_and_wait(chan,"vm-starmain");
2833 /* "press star to return to the main menu" */
2835 cmd = ast_waitfordigit(chan,6000);
2847 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
2849 char todir[256], fn[256], ext_context[256], *stringp;
2850 int newmsgs = 0, oldmsgs = 0;
2852 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
2853 make_file(fn, sizeof(fn), todir, msgnum);
2854 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
2856 /* Attach only the first format */
2857 fmt = ast_strdupa(fmt);
2860 strsep(&stringp, "|");
2862 if (!ast_strlen_zero(vmu->email)) {
2863 int attach_user_voicemail = attach_voicemail;
2864 char *myserveremail = serveremail;
2865 if (vmu->attach > -1)
2866 attach_user_voicemail = vmu->attach;
2867 if (!ast_strlen_zero(vmu->serveremail))
2868 myserveremail = vmu->serveremail;
2869 sendmail(myserveremail, vmu, msgnum, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail);
2872 if (!ast_strlen_zero(vmu->pager)) {
2873 char *myserveremail = serveremail;
2874 if (!ast_strlen_zero(vmu->serveremail))
2875 myserveremail = vmu->serveremail;
2876 sendpage(myserveremail, vmu->pager, msgnum, vmu->mailbox, cidnum, cidname, duration, vmu);
2879 ast_log(LOG_ERROR, "Out of memory\n");
2883 DELETE(todir, msgnum, fn);
2886 /* Leave voicemail for someone */
2887 if (ast_app_has_voicemail(ext_context, NULL)) {
2888 ast_app_messagecount(ext_context, &newmsgs, &oldmsgs);
2890 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
2891 run_externnotify(chan->context, ext_context);
2895 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt,int flag)
2897 char username[70]="";
2902 struct ast_config *mif;
2906 char ext_context[256]="";
2907 int res = 0, cmd = 0;
2908 struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2911 int saved_messages = 0, found = 0;
2912 int valid_extensions = 0;
2914 while (!res && !valid_extensions) {
2916 int use_directory = 0;
2917 if( directory_forward ) {
2921 while((cmd >= 0) && !done ){
2938 /* Press 1 to enter an extension press 2 to use the directory */
2939 cmd = ast_play_and_wait(chan,"vm-forward");
2941 cmd = ast_waitfordigit(chan,3000);
2952 if( cmd<0 || cmd=='t' )
2956 if( use_directory ) {
2957 /* use app_directory */
2959 char old_context[sizeof(chan->context)];
2960 char old_exten[sizeof(chan->exten)];
2962 struct ast_app* app;
2965 app = pbx_findapp("Directory");
2967 /* make mackup copies */
2968 memcpy(old_context, chan->context, sizeof(chan->context));
2969 memcpy(old_exten, chan->exten, sizeof(chan->exten));
2970 old_priority = chan->priority;
2972 /* call the the Directory, changes the channel */
2973 res = pbx_exec(chan, app, ((context)?context:chan->context), 1);
2975 strncpy(username, chan->exten, sizeof(username)-1);
2977 /* restore the old context, exten, and priority */
2978 memcpy(chan->context, old_context, sizeof(chan->context));
2979 memcpy(chan->exten, old_exten, sizeof(chan->exten));
2980 chan->priority = old_priority;
2983 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
2984 directory_forward = 0;
2987 /* Ask for an extension */
2988 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
2991 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2995 /* start all over if no username */
2996 if (ast_strlen_zero(username))
2999 s = strsep(&stringp, "*");
3000 /* start optimistic */
3001 valid_extensions = 1;
3003 /* find_user is going to malloc since we have a NULL as first argument */
3004 if ((receiver = find_user(NULL, context, s))) {
3006 vmtmp = extensions = receiver;
3008 vmtmp->next = receiver;
3013 valid_extensions = 0;
3016 s = strsep(&stringp, "*");
3018 /* break from the loop of reading the extensions */
3019 if (valid_extensions)
3021 /* "I am sorry, that's not a valid extension. Please try again." */
3022 res = ast_play_and_wait(chan, "pbx-invalid");
3024 /* check if we're clear to proceed */
3025 if (!extensions || !valid_extensions)
3029 /* Send VoiceMail */
3030 cmd=leave_voicemail(chan,username,0,0,0);
3032 /* Forward VoiceMail */
3033 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
3035 while (!res && vmtmp) {
3036 /* if (ast_play_and_wait(chan, "vm-savedto"))
3039 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
3040 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
3041 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
3042 ast_log(LOG_DEBUG, sys);
3043 ast_safe_system(sys);
3045 todircount = count_messages(todir);
3046 strncpy(tmp, fmt, sizeof(tmp) - 1);
3048 while ((s = strsep(&stringp, "|"))) {
3049 /* XXX This is a hack -- we should use build_filename or similar XXX */
3050 if (!strcasecmp(s, "wav49"))
3052 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
3053 ast_log(LOG_DEBUG, sys);
3054 ast_safe_system(sys);
3056 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
3057 ast_log(LOG_DEBUG, sys);
3058 ast_safe_system(sys);
3059 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
3061 /* load the information on the source message so we can send an e-mail like a new message */
3062 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
3063 if ((mif=ast_load(miffile))) {
3065 /* set callerid and duration variables */
3066 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
3067 s = ast_variable_retrieve(mif, NULL, "duration");
3072 if (!ast_strlen_zero(vmtmp->email)) {
3073 int attach_user_voicemail = attach_voicemail;
3074 char *myserveremail = serveremail;
3075 if (vmtmp->attach > -1)
3076 attach_user_voicemail = vmtmp->attach;
3077 if (!ast_strlen_zero(vmtmp->serveremail))
3078 myserveremail = vmtmp->serveremail;
3079 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, fn, tmp, duration, attach_user_voicemail);
3082 if (!ast_strlen_zero(vmtmp->pager)) {
3083 char *myserveremail = serveremail;
3084 if (!ast_strlen_zero(vmtmp->serveremail))
3085 myserveremail = vmtmp->serveremail;
3086 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, duration, vmtmp);
3089 ast_destroy(mif); /* or here */
3091 /* Leave voicemail for someone */
3092 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context, NULL));
3093 run_externnotify(chan->context, ext_context);
3097 vmtmp = vmtmp->next;
3100 if (saved_messages > 0) {
3101 /* give confirmation that the message was saved */
3102 /* commented out since we can't forward batches yet
3103 if (saved_messages == 1)
3104 res = ast_play_and_wait(chan, "vm-message");
3106 res = ast_play_and_wait(chan, "vm-messages");
3108 res = ast_play_and_wait(chan, "vm-saved"); */
3110 res = ast_play_and_wait(chan, "vm-msgsaved");
3114 return res ? res : cmd;
3117 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
3120 if ((res = ast_streamfile(chan, file, chan->language)))
3121 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
3123 res = ast_waitstream(chan, AST_DIGIT_ANY);
3127 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
3129 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", skipms);
3132 static int play_message_category(struct ast_channel *chan, char *category)
3136 if (category && !ast_strlen_zero(category))
3137 res = ast_play_and_wait(chan, category);
3142 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
3145 struct vm_zone *the_zone = NULL;
3149 if (sscanf(origtime,"%ld",&tin) < 1) {
3150 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
3155 /* Does this user have a timezone specified? */
3156 if (!ast_strlen_zero(vmu->zonetag)) {
3157 /* Find the zone in the list */
3161 if (!strcmp(z->name, vmu->zonetag)) {
3169 /* No internal variable parsing for now, so we'll comment it out for the time being */
3171 /* Set the DIFF_* variables */
3172 localtime_r(&t, &time_now);
3173 gettimeofday(&tv_now,NULL);
3174 tnow = tv_now.tv_sec;
3175 localtime_r(&tnow,&time_then);
3177 /* Day difference */
3178 if (time_now.tm_year == time_then.tm_year)
3179 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
3181 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
3182 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
3184 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
3187 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
3188 else if(!strcasecmp(chan->language,"de")) /* GERMAN syntax */
3189 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
3190 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
3191 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
3192 else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
3193 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
3195 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
3197 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
3204 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
3208 char *callerid, *name;
3209 char prefile[256]="";
3212 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
3213 /* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
3214 if ((cid == NULL)||(context == NULL))
3217 /* Strip off caller ID number from name */
3218 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
3219 ast_callerid_parse(cid, &name, &callerid);
3220 if ((callerid != NULL)&&(!res)&&(!ast_strlen_zero(callerid))){
3221 /* Check for internal contexts and only */
3222 /* say extension when the call didn't come from an internal context in the list */
3223 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
3224 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
3225 if ((strcmp(cidinternalcontexts[i], context) == 0))
3228 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
3230 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
3231 if (!ast_strlen_zero(prefile)) {
3232 /* See if we can find a recorded name for this person instead of their extension number */
3233 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3234 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
3236 res = wait_file2(chan, vms, "vm-from");
3237 res = ast_streamfile(chan, prefile, chan->language) > -1;
3238 res = ast_waitstream(chan, "");
3240 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
3241 /* BB: Say "from extension" as one saying to sound smoother */
3243 res = wait_file2(chan, vms, "vm-from-extension");
3244 res = ast_say_digit_str(chan, callerid, "", chan->language);
3251 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
3252 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
3254 res = wait_file2(chan, vms, "vm-from-phonenumber");
3255 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
3258 /* Number unknown */
3259 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
3261 /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
3262 res = wait_file2(chan, vms, "vm-unknown-caller");
3267 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, char *duration, int minduration)
3272 /* Verify that we have a duration for the message */
3273 if((duration == NULL))
3276 /* Convert from seconds to minutes */
3277 durations=atoi(duration);
3278 durationm=(durations / 60);
3280 ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
3282 if((!res)&&(durationm>=minduration)) {
3283 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, (char *) NULL);
3284 res = wait_file2(chan, vms, "vm-minutes");
3289 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
3292 char filename[256],*origtime, *cid, *context, *duration;
3294 struct ast_config *msg_cfg;
3297 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
3298 adsi_message(chan, vms);
3300 res = wait_file2(chan, vms, "vm-first"); /* "First" */
3301 else if (vms->curmsg == vms->lastmsg)
3302 res = wait_file2(chan, vms, "vm-last"); /* "last" */
3304 res = wait_file2(chan, vms, "vm-message"); /* "message" */
3305 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
3307 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
3311 /* Retrieve info from VM attribute file */
3312 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
3313 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
3314 RETRIEVE(vms->curdir, vms->curmsg);
3315 msg_cfg = ast_load(filename);
3317 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
3321 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
3322 ast_log(LOG_WARNING, "No origtime?!\n");
3323 DISPOSE(vms->curdir, vms->curmsg);
3327 cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
3328 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
3329 category = ast_variable_retrieve(msg_cfg, "message", "category");
3331 context = ast_variable_retrieve(msg_cfg, "message", "context");
3332 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
3333 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
3336 res = play_message_category(chan, category);
3337 if ((!res)&&(vmu->envelope))
3338 res = play_message_datetime(chan, vmu, origtime, filename);
3339 if ((!res)&&(vmu->saycid))
3340 res = play_message_callerid(chan, vms, cid, context, 0);
3341 if ((!res)&&(vmu->sayduration))
3342 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
3343 /* Allow pressing '1' to skip envelope / callerid */
3346 ast_destroy(msg_cfg);
3349 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
3350 vms->heard[vms->curmsg] = 1;
3352 res = wait_file(chan, vms, vms->fn);
3354 DISPOSE(vms->curdir, vms->curmsg);
3358 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
3360 strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
3361 make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
3362 vms->lastmsg = count_messages(vms->curdir) - 1;
3365 The following test is needed in case sequencing gets messed up.
3366 There appears to be more than one way to mess up sequence, so
3367 we will not try to find all of the root causes--just fix it when
3371 if(vms->lastmsg != last_message_index(vms->curdir))
3373 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
3374 resequence_mailbox(vms->curdir);
3377 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
3380 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
3383 if (vms->lastmsg > -1) {
3384 /* Get the deleted messages fixed */