2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Comedian Mail - Voicemail System
25 * \ingroup applications
29 * 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr)
30 * George Konstantoulakis <gkon@inaccessnetworks.com>
32 * 05-10-2005 : Support for Swedish and Norwegian added by Daniel Nylander, http://www.danielnylander.se/
34 * 05-11-2005 : An option for maximum number of messsages per mailbox added by GDS Partners (www.gdspartners.com)
35 * 07-11-2005 : An issue with voicemail synchronization has been fixed by GDS Partners (www.gdspartners.com)
36 * Stojan Sljivic <stojan.sljivic@gdspartners.com>
48 #include <sys/types.h>
55 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
57 #include "asterisk/lock.h"
58 #include "asterisk/file.h"
59 #include "asterisk/logger.h"
60 #include "asterisk/channel.h"
61 #include "asterisk/pbx.h"
62 #include "asterisk/options.h"
63 #include "asterisk/config.h"
64 #include "asterisk/say.h"
65 #include "asterisk/module.h"
66 #include "asterisk/adsi.h"
67 #include "asterisk/app.h"
68 #include "asterisk/manager.h"
69 #include "asterisk/dsp.h"
70 #include "asterisk/localtime.h"
71 #include "asterisk/cli.h"
72 #include "asterisk/utils.h"
73 #ifdef USE_ODBC_STORAGE
74 #include "asterisk/res_odbc.h"
77 #define COMMAND_TIMEOUT 5000
79 #define VOICEMAIL_CONFIG "voicemail.conf"
80 #define ASTERISK_USERNAME "asterisk"
82 /* Default mail command to mail voicemail. Change it with the
83 mailcmd= command in voicemail.conf */
84 #define SENDMAIL "/usr/sbin/sendmail -t"
86 #define INTRO "vm-intro"
89 #define MAXMSGLIMIT 9999
91 #define BASEMAXINLINE 256
92 #define BASELINELEN 72
93 #define BASEMAXINLINE 256
96 #define MAX_DATETIME_FORMAT 512
97 #define MAX_NUM_CID_CONTEXTS 10
99 #define VM_REVIEW (1 << 0)
100 #define VM_OPERATOR (1 << 1)
101 #define VM_SAYCID (1 << 2)
102 #define VM_SVMAIL (1 << 3)
103 #define VM_ENVELOPE (1 << 4)
104 #define VM_SAYDURATION (1 << 5)
105 #define VM_SKIPAFTERCMD (1 << 6)
106 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
107 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
108 #define VM_PBXSKIP (1 << 9)
109 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
110 #define VM_ATTACH (1 << 11)
111 #define VM_DELETE (1 << 12)
112 #define VM_ALLOCED (1 << 13)
113 #define VM_SEARCH (1 << 14)
115 #define ERROR_LOCK_PATH -100
118 OPT_SILENT = (1 << 0),
119 OPT_BUSY_GREETING = (1 << 1),
120 OPT_UNAVAIL_GREETING = (1 << 2),
121 OPT_RECORDGAIN = (1 << 3),
122 OPT_PREPEND_MAILBOX = (1 << 4),
123 OPT_PRIORITY_JUMP = (1 << 5),
127 OPT_ARG_RECORDGAIN = 0,
128 OPT_ARG_ARRAY_SIZE = 1,
131 AST_APP_OPTIONS(vm_app_options, {
132 AST_APP_OPTION('s', OPT_SILENT),
133 AST_APP_OPTION('b', OPT_BUSY_GREETING),
134 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
135 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
136 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
137 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
140 static int load_config(void);
142 /*! \page vmlang Voicemail Language Syntaxes Supported
144 \par Syntaxes supported, not really language codes.
151 \arg \b pt - Portuguese
153 \arg \b no - Norwegian
156 German requires the following additional soundfile:
157 \arg \b 1F einE (feminine)
159 Spanish requires the following additional soundfile:
160 \arg \b 1M un (masculine)
162 Dutch, Portuguese & Spanish require the following additional soundfiles:
163 \arg \b vm-INBOXs singular of 'new'
164 \arg \b vm-Olds singular of 'old/heard/read'
167 \arg \b vm-INBOX nieuwe (nl)
168 \arg \b vm-Old oude (nl)
171 \arg \b vm-nytt singular of 'new'
172 \arg \b vm-nya plural of 'new'
173 \arg \b vm-gammalt singular of 'old'
174 \arg \b vm-gamla plural of 'old'
175 \arg \b digits/ett 'one', not always same as 'digits/1'
178 \arg \b vm-ny singular of 'new'
179 \arg \b vm-nye plural of 'new'
180 \arg \b vm-gammel singular of 'old'
181 \arg \b vm-gamle plural of 'old'
189 Italian requires the following additional soundfile:
193 \arg \b vm-nuovi new plural
194 \arg \b vm-vecchio old
195 \arg \b vm-vecchi old plural
197 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
198 spelled among others when you have to change folder. For the above reasons, vm-INBOX
199 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
208 unsigned char iobuf[BASEMAXINLINE];
211 /*! Structure for linked list of users */
213 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
214 char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */
215 char password[80]; /*!< Secret pin code, numbers only */
216 char fullname[80]; /*!< Full name, for directory app */
217 char email[80]; /*!< E-mail address */
218 char pager[80]; /*!< E-mail address to pager (no attachment) */
219 char serveremail[80]; /*!< From: Mail address */
220 char mailcmd[160]; /*!< Configurable mail command */
221 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
222 char zonetag[80]; /*!< Time zone */
225 char uniqueid[20]; /*!< Unique integer identifier */
227 unsigned int flags; /*!< VM_ flags */
229 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
230 struct ast_vm_user *next;
236 char msg_format[512];
237 struct vm_zone *next;
256 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
257 int option, signed char record_gain);
258 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
259 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
260 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
261 signed char record_gain);
262 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
263 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
265 static void apply_options(struct ast_vm_user *vmu, const char *options);
267 #ifdef USE_ODBC_STORAGE
268 static char odbc_database[80];
269 static char odbc_table[80];
270 #define RETRIEVE(a,b) retrieve_file(a,b)
271 #define DISPOSE(a,b) remove_file(a,b)
272 #define STORE(a,b,c,d) store_file(a,b,c,d)
273 #define EXISTS(a,b,c,d) (message_exists(a,b))
274 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
275 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
276 #define DELETE(a,b,c) (delete_file(a,b))
278 #define RETRIEVE(a,b)
280 #define STORE(a,b,c,d)
281 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
282 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
283 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
284 #define DELETE(a,b,c) (vm_delete(c))
287 static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
289 static char ext_pass_cmd[128];
291 static char *tdesc = "Comedian Mail (Voicemail System)";
293 static char *addesc = "Comedian Mail";
295 static char *synopsis_vm =
296 "Leave a Voicemail message";
298 static char *descrip_vm =
299 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
300 "application allows the calling party to leave a message for the specified\n"
301 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
302 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
303 "specified mailbox does not exist.\n"
304 " The Voicemail application will exit if any of the following DTMF digits are\n"
306 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
307 " * - Jump to the 'a' extension in the current dialplan context.\n"
308 " This application will set the following channel variable upon completion:\n"
309 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
310 " application. The possible values are:\n"
311 " SUCCESS | USEREXIT | FAILED\n\n"
313 " b - Play the 'busy' greeting to the calling party.\n"
314 " g(#) - Use the specified amount of gain when recording the voicemail\n"
315 " message. The units are whole-number decibels (dB).\n"
316 " s - Skip the playback of instructions for leaving a message to the\n"
318 " u - Play the 'unavailble greeting.\n"
319 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
322 static char *synopsis_vmain =
323 "Check Voicemail messages";
325 static char *descrip_vmain =
326 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
327 "calling party to check voicemail messages. A specific mailbox, and optional\n"
328 "corresponding context, may be specified. If a mailbox is not provided, the\n"
329 "calling party will be prompted to enter one. If a context is not specified,\n"
330 "the 'default' context will be used.\n\n"
332 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
333 " is entered by the caller.\n"
334 " g(#) - Use the specified amount of gain when recording a voicemail\n"
335 " message. The units are whole-number decibels (dB).\n"
336 " s - Skip checking the passcode for the mailbox.\n";
338 static char *synopsis_vm_box_exists =
339 "Check to see if Voicemail mailbox exists";
341 static char *descrip_vm_box_exists =
342 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
343 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
345 " This application will set the following channel variable upon completion:\n"
346 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
347 " MailboxExists application. Possible values include:\n"
348 " SUCCESS | FAILED\n\n"
350 " j - Jump to priority n+101 if the mailbox is found.\n";
352 static char *synopsis_vmauthenticate =
353 "Authenticate with Voicemail passwords";
355 static char *descrip_vmauthenticate =
356 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
357 "same way as the Authenticate application, but the passwords are taken from\n"
359 " If the mailbox is specified, only that mailbox's password will be considered\n"
360 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
361 "be set with the authenticated mailbox.\n\n"
363 " s - Skip playing the initial prompts.\n";
365 /* Leave a message */
366 static char *app = "VoiceMail";
368 /* Check mail, control, etc */
369 static char *app2 = "VoiceMailMain";
371 static char *app3 = "MailboxExists";
372 static char *app4 = "VMAuthenticate";
374 AST_MUTEX_DEFINE_STATIC(vmlock);
375 struct ast_vm_user *users;
376 struct ast_vm_user *usersl;
377 struct vm_zone *zones = NULL;
378 struct vm_zone *zonesl = NULL;
379 static int maxsilence;
381 static int silencethreshold = 128;
382 static char serveremail[80];
383 static char mailcmd[160]; /* Configurable mail cmd */
384 static char externnotify[160];
386 static char vmfmts[80];
387 static int vmminmessage;
388 static int vmmaxmessage;
391 static int maxlogins;
393 static struct ast_flags globalflags = {0};
395 static int saydurationminfo;
397 static char dialcontext[AST_MAX_CONTEXT];
398 static char callcontext[AST_MAX_CONTEXT];
399 static char exitcontext[AST_MAX_CONTEXT];
401 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
404 static char *emailbody = NULL;
405 static char *emailsubject = NULL;
406 static char *pagerbody = NULL;
407 static char *pagersubject = NULL;
408 static char fromstring[100];
409 static char pagerfromstring[100];
410 static char emailtitle[100];
411 static char charset[32] = "ISO-8859-1";
413 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
414 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
415 static int adsiver = 1;
416 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
422 static void populate_defaults(struct ast_vm_user *vmu)
424 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
425 if (saydurationminfo)
426 vmu->saydurationm = saydurationminfo;
428 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
430 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
432 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
434 vmu->maxmsg = maxmsg;
437 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
440 if (!strcasecmp(var, "attach")) {
441 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
442 } else if (!strcasecmp(var, "serveremail")) {
443 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
444 } else if (!strcasecmp(var, "language")) {
445 ast_copy_string(vmu->language, value, sizeof(vmu->language));
446 } else if (!strcasecmp(var, "tz")) {
447 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
448 } else if (!strcasecmp(var, "delete")) {
449 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
450 } else if (!strcasecmp(var, "saycid")){
451 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
452 } else if (!strcasecmp(var,"sendvoicemail")){
453 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
454 } else if (!strcasecmp(var, "review")){
455 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
456 } else if (!strcasecmp(var, "operator")){
457 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
458 } else if (!strcasecmp(var, "envelope")){
459 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
460 } else if (!strcasecmp(var, "sayduration")){
461 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
462 } else if (!strcasecmp(var, "saydurationm")){
463 if (sscanf(value, "%d", &x) == 1) {
464 vmu->saydurationm = x;
466 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
468 } else if (!strcasecmp(var, "forcename")){
469 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
470 } else if (!strcasecmp(var, "forcegreetings")){
471 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
472 } else if (!strcasecmp(var, "callback")) {
473 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
474 } else if (!strcasecmp(var, "dialout")) {
475 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
476 } else if (!strcasecmp(var, "exitcontext")) {
477 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
478 } else if (!strcasecmp(var, "maxmsg")) {
479 vmu->maxmsg = atoi(value);
480 if (vmu->maxmsg <= 0) {
481 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
482 vmu->maxmsg = MAXMSG;
483 } else if (vmu->maxmsg > MAXMSGLIMIT) {
484 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
485 vmu->maxmsg = MAXMSGLIMIT;
487 } else if (!strcasecmp(var, "options")) {
488 apply_options(vmu, value);
492 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
495 if (!ast_strlen_zero(vmu->uniqueid)) {
496 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
498 ast_copy_string(vmu->password, password, sizeof(vmu->password));
508 static void apply_options(struct ast_vm_user *vmu, const char *options)
509 { /* Destructively Parse options and apply */
513 stringp = ast_strdupa(options);
514 while ((s = strsep(&stringp, "|"))) {
516 if ((var = strsep(&value, "=")) && value) {
517 apply_option(vmu, var, value);
522 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
524 struct ast_variable *var, *tmp;
525 struct ast_vm_user *retval;
530 retval=malloc(sizeof(struct ast_vm_user));
533 memset(retval, 0, sizeof(struct ast_vm_user));
535 ast_set_flag(retval, VM_ALLOCED);
537 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
538 populate_defaults(retval);
539 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
540 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
542 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
546 printf("%s => %s\n", tmp->name, tmp->value);
547 if (!strcasecmp(tmp->name, "password")) {
548 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
549 } else if (!strcasecmp(tmp->name, "uniqueid")) {
550 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
551 } else if (!strcasecmp(tmp->name, "pager")) {
552 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
553 } else if (!strcasecmp(tmp->name, "email")) {
554 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
555 } else if (!strcasecmp(tmp->name, "fullname")) {
556 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
557 } else if (!strcasecmp(tmp->name, "context")) {
558 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
560 apply_option(retval, tmp->name, tmp->value);
572 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
574 /* This function could be made to generate one from a database, too */
575 struct ast_vm_user *vmu=NULL, *cur;
576 ast_mutex_lock(&vmlock);
579 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
583 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
585 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
593 /* Make a copy, so that on a reload, we have no race */
594 vmu = malloc(sizeof(struct ast_vm_user));
596 memcpy(vmu, cur, sizeof(struct ast_vm_user));
597 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
601 vmu = find_user_realtime(ivm, context, mailbox);
602 ast_mutex_unlock(&vmlock);
606 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
608 /* This function could be made to generate one from a database, too */
609 struct ast_vm_user *cur;
611 ast_mutex_lock(&vmlock);
614 if ((!context || !strcasecmp(context, cur->context)) &&
615 (!strcasecmp(mailbox, cur->mailbox)))
620 ast_copy_string(cur->password, newpass, sizeof(cur->password));
623 ast_mutex_unlock(&vmlock);
627 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
629 /* There's probably a better way of doing this. */
630 /* That's why I've put the password change in a separate function. */
631 /* This could also be done with a database function */
638 char currcontext[256] ="";
639 char tmpin[AST_CONFIG_MAX_PATH];
640 char tmpout[AST_CONFIG_MAX_PATH];
643 if (!change_password_realtime(vmu, newpassword))
646 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
647 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
648 configin = fopen(tmpin,"r");
650 configout = fopen(tmpout,"w+");
653 if (!configin || !configout) {
657 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
661 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
665 while (!feof(configin)) {
666 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
668 /* Read in the line */
669 fgets(inbuf, sizeof(inbuf), configin);
672 if (ast_strlen_zero(inbuf)) {
673 fprintf(configout, "\n");
677 /* Make a backup of it */
678 ast_copy_string(orig, inbuf, sizeof(orig));
681 Read the file line by line, split each line into a comment and command section
682 only parse the command portion of the line
684 if (inbuf[strlen(inbuf) - 1] == '\n')
685 inbuf[strlen(inbuf) - 1] = '\0';
687 if ((comment = strchr(inbuf, ';')))
688 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
690 if (ast_strlen_zero(inbuf)) {
691 fprintf(configout, "%s", orig);
695 /* Check for a context, first '[' to first ']' */
696 if ((tmpctx = strchr(inbuf, '['))) {
697 tmpctxend = strchr(tmpctx, ']');
700 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
701 fprintf(configout, "%s", orig);
706 /* This isn't a context line, check for MBX => PSWD... */
708 if ((pass = strchr(user, '='))) {
709 /* We have a line in the form of aaaaa=aaaaaa */
712 user = ast_strip(user);
717 pass = ast_skip_blanks(pass);
720 Since no whitespace allowed in fields, or more correctly white space
721 inside the fields is there for a purpose, we can just terminate pass
722 at the comma or EOL whichever comes first.
724 if ((rest = strchr(pass, ',')))
730 /* Compare user, pass AND context */
731 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
732 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
733 !strcasecmp(currcontext, vmu->context)) {
734 /* This is the line */
736 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
738 fprintf(configout, "%s => %s", user, newpassword);
740 /* If there was a comment on the line print it out */
742 fprintf(configout, ";%s\n", comment);
744 fprintf(configout, "\n");
747 /* Put it back like it was */
748 fprintf(configout, "%s", orig);
754 stat(tmpin, &statbuf);
755 chmod(tmpout, statbuf.st_mode);
756 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
758 rename(tmpout, tmpin);
759 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
760 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
763 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
766 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
767 if (!ast_safe_system(buf))
768 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
771 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
773 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, mailbox);
776 static int make_file(char *dest, int len, char *dir, int num)
778 return snprintf(dest, len, "%s/msg%04d", dir, num);
781 /* only return failure if ast_lock_path returns 'timeout',
782 not if the path does not exist or any other reason
784 static int vm_lock_path(const char *path)
786 switch (ast_lock_path(path)) {
787 case AST_LOCK_TIMEOUT:
795 #ifdef USE_ODBC_STORAGE
796 static int retrieve_file(char *dir, int msgnum)
803 SQLSMALLINT colcount=0;
810 SQLSMALLINT datatype;
811 SQLSMALLINT decimaldigits;
812 SQLSMALLINT nullable;
821 obj = fetch_odbc_obj(odbc_database, 0);
823 ast_copy_string(fmt, vmfmts, sizeof(fmt));
824 c = strchr(fmt, '|');
827 if (!strcasecmp(fmt, "wav49"))
829 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
831 make_file(fn, sizeof(fn), dir, msgnum);
833 ast_copy_string(fn, dir, sizeof(fn));
834 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
835 f = fopen(full_fn, "w+");
836 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
837 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
838 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
839 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
842 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
843 res = SQLPrepare(stmt, sql, SQL_NTS);
844 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
845 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
846 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
849 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
850 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
851 res = odbc_smart_execute(obj, stmt);
852 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
853 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
854 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
857 res = SQLFetch(stmt);
858 if (res == SQL_NO_DATA) {
859 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
862 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
863 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
864 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
867 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
869 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
870 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
873 res = SQLNumResultCols(stmt, &colcount);
874 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
875 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
876 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
880 fprintf(f, "[message]\n");
881 for (x=0;x<colcount;x++) {
883 collen = sizeof(coltitle);
884 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
885 &datatype, &colsize, &decimaldigits, &nullable);
886 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
887 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
888 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
891 if (!strcasecmp(coltitle, "recording")) {
892 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
894 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
897 lseek(fd, fdlen - 1, SEEK_SET);
898 if (write(fd, tmp, 1) != 1) {
903 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
906 memset(fdm, 0, fdlen);
907 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
908 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
909 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
910 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
915 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
916 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
917 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
918 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
921 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
922 fprintf(f, "%s=%s\n", coltitle, rowdata);
925 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
927 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
938 static int remove_file(char *dir, int msgnum)
945 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
946 make_file(fn, sizeof(fn), dir, msgnum);
948 ast_copy_string(fn, dir, sizeof(fn));
949 ast_filedelete(fn, NULL);
950 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
955 static int last_message_index(struct ast_vm_user *vmu, char *dir)
964 obj = fetch_odbc_obj(odbc_database, 0);
966 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
967 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
968 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
971 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
972 res = SQLPrepare(stmt, sql, SQL_NTS);
973 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
974 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
975 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
978 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
979 res = odbc_smart_execute(obj, stmt);
980 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
981 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
982 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
985 res = SQLFetch(stmt);
986 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
987 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
988 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
991 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
992 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
993 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
994 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
997 if (sscanf(rowdata, "%d", &x) != 1)
998 ast_log(LOG_WARNING, "Failed to read message count!\n");
999 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1001 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1006 static int message_exists(char *dir, int msgnum)
1016 obj = fetch_odbc_obj(odbc_database, 0);
1018 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1019 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1020 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1021 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1024 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1025 res = SQLPrepare(stmt, sql, SQL_NTS);
1026 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1027 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1028 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1031 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1032 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1033 res = odbc_smart_execute(obj, stmt);
1034 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1035 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1036 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1039 res = SQLFetch(stmt);
1040 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1041 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1042 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1045 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1046 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1047 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1048 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1051 if (sscanf(rowdata, "%d", &x) != 1)
1052 ast_log(LOG_WARNING, "Failed to read message count!\n");
1053 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1055 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1060 static int count_messages(struct ast_vm_user *vmu, char *dir)
1062 return last_message_index(vmu, dir) + 1;
1065 static void delete_file(char *sdir, int smsg)
1073 obj = fetch_odbc_obj(odbc_database, 0);
1075 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1076 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1077 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1078 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1081 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1082 res = SQLPrepare(stmt, sql, SQL_NTS);
1083 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1084 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1085 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1088 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1089 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1090 res = odbc_smart_execute(obj, stmt);
1091 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1092 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1093 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1096 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1098 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1103 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1112 delete_file(ddir, dmsg);
1113 obj = fetch_odbc_obj(odbc_database, 0);
1115 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1116 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1117 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1118 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1119 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1122 #ifdef EXTENDED_ODBC_STORAGE
1123 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
1125 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
1127 res = SQLPrepare(stmt, sql, SQL_NTS);
1128 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1129 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1130 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1133 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1134 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1135 #ifdef EXTENDED_ODBC_STORAGE
1136 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1137 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1138 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1139 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1141 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1142 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1144 res = odbc_smart_execute(obj, stmt);
1145 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1146 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1147 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1150 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1152 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1157 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1172 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1173 char *category = "";
1174 struct ast_config *cfg=NULL;
1177 delete_file(dir, msgnum);
1178 obj = fetch_odbc_obj(odbc_database, 0);
1180 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1181 c = strchr(fmt, '|');
1184 if (!strcasecmp(fmt, "wav49"))
1186 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1188 make_file(fn, sizeof(fn), dir, msgnum);
1190 ast_copy_string(fn, dir, sizeof(fn));
1191 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1192 cfg = ast_config_load(full_fn);
1193 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1194 fd = open(full_fn, O_RDWR);
1196 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1200 context = ast_variable_retrieve(cfg, "message", "context");
1201 if (!context) context = "";
1202 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1203 if (!macrocontext) macrocontext = "";
1204 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1205 if (!callerid) callerid = "";
1206 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1207 if (!origtime) origtime = "";
1208 duration = ast_variable_retrieve(cfg, "message", "duration");
1209 if (!duration) duration = "";
1210 category = ast_variable_retrieve(cfg, "message", "category");
1211 if (!category) category = "";
1213 fdlen = lseek(fd, 0, SEEK_END);
1214 lseek(fd, 0, SEEK_SET);
1215 printf("Length is %d\n", fdlen);
1216 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1218 ast_log(LOG_WARNING, "Memory map failed!\n");
1221 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1222 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1223 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1226 if (!ast_strlen_zero(category))
1227 #ifdef EXTENDED_ODBC_STORAGE
1228 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1230 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
1233 #ifdef EXTENDED_ODBC_STORAGE
1234 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1236 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
1238 res = SQLPrepare(stmt, sql, SQL_NTS);
1239 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1240 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1241 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1244 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1245 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1246 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1247 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1248 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1249 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1250 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1251 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1252 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1253 #ifdef EXTENDED_ODBC_STORAGE
1254 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1255 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1256 if (!ast_strlen_zero(category))
1257 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1259 if (!ast_strlen_zero(category))
1260 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1262 res = odbc_smart_execute(obj, stmt);
1263 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1264 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1265 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1268 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1270 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1273 ast_config_destroy(cfg);
1281 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1290 delete_file(ddir, dmsg);
1291 obj = fetch_odbc_obj(odbc_database, 0);
1293 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1294 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1295 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1296 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1297 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1300 #ifdef EXTENDED_ODBC_STORAGE
1301 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1303 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
1305 res = SQLPrepare(stmt, sql, SQL_NTS);
1306 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1307 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1308 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1311 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1312 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1313 #ifdef EXTENDED_ODBC_STORAGE
1314 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1315 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1316 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1317 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1319 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1320 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1322 res = odbc_smart_execute(obj, stmt);
1323 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1324 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1325 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1328 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1330 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1337 static int count_messages(struct ast_vm_user *vmu, char *dir)
1339 /* Find all .txt files - even if they are not in sequence from 0000 */
1343 struct dirent *vment = NULL;
1345 if (vm_lock_path(dir))
1346 return ERROR_LOCK_PATH;
1348 if ((vmdir = opendir(dir))) {
1349 while ((vment = readdir(vmdir))) {
1350 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1355 ast_unlock_path(dir);
1360 static void rename_file(char *sfn, char *dfn)
1364 ast_filerename(sfn,dfn,NULL);
1365 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1366 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1370 static int copy(char *infile, char *outfile)
1378 #ifdef HARDLINK_WHEN_POSSIBLE
1379 /* Hard link if possible; saves disk space & is faster */
1380 if (link(infile, outfile)) {
1382 if ((ifd = open(infile, O_RDONLY)) < 0) {
1383 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1386 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1387 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1392 len = read(ifd, buf, sizeof(buf));
1394 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1400 res = write(ofd, buf, len);
1401 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1402 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1412 #ifdef HARDLINK_WHEN_POSSIBLE
1414 /* Hard link succeeded */
1420 static void copy_file(char *frompath, char *topath)
1422 char frompath2[256],topath2[256];
1423 ast_filecopy(frompath, topath, NULL);
1424 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1425 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1426 copy(frompath2, topath2);
1430 * A negative return value indicates an error.
1432 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1437 if (vm_lock_path(dir))
1438 return ERROR_LOCK_PATH;
1440 for (x = 0; x < vmu->maxmsg; x++) {
1441 make_file(fn, sizeof(fn), dir, x);
1442 if (ast_fileexists(fn, NULL, NULL) < 1)
1445 ast_unlock_path(dir);
1450 static int vm_delete(char *file)
1455 txtsize = (strlen(file) + 5)*sizeof(char);
1456 txt = (char *)alloca(txtsize);
1457 /* Sprintf here would safe because we alloca'd exactly the right length,
1458 * but trying to eliminate all sprintf's anyhow
1460 snprintf(txt, txtsize, "%s.txt", file);
1462 return ast_filedelete(file, NULL);
1468 inbuf(struct baseio *bio, FILE *fi)
1475 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1490 inchar(struct baseio *bio, FILE *fi)
1492 if (bio->iocp>=bio->iolen) {
1493 if (!inbuf(bio, fi))
1497 return bio->iobuf[bio->iocp++];
1501 ochar(struct baseio *bio, int c, FILE *so)
1503 if (bio->linelength>=BASELINELEN) {
1504 if (fputs(eol,so)==EOF)
1510 if (putc(((unsigned char)c),so)==EOF)
1518 static int base_encode(char *filename, FILE *so)
1520 unsigned char dtable[BASEMAXINLINE];
1525 memset(&bio, 0, sizeof(bio));
1526 bio.iocp = BASEMAXINLINE;
1528 if (!(fi = fopen(filename, "rb"))) {
1529 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1533 for (i= 0;i<9;i++) {
1536 dtable[26+i]= 'a'+i;
1537 dtable[26+i+9]= 'j'+i;
1539 for (i= 0;i<8;i++) {
1540 dtable[i+18]= 'S'+i;
1541 dtable[26+i+18]= 's'+i;
1543 for (i= 0;i<10;i++) {
1544 dtable[52+i]= '0'+i;
1550 unsigned char igroup[3],ogroup[4];
1553 igroup[0]= igroup[1]= igroup[2]= 0;
1555 for (n= 0;n<3;n++) {
1556 if ((c = inchar(&bio, fi)) == EOF) {
1561 igroup[n]= (unsigned char)c;
1565 ogroup[0]= dtable[igroup[0]>>2];
1566 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1567 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1568 ogroup[3]= dtable[igroup[2]&0x3F];
1578 ochar(&bio, ogroup[i], so);
1582 if (fputs(eol,so)==EOF)
1590 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize)
1593 /* Prepare variables for substition in email body and subject */
1594 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1595 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1596 snprintf(passdata, passdatasize, "%d", msgnum);
1597 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1598 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1599 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1600 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1601 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1602 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1603 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1607 * fill in *tm for current time according to the proper timezone, if any.
1608 * Return tm so it can be used as a function argument.
1610 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1612 const struct vm_zone *z = NULL;
1613 time_t t = time(NULL);
1615 /* Does this user have a timezone specified? */
1616 if (!ast_strlen_zero(vmu->zonetag)) {
1617 /* Find the zone in the list */
1618 for (z = zones; z ; z = z->next)
1619 if (!strcmp(z->name, vmu->zonetag))
1622 ast_localtime(&t, tm, z ? z->timezone : NULL);
1626 /* same as mkstemp, but return a FILE * */
1627 static FILE *vm_mkftemp(char *template)
1630 int pfd = mkstemp(template);
1632 p = fdopen(pfd, "w");
1641 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail)
1645 char host[MAXHOSTNAMELEN] = "";
1650 char tmp[80] = "/tmp/astmail-XXXXXX";
1654 if (vmu && ast_strlen_zero(vmu->email)) {
1655 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1658 if (!strcmp(format, "wav49"))
1660 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
1661 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1663 p = vm_mkftemp(tmp);
1665 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1668 gethostname(host, sizeof(host)-1);
1669 if (strchr(srcemail, '@'))
1670 ast_copy_string(who, srcemail, sizeof(who));
1672 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1674 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1675 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1676 fprintf(p, "Date: %s\n", date);
1678 /* Set date format for voicemail mail */
1679 strftime(date, sizeof(date), emaildateformat, &tm);
1682 struct ast_channel *ast = ast_channel_alloc(0);
1685 int vmlen = strlen(fromstring)*3 + 200;
1686 if ((passdata = alloca(vmlen))) {
1687 memset(passdata, 0, vmlen);
1688 prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1689 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1690 fprintf(p, "From: %s <%s>\n",passdata,who);
1691 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1692 ast_channel_free(ast);
1693 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1695 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1696 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1699 struct ast_channel *ast = ast_channel_alloc(0);
1702 int vmlen = strlen(emailsubject)*3 + 200;
1703 if ((passdata = alloca(vmlen))) {
1704 memset(passdata, 0, vmlen);
1705 prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1706 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1707 fprintf(p, "Subject: %s\n",passdata);
1708 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1709 ast_channel_free(ast);
1710 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1713 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1715 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1716 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1718 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1719 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
1720 fprintf(p, "MIME-Version: 1.0\n");
1721 if (attach_user_voicemail) {
1722 /* Something unique. */
1723 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
1725 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1727 fprintf(p, "--%s\n", bound);
1729 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1731 struct ast_channel *ast = ast_channel_alloc(0);
1734 int vmlen = strlen(emailbody)*3 + 200;
1735 if ((passdata = alloca(vmlen))) {
1736 memset(passdata, 0, vmlen);
1737 prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1738 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1739 fprintf(p, "%s\n",passdata);
1740 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1741 ast_channel_free(ast);
1742 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1744 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1746 "in mailbox %s from %s, on %s so you might\n"
1747 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1748 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1750 if (attach_user_voicemail) {
1751 /* Eww. We want formats to tell us their own MIME type */
1752 char *ctype = "audio/x-";
1753 if (!strcasecmp(format, "ogg"))
1754 ctype = "application/";
1756 fprintf(p, "--%s\n", bound);
1757 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1758 fprintf(p, "Content-Transfer-Encoding: base64\n");
1759 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1760 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1762 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1763 base_encode(fname, p);
1764 fprintf(p, "\n\n--%s--\n.\n", bound);
1767 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1768 ast_safe_system(tmp2);
1769 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1774 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu)
1777 char host[MAXHOSTNAMELEN]="";
1780 char tmp[80] = "/tmp/astmail-XXXXXX";
1783 FILE *p = vm_mkftemp(tmp);
1786 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1789 gethostname(host, sizeof(host)-1);
1790 if (strchr(srcemail, '@'))
1791 ast_copy_string(who, srcemail, sizeof(who));
1793 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1795 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1796 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1797 fprintf(p, "Date: %s\n", date);
1799 if (*pagerfromstring) {
1800 struct ast_channel *ast = ast_channel_alloc(0);
1803 int vmlen = strlen(fromstring)*3 + 200;
1804 if ((passdata = alloca(vmlen))) {
1805 memset(passdata, 0, vmlen);
1806 prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1807 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1808 fprintf(p, "From: %s <%s>\n",passdata,who);
1810 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1811 ast_channel_free(ast);
1812 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1814 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1815 fprintf(p, "To: %s\n", pager);
1817 struct ast_channel *ast = ast_channel_alloc(0);
1820 int vmlen = strlen(pagersubject)*3 + 200;
1821 if ((passdata = alloca(vmlen))) {
1822 memset(passdata, 0, vmlen);
1823 prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1824 pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
1825 fprintf(p, "Subject: %s\n\n",passdata);
1826 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1827 ast_channel_free(ast);
1828 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1830 fprintf(p, "Subject: New VM\n\n");
1831 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1833 struct ast_channel *ast = ast_channel_alloc(0);
1836 int vmlen = strlen(pagerbody)*3 + 200;
1837 if ((passdata = alloca(vmlen))) {
1838 memset(passdata, 0, vmlen);
1839 prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1840 pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
1841 fprintf(p, "%s\n",passdata);
1842 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1843 ast_channel_free(ast);
1844 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1846 fprintf(p, "New %s long msg in box %s\n"
1847 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1850 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1851 ast_safe_system(tmp2);
1852 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1857 static int get_date(char *s, int len)
1862 localtime_r(&t,&tm);
1863 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1866 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1870 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
1872 if (ast_fileexists(fn, NULL, NULL) > 0) {
1873 res = ast_streamfile(chan, fn, chan->language);
1878 res = ast_waitstream(chan, ecodes);
1884 /* Dispose just in case */
1886 res = ast_streamfile(chan, "vm-theperson", chan->language);
1889 res = ast_waitstream(chan, ecodes);
1892 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1897 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1899 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1902 res = ast_waitstream(chan, ecodes);
1906 static void free_user(struct ast_vm_user *vmu)
1908 if (ast_test_flag(vmu, VM_ALLOCED))
1912 static void free_zone(struct vm_zone *z)
1917 static char *mbox(int id)
1945 #ifdef USE_ODBC_STORAGE
1946 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
1960 /* If no mailbox, return immediately */
1961 if (ast_strlen_zero(mailbox))
1964 ast_copy_string(tmp, mailbox, sizeof(tmp));
1966 context = strchr(tmp, '@');
1971 context = "default";
1974 obj = fetch_odbc_obj(odbc_database, 0);
1976 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1977 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1978 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1981 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir LIKE '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
1982 res = SQLPrepare(stmt, sql, SQL_NTS);
1983 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1984 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1985 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1988 res = odbc_smart_execute(obj, stmt);
1989 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1990 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1991 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1994 res = SQLFetch(stmt);
1995 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1996 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1997 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2000 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2001 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2002 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2003 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2006 *newmsgs = atoi(rowdata);
2007 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2009 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2010 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2011 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2014 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "Old");
2015 res = SQLPrepare(stmt, sql, SQL_NTS);
2016 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2017 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2018 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2021 res = odbc_smart_execute(obj, stmt);
2022 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2023 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2024 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2027 res = SQLFetch(stmt);
2028 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2029 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2030 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2033 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2034 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2035 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2036 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2039 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2040 *oldmsgs = atoi(rowdata);
2043 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2049 static int has_voicemail(const char *mailbox, const char *folder)
2060 /* If no mailbox, return immediately */
2061 if (ast_strlen_zero(mailbox))
2064 ast_copy_string(tmp, mailbox, sizeof(tmp));
2066 context = strchr(tmp, '@');
2071 context = "default";
2074 obj = fetch_odbc_obj(odbc_database, 0);
2076 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2077 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2078 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2081 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2082 res = SQLPrepare(stmt, sql, SQL_NTS);
2083 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2084 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2085 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2088 res = odbc_smart_execute(obj, stmt);
2089 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2090 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2091 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2094 res = SQLFetch(stmt);
2095 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2096 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2097 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2100 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2101 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2102 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2103 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2106 nummsgs = atoi(rowdata);
2107 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2109 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2120 static int has_voicemail(const char *mailbox, const char *folder)
2131 /* If no mailbox, return immediately */
2132 if (ast_strlen_zero(mailbox))
2134 if (strchr(mailbox, ',')) {
2135 ast_copy_string(tmp, mailbox, sizeof(tmp));
2138 while((cur = strsep(&mb, ","))) {
2139 if (!ast_strlen_zero(cur)) {
2140 if (has_voicemail(cur, folder))
2146 ast_copy_string(tmp, mailbox, sizeof(tmp));
2147 context = strchr(tmp, '@');
2152 context = "default";
2153 snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
2157 while ((de = readdir(dir))) {
2158 if (!strncasecmp(de->d_name, "msg", 3))
2168 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2181 /* If no mailbox, return immediately */
2182 if (ast_strlen_zero(mailbox))
2184 if (strchr(mailbox, ',')) {
2186 ast_copy_string(tmp, mailbox, sizeof(tmp));
2189 while((cur = strsep(&mb, ", "))) {
2190 if (!ast_strlen_zero(cur)) {
2191 if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2203 ast_copy_string(tmp, mailbox, sizeof(tmp));
2204 context = strchr(tmp, '@');
2209 context = "default";
2211 snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
2214 while ((de = readdir(dir))) {
2215 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2216 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2224 snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
2227 while ((de = readdir(dir))) {
2228 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2229 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2241 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
2243 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt)
2245 char fromdir[256], todir[256], frompath[256], topath[256];
2246 char *frombox = mbox(imbox);
2249 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2251 make_dir(todir, sizeof(todir), recip->context, "", "");
2252 /* It's easier just to try to make it than to check for its existence */
2253 if (mkdir(todir, 0700) && (errno != EEXIST))
2254 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
2255 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "");
2256 /* It's easier just to try to make it than to check for its existence */
2257 if (mkdir(todir, 0700) && (errno != EEXIST))
2258 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
2259 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2260 if (mkdir(todir, 0700) && (errno != EEXIST))
2261 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
2263 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2264 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2266 if (vm_lock_path(todir))
2267 return ERROR_LOCK_PATH;
2271 make_file(topath, sizeof(topath), todir, recipmsgnum);
2272 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2275 } while (recipmsgnum < recip->maxmsg);
2276 if (recipmsgnum < recip->maxmsg) {
2277 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2279 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2281 ast_unlock_path(todir);
2282 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2287 static void run_externnotify(char *context, char *extension)
2289 char arguments[255];
2290 char ext_context[256] = "";
2291 int newvoicemails = 0, oldvoicemails = 0;
2293 if (!ast_strlen_zero(context))
2294 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2296 ast_copy_string(ext_context, extension, sizeof(ext_context));
2298 if (!ast_strlen_zero(externnotify)) {
2299 if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
2300 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2302 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2303 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2304 ast_safe_system(arguments);
2309 struct leave_vm_options {
2311 signed char record_gain;
2314 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2327 char prefile[256]="";
2328 char tempfile[256]="";
2329 char ext_context[256] = "";
2332 char ecodes[16] = "#";
2333 char tmp[256] = "", *tmpptr;
2334 struct ast_vm_user *vmu;
2335 struct ast_vm_user svm;
2336 const char *category = NULL;
2338 ast_copy_string(tmp, ext, sizeof(tmp));
2340 context = strchr(tmp, '@');
2344 tmpptr = strchr(context, '&');
2346 tmpptr = strchr(ext, '&');
2354 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2356 if (!(vmu = find_user(&svm, context, ext))) {
2357 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2358 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2359 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2360 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2364 /* Setup pre-file if appropriate */
2365 if (strcmp(vmu->context, "default"))
2366 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2368 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2369 if (ast_test_flag(options, OPT_BUSY_GREETING))
2370 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2371 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2372 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2373 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2374 RETRIEVE(tempfile, -1);
2375 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2376 ast_copy_string(prefile, tempfile, sizeof(prefile));
2377 DISPOSE(tempfile, -1);
2378 make_dir(dir, sizeof(dir), vmu->context, "", "");
2379 /* It's easier just to try to make it than to check for its existence */
2380 if (mkdir(dir, 0700) && (errno != EEXIST))
2381 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2382 make_dir(dir, sizeof(dir), vmu->context, ext, "");
2383 /* It's easier just to try to make it than to check for its existence */
2384 if (mkdir(dir, 0700) && (errno != EEXIST))
2385 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2386 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
2387 if (mkdir(dir, 0700) && (errno != EEXIST))
2388 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2390 /* Check current or macro-calling context for special extensions */
2391 if (!ast_strlen_zero(vmu->exit)) {
2392 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2393 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2394 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
2395 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2396 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2397 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2401 if (!ast_strlen_zero(vmu->exit)) {
2402 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2403 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2404 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2405 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2406 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2407 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2411 /* Play the beginning intro if desired */
2412 if (!ast_strlen_zero(prefile)) {
2413 RETRIEVE(prefile, -1);
2414 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2415 if (ast_streamfile(chan, prefile, chan->language) > -1)
2416 res = ast_waitstream(chan, ecodes);
2418 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2419 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2421 DISPOSE(prefile, -1);
2423 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2425 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2430 /* On a '#' we skip the instructions */
2431 ast_set_flag(options, OPT_SILENT);
2434 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2435 res = ast_streamfile(chan, INTRO, chan->language);
2437 res = ast_waitstream(chan, ecodes);
2439 ast_set_flag(options, OPT_SILENT);
2444 ast_stopstream(chan);
2445 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2446 other than the operator -- an automated attendant or mailbox login for example */
2448 chan->exten[0] = 'a';
2449 chan->exten[1] = '\0';
2450 if (!ast_strlen_zero(vmu->exit)) {
2451 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2452 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2453 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2457 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2460 /* Check for a '0' here */
2463 if (ast_test_flag(vmu, VM_OPERATOR)) {
2464 chan->exten[0] = 'o';
2465 chan->exten[1] = '\0';
2466 if (!ast_strlen_zero(vmu->exit)) {
2467 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2468 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2469 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2471 ast_play_and_wait(chan, "transfer");
2474 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2477 ast_play_and_wait(chan, "vm-sorry");
2478 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2484 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2487 /* The meat of recording the message... All the announcements and beeps have been played*/
2488 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2489 if (!ast_strlen_zero(fmt)) {
2492 if (vm_lock_path(dir)) {
2494 return ERROR_LOCK_PATH;
2498 * This operation can be very expensive if done say over NFS or if the mailbox has 100+ messages
2499 * in the mailbox. So we should get this first so we don't cut off the first few seconds of the
2503 make_file(fn, sizeof(fn), dir, msgnum);
2504 if (!EXISTS(dir,msgnum,fn,chan->language))
2507 } while (msgnum < vmu->maxmsg);
2509 /* Now play the beep once we have the message number for our next message. */
2511 /* Unless we're *really* silent, try to send the beep */
2512 res = ast_streamfile(chan, "beep", chan->language);
2514 res = ast_waitstream(chan, "");
2516 if (msgnum < vmu->maxmsg) {
2517 /* assign a variable with the name of the voicemail file */
2518 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
2520 /* Store information */
2521 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2522 txt = fopen(txtfile, "w+");
2524 get_date(date, sizeof(date));
2527 "; Message Information file\n"
2546 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2547 date, (long)time(NULL),
2548 category ? category : "");
2550 ast_log(LOG_WARNING, "Error opening text file for output\n");
2551 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2560 fprintf(txt, "duration=%d\n", duration);
2564 if (duration < vmminmessage) {
2565 if (option_verbose > 2)
2566 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2567 DELETE(dir,msgnum,fn);
2568 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2569 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2572 /* Are there to be more recipients of this message? */
2574 struct ast_vm_user recipu, *recip;
2575 char *exten, *context;
2577 exten = strsep(&tmpptr, "&");
2578 context = strchr(exten, '@');
2583 if ((recip = find_user(&recipu, context, exten))) {
2584 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2588 if (ast_fileexists(fn, NULL, NULL)) {
2589 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2590 STORE(dir, vmu->mailbox, vmu->context, msgnum);
2591 DISPOSE(dir, msgnum);
2593 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
2595 ast_unlock_path(dir);
2596 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2598 res = ast_waitstream(chan, "");
2599 ast_log(LOG_WARNING, "No more messages possible\n");
2600 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2603 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2610 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
2612 /* we know max messages, so stop process when number is hit */
2618 if (vm_lock_path(dir))
2619 return ERROR_LOCK_PATH;
2621 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
2622 make_file(sfn, sizeof(sfn), dir, x);
2623 if (EXISTS(dir, x, sfn, NULL)) {
2626 make_file(dfn, sizeof(dfn), dir, dest);
2627 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
2633 ast_unlock_path(dir);
2639 static int say_and_wait(struct ast_channel *chan, int num, char *language)
2642 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2646 static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *context, char *username, int box)
2651 char *dbox = mbox(box);
2653 make_file(sfn, sizeof(sfn), dir, msg);
2654 make_dir(ddir, sizeof(ddir), context, username, dbox);
2657 if (vm_lock_path(ddir))
2658 return ERROR_LOCK_PATH;
2660 for (x = 0; x < vmu->maxmsg; x++) {
2661 make_file(dfn, sizeof(dfn), ddir, x);
2662 if (!EXISTS(ddir, x, dfn, NULL))
2665 if (x >= vmu->maxmsg) {
2666 ast_unlock_path(ddir);
2669 if (strcmp(sfn, dfn)) {
2670 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
2672 ast_unlock_path(ddir);
2677 static int adsi_logo(unsigned char *buf)
2680 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2681 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2685 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2687 unsigned char buf[256];
2693 bytes += adsi_data_mode(buf + bytes);
2694 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2697 bytes += adsi_logo(buf);
2698 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2700 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
2702 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2703 bytes += adsi_data_mode(buf + bytes);
2704 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2706 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2708 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2709 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2710 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2711 bytes += adsi_voice_mode(buf + bytes, 0);
2712 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2719 bytes += adsi_logo(buf);
2720 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2721 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
2722 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2723 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2726 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2727 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2728 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2729 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2730 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2731 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2732 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2735 /* Add another dot */
2737 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
2738 bytes += adsi_voice_mode(buf + bytes, 0);
2740 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2741 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2745 /* These buttons we load but don't use yet */
2746 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2747 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2748 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2749 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2750 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2751 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2752 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2755 /* Add another dot */
2757 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2758 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2759 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2764 snprintf(num, sizeof(num), "%d", x);
2765 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2767 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2768 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2771 /* Add another dot */
2773 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2774 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2775 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2778 if (adsi_end_download(chan)) {
2780 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2781 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2782 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2783 bytes += adsi_voice_mode(buf + bytes, 0);
2784 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2788 bytes += adsi_download_disconnect(buf + bytes);
2789 bytes += adsi_voice_mode(buf + bytes, 0);
2790 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2792 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2797 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2798 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2800 ast_log(LOG_DEBUG, "Restarting session...\n");
2803 /* Load the session now */
2804 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2806 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2808 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2810 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2814 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2817 if (!adsi_available(chan))
2819 x = adsi_load_session(chan, adsifdn, adsiver, 1);
2823 if (adsi_load_vmail(chan, useadsi)) {
2824 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2831 static void adsi_login(struct ast_channel *chan)
2833 unsigned char buf[256];
2835 unsigned char keys[8];
2837 if (!adsi_available(chan))
2842 /* Set one key for next */
2843 keys[3] = ADSI_KEY_APPS + 3;
2845 bytes += adsi_logo(buf + bytes);
2846 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2847 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2848 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2849 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2850 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2851 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2852 bytes += adsi_set_keys(buf + bytes, keys);
2853 bytes += adsi_voice_mode(buf + bytes, 0);
2854 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2857 static void adsi_password(struct ast_channel *chan)
2859 unsigned char buf[256];
2861 unsigned char keys[8];
2863 if (!adsi_available(chan))
2868 /* Set one key for next */
2869 keys[3] = ADSI_KEY_APPS + 3;
2871 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2872 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2873 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2874 bytes += adsi_set_keys(buf + bytes, keys);
2875 bytes += adsi_voice_mode(buf + bytes, 0);
2876 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2879 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2881 unsigned char buf[256];
2883 unsigned char keys[8];
2886 if (!adsi_available(chan))
2890 y = ADSI_KEY_APPS + 12 + start + x;
2891 if (y > ADSI_KEY_APPS + 12 + 4)
2893 keys[x] = ADSI_KEY_SKT | y;
2895 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2899 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2900 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2901 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2902 bytes += adsi_set_keys(buf + bytes, keys);
2903 bytes += adsi_voice_mode(buf + bytes, 0);
2905 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2908 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2911 unsigned char buf[256];
2912 char buf1[256], buf2[256];
2918 char datetime[21]="";
2921 unsigned char keys[8];
2925 if (!adsi_available(chan))
2928 /* Retrieve important info */
2929 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
2930 f = fopen(fn2, "r");
2933 fgets((char *)buf, sizeof(buf), f);
2936 stringp = (char *)buf;
2937 strsep(&stringp, "=");
2938 val = strsep(&stringp, "=");
2939 if (!ast_strlen_zero(val)) {
2940 if (!strcmp((char *)buf, "callerid"))
2941 ast_copy_string(cid, val, sizeof(cid));
2942 if (!strcmp((char *)buf, "origdate"))
2943 ast_copy_string(datetime, val, sizeof(datetime));
2949 /* New meaning for keys */
2951 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2956 /* No prev key, provide "Folder" instead */
2957 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2959 if (vms->curmsg >= vms->lastmsg) {
2960 /* If last message ... */
2962 /* but not only message, provide "Folder" instead */
2963 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2964 bytes += adsi_voice_mode(buf + bytes, 0);
2967 /* Otherwise if only message, leave blank */
2972 if (!ast_strlen_zero(cid)) {
2973 ast_callerid_parse(cid, &name, &num);
2977 name = "Unknown Caller";
2979 /* If deleted, show "undeleted" */
2981 if (vms->deleted[vms->curmsg])
2982 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2985 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2986 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
2987 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
2988 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
2990 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2991 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2992 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2993 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2994 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2995 bytes += adsi_set_keys(buf + bytes, keys);
2996 bytes += adsi_voice_mode(buf + bytes, 0);
2998 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3001 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3004 unsigned char buf[256];
3005 unsigned char keys[8];
3009 if (!adsi_available(chan))
3012 /* New meaning for keys */
3014 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3020 /* No prev key, provide "Folder" instead */
3021 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3023 if (vms->curmsg >= vms->lastmsg) {
3024 /* If last message ... */
3026 /* but not only message, provide "Folder" instead */
3027 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3029 /* Otherwise if only message, leave blank */
3034 /* If deleted, show "undeleted" */
3035 if (vms->deleted[vms->curmsg])
3036 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3039 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3040 bytes += adsi_set_keys(buf + bytes, keys);
3041 bytes += adsi_voice_mode(buf + bytes, 0);
3043 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3046 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
3048 unsigned char buf[256] = "";
3049 char buf1[256] = "", buf2[256] = "";
3051 unsigned char keys[8];
3054 char *newm = (vms->newmessages == 1) ? "message" : "messages";
3055 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
3056 if (!adsi_available(chan))
3058 if (vms->newmessages) {
3059 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
3060 if (vms->oldmessages) {
3061 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
3062 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
3064 snprintf(buf2, sizeof(buf2), "%s.", newm);
3066 } else if (vms->oldmessages) {
3067 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
3068 snprintf(buf2, sizeof(buf2), "%s.", oldm);
3070 strcpy(buf1, "You have no messages.");
3074 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3075 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3076 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3079 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3083 /* Don't let them listen if there are none */
3084 if (vms->lastmsg < 0)
3086 bytes += adsi_set_keys(buf + bytes, keys);
3088 bytes += adsi_voice_mode(buf + bytes, 0);
3090 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3093 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
3095 unsigned char buf[256] = "";
3096 char buf1[256] = "", buf2[256] = "";
3098 unsigned char keys[8];
3101 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
3103 if (!adsi_available(chan))
3106 /* Original command keys */
3108 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3113 if ((vms->lastmsg + 1) < 1)
3116 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
3117 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
3119 if (vms->lastmsg + 1)
3120 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
3122 strcpy(buf2, "no messages.");
3123 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3124 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3125 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
3126 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3127 bytes += adsi_set_keys(buf + bytes, keys);
3129 bytes += adsi_voice_mode(buf + bytes, 0);
3131 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3136 static void adsi_clear(struct ast_channel *chan)
3140 if (!adsi_available(chan))
3142 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3143 bytes += adsi_voice_mode(buf + bytes, 0);
3145 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3149 static void adsi_goodbye(struct ast_channel *chan)
3151 unsigned char buf[256];
3154 if (!adsi_available(chan))
3156 bytes += adsi_logo(buf + bytes);
3157 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
3158 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
3159 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3160 bytes += adsi_voice_mode(buf + bytes, 0);
3162 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3165 /*--- get_folder: Folder menu ---*/
3166 /* Plays "press 1 for INBOX messages" etc
3167 Should possibly be internationalized
3169 static int get_folder(struct ast_channel *chan, int start)
3174 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
3177 for (x = start; x< 5; x++) { /* For all folders */
3178 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
3180 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
3183 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
3184 d = vm_play_folder_name(chan, fn);
3187 d = ast_waitfordigit(chan, 500);
3191 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
3194 d = ast_waitfordigit(chan, 4000);
3198 static int get_folder2(struct ast_channel *chan, char *fn, int start)
3201 res = ast_play_and_wait(chan, fn); /* Folder name */
3202 while (((res < '0') || (res > '9')) &&
3203 (res != '#') && (res >= 0)) {
3204 res = get_folder(chan, 0);
3209 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts,
3210 char *context, signed char record_gain)
3215 signed char zero_gain = 0;
3217 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
3222 /* prepend a message to the current message and return */
3225 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
3227 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
3228 cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
3230 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
3240 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
3241 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3243 cmd = ast_play_and_wait(chan,"vm-starmain");
3244 /* "press star to return to the main menu" */
3246 cmd = ast_waitfordigit(chan,6000);
3258 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
3260 char todir[256], fn[256], ext_context[256], *stringp;
3261 int newmsgs = 0, oldmsgs = 0;
3263 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
3264 make_file(fn, sizeof(fn), todir, msgnum);
3265 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
3267 /* Attach only the first format */
3268 fmt = ast_strdupa(fmt);
3271 strsep(&stringp, "|");
3273 if (!ast_strlen_zero(vmu->email)) {
3274 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3275 char *myserveremail = serveremail;
3276 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
3277 if (!ast_strlen_zero(vmu->serveremail))
3278 myserveremail = vmu->serveremail;
3279 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail);
3282 if (!ast_strlen_zero(vmu->pager)) {
3283 char *myserveremail = serveremail;
3284 if (!ast_strlen_zero(vmu->serveremail))
3285 myserveremail = vmu->serveremail;
3286 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu);
3289 ast_log(LOG_ERROR, "Out of memory\n");
3292 if (ast_test_flag(vmu, VM_DELETE)) {
3293 DELETE(todir, msgnum, fn);
3296 /* Leave voicemail for someone */
3297 if (ast_app_has_voicemail(ext_context, NULL)) {
3298 ast_app_messagecount(ext_context, &newmsgs, &oldmsgs);
3300 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);
3301 run_externnotify(vmu->context, vmu->mailbox);
3305 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender,
3306 char *fmt, int flag, signed char record_gain)
3308 char username[70]="";
3313 struct ast_config *mif;
3317 char ext_context[256]="";
3318 int res = 0, cmd = 0;
3319 struct ast_vm_user *receiver = NULL, *extensions = NULL, *vmtmp = NULL, *vmfree;
3322 int saved_messages = 0, found = 0;
3323 int valid_extensions = 0;
3325 while (!res && !valid_extensions) {
3326 int use_directory = 0;
3327 if(ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
3331 while((cmd >= 0) && !done ){
3348 /* Press 1 to enter an extension press 2 to use the directory */
3349 cmd = ast_play_and_wait(chan,"vm-forward");
3351 cmd = ast_waitfordigit(chan,3000);
3362 if( cmd<0 || cmd=='t' )
3366 if( use_directory ) {
3367 /* use app_directory */
3369 char old_context[sizeof(chan->context)];
3370 char old_exten[sizeof(chan->exten)];
3372 struct ast_app* app;
3375 app = pbx_findapp("Directory");
3377 /* make mackup copies */
3378 memcpy(old_context, chan->context, sizeof(chan->context));
3379 memcpy(old_exten, chan->exten, sizeof(chan->exten));
3380 old_priority = chan->priority;
3382 /* call the the Directory, changes the channel */
3383 res = pbx_exec(chan, app, ((context)?context:chan->context), 1);
3385 ast_copy_string(username, chan->exten, sizeof(username));
3387 /* restore the old context, exten, and priority */
3388 memcpy(chan->context, old_context, sizeof(chan->context));
3389 memcpy(chan->exten, old_exten, sizeof(chan->exten));
3390 chan->priority = old_priority;
3393 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
3394 ast_clear_flag((&globalflags), VM_DIRECFORWARD);
3397 /* Ask for an extension */
3398 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
3401 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
3405 /* start all over if no username */
3406 if (ast_strlen_zero(username))
3409 s = strsep(&stringp, "*");
3410 /* start optimistic */
3411 valid_extensions = 1;
3413 /* find_user is going to malloc since we have a NULL as first argument */
3414 if ((receiver = find_user(NULL, context, s))) {
3416 vmtmp = extensions = receiver;
3418 vmtmp->next = receiver;
3423 valid_extensions = 0;
3426 s = strsep(&stringp, "*");
3428 /* break from the loop of reading the extensions */
3429 if (valid_extensions)
3431 /* "I am sorry, that's not a valid extension. Please try again." */
3432 res = ast_play_and_wait(chan, "pbx-invalid");
3434 /* check if we're clear to proceed */
3435 if (!extensions || !valid_extensions)
3439 struct leave_vm_options leave_options;
3441 /* Send VoiceMail */
3442 memset(&leave_options, 0, sizeof(leave_options));
3443 leave_options.record_gain = record_gain;
3444 cmd = leave_voicemail(chan, username, &leave_options);
3446 /* Forward VoiceMail */
3447 RETRIEVE(dir, curmsg);
3448 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context, record_gain);
3450 while (!res && vmtmp) {
3451 /* if (ast_play_and_wait(chan, "vm-savedto"))
3454 snprintf(todir, sizeof(todir), "%s%s/%s/INBOX", VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
3455 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
3456 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
3457 ast_log(LOG_DEBUG, "%s", sys);
3458 ast_safe_system(sys);