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
23 * \author Mark Spencer <markster@digium.com>
27 * \ingroup applications
31 * 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr)
32 * George Konstantoulakis <gkon@inaccessnetworks.com>
34 * 05-10-2005 : Support for Swedish and Norwegian added by Daniel Nylander, http://www.danielnylander.se/
36 * 05-11-2005 : An option for maximum number of messsages per mailbox added by GDS Partners (www.gdspartners.com)
37 * 07-11-2005 : An issue with voicemail synchronization has been fixed by GDS Partners (www.gdspartners.com)
38 * Stojan Sljivic <stojan.sljivic@gdspartners.com>
50 #include <sys/types.h>
57 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
59 #include "asterisk/lock.h"
60 #include "asterisk/file.h"
61 #include "asterisk/logger.h"
62 #include "asterisk/channel.h"
63 #include "asterisk/pbx.h"
64 #include "asterisk/options.h"
65 #include "asterisk/config.h"
66 #include "asterisk/say.h"
67 #include "asterisk/module.h"
68 #include "asterisk/adsi.h"
69 #include "asterisk/app.h"
70 #include "asterisk/manager.h"
71 #include "asterisk/dsp.h"
72 #include "asterisk/localtime.h"
73 #include "asterisk/cli.h"
74 #include "asterisk/utils.h"
75 #ifdef USE_ODBC_STORAGE
76 #include "asterisk/res_odbc.h"
79 #define COMMAND_TIMEOUT 5000
81 #define VOICEMAIL_CONFIG "voicemail.conf"
82 #define ASTERISK_USERNAME "asterisk"
84 /* Default mail command to mail voicemail. Change it with the
85 mailcmd= command in voicemail.conf */
86 #define SENDMAIL "/usr/sbin/sendmail -t"
88 #define INTRO "vm-intro"
91 #define MAXMSGLIMIT 9999
93 #define BASEMAXINLINE 256
94 #define BASELINELEN 72
95 #define BASEMAXINLINE 256
98 #define MAX_DATETIME_FORMAT 512
99 #define MAX_NUM_CID_CONTEXTS 10
101 #define VM_REVIEW (1 << 0)
102 #define VM_OPERATOR (1 << 1)
103 #define VM_SAYCID (1 << 2)
104 #define VM_SVMAIL (1 << 3)
105 #define VM_ENVELOPE (1 << 4)
106 #define VM_SAYDURATION (1 << 5)
107 #define VM_SKIPAFTERCMD (1 << 6)
108 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
109 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
110 #define VM_PBXSKIP (1 << 9)
111 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
112 #define VM_ATTACH (1 << 11)
113 #define VM_DELETE (1 << 12)
114 #define VM_ALLOCED (1 << 13)
115 #define VM_SEARCH (1 << 14)
117 #define ERROR_LOCK_PATH -100
120 OPT_SILENT = (1 << 0),
121 OPT_BUSY_GREETING = (1 << 1),
122 OPT_UNAVAIL_GREETING = (1 << 2),
123 OPT_RECORDGAIN = (1 << 3),
124 OPT_PREPEND_MAILBOX = (1 << 4),
125 OPT_PRIORITY_JUMP = (1 << 5),
126 OPT_AUTOPLAY = (1 << 6),
130 OPT_ARG_RECORDGAIN = 0,
131 OPT_ARG_ARRAY_SIZE = 1,
132 OPT_ARG_PLAYFOLDER = 2,
135 AST_APP_OPTIONS(vm_app_options, {
136 AST_APP_OPTION('s', OPT_SILENT),
137 AST_APP_OPTION('b', OPT_BUSY_GREETING),
138 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
139 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
140 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
141 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
142 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
145 static int load_config(void);
147 /*! \page vmlang Voicemail Language Syntaxes Supported
149 \par Syntaxes supported, not really language codes.
156 \arg \b pt - Portuguese
158 \arg \b no - Norwegian
161 German requires the following additional soundfile:
162 \arg \b 1F einE (feminine)
164 Spanish requires the following additional soundfile:
165 \arg \b 1M un (masculine)
167 Dutch, Portuguese & Spanish require the following additional soundfiles:
168 \arg \b vm-INBOXs singular of 'new'
169 \arg \b vm-Olds singular of 'old/heard/read'
172 \arg \b vm-INBOX nieuwe (nl)
173 \arg \b vm-Old oude (nl)
176 \arg \b vm-nytt singular of 'new'
177 \arg \b vm-nya plural of 'new'
178 \arg \b vm-gammalt singular of 'old'
179 \arg \b vm-gamla plural of 'old'
180 \arg \b digits/ett 'one', not always same as 'digits/1'
183 \arg \b vm-ny singular of 'new'
184 \arg \b vm-nye plural of 'new'
185 \arg \b vm-gammel singular of 'old'
186 \arg \b vm-gamle plural of 'old'
194 Italian requires the following additional soundfile:
198 \arg \b vm-nuovi new plural
199 \arg \b vm-vecchio old
200 \arg \b vm-vecchi old plural
202 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
203 spelled among others when you have to change folder. For the above reasons, vm-INBOX
204 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
213 unsigned char iobuf[BASEMAXINLINE];
216 /*! Structure for linked list of users */
218 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
219 char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */
220 char password[80]; /*!< Secret pin code, numbers only */
221 char fullname[80]; /*!< Full name, for directory app */
222 char email[80]; /*!< E-mail address */
223 char pager[80]; /*!< E-mail address to pager (no attachment) */
224 char serveremail[80]; /*!< From: Mail address */
225 char mailcmd[160]; /*!< Configurable mail command */
226 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
227 char zonetag[80]; /*!< Time zone */
230 char uniqueid[20]; /*!< Unique integer identifier */
232 unsigned int flags; /*!< VM_ flags */
234 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
235 struct ast_vm_user *next;
241 char msg_format[512];
242 struct vm_zone *next;
261 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
262 int option, signed char record_gain);
263 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
264 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
265 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
266 signed char record_gain);
267 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
268 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
270 static void apply_options(struct ast_vm_user *vmu, const char *options);
272 #ifdef USE_ODBC_STORAGE
273 static char odbc_database[80];
274 static char odbc_table[80];
275 #define RETRIEVE(a,b) retrieve_file(a,b)
276 #define DISPOSE(a,b) remove_file(a,b)
277 #define STORE(a,b,c,d) store_file(a,b,c,d)
278 #define EXISTS(a,b,c,d) (message_exists(a,b))
279 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
280 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
281 #define DELETE(a,b,c) (delete_file(a,b))
283 #define RETRIEVE(a,b)
285 #define STORE(a,b,c,d)
286 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
287 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
288 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
289 #define DELETE(a,b,c) (vm_delete(c))
292 static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
294 static char ext_pass_cmd[128];
296 static char *tdesc = "Comedian Mail (Voicemail System)";
298 static char *addesc = "Comedian Mail";
300 static char *synopsis_vm =
301 "Leave a Voicemail message";
303 static char *descrip_vm =
304 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
305 "application allows the calling party to leave a message for the specified\n"
306 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
307 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
308 "specified mailbox does not exist.\n"
309 " The Voicemail application will exit if any of the following DTMF digits are\n"
311 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
312 " * - Jump to the 'a' extension in the current dialplan context.\n"
313 " This application will set the following channel variable upon completion:\n"
314 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
315 " application. The possible values are:\n"
316 " SUCCESS | USEREXIT | FAILED\n\n"
318 " b - Play the 'busy' greeting to the calling party.\n"
319 " g(#) - Use the specified amount of gain when recording the voicemail\n"
320 " message. The units are whole-number decibels (dB).\n"
321 " s - Skip the playback of instructions for leaving a message to the\n"
323 " u - Play the 'unavailble greeting.\n"
324 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
327 static char *synopsis_vmain =
328 "Check Voicemail messages";
330 static char *descrip_vmain =
331 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
332 "calling party to check voicemail messages. A specific mailbox, and optional\n"
333 "corresponding context, may be specified. If a mailbox is not provided, the\n"
334 "calling party will be prompted to enter one. If a context is not specified,\n"
335 "the 'default' context will be used.\n\n"
337 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
338 " is entered by the caller.\n"
339 " g(#) - Use the specified amount of gain when recording a voicemail\n"
340 " message. The units are whole-number decibels (dB).\n"
341 " s - Skip checking the passcode for the mailbox.\n"
342 " a(#) - Skip folder prompt and go directly to folder specified, default 1\n";
344 static char *synopsis_vm_box_exists =
345 "Check to see if Voicemail mailbox exists";
347 static char *descrip_vm_box_exists =
348 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
349 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
351 " This application will set the following channel variable upon completion:\n"
352 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
353 " MailboxExists application. Possible values include:\n"
354 " SUCCESS | FAILED\n\n"
356 " j - Jump to priority n+101 if the mailbox is found.\n";
358 static char *synopsis_vmauthenticate =
359 "Authenticate with Voicemail passwords";
361 static char *descrip_vmauthenticate =
362 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
363 "same way as the Authenticate application, but the passwords are taken from\n"
365 " If the mailbox is specified, only that mailbox's password will be considered\n"
366 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
367 "be set with the authenticated mailbox.\n\n"
369 " s - Skip playing the initial prompts.\n";
371 /* Leave a message */
372 static char *app = "VoiceMail";
374 /* Check mail, control, etc */
375 static char *app2 = "VoiceMailMain";
377 static char *app3 = "MailboxExists";
378 static char *app4 = "VMAuthenticate";
380 AST_MUTEX_DEFINE_STATIC(vmlock);
381 struct ast_vm_user *users;
382 struct ast_vm_user *usersl;
383 struct vm_zone *zones = NULL;
384 struct vm_zone *zonesl = NULL;
385 static int maxsilence;
387 static int silencethreshold = 128;
388 static char serveremail[80];
389 static char mailcmd[160]; /* Configurable mail cmd */
390 static char externnotify[160];
392 static char vmfmts[80];
393 static int vmminmessage;
394 static int vmmaxmessage;
397 static int maxlogins;
399 static struct ast_flags globalflags = {0};
401 static int saydurationminfo;
403 static char dialcontext[AST_MAX_CONTEXT];
404 static char callcontext[AST_MAX_CONTEXT];
405 static char exitcontext[AST_MAX_CONTEXT];
407 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
410 static char *emailbody = NULL;
411 static char *emailsubject = NULL;
412 static char *pagerbody = NULL;
413 static char *pagersubject = NULL;
414 static char fromstring[100];
415 static char pagerfromstring[100];
416 static char emailtitle[100];
417 static char charset[32] = "ISO-8859-1";
419 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
420 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
421 static int adsiver = 1;
422 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
428 static void populate_defaults(struct ast_vm_user *vmu)
430 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
431 if (saydurationminfo)
432 vmu->saydurationm = saydurationminfo;
434 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
436 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
438 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
440 vmu->maxmsg = maxmsg;
443 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
446 if (!strcasecmp(var, "attach")) {
447 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
448 } else if (!strcasecmp(var, "serveremail")) {
449 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
450 } else if (!strcasecmp(var, "language")) {
451 ast_copy_string(vmu->language, value, sizeof(vmu->language));
452 } else if (!strcasecmp(var, "tz")) {
453 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
454 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
455 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
456 } else if (!strcasecmp(var, "saycid")){
457 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
458 } else if (!strcasecmp(var,"sendvoicemail")){
459 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
460 } else if (!strcasecmp(var, "review")){
461 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
462 } else if (!strcasecmp(var, "operator")){
463 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
464 } else if (!strcasecmp(var, "envelope")){
465 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
466 } else if (!strcasecmp(var, "sayduration")){
467 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
468 } else if (!strcasecmp(var, "saydurationm")){
469 if (sscanf(value, "%d", &x) == 1) {
470 vmu->saydurationm = x;
472 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
474 } else if (!strcasecmp(var, "forcename")){
475 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
476 } else if (!strcasecmp(var, "forcegreetings")){
477 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
478 } else if (!strcasecmp(var, "callback")) {
479 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
480 } else if (!strcasecmp(var, "dialout")) {
481 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
482 } else if (!strcasecmp(var, "exitcontext")) {
483 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
484 } else if (!strcasecmp(var, "maxmsg")) {
485 vmu->maxmsg = atoi(value);
486 if (vmu->maxmsg <= 0) {
487 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
488 vmu->maxmsg = MAXMSG;
489 } else if (vmu->maxmsg > MAXMSGLIMIT) {
490 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
491 vmu->maxmsg = MAXMSGLIMIT;
493 } else if (!strcasecmp(var, "options")) {
494 apply_options(vmu, value);
498 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
501 if (!ast_strlen_zero(vmu->uniqueid)) {
502 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
504 ast_copy_string(vmu->password, password, sizeof(vmu->password));
514 static void apply_options(struct ast_vm_user *vmu, const char *options)
515 { /* Destructively Parse options and apply */
519 stringp = ast_strdupa(options);
520 while ((s = strsep(&stringp, "|"))) {
522 if ((var = strsep(&value, "=")) && value) {
523 apply_option(vmu, var, value);
528 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
530 struct ast_variable *var, *tmp;
531 struct ast_vm_user *retval;
536 retval=malloc(sizeof(struct ast_vm_user));
539 memset(retval, 0, sizeof(struct ast_vm_user));
541 ast_set_flag(retval, VM_ALLOCED);
543 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
544 populate_defaults(retval);
545 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
546 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
548 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
552 printf("%s => %s\n", tmp->name, tmp->value);
553 if (!strcasecmp(tmp->name, "password")) {
554 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
555 } else if (!strcasecmp(tmp->name, "uniqueid")) {
556 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
557 } else if (!strcasecmp(tmp->name, "pager")) {
558 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
559 } else if (!strcasecmp(tmp->name, "email")) {
560 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
561 } else if (!strcasecmp(tmp->name, "fullname")) {
562 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
563 } else if (!strcasecmp(tmp->name, "context")) {
564 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
566 apply_option(retval, tmp->name, tmp->value);
578 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
580 /* This function could be made to generate one from a database, too */
581 struct ast_vm_user *vmu=NULL, *cur;
582 ast_mutex_lock(&vmlock);
585 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
589 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
591 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
599 /* Make a copy, so that on a reload, we have no race */
600 vmu = malloc(sizeof(struct ast_vm_user));
602 memcpy(vmu, cur, sizeof(struct ast_vm_user));
603 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
607 vmu = find_user_realtime(ivm, context, mailbox);
608 ast_mutex_unlock(&vmlock);
612 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
614 /* This function could be made to generate one from a database, too */
615 struct ast_vm_user *cur;
617 ast_mutex_lock(&vmlock);
620 if ((!context || !strcasecmp(context, cur->context)) &&
621 (!strcasecmp(mailbox, cur->mailbox)))
626 ast_copy_string(cur->password, newpass, sizeof(cur->password));
629 ast_mutex_unlock(&vmlock);
633 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
635 /* There's probably a better way of doing this. */
636 /* That's why I've put the password change in a separate function. */
637 /* This could also be done with a database function */
644 char currcontext[256] ="";
645 char tmpin[AST_CONFIG_MAX_PATH];
646 char tmpout[AST_CONFIG_MAX_PATH];
649 if (!change_password_realtime(vmu, newpassword))
652 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
653 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
654 configin = fopen(tmpin,"r");
656 configout = fopen(tmpout,"w+");
659 if (!configin || !configout) {
663 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
667 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
671 while (!feof(configin)) {
672 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
674 /* Read in the line */
675 fgets(inbuf, sizeof(inbuf), configin);
678 if (ast_strlen_zero(inbuf)) {
679 fprintf(configout, "\n");
683 /* Make a backup of it */
684 ast_copy_string(orig, inbuf, sizeof(orig));
687 Read the file line by line, split each line into a comment and command section
688 only parse the command portion of the line
690 if (inbuf[strlen(inbuf) - 1] == '\n')
691 inbuf[strlen(inbuf) - 1] = '\0';
693 if ((comment = strchr(inbuf, ';')))
694 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
696 if (ast_strlen_zero(inbuf)) {
697 fprintf(configout, "%s", orig);
701 /* Check for a context, first '[' to first ']' */
702 if ((tmpctx = strchr(inbuf, '['))) {
703 tmpctxend = strchr(tmpctx, ']');
706 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
707 fprintf(configout, "%s", orig);
712 /* This isn't a context line, check for MBX => PSWD... */
714 if ((pass = strchr(user, '='))) {
715 /* We have a line in the form of aaaaa=aaaaaa */
718 user = ast_strip(user);
723 pass = ast_skip_blanks(pass);
726 Since no whitespace allowed in fields, or more correctly white space
727 inside the fields is there for a purpose, we can just terminate pass
728 at the comma or EOL whichever comes first.
730 if ((rest = strchr(pass, ',')))
736 /* Compare user, pass AND context */
737 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
738 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
739 !strcasecmp(currcontext, vmu->context)) {
740 /* This is the line */
742 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
744 fprintf(configout, "%s => %s", user, newpassword);
746 /* If there was a comment on the line print it out */
748 fprintf(configout, ";%s\n", comment);
750 fprintf(configout, "\n");
753 /* Put it back like it was */
754 fprintf(configout, "%s", orig);
760 stat(tmpin, &statbuf);
761 chmod(tmpout, statbuf.st_mode);
762 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
764 rename(tmpout, tmpin);
765 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
766 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
769 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
772 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
773 if (!ast_safe_system(buf))
774 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
777 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
779 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, mailbox);
782 static int make_file(char *dest, int len, char *dir, int num)
784 return snprintf(dest, len, "%s/msg%04d", dir, num);
787 /* only return failure if ast_lock_path returns 'timeout',
788 not if the path does not exist or any other reason
790 static int vm_lock_path(const char *path)
792 switch (ast_lock_path(path)) {
793 case AST_LOCK_TIMEOUT:
801 #ifdef USE_ODBC_STORAGE
802 static int retrieve_file(char *dir, int msgnum)
809 SQLSMALLINT colcount=0;
816 SQLSMALLINT datatype;
817 SQLSMALLINT decimaldigits;
818 SQLSMALLINT nullable;
827 obj = fetch_odbc_obj(odbc_database, 0);
829 ast_copy_string(fmt, vmfmts, sizeof(fmt));
830 c = strchr(fmt, '|');
833 if (!strcasecmp(fmt, "wav49"))
835 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
837 make_file(fn, sizeof(fn), dir, msgnum);
839 ast_copy_string(fn, dir, sizeof(fn));
840 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
841 f = fopen(full_fn, "w+");
842 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
843 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
844 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
845 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
848 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
849 res = SQLPrepare(stmt, sql, SQL_NTS);
850 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
851 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
852 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
855 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
856 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
857 res = odbc_smart_execute(obj, stmt);
858 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
859 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
860 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
863 res = SQLFetch(stmt);
864 if (res == SQL_NO_DATA) {
865 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
868 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
869 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
870 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
873 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
875 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
876 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
879 res = SQLNumResultCols(stmt, &colcount);
880 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
881 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
882 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
886 fprintf(f, "[message]\n");
887 for (x=0;x<colcount;x++) {
889 collen = sizeof(coltitle);
890 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
891 &datatype, &colsize, &decimaldigits, &nullable);
892 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
893 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
894 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
897 if (!strcasecmp(coltitle, "recording")) {
898 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
900 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
903 lseek(fd, fdlen - 1, SEEK_SET);
904 if (write(fd, tmp, 1) != 1) {
909 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
912 memset(fdm, 0, fdlen);
913 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
914 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
915 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
916 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
921 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
922 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
923 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
924 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
927 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
928 fprintf(f, "%s=%s\n", coltitle, rowdata);
931 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
933 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
944 static int remove_file(char *dir, int msgnum)
951 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
952 make_file(fn, sizeof(fn), dir, msgnum);
954 ast_copy_string(fn, dir, sizeof(fn));
955 ast_filedelete(fn, NULL);
956 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
961 static int last_message_index(struct ast_vm_user *vmu, char *dir)
970 obj = fetch_odbc_obj(odbc_database, 0);
972 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
973 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
974 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
977 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
978 res = SQLPrepare(stmt, sql, SQL_NTS);
979 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
980 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
981 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
984 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
985 res = odbc_smart_execute(obj, stmt);
986 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
987 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
988 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
991 res = SQLFetch(stmt);
992 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
993 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
994 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
997 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
998 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
999 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1000 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1003 if (sscanf(rowdata, "%d", &x) != 1)
1004 ast_log(LOG_WARNING, "Failed to read message count!\n");
1005 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1007 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1012 static int message_exists(char *dir, int msgnum)
1022 obj = fetch_odbc_obj(odbc_database, 0);
1024 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1025 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1026 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1027 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1030 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1031 res = SQLPrepare(stmt, sql, SQL_NTS);
1032 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1033 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1034 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1037 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1038 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1039 res = odbc_smart_execute(obj, stmt);
1040 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1041 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1042 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1045 res = SQLFetch(stmt);
1046 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1047 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1048 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1051 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1052 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1053 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1054 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1057 if (sscanf(rowdata, "%d", &x) != 1)
1058 ast_log(LOG_WARNING, "Failed to read message count!\n");
1059 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1061 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1066 static int count_messages(struct ast_vm_user *vmu, char *dir)
1068 return last_message_index(vmu, dir) + 1;
1071 static void delete_file(char *sdir, int smsg)
1079 obj = fetch_odbc_obj(odbc_database, 0);
1081 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1082 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1083 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1084 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1087 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1088 res = SQLPrepare(stmt, sql, SQL_NTS);
1089 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1090 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1091 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1094 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1095 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1096 res = odbc_smart_execute(obj, stmt);
1097 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1098 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1099 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1102 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1104 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1109 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1118 delete_file(ddir, dmsg);
1119 obj = fetch_odbc_obj(odbc_database, 0);
1121 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1122 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1123 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1124 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1125 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1128 #ifdef EXTENDED_ODBC_STORAGE
1129 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);
1131 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);
1133 res = SQLPrepare(stmt, sql, SQL_NTS);
1134 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1135 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1136 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1139 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1140 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1141 #ifdef EXTENDED_ODBC_STORAGE
1142 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1143 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1144 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1145 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1147 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1148 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1150 res = odbc_smart_execute(obj, stmt);
1151 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1152 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1153 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1156 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1158 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1163 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1178 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1179 char *category = "";
1180 struct ast_config *cfg=NULL;
1183 delete_file(dir, msgnum);
1184 obj = fetch_odbc_obj(odbc_database, 0);
1186 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1187 c = strchr(fmt, '|');
1190 if (!strcasecmp(fmt, "wav49"))
1192 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1194 make_file(fn, sizeof(fn), dir, msgnum);
1196 ast_copy_string(fn, dir, sizeof(fn));
1197 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1198 cfg = ast_config_load(full_fn);
1199 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1200 fd = open(full_fn, O_RDWR);
1202 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1206 context = ast_variable_retrieve(cfg, "message", "context");
1207 if (!context) context = "";
1208 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1209 if (!macrocontext) macrocontext = "";
1210 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1211 if (!callerid) callerid = "";
1212 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1213 if (!origtime) origtime = "";
1214 duration = ast_variable_retrieve(cfg, "message", "duration");
1215 if (!duration) duration = "";
1216 category = ast_variable_retrieve(cfg, "message", "category");
1217 if (!category) category = "";
1219 fdlen = lseek(fd, 0, SEEK_END);
1220 lseek(fd, 0, SEEK_SET);
1221 printf("Length is %d\n", fdlen);
1222 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1224 ast_log(LOG_WARNING, "Memory map failed!\n");
1227 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1228 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1229 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1232 if (!ast_strlen_zero(category))
1233 #ifdef EXTENDED_ODBC_STORAGE
1234 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1236 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
1239 #ifdef EXTENDED_ODBC_STORAGE
1240 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1242 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
1244 res = SQLPrepare(stmt, sql, SQL_NTS);
1245 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1246 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1247 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1250 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1251 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1252 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1253 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1254 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1255 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1256 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1257 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1258 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1259 #ifdef EXTENDED_ODBC_STORAGE
1260 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1261 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1262 if (!ast_strlen_zero(category))
1263 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1265 if (!ast_strlen_zero(category))
1266 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1268 res = odbc_smart_execute(obj, stmt);
1269 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1270 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1271 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1274 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1276 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1279 ast_config_destroy(cfg);
1287 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1296 delete_file(ddir, dmsg);
1297 obj = fetch_odbc_obj(odbc_database, 0);
1299 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1300 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1301 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1302 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1303 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1306 #ifdef EXTENDED_ODBC_STORAGE
1307 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1309 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
1311 res = SQLPrepare(stmt, sql, SQL_NTS);
1312 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1313 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1314 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1317 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1318 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1319 #ifdef EXTENDED_ODBC_STORAGE
1320 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1321 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1322 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1323 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1325 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1326 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1328 res = odbc_smart_execute(obj, stmt);
1329 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1330 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1331 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1334 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1336 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1343 static int count_messages(struct ast_vm_user *vmu, char *dir)
1345 /* Find all .txt files - even if they are not in sequence from 0000 */
1349 struct dirent *vment = NULL;
1351 if (vm_lock_path(dir))
1352 return ERROR_LOCK_PATH;
1354 if ((vmdir = opendir(dir))) {
1355 while ((vment = readdir(vmdir))) {
1356 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1361 ast_unlock_path(dir);
1366 static void rename_file(char *sfn, char *dfn)
1370 ast_filerename(sfn,dfn,NULL);
1371 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1372 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1376 static int copy(char *infile, char *outfile)
1384 #ifdef HARDLINK_WHEN_POSSIBLE
1385 /* Hard link if possible; saves disk space & is faster */
1386 if (link(infile, outfile)) {
1388 if ((ifd = open(infile, O_RDONLY)) < 0) {
1389 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1392 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1393 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1398 len = read(ifd, buf, sizeof(buf));
1400 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1406 res = write(ofd, buf, len);
1407 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1408 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1418 #ifdef HARDLINK_WHEN_POSSIBLE
1420 /* Hard link succeeded */
1426 static void copy_file(char *frompath, char *topath)
1428 char frompath2[256],topath2[256];
1429 ast_filecopy(frompath, topath, NULL);
1430 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1431 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1432 copy(frompath2, topath2);
1436 * A negative return value indicates an error.
1438 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1443 if (vm_lock_path(dir))
1444 return ERROR_LOCK_PATH;
1446 for (x = 0; x < vmu->maxmsg; x++) {
1447 make_file(fn, sizeof(fn), dir, x);
1448 if (ast_fileexists(fn, NULL, NULL) < 1)
1451 ast_unlock_path(dir);
1456 static int vm_delete(char *file)
1461 txtsize = (strlen(file) + 5)*sizeof(char);
1462 txt = (char *)alloca(txtsize);
1463 /* Sprintf here would safe because we alloca'd exactly the right length,
1464 * but trying to eliminate all sprintf's anyhow
1466 snprintf(txt, txtsize, "%s.txt", file);
1468 return ast_filedelete(file, NULL);
1474 inbuf(struct baseio *bio, FILE *fi)
1481 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1496 inchar(struct baseio *bio, FILE *fi)
1498 if (bio->iocp>=bio->iolen) {
1499 if (!inbuf(bio, fi))
1503 return bio->iobuf[bio->iocp++];
1507 ochar(struct baseio *bio, int c, FILE *so)
1509 if (bio->linelength>=BASELINELEN) {
1510 if (fputs(eol,so)==EOF)
1516 if (putc(((unsigned char)c),so)==EOF)
1524 static int base_encode(char *filename, FILE *so)
1526 unsigned char dtable[BASEMAXINLINE];
1531 memset(&bio, 0, sizeof(bio));
1532 bio.iocp = BASEMAXINLINE;
1534 if (!(fi = fopen(filename, "rb"))) {
1535 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1539 for (i= 0;i<9;i++) {
1542 dtable[26+i]= 'a'+i;
1543 dtable[26+i+9]= 'j'+i;
1545 for (i= 0;i<8;i++) {
1546 dtable[i+18]= 'S'+i;
1547 dtable[26+i+18]= 's'+i;
1549 for (i= 0;i<10;i++) {
1550 dtable[52+i]= '0'+i;
1556 unsigned char igroup[3],ogroup[4];
1559 igroup[0]= igroup[1]= igroup[2]= 0;
1561 for (n= 0;n<3;n++) {
1562 if ((c = inchar(&bio, fi)) == EOF) {
1567 igroup[n]= (unsigned char)c;
1571 ogroup[0]= dtable[igroup[0]>>2];
1572 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1573 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1574 ogroup[3]= dtable[igroup[2]&0x3F];
1584 ochar(&bio, ogroup[i], so);
1588 if (fputs(eol,so)==EOF)
1596 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, const char *category)
1599 /* Prepare variables for substition in email body and subject */
1600 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1601 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1602 snprintf(passdata, passdatasize, "%d", msgnum);
1603 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1604 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1605 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1606 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1607 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1608 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1609 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1610 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1614 * fill in *tm for current time according to the proper timezone, if any.
1615 * Return tm so it can be used as a function argument.
1617 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1619 const struct vm_zone *z = NULL;
1620 time_t t = time(NULL);
1622 /* Does this user have a timezone specified? */
1623 if (!ast_strlen_zero(vmu->zonetag)) {
1624 /* Find the zone in the list */
1625 for (z = zones; z ; z = z->next)
1626 if (!strcmp(z->name, vmu->zonetag))
1629 ast_localtime(&t, tm, z ? z->timezone : NULL);
1633 /* same as mkstemp, but return a FILE * */
1634 static FILE *vm_mkftemp(char *template)
1637 int pfd = mkstemp(template);
1639 p = fdopen(pfd, "w");
1648 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, const char *category)
1652 char host[MAXHOSTNAMELEN] = "";
1657 char tmp[80] = "/tmp/astmail-XXXXXX";
1661 if (vmu && ast_strlen_zero(vmu->email)) {
1662 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1665 if (!strcmp(format, "wav49"))
1667 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));
1668 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1670 p = vm_mkftemp(tmp);
1672 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1675 gethostname(host, sizeof(host)-1);
1676 if (strchr(srcemail, '@'))
1677 ast_copy_string(who, srcemail, sizeof(who));
1679 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1681 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1682 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1683 fprintf(p, "Date: %s\n", date);
1685 /* Set date format for voicemail mail */
1686 strftime(date, sizeof(date), emaildateformat, &tm);
1689 struct ast_channel *ast = ast_channel_alloc(0);
1692 int vmlen = strlen(fromstring)*3 + 200;
1693 if ((passdata = alloca(vmlen))) {
1694 memset(passdata, 0, vmlen);
1695 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1696 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1697 fprintf(p, "From: %s <%s>\n",passdata,who);
1698 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1699 ast_channel_free(ast);
1700 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1702 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1703 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1706 struct ast_channel *ast = ast_channel_alloc(0);
1709 int vmlen = strlen(emailsubject)*3 + 200;
1710 if ((passdata = alloca(vmlen))) {
1711 memset(passdata, 0, vmlen);
1712 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1713 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1714 fprintf(p, "Subject: %s\n",passdata);
1715 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1716 ast_channel_free(ast);
1717 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1720 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1722 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1723 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1725 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1726 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
1727 fprintf(p, "MIME-Version: 1.0\n");
1728 if (attach_user_voicemail) {
1729 /* Something unique. */
1730 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
1732 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1734 fprintf(p, "--%s\n", bound);
1736 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1738 struct ast_channel *ast = ast_channel_alloc(0);
1741 int vmlen = strlen(emailbody)*3 + 200;
1742 if ((passdata = alloca(vmlen))) {
1743 memset(passdata, 0, vmlen);
1744 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1745 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1746 fprintf(p, "%s\n",passdata);
1747 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1748 ast_channel_free(ast);
1749 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1751 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1753 "in mailbox %s from %s, on %s so you might\n"
1754 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1755 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1757 if (attach_user_voicemail) {
1758 /* Eww. We want formats to tell us their own MIME type */
1759 char *ctype = "audio/x-";
1760 if (!strcasecmp(format, "ogg"))
1761 ctype = "application/";
1763 fprintf(p, "--%s\n", bound);
1764 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1765 fprintf(p, "Content-Transfer-Encoding: base64\n");
1766 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1767 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1769 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1770 base_encode(fname, p);
1771 fprintf(p, "\n\n--%s--\n.\n", bound);
1774 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1775 ast_safe_system(tmp2);
1776 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1781 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category)
1784 char host[MAXHOSTNAMELEN]="";
1787 char tmp[80] = "/tmp/astmail-XXXXXX";
1790 FILE *p = vm_mkftemp(tmp);
1793 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1796 gethostname(host, sizeof(host)-1);
1797 if (strchr(srcemail, '@'))
1798 ast_copy_string(who, srcemail, sizeof(who));
1800 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1802 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1803 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1804 fprintf(p, "Date: %s\n", date);
1806 if (*pagerfromstring) {
1807 struct ast_channel *ast = ast_channel_alloc(0);
1810 int vmlen = strlen(fromstring)*3 + 200;
1811 if ((passdata = alloca(vmlen))) {
1812 memset(passdata, 0, vmlen);
1813 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1814 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1815 fprintf(p, "From: %s <%s>\n",passdata,who);
1817 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1818 ast_channel_free(ast);
1819 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1821 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1822 fprintf(p, "To: %s\n", pager);
1824 struct ast_channel *ast = ast_channel_alloc(0);
1827 int vmlen = strlen(pagersubject)*3 + 200;
1828 if ((passdata = alloca(vmlen))) {
1829 memset(passdata, 0, vmlen);
1830 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1831 pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
1832 fprintf(p, "Subject: %s\n\n",passdata);
1833 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1834 ast_channel_free(ast);
1835 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1837 fprintf(p, "Subject: New VM\n\n");
1838 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1840 struct ast_channel *ast = ast_channel_alloc(0);
1843 int vmlen = strlen(pagerbody)*3 + 200;
1844 if ((passdata = alloca(vmlen))) {
1845 memset(passdata, 0, vmlen);
1846 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1847 pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
1848 fprintf(p, "%s\n",passdata);
1849 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1850 ast_channel_free(ast);
1851 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1853 fprintf(p, "New %s long msg in box %s\n"
1854 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1857 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1858 ast_safe_system(tmp2);
1859 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1864 static int get_date(char *s, int len)
1869 localtime_r(&t,&tm);
1870 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1873 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1877 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
1879 if (ast_fileexists(fn, NULL, NULL) > 0) {
1880 res = ast_streamfile(chan, fn, chan->language);
1885 res = ast_waitstream(chan, ecodes);
1891 /* Dispose just in case */
1893 res = ast_streamfile(chan, "vm-theperson", chan->language);
1896 res = ast_waitstream(chan, ecodes);
1899 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1904 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1906 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1909 res = ast_waitstream(chan, ecodes);
1913 static void free_user(struct ast_vm_user *vmu)
1915 if (ast_test_flag(vmu, VM_ALLOCED))
1919 static void free_zone(struct vm_zone *z)
1924 static char *mbox(int id)
1952 #ifdef USE_ODBC_STORAGE
1953 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
1967 /* If no mailbox, return immediately */
1968 if (ast_strlen_zero(mailbox))
1971 ast_copy_string(tmp, mailbox, sizeof(tmp));
1973 context = strchr(tmp, '@');
1978 context = "default";
1981 obj = fetch_odbc_obj(odbc_database, 0);
1983 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1984 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1985 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1988 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir LIKE '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
1989 res = SQLPrepare(stmt, sql, SQL_NTS);
1990 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1991 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1992 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1995 res = odbc_smart_execute(obj, stmt);
1996 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1997 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1998 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2001 res = SQLFetch(stmt);
2002 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2003 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2004 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2007 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2008 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2009 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2010 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2013 *newmsgs = atoi(rowdata);
2014 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2016 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2017 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2018 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2021 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "Old");
2022 res = SQLPrepare(stmt, sql, SQL_NTS);
2023 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2024 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2025 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2028 res = odbc_smart_execute(obj, stmt);
2029 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2030 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2031 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2034 res = SQLFetch(stmt);
2035 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2036 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2037 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2040 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2041 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2042 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2043 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2046 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2047 *oldmsgs = atoi(rowdata);
2050 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2056 static int has_voicemail(const char *mailbox, const char *folder)
2067 /* If no mailbox, return immediately */
2068 if (ast_strlen_zero(mailbox))
2071 ast_copy_string(tmp, mailbox, sizeof(tmp));
2073 context = strchr(tmp, '@');
2078 context = "default";
2081 obj = fetch_odbc_obj(odbc_database, 0);
2083 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2084 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2085 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2088 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2089 res = SQLPrepare(stmt, sql, SQL_NTS);
2090 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2091 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2092 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2095 res = odbc_smart_execute(obj, stmt);
2096 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2097 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2098 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2101 res = SQLFetch(stmt);
2102 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2103 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2104 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2107 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2108 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2109 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2110 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2113 nummsgs = atoi(rowdata);
2114 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2116 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2127 static int has_voicemail(const char *mailbox, const char *folder)
2138 /* If no mailbox, return immediately */
2139 if (ast_strlen_zero(mailbox))
2141 if (strchr(mailbox, ',')) {
2142 ast_copy_string(tmp, mailbox, sizeof(tmp));
2145 while((cur = strsep(&mb, ","))) {
2146 if (!ast_strlen_zero(cur)) {
2147 if (has_voicemail(cur, folder))
2153 ast_copy_string(tmp, mailbox, sizeof(tmp));
2154 context = strchr(tmp, '@');
2159 context = "default";
2160 snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
2164 while ((de = readdir(dir))) {
2165 if (!strncasecmp(de->d_name, "msg", 3))
2175 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2188 /* If no mailbox, return immediately */
2189 if (ast_strlen_zero(mailbox))
2191 if (strchr(mailbox, ',')) {
2193 ast_copy_string(tmp, mailbox, sizeof(tmp));
2196 while((cur = strsep(&mb, ", "))) {
2197 if (!ast_strlen_zero(cur)) {
2198 if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2210 ast_copy_string(tmp, mailbox, sizeof(tmp));
2211 context = strchr(tmp, '@');
2216 context = "default";
2218 snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
2221 while ((de = readdir(dir))) {
2222 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2223 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2231 snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
2234 while ((de = readdir(dir))) {
2235 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2236 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2248 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
2250 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)
2252 char fromdir[256], todir[256], frompath[256], topath[256];
2253 char *frombox = mbox(imbox);
2256 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2258 make_dir(todir, sizeof(todir), recip->context, "", "");
2259 /* It's easier just to try to make it than to check for its existence */
2260 if (mkdir(todir, 0700) && (errno != EEXIST))
2261 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
2262 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "");
2263 /* It's easier just to try to make it than to check for its existence */
2264 if (mkdir(todir, 0700) && (errno != EEXIST))
2265 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
2266 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2267 if (mkdir(todir, 0700) && (errno != EEXIST))
2268 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
2270 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2271 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2273 if (vm_lock_path(todir))
2274 return ERROR_LOCK_PATH;
2278 make_file(topath, sizeof(topath), todir, recipmsgnum);
2279 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2282 } while (recipmsgnum < recip->maxmsg);
2283 if (recipmsgnum < recip->maxmsg) {
2284 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2286 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2288 ast_unlock_path(todir);
2289 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2294 static void run_externnotify(char *context, char *extension)
2296 char arguments[255];
2297 char ext_context[256] = "";
2298 int newvoicemails = 0, oldvoicemails = 0;
2300 if (!ast_strlen_zero(context))
2301 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2303 ast_copy_string(ext_context, extension, sizeof(ext_context));
2305 if (!ast_strlen_zero(externnotify)) {
2306 if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
2307 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2309 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2310 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2311 ast_safe_system(arguments);
2316 struct leave_vm_options {
2318 signed char record_gain;
2321 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2334 char prefile[256]="";
2335 char tempfile[256]="";
2336 char ext_context[256] = "";
2339 char ecodes[16] = "#";
2340 char tmp[256] = "", *tmpptr;
2341 struct ast_vm_user *vmu;
2342 struct ast_vm_user svm;
2343 const char *category = NULL;
2345 ast_copy_string(tmp, ext, sizeof(tmp));
2347 context = strchr(tmp, '@');
2351 tmpptr = strchr(context, '&');
2353 tmpptr = strchr(ext, '&');
2361 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2363 if (!(vmu = find_user(&svm, context, ext))) {
2364 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2365 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2366 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2367 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2371 /* Setup pre-file if appropriate */
2372 if (strcmp(vmu->context, "default"))
2373 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2375 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2376 if (ast_test_flag(options, OPT_BUSY_GREETING))
2377 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2378 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2379 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2380 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2381 RETRIEVE(tempfile, -1);
2382 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2383 ast_copy_string(prefile, tempfile, sizeof(prefile));
2384 DISPOSE(tempfile, -1);
2385 make_dir(dir, sizeof(dir), vmu->context, "", "");
2386 /* It's easier just to try to make it than to check for its existence */
2387 if (mkdir(dir, 0700) && (errno != EEXIST))
2388 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2389 make_dir(dir, sizeof(dir), vmu->context, ext, "");
2390 /* It's easier just to try to make it than to check for its existence */
2391 if (mkdir(dir, 0700) && (errno != EEXIST))
2392 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2393 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
2394 if (mkdir(dir, 0700) && (errno != EEXIST))
2395 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2397 /* Check current or macro-calling context for special extensions */
2398 if (!ast_strlen_zero(vmu->exit)) {
2399 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2400 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2401 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
2402 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2403 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2404 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2408 if (!ast_strlen_zero(vmu->exit)) {
2409 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2410 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2411 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2412 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2413 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2414 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2418 /* Play the beginning intro if desired */
2419 if (!ast_strlen_zero(prefile)) {
2420 RETRIEVE(prefile, -1);
2421 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2422 if (ast_streamfile(chan, prefile, chan->language) > -1)
2423 res = ast_waitstream(chan, ecodes);
2425 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2426 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2428 DISPOSE(prefile, -1);
2430 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2432 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2437 /* On a '#' we skip the instructions */
2438 ast_set_flag(options, OPT_SILENT);
2441 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2442 res = ast_streamfile(chan, INTRO, chan->language);
2444 res = ast_waitstream(chan, ecodes);
2446 ast_set_flag(options, OPT_SILENT);
2451 ast_stopstream(chan);
2452 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2453 other than the operator -- an automated attendant or mailbox login for example */
2455 chan->exten[0] = 'a';
2456 chan->exten[1] = '\0';
2457 if (!ast_strlen_zero(vmu->exit)) {
2458 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2459 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2460 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2464 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2467 /* Check for a '0' here */
2470 if (ast_test_flag(vmu, VM_OPERATOR)) {
2471 chan->exten[0] = 'o';
2472 chan->exten[1] = '\0';
2473 if (!ast_strlen_zero(vmu->exit)) {
2474 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2475 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2476 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2478 ast_play_and_wait(chan, "transfer");
2481 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2484 ast_play_and_wait(chan, "vm-sorry");
2485 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2491 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2494 /* The meat of recording the message... All the announcements and beeps have been played*/
2495 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2496 if (!ast_strlen_zero(fmt)) {
2499 if (vm_lock_path(dir)) {
2501 return ERROR_LOCK_PATH;
2505 * This operation can be very expensive if done say over NFS or if the mailbox has 100+ messages
2506 * in the mailbox. So we should get this first so we don't cut off the first few seconds of the
2510 make_file(fn, sizeof(fn), dir, msgnum);
2511 if (!EXISTS(dir,msgnum,fn,chan->language))
2514 } while (msgnum < vmu->maxmsg);
2516 /* Now play the beep once we have the message number for our next message. */
2518 /* Unless we're *really* silent, try to send the beep */
2519 res = ast_streamfile(chan, "beep", chan->language);
2521 res = ast_waitstream(chan, "");
2523 if (msgnum < vmu->maxmsg) {
2524 /* assign a variable with the name of the voicemail file */
2525 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
2527 /* Store information */
2528 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2529 txt = fopen(txtfile, "w+");
2531 get_date(date, sizeof(date));
2534 "; Message Information file\n"
2553 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2554 date, (long)time(NULL),
2555 category ? category : "");
2557 ast_log(LOG_WARNING, "Error opening text file for output\n");
2558 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2567 fprintf(txt, "duration=%d\n", duration);
2571 if (duration < vmminmessage) {
2572 if (option_verbose > 2)
2573 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2574 DELETE(dir,msgnum,fn);
2575 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2576 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2579 /* Are there to be more recipients of this message? */
2581 struct ast_vm_user recipu, *recip;
2582 char *exten, *context;
2584 exten = strsep(&tmpptr, "&");
2585 context = strchr(exten, '@');
2590 if ((recip = find_user(&recipu, context, exten))) {
2591 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2595 if (ast_fileexists(fn, NULL, NULL)) {
2596 STORE(dir, vmu->mailbox, vmu->context, msgnum);
2597 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2598 DISPOSE(dir, msgnum);
2600 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
2602 ast_unlock_path(dir);
2603 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2605 res = ast_waitstream(chan, "");
2606 ast_log(LOG_WARNING, "No more messages possible\n");
2607 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2610 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2617 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
2619 /* we know max messages, so stop process when number is hit */
2625 if (vm_lock_path(dir))
2626 return ERROR_LOCK_PATH;
2628 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
2629 make_file(sfn, sizeof(sfn), dir, x);
2630 if (EXISTS(dir, x, sfn, NULL)) {
2633 make_file(dfn, sizeof(dfn), dir, dest);
2634 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
2640 ast_unlock_path(dir);
2646 static int say_and_wait(struct ast_channel *chan, int num, char *language)
2649 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2653 static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *context, char *username, int box)
2658 char *dbox = mbox(box);
2660 make_file(sfn, sizeof(sfn), dir, msg);
2661 make_dir(ddir, sizeof(ddir), context, username, dbox);
2664 if (vm_lock_path(ddir))
2665 return ERROR_LOCK_PATH;
2667 for (x = 0; x < vmu->maxmsg; x++) {
2668 make_file(dfn, sizeof(dfn), ddir, x);
2669 if (!EXISTS(ddir, x, dfn, NULL))
2672 if (x >= vmu->maxmsg) {
2673 ast_unlock_path(ddir);
2676 if (strcmp(sfn, dfn)) {
2677 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
2679 ast_unlock_path(ddir);
2684 static int adsi_logo(unsigned char *buf)
2687 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2688 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2692 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2694 unsigned char buf[256];
2700 bytes += adsi_data_mode(buf + bytes);
2701 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2704 bytes += adsi_logo(buf);
2705 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2707 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
2709 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2710 bytes += adsi_data_mode(buf + bytes);
2711 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2713 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2715 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2716 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2717 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2718 bytes += adsi_voice_mode(buf + bytes, 0);
2719 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2726 bytes += adsi_logo(buf);
2727 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2728 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
2729 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2730 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2733 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2734 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2735 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2736 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2737 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2738 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2739 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2742 /* Add another dot */
2744 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
2745 bytes += adsi_voice_mode(buf + bytes, 0);
2747 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2748 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2752 /* These buttons we load but don't use yet */
2753 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2754 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2755 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2756 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2757 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2758 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2759 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2762 /* Add another dot */
2764 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2765 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2766 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2771 snprintf(num, sizeof(num), "%d", x);
2772 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2774 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2775 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2778 /* Add another dot */
2780 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2781 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2782 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2785 if (adsi_end_download(chan)) {
2787 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2788 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2789 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2790 bytes += adsi_voice_mode(buf + bytes, 0);
2791 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2795 bytes += adsi_download_disconnect(buf + bytes);
2796 bytes += adsi_voice_mode(buf + bytes, 0);
2797 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2799 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2804 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2805 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2807 ast_log(LOG_DEBUG, "Restarting session...\n");
2810 /* Load the session now */
2811 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2813 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2815 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2817 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2821 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2824 if (!adsi_available(chan))
2826 x = adsi_load_session(chan, adsifdn, adsiver, 1);
2830 if (adsi_load_vmail(chan, useadsi)) {
2831 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2838 static void adsi_login(struct ast_channel *chan)
2840 unsigned char buf[256];
2842 unsigned char keys[8];
2844 if (!adsi_available(chan))
2849 /* Set one key for next */
2850 keys[3] = ADSI_KEY_APPS + 3;
2852 bytes += adsi_logo(buf + bytes);
2853 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2854 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2855 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2856 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2857 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2858 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2859 bytes += adsi_set_keys(buf + bytes, keys);
2860 bytes += adsi_voice_mode(buf + bytes, 0);
2861 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2864 static void adsi_password(struct ast_channel *chan)
2866 unsigned char buf[256];
2868 unsigned char keys[8];
2870 if (!adsi_available(chan))
2875 /* Set one key for next */
2876 keys[3] = ADSI_KEY_APPS + 3;
2878 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2879 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2880 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2881 bytes += adsi_set_keys(buf + bytes, keys);
2882 bytes += adsi_voice_mode(buf + bytes, 0);
2883 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2886 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2888 unsigned char buf[256];
2890 unsigned char keys[8];
2893 if (!adsi_available(chan))
2897 y = ADSI_KEY_APPS + 12 + start + x;
2898 if (y > ADSI_KEY_APPS + 12 + 4)
2900 keys[x] = ADSI_KEY_SKT | y;
2902 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2906 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2907 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2908 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2909 bytes += adsi_set_keys(buf + bytes, keys);
2910 bytes += adsi_voice_mode(buf + bytes, 0);
2912 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2915 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2918 unsigned char buf[256];
2919 char buf1[256], buf2[256];
2925 char datetime[21]="";
2928 unsigned char keys[8];
2932 if (!adsi_available(chan))
2935 /* Retrieve important info */
2936 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
2937 f = fopen(fn2, "r");
2940 fgets((char *)buf, sizeof(buf), f);
2943 stringp = (char *)buf;
2944 strsep(&stringp, "=");
2945 val = strsep(&stringp, "=");
2946 if (!ast_strlen_zero(val)) {
2947 if (!strcmp((char *)buf, "callerid"))
2948 ast_copy_string(cid, val, sizeof(cid));
2949 if (!strcmp((char *)buf, "origdate"))
2950 ast_copy_string(datetime, val, sizeof(datetime));
2956 /* New meaning for keys */
2958 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2963 /* No prev key, provide "Folder" instead */
2964 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2966 if (vms->curmsg >= vms->lastmsg) {
2967 /* If last message ... */
2969 /* but not only message, provide "Folder" instead */
2970 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2971 bytes += adsi_voice_mode(buf + bytes, 0);
2974 /* Otherwise if only message, leave blank */
2979 if (!ast_strlen_zero(cid)) {
2980 ast_callerid_parse(cid, &name, &num);
2984 name = "Unknown Caller";
2986 /* If deleted, show "undeleted" */
2988 if (vms->deleted[vms->curmsg])
2989 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2992 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2993 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
2994 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
2995 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
2997 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2998 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2999 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3000 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3001 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3002 bytes += adsi_set_keys(buf + bytes, keys);
3003 bytes += adsi_voice_mode(buf + bytes, 0);
3005 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3008 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3011 unsigned char buf[256];
3012 unsigned char keys[8];
3016 if (!adsi_available(chan))
3019 /* New meaning for keys */
3021 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3027 /* No prev key, provide "Folder" instead */
3028 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3030 if (vms->curmsg >= vms->lastmsg) {
3031 /* If last message ... */
3033 /* but not only message, provide "Folder" instead */
3034 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3036 /* Otherwise if only message, leave blank */
3041 /* If deleted, show "undeleted" */
3042 if (vms->deleted[vms->curmsg])
3043 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3046 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3047 bytes += adsi_set_keys(buf + bytes, keys);
3048 bytes += adsi_voice_mode(buf + bytes, 0);
3050 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3053 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
3055 unsigned char buf[256] = "";
3056 char buf1[256] = "", buf2[256] = "";
3058 unsigned char keys[8];
3061 char *newm = (vms->newmessages == 1) ? "message" : "messages";
3062 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
3063 if (!adsi_available(chan))
3065 if (vms->newmessages) {
3066 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
3067 if (vms->oldmessages) {
3068 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
3069 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
3071 snprintf(buf2, sizeof(buf2), "%s.", newm);
3073 } else if (vms->oldmessages) {
3074 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
3075 snprintf(buf2, sizeof(buf2), "%s.", oldm);
3077 strcpy(buf1, "You have no messages.");
3081 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3082 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3083 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3086 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3090 /* Don't let them listen if there are none */
3091 if (vms->lastmsg < 0)
3093 bytes += adsi_set_keys(buf + bytes, keys);
3095 bytes += adsi_voice_mode(buf + bytes, 0);
3097 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3100 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
3102 unsigned char buf[256] = "";
3103 char buf1[256] = "", buf2[256] = "";
3105 unsigned char keys[8];
3108 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
3110 if (!adsi_available(chan))
3113 /* Original command keys */
3115 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3120 if ((vms->lastmsg + 1) < 1)
3123 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
3124 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
3126 if (vms->lastmsg + 1)
3127 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
3129 strcpy(buf2, "no messages.");
3130 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3131 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3132 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
3133 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3134 bytes += adsi_set_keys(buf + bytes, keys);
3136 bytes += adsi_voice_mode(buf + bytes, 0);
3138 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3143 static void adsi_clear(struct ast_channel *chan)
3147 if (!adsi_available(chan))
3149 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3150 bytes += adsi_voice_mode(buf + bytes, 0);
3152 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3156 static void adsi_goodbye(struct ast_channel *chan)
3158 unsigned char buf[256];
3161 if (!adsi_available(chan))
3163 bytes += adsi_logo(buf + bytes);
3164 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
3165 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
3166 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3167 bytes += adsi_voice_mode(buf + bytes, 0);
3169 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3172 /*--- get_folder: Folder menu ---*/
3173 /* Plays "press 1 for INBOX messages" etc
3174 Should possibly be internationalized
3176 static int get_folder(struct ast_channel *chan, int start)
3181 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
3184 for (x = start; x< 5; x++) { /* For all folders */
3185 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
3187 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
3190 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
3191 d = vm_play_folder_name(chan, fn);
3194 d = ast_waitfordigit(chan, 500);
3198 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
3201 d = ast_waitfordigit(chan, 4000);
3205 static int get_folder2(struct ast_channel *chan, char *fn, int start)
3208 res = ast_play_and_wait(chan, fn); /* Folder name */
3209 while (((res < '0') || (res > '9')) &&
3210 (res != '#') && (res >= 0)) {
3211 res = get_folder(chan, 0);
3216 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts,
3217 char *context, signed char record_gain)
3222 signed char zero_gain = 0;
3224 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
3229 /* prepend a message to the current message and return */
3232 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
3234 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
3235 cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
3237 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
3247 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
3248 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3250 cmd = ast_play_and_wait(chan,"vm-starmain");
3251 /* "press star to return to the main menu" */
3253 cmd = ast_waitfordigit(chan,6000);
3265 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
3267 char todir[256], fn[256], ext_context[256], *stringp;
3268 int newmsgs = 0, oldmsgs = 0;
3269 const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3271 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
3272 make_file(fn, sizeof(fn), todir, msgnum);
3273 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
3275 /* Attach only the first format */
3276 fmt = ast_strdupa(fmt);
3279 strsep(&stringp, "|");
3281 if (!ast_strlen_zero(vmu->email)) {
3282 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3283 char *myserveremail = serveremail;
3284 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
3285 if (!ast_strlen_zero(vmu->serveremail))
3286 myserveremail = vmu->serveremail;
3287 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, category);
3290 if (!ast_strlen_zero(vmu->pager)) {
3291 char *myserveremail = serveremail;
3292 if (!ast_strlen_zero(vmu->serveremail))
3293 myserveremail = vmu->serveremail;
3294 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
3297 ast_log(LOG_ERROR, "Out of memory\n");
3300 if (ast_test_flag(vmu, VM_DELETE)) {
3301 DELETE(todir, msgnum, fn);
3304 /* Leave voicemail for someone */
3305 if (ast_app_has_voicemail(ext_context, NULL)) {
3306 ast_app_messagecount(ext_context, &newmsgs, &oldmsgs);
3308 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);
3309 run_externnotify(vmu->context, vmu->mailbox);
3313 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender,
3314 char *fmt, int flag, signed char record_gain)
3316 char username[70]="";
3321 struct ast_config *mif;
3325 char ext_context[256]="";
3326 int res = 0, cmd = 0;
3327 struct ast_vm_user *receiver = NULL, *extensions = NULL, *vmtmp = NULL, *vmfree;
3330 int saved_messages = 0, found = 0;
3331 int valid_extensions = 0;
3333 while (!res && !valid_extensions) {
3334 int use_directory = 0;
3335 if(ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
3339 while((cmd >= 0) && !done ){
3356 /* Press 1 to enter an extension press 2 to use the directory */
3357 cmd = ast_play_and_wait(chan,"vm-forward");
3359 cmd = ast_waitfordigit(chan,3000);
3370 if( cmd<0 || cmd=='t' )
3374 if( use_directory ) {
3375 /* use app_directory */
3377 char old_context[sizeof(chan->context)];
3378 char old_exten[sizeof(chan->exten)];
3380 struct ast_app* app;
3383 app = pbx_findapp("Directory");
3385 /* make mackup copies */
3386 memcpy(old_context, chan->context, sizeof(chan->context));
3387 memcpy(old_exten, chan->exten, sizeof(chan->exten));
3388 old_priority = chan->priority;
3390 /* call the the Directory, changes the channel */
3391 res = pbx_exec(chan, app, ((context)?context:chan->context), 1);
3393 ast_copy_string(username, chan->exten, sizeof(username));
3395 /* restore the old context, exten, and priority */
3396 memcpy(chan->context, old_context, sizeof(chan->context));
3397 memcpy(chan->exten, old_exten, sizeof(chan->exten));
3398 chan->priority = old_priority;
3401 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
3402 ast_clear_flag((&globalflags), VM_DIRECFORWARD);
3405 /* Ask for an extension */
3406 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
3409 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
3413 /* start all over if no username */
3414 if (ast_strlen_zero(username))
3417 s = strsep(&stringp, "*");
3418 /* start optimistic */
3419 valid_extensions = 1;
3421 /* find_user is going to malloc since we have a NULL as first argument */
3422 if ((receiver = find_user(NULL, context, s))) {
3424 vmtmp = extensions = receiver;
3426 vmtmp->next = receiver;
3431 valid_extensions = 0;
3434 s = strsep(&stringp, "*");
3436 /* break from the loop of reading the extensions */
3437 if (valid_extensions)
3439 /* "I am sorry, that's not a valid extension. Please try again." */
3440 res = ast_play_and_wait(chan, "pbx-invalid");
3442 /* check if we're clear to proceed */
3443 if (!extensions || !valid_extensions)
3447 struct leave_vm_options leave_options;
3449 /* Send VoiceMail */
3450 memset(&leave_options, 0, sizeof(leave_options));
3451 leave_options.record_gain = record_gain;
3452 cmd = leave_voicemail(chan, username, &leave_options);
3455 /* Forward VoiceMail */
3456 RETRIEVE(dir, curmsg);
3457 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context, record_gain);
3459 while (!res && vmtmp) {
3460 /* if (ast_play_and_wait(chan, "v