2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, 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 #include "asterisk/stringfields.h"
77 #include "asterisk/smdi.h"
78 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
80 #ifdef USE_ODBC_STORAGE
81 #include "asterisk/res_odbc.h"
84 #define COMMAND_TIMEOUT 5000
85 #define VOICEMAIL_DIR_MODE 0770
86 #define VOICEMAIL_FILE_MODE 0660
88 #define VOICEMAIL_CONFIG "voicemail.conf"
89 #define ASTERISK_USERNAME "asterisk"
91 /* Default mail command to mail voicemail. Change it with the
92 mailcmd= command in voicemail.conf */
93 #define SENDMAIL "/usr/sbin/sendmail -t"
95 #define INTRO "vm-intro"
98 #define MAXMSGLIMIT 9999
100 #define BASEMAXINLINE 256
101 #define BASELINELEN 72
102 #define BASEMAXINLINE 256
105 #define MAX_DATETIME_FORMAT 512
106 #define MAX_NUM_CID_CONTEXTS 10
108 #define VM_REVIEW (1 << 0)
109 #define VM_OPERATOR (1 << 1)
110 #define VM_SAYCID (1 << 2)
111 #define VM_SVMAIL (1 << 3)
112 #define VM_ENVELOPE (1 << 4)
113 #define VM_SAYDURATION (1 << 5)
114 #define VM_SKIPAFTERCMD (1 << 6)
115 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
116 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
117 #define VM_PBXSKIP (1 << 9)
118 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
119 #define VM_ATTACH (1 << 11)
120 #define VM_DELETE (1 << 12)
121 #define VM_ALLOCED (1 << 13)
122 #define VM_SEARCH (1 << 14)
124 #define ERROR_LOCK_PATH -100
127 OPT_SILENT = (1 << 0),
128 OPT_BUSY_GREETING = (1 << 1),
129 OPT_UNAVAIL_GREETING = (1 << 2),
130 OPT_RECORDGAIN = (1 << 3),
131 OPT_PREPEND_MAILBOX = (1 << 4),
132 OPT_PRIORITY_JUMP = (1 << 5),
133 OPT_AUTOPLAY = (1 << 6),
137 OPT_ARG_RECORDGAIN = 0,
138 OPT_ARG_PLAYFOLDER = 1,
139 /* This *must* be the last value in this enum! */
140 OPT_ARG_ARRAY_SIZE = 2,
143 AST_APP_OPTIONS(vm_app_options, {
144 AST_APP_OPTION('s', OPT_SILENT),
145 AST_APP_OPTION('b', OPT_BUSY_GREETING),
146 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
147 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
148 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
149 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
150 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
153 static int load_config(void);
155 /*! \page vmlang Voicemail Language Syntaxes Supported
157 \par Syntaxes supported, not really language codes.
164 \arg \b pt - Portuguese
166 \arg \b no - Norwegian
169 German requires the following additional soundfile:
170 \arg \b 1F einE (feminine)
172 Spanish requires the following additional soundfile:
173 \arg \b 1M un (masculine)
175 Dutch, Portuguese & Spanish require the following additional soundfiles:
176 \arg \b vm-INBOXs singular of 'new'
177 \arg \b vm-Olds singular of 'old/heard/read'
180 \arg \b vm-INBOX nieuwe (nl)
181 \arg \b vm-Old oude (nl)
184 \arg \b vm-nytt singular of 'new'
185 \arg \b vm-nya plural of 'new'
186 \arg \b vm-gammalt singular of 'old'
187 \arg \b vm-gamla plural of 'old'
188 \arg \b digits/ett 'one', not always same as 'digits/1'
191 \arg \b vm-ny singular of 'new'
192 \arg \b vm-nye plural of 'new'
193 \arg \b vm-gammel singular of 'old'
194 \arg \b vm-gamle plural of 'old'
202 Italian requires the following additional soundfile:
206 \arg \b vm-nuovi new plural
207 \arg \b vm-vecchio old
208 \arg \b vm-vecchi old plural
210 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
211 spelled among others when you have to change folder. For the above reasons, vm-INBOX
212 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
221 unsigned char iobuf[BASEMAXINLINE];
224 /*! Structure for linked list of users */
226 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
227 char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */
228 char password[80]; /*!< Secret pin code, numbers only */
229 char fullname[80]; /*!< Full name, for directory app */
230 char email[80]; /*!< E-mail address */
231 char pager[80]; /*!< E-mail address to pager (no attachment) */
232 char serveremail[80]; /*!< From: Mail address */
233 char mailcmd[160]; /*!< Configurable mail command */
234 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
235 char zonetag[80]; /*!< Time zone */
238 char uniqueid[20]; /*!< Unique integer identifier */
240 unsigned int flags; /*!< VM_ flags */
242 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
243 AST_LIST_ENTRY(ast_vm_user) list;
249 char msg_format[512];
250 struct vm_zone *next;
269 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
270 int option, signed char record_gain);
271 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
272 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
273 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
274 signed char record_gain);
275 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
276 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
278 static void apply_options(struct ast_vm_user *vmu, const char *options);
280 #ifdef USE_ODBC_STORAGE
281 static char odbc_database[80];
282 static char odbc_table[80];
283 #define RETRIEVE(a,b) retrieve_file(a,b)
284 #define DISPOSE(a,b) remove_file(a,b)
285 #define STORE(a,b,c,d) store_file(a,b,c,d)
286 #define EXISTS(a,b,c,d) (message_exists(a,b))
287 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
288 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
289 #define DELETE(a,b,c) (delete_file(a,b))
291 #define RETRIEVE(a,b)
293 #define STORE(a,b,c,d)
294 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
295 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
296 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
297 #define DELETE(a,b,c) (vm_delete(c))
300 static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
302 static char ext_pass_cmd[128];
304 static char *tdesc = "Comedian Mail (Voicemail System)";
306 static char *addesc = "Comedian Mail";
308 static char *synopsis_vm =
309 "Leave a Voicemail message";
311 static char *descrip_vm =
312 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
313 "application allows the calling party to leave a message for the specified\n"
314 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
315 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
316 "specified mailbox does not exist.\n"
317 " The Voicemail application will exit if any of the following DTMF digits are\n"
319 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
320 " * - Jump to the 'a' extension in the current dialplan context.\n"
321 " This application will set the following channel variable upon completion:\n"
322 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
323 " application. The possible values are:\n"
324 " SUCCESS | USEREXIT | FAILED\n\n"
326 " b - Play the 'busy' greeting to the calling party.\n"
327 " g(#) - Use the specified amount of gain when recording the voicemail\n"
328 " message. The units are whole-number decibels (dB).\n"
329 " s - Skip the playback of instructions for leaving a message to the\n"
331 " u - Play the 'unavailble greeting.\n"
332 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
335 static char *synopsis_vmain =
336 "Check Voicemail messages";
338 static char *descrip_vmain =
339 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
340 "calling party to check voicemail messages. A specific mailbox, and optional\n"
341 "corresponding context, may be specified. If a mailbox is not provided, the\n"
342 "calling party will be prompted to enter one. If a context is not specified,\n"
343 "the 'default' context will be used.\n\n"
345 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
346 " is entered by the caller.\n"
347 " g(#) - Use the specified amount of gain when recording a voicemail\n"
348 " message. The units are whole-number decibels (dB).\n"
349 " s - Skip checking the passcode for the mailbox.\n"
350 " a(#) - Skip folder prompt and go directly to folder specified.\n"
351 " Defaults to INBOX\n";
353 static char *synopsis_vm_box_exists =
354 "Check to see if Voicemail mailbox exists";
356 static char *descrip_vm_box_exists =
357 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
358 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
360 " This application will set the following channel variable upon completion:\n"
361 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
362 " MailboxExists application. Possible values include:\n"
363 " SUCCESS | FAILED\n\n"
365 " j - Jump to priority n+101 if the mailbox is found.\n";
367 static char *synopsis_vmauthenticate =
368 "Authenticate with Voicemail passwords";
370 static char *descrip_vmauthenticate =
371 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
372 "same way as the Authenticate application, but the passwords are taken from\n"
374 " If the mailbox is specified, only that mailbox's password will be considered\n"
375 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
376 "be set with the authenticated mailbox.\n\n"
378 " s - Skip playing the initial prompts.\n";
380 /* Leave a message */
381 static char *app = "VoiceMail";
383 /* Check mail, control, etc */
384 static char *app2 = "VoiceMailMain";
386 static char *app3 = "MailboxExists";
387 static char *app4 = "VMAuthenticate";
389 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
390 struct vm_zone *zones = NULL;
391 struct vm_zone *zonesl = NULL;
392 static int maxsilence;
394 static int silencethreshold = 128;
395 static char serveremail[80];
396 static char mailcmd[160]; /* Configurable mail cmd */
397 static char externnotify[160];
399 static struct ast_smdi_interface *smdi_iface = NULL;
401 static char vmfmts[80];
402 static int vmminmessage;
403 static int vmmaxmessage;
406 static int maxlogins;
408 static struct ast_flags globalflags = {0};
410 static int saydurationminfo;
412 static char dialcontext[AST_MAX_CONTEXT];
413 static char callcontext[AST_MAX_CONTEXT];
414 static char exitcontext[AST_MAX_CONTEXT];
416 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
419 static char *emailbody = NULL;
420 static char *emailsubject = NULL;
421 static char *pagerbody = NULL;
422 static char *pagersubject = NULL;
423 static char fromstring[100];
424 static char pagerfromstring[100];
425 static char emailtitle[100];
426 static char charset[32] = "ISO-8859-1";
428 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
429 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
430 static int adsiver = 1;
431 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
435 static void populate_defaults(struct ast_vm_user *vmu)
437 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
438 if (saydurationminfo)
439 vmu->saydurationm = saydurationminfo;
441 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
443 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
445 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
447 vmu->maxmsg = maxmsg;
450 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
453 if (!strcasecmp(var, "attach")) {
454 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
455 } else if (!strcasecmp(var, "serveremail")) {
456 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
457 } else if (!strcasecmp(var, "language")) {
458 ast_copy_string(vmu->language, value, sizeof(vmu->language));
459 } else if (!strcasecmp(var, "tz")) {
460 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
461 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
462 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
463 } else if (!strcasecmp(var, "saycid")){
464 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
465 } else if (!strcasecmp(var,"sendvoicemail")){
466 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
467 } else if (!strcasecmp(var, "review")){
468 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
469 } else if (!strcasecmp(var, "operator")){
470 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
471 } else if (!strcasecmp(var, "envelope")){
472 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
473 } else if (!strcasecmp(var, "sayduration")){
474 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
475 } else if (!strcasecmp(var, "saydurationm")){
476 if (sscanf(value, "%d", &x) == 1) {
477 vmu->saydurationm = x;
479 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
481 } else if (!strcasecmp(var, "forcename")){
482 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
483 } else if (!strcasecmp(var, "forcegreetings")){
484 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
485 } else if (!strcasecmp(var, "callback")) {
486 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
487 } else if (!strcasecmp(var, "dialout")) {
488 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
489 } else if (!strcasecmp(var, "exitcontext")) {
490 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
491 } else if (!strcasecmp(var, "maxmsg")) {
492 vmu->maxmsg = atoi(value);
493 if (vmu->maxmsg <= 0) {
494 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
495 vmu->maxmsg = MAXMSG;
496 } else if (vmu->maxmsg > MAXMSGLIMIT) {
497 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
498 vmu->maxmsg = MAXMSGLIMIT;
500 } else if (!strcasecmp(var, "options")) {
501 apply_options(vmu, value);
505 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
508 if (!ast_strlen_zero(vmu->uniqueid)) {
509 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
511 ast_copy_string(vmu->password, password, sizeof(vmu->password));
521 static void apply_options(struct ast_vm_user *vmu, const char *options)
522 { /* Destructively Parse options and apply */
526 stringp = ast_strdupa(options);
527 while ((s = strsep(&stringp, "|"))) {
529 if ((var = strsep(&value, "=")) && value) {
530 apply_option(vmu, var, value);
535 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
537 struct ast_variable *var, *tmp;
538 struct ast_vm_user *retval;
540 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
542 ast_set_flag(retval, VM_ALLOCED);
544 memset(retval, 0, sizeof(*retval));
546 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
547 populate_defaults(retval);
548 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
549 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
551 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
555 printf("%s => %s\n", tmp->name, tmp->value);
556 if (!strcasecmp(tmp->name, "password")) {
557 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
558 } else if (!strcasecmp(tmp->name, "uniqueid")) {
559 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
560 } else if (!strcasecmp(tmp->name, "pager")) {
561 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
562 } else if (!strcasecmp(tmp->name, "email")) {
563 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
564 } else if (!strcasecmp(tmp->name, "fullname")) {
565 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
566 } else if (!strcasecmp(tmp->name, "context")) {
567 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
569 apply_option(retval, tmp->name, tmp->value);
581 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
583 /* This function could be made to generate one from a database, too */
584 struct ast_vm_user *vmu=NULL, *cur;
585 AST_LIST_LOCK(&users);
587 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
590 AST_LIST_TRAVERSE(&users, cur, list) {
591 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
593 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
597 /* Make a copy, so that on a reload, we have no race */
598 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
599 memcpy(vmu, cur, sizeof(*vmu));
600 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
601 AST_LIST_NEXT(vmu, list) = NULL;
604 vmu = find_user_realtime(ivm, context, mailbox);
605 AST_LIST_UNLOCK(&users);
609 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
611 /* This function could be made to generate one from a database, too */
612 struct ast_vm_user *cur;
614 AST_LIST_LOCK(&users);
615 AST_LIST_TRAVERSE(&users, cur, list) {
616 if ((!context || !strcasecmp(context, cur->context)) &&
617 (!strcasecmp(mailbox, cur->mailbox)))
621 ast_copy_string(cur->password, newpass, sizeof(cur->password));
624 AST_LIST_UNLOCK(&users);
628 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
630 /* There's probably a better way of doing this. */
631 /* That's why I've put the password change in a separate function. */
632 /* This could also be done with a database function */
639 char currcontext[256] ="";
640 char tmpin[AST_CONFIG_MAX_PATH];
641 char tmpout[AST_CONFIG_MAX_PATH];
644 if (!change_password_realtime(vmu, newpassword))
647 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
648 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
649 configin = fopen(tmpin,"r");
651 configout = fopen(tmpout,"w+");
654 if (!configin || !configout) {
658 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
662 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
666 while (!feof(configin)) {
667 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
669 /* Read in the line */
670 fgets(inbuf, sizeof(inbuf), configin);
673 if (ast_strlen_zero(inbuf)) {
674 fprintf(configout, "\n");
678 /* Make a backup of it */
679 ast_copy_string(orig, inbuf, sizeof(orig));
682 Read the file line by line, split each line into a comment and command section
683 only parse the command portion of the line
685 if (inbuf[strlen(inbuf) - 1] == '\n')
686 inbuf[strlen(inbuf) - 1] = '\0';
688 if ((comment = strchr(inbuf, ';')))
689 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
691 if (ast_strlen_zero(inbuf)) {
692 fprintf(configout, "%s", orig);
696 /* Check for a context, first '[' to first ']' */
697 if ((tmpctx = strchr(inbuf, '['))) {
698 tmpctxend = strchr(tmpctx, ']');
701 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
702 fprintf(configout, "%s", orig);
707 /* This isn't a context line, check for MBX => PSWD... */
709 if ((pass = strchr(user, '='))) {
710 /* We have a line in the form of aaaaa=aaaaaa */
713 user = ast_strip(user);
718 pass = ast_skip_blanks(pass);
721 Since no whitespace allowed in fields, or more correctly white space
722 inside the fields is there for a purpose, we can just terminate pass
723 at the comma or EOL whichever comes first.
725 if ((rest = strchr(pass, ',')))
731 /* Compare user, pass AND context */
732 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
733 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
734 !strcasecmp(currcontext, vmu->context)) {
735 /* This is the line */
737 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
739 fprintf(configout, "%s => %s", user, newpassword);
741 /* If there was a comment on the line print it out */
743 fprintf(configout, ";%s\n", comment);
745 fprintf(configout, "\n");
748 /* Put it back like it was */
749 fprintf(configout, "%s", orig);
755 stat(tmpin, &statbuf);
756 chmod(tmpout, statbuf.st_mode);
757 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
759 rename(tmpout, tmpin);
760 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
761 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
764 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
767 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
768 if (!ast_safe_system(buf))
769 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
772 static int make_dir(char *dest, int len, char *context, char *ext, char *folder)
774 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
777 static int make_file(char *dest, int len, char *dir, int num)
779 return snprintf(dest, len, "%s/msg%04d", dir, num);
782 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
783 * \param dest String. base directory.
784 * \param context String. Ignored if is null or empty string.
785 * \param ext String. Ignored if is null or empty string.
786 * \param folder String. Ignored if is null or empty string.
787 * \return 0 on failure, 1 on success.
789 static int create_dirpath(char *dest, int len, char *context, char *ext, char *folder)
791 mode_t mode = VOICEMAIL_DIR_MODE;
793 if (!ast_strlen_zero(context)) {
794 make_dir(dest, len, context, "", "");
795 if(mkdir(dest, mode) && errno != EEXIST) {
796 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
800 if (!ast_strlen_zero(ext)) {
801 make_dir(dest, len, context, ext, "");
802 if(mkdir(dest, mode) && errno != EEXIST) {
803 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
807 if (!ast_strlen_zero(folder)) {
808 make_dir(dest, len, context, ext, folder);
809 if(mkdir(dest, mode) && errno != EEXIST) {
810 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
817 /* only return failure if ast_lock_path returns 'timeout',
818 not if the path does not exist or any other reason
820 static int vm_lock_path(const char *path)
822 switch (ast_lock_path(path)) {
823 case AST_LOCK_TIMEOUT:
831 #ifdef USE_ODBC_STORAGE
832 static int retrieve_file(char *dir, int msgnum)
839 SQLSMALLINT colcount=0;
846 SQLSMALLINT datatype;
847 SQLSMALLINT decimaldigits;
848 SQLSMALLINT nullable;
857 obj = fetch_odbc_obj(odbc_database, 0);
859 ast_copy_string(fmt, vmfmts, sizeof(fmt));
860 c = strchr(fmt, '|');
863 if (!strcasecmp(fmt, "wav49"))
865 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
867 make_file(fn, sizeof(fn), dir, msgnum);
869 ast_copy_string(fn, dir, sizeof(fn));
870 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
871 f = fopen(full_fn, "w+");
872 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
873 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
874 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
875 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
878 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
879 res = SQLPrepare(stmt, sql, SQL_NTS);
880 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
881 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
882 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
885 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
886 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
887 res = odbc_smart_execute(obj, stmt);
888 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
889 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
890 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
893 res = SQLFetch(stmt);
894 if (res == SQL_NO_DATA) {
895 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
898 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
899 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
900 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
903 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
905 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
906 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
909 res = SQLNumResultCols(stmt, &colcount);
910 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
911 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
912 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
916 fprintf(f, "[message]\n");
917 for (x=0;x<colcount;x++) {
919 collen = sizeof(coltitle);
920 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
921 &datatype, &colsize, &decimaldigits, &nullable);
922 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
923 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
924 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
927 if (!strcasecmp(coltitle, "recording")) {
928 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
930 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
933 lseek(fd, fdlen - 1, SEEK_SET);
934 if (write(fd, tmp, 1) != 1) {
939 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
942 memset(fdm, 0, fdlen);
943 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
944 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
945 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
946 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
951 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
952 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
953 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
954 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
957 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
958 fprintf(f, "%s=%s\n", coltitle, rowdata);
961 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
963 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
974 static int remove_file(char *dir, int msgnum)
981 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
982 make_file(fn, sizeof(fn), dir, msgnum);
984 ast_copy_string(fn, dir, sizeof(fn));
985 ast_filedelete(fn, NULL);
986 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
991 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1000 obj = fetch_odbc_obj(odbc_database, 0);
1002 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1003 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1004 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1007 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1008 res = SQLPrepare(stmt, sql, SQL_NTS);
1009 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1010 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1011 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1014 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1015 res = odbc_smart_execute(obj, stmt);
1016 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1017 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1018 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1021 res = SQLFetch(stmt);
1022 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1023 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1024 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1027 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1028 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1029 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1030 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1033 if (sscanf(rowdata, "%d", &x) != 1)
1034 ast_log(LOG_WARNING, "Failed to read message count!\n");
1035 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1037 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1042 static int message_exists(char *dir, int msgnum)
1052 obj = fetch_odbc_obj(odbc_database, 0);
1054 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1055 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1056 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1057 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1060 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1061 res = SQLPrepare(stmt, sql, SQL_NTS);
1062 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1063 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1064 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1067 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1068 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1069 res = odbc_smart_execute(obj, stmt);
1070 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1071 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1072 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1075 res = SQLFetch(stmt);
1076 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1077 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1078 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1081 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1082 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1083 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1084 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1087 if (sscanf(rowdata, "%d", &x) != 1)
1088 ast_log(LOG_WARNING, "Failed to read message count!\n");
1089 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1091 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1096 static int count_messages(struct ast_vm_user *vmu, char *dir)
1098 return last_message_index(vmu, dir) + 1;
1101 static void delete_file(char *sdir, int smsg)
1109 obj = fetch_odbc_obj(odbc_database, 0);
1111 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1112 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1113 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1114 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1117 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1118 res = SQLPrepare(stmt, sql, SQL_NTS);
1119 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1120 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1121 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1124 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1125 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1126 res = odbc_smart_execute(obj, stmt);
1127 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1128 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1129 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1132 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1134 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1139 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1148 delete_file(ddir, dmsg);
1149 obj = fetch_odbc_obj(odbc_database, 0);
1151 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1152 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1153 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1154 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1155 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1158 #ifdef EXTENDED_ODBC_STORAGE
1159 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);
1161 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);
1163 res = SQLPrepare(stmt, sql, SQL_NTS);
1164 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1165 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1166 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1169 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1170 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1171 #ifdef EXTENDED_ODBC_STORAGE
1172 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1173 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1174 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1175 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1177 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1178 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1180 res = odbc_smart_execute(obj, stmt);
1181 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1182 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1183 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1186 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1188 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1193 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1208 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1209 char *category = "";
1210 struct ast_config *cfg=NULL;
1213 delete_file(dir, msgnum);
1214 obj = fetch_odbc_obj(odbc_database, 0);
1216 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1217 c = strchr(fmt, '|');
1220 if (!strcasecmp(fmt, "wav49"))
1222 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1224 make_file(fn, sizeof(fn), dir, msgnum);
1226 ast_copy_string(fn, dir, sizeof(fn));
1227 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1228 cfg = ast_config_load(full_fn);
1229 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1230 fd = open(full_fn, O_RDWR);
1232 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1236 context = ast_variable_retrieve(cfg, "message", "context");
1237 if (!context) context = "";
1238 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1239 if (!macrocontext) macrocontext = "";
1240 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1241 if (!callerid) callerid = "";
1242 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1243 if (!origtime) origtime = "";
1244 duration = ast_variable_retrieve(cfg, "message", "duration");
1245 if (!duration) duration = "";
1246 category = ast_variable_retrieve(cfg, "message", "category");
1247 if (!category) category = "";
1249 fdlen = lseek(fd, 0, SEEK_END);
1250 lseek(fd, 0, SEEK_SET);
1251 printf("Length is %d\n", fdlen);
1252 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1254 ast_log(LOG_WARNING, "Memory map failed!\n");
1257 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1258 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1259 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1262 if (!ast_strlen_zero(category))
1263 #ifdef EXTENDED_ODBC_STORAGE
1264 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1266 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
1269 #ifdef EXTENDED_ODBC_STORAGE
1270 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1272 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
1274 res = SQLPrepare(stmt, sql, SQL_NTS);
1275 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1276 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1277 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1280 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1281 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1282 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1283 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1284 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1285 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1286 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1287 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1288 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1289 #ifdef EXTENDED_ODBC_STORAGE
1290 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1291 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1292 if (!ast_strlen_zero(category))
1293 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1295 if (!ast_strlen_zero(category))
1296 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1298 res = odbc_smart_execute(obj, stmt);
1299 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1300 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1301 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1304 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1306 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1309 ast_config_destroy(cfg);
1317 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1326 delete_file(ddir, dmsg);
1327 obj = fetch_odbc_obj(odbc_database, 0);
1329 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1330 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1331 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1332 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1333 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1336 #ifdef EXTENDED_ODBC_STORAGE
1337 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1339 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
1341 res = SQLPrepare(stmt, sql, SQL_NTS);
1342 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1343 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1344 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1347 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1348 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1349 #ifdef EXTENDED_ODBC_STORAGE
1350 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1351 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1352 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1353 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1355 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1356 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1358 res = odbc_smart_execute(obj, stmt);
1359 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1360 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1361 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1364 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1366 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1373 static int count_messages(struct ast_vm_user *vmu, char *dir)
1375 /* Find all .txt files - even if they are not in sequence from 0000 */
1379 struct dirent *vment = NULL;
1381 if (vm_lock_path(dir))
1382 return ERROR_LOCK_PATH;
1384 if ((vmdir = opendir(dir))) {
1385 while ((vment = readdir(vmdir))) {
1386 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1391 ast_unlock_path(dir);
1396 static void rename_file(char *sfn, char *dfn)
1400 ast_filerename(sfn,dfn,NULL);
1401 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1402 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1406 static int copy(char *infile, char *outfile)
1414 #ifdef HARDLINK_WHEN_POSSIBLE
1415 /* Hard link if possible; saves disk space & is faster */
1416 if (link(infile, outfile)) {
1418 if ((ifd = open(infile, O_RDONLY)) < 0) {
1419 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1422 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1423 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1428 len = read(ifd, buf, sizeof(buf));
1430 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1436 res = write(ofd, buf, len);
1437 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1438 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1448 #ifdef HARDLINK_WHEN_POSSIBLE
1450 /* Hard link succeeded */
1456 static void copy_file(char *frompath, char *topath)
1458 char frompath2[256],topath2[256];
1459 ast_filecopy(frompath, topath, NULL);
1460 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1461 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1462 copy(frompath2, topath2);
1466 * A negative return value indicates an error.
1468 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1473 if (vm_lock_path(dir))
1474 return ERROR_LOCK_PATH;
1476 for (x = 0; x < vmu->maxmsg; x++) {
1477 make_file(fn, sizeof(fn), dir, x);
1478 if (ast_fileexists(fn, NULL, NULL) < 1)
1481 ast_unlock_path(dir);
1486 static int vm_delete(char *file)
1491 txtsize = (strlen(file) + 5)*sizeof(char);
1492 txt = alloca(txtsize);
1493 /* Sprintf here would safe because we alloca'd exactly the right length,
1494 * but trying to eliminate all sprintf's anyhow
1496 snprintf(txt, txtsize, "%s.txt", file);
1498 return ast_filedelete(file, NULL);
1504 inbuf(struct baseio *bio, FILE *fi)
1511 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1526 inchar(struct baseio *bio, FILE *fi)
1528 if (bio->iocp>=bio->iolen) {
1529 if (!inbuf(bio, fi))
1533 return bio->iobuf[bio->iocp++];
1537 ochar(struct baseio *bio, int c, FILE *so)
1539 if (bio->linelength>=BASELINELEN) {
1540 if (fputs(eol,so)==EOF)
1546 if (putc(((unsigned char)c),so)==EOF)
1554 static int base_encode(char *filename, FILE *so)
1556 unsigned char dtable[BASEMAXINLINE];
1561 memset(&bio, 0, sizeof(bio));
1562 bio.iocp = BASEMAXINLINE;
1564 if (!(fi = fopen(filename, "rb"))) {
1565 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1569 for (i= 0;i<9;i++) {
1572 dtable[26+i]= 'a'+i;
1573 dtable[26+i+9]= 'j'+i;
1575 for (i= 0;i<8;i++) {
1576 dtable[i+18]= 'S'+i;
1577 dtable[26+i+18]= 's'+i;
1579 for (i= 0;i<10;i++) {
1580 dtable[52+i]= '0'+i;
1586 unsigned char igroup[3],ogroup[4];
1589 igroup[0]= igroup[1]= igroup[2]= 0;
1591 for (n= 0;n<3;n++) {
1592 if ((c = inchar(&bio, fi)) == EOF) {
1597 igroup[n]= (unsigned char)c;
1601 ogroup[0]= dtable[igroup[0]>>2];
1602 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1603 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1604 ogroup[3]= dtable[igroup[2]&0x3F];
1614 ochar(&bio, ogroup[i], so);
1618 if (fputs(eol,so)==EOF)
1626 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)
1629 /* Prepare variables for substition in email body and subject */
1630 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1631 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1632 snprintf(passdata, passdatasize, "%d", msgnum);
1633 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1634 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1635 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1636 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1637 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1638 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1639 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1640 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1644 * fill in *tm for current time according to the proper timezone, if any.
1645 * Return tm so it can be used as a function argument.
1647 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1649 const struct vm_zone *z = NULL;
1650 time_t t = time(NULL);
1652 /* Does this user have a timezone specified? */
1653 if (!ast_strlen_zero(vmu->zonetag)) {
1654 /* Find the zone in the list */
1655 for (z = zones; z ; z = z->next)
1656 if (!strcmp(z->name, vmu->zonetag))
1659 ast_localtime(&t, tm, z ? z->timezone : NULL);
1663 /* same as mkstemp, but return a FILE * */
1664 static FILE *vm_mkftemp(char *template)
1667 int pfd = mkstemp(template);
1669 p = fdopen(pfd, "w");
1678 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)
1682 char host[MAXHOSTNAMELEN] = "";
1687 char tmp[80] = "/tmp/astmail-XXXXXX";
1691 if (vmu && ast_strlen_zero(vmu->email)) {
1692 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1695 if (!strcmp(format, "wav49"))
1697 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));
1698 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1700 p = vm_mkftemp(tmp);
1702 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1705 gethostname(host, sizeof(host)-1);
1706 if (strchr(srcemail, '@'))
1707 ast_copy_string(who, srcemail, sizeof(who));
1709 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1711 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1712 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1713 fprintf(p, "Date: %s\n", date);
1715 /* Set date format for voicemail mail */
1716 strftime(date, sizeof(date), emaildateformat, &tm);
1719 struct ast_channel *ast;
1720 if ((ast = ast_channel_alloc(0))) {
1722 int vmlen = strlen(fromstring)*3 + 200;
1723 if ((passdata = alloca(vmlen))) {
1724 memset(passdata, 0, vmlen);
1725 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1726 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1727 fprintf(p, "From: %s <%s>\n",passdata,who);
1728 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1729 ast_channel_free(ast);
1730 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1732 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1733 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1736 struct ast_channel *ast;
1737 if ((ast = ast_channel_alloc(0))) {
1739 int vmlen = strlen(emailsubject)*3 + 200;
1740 if ((passdata = alloca(vmlen))) {
1741 memset(passdata, 0, vmlen);
1742 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1743 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1744 fprintf(p, "Subject: %s\n",passdata);
1745 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1746 ast_channel_free(ast);
1747 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1750 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1752 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1753 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1755 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1756 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
1757 fprintf(p, "MIME-Version: 1.0\n");
1758 if (attach_user_voicemail) {
1759 /* Something unique. */
1760 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
1762 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1764 fprintf(p, "--%s\n", bound);
1766 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1768 struct ast_channel *ast;
1769 if ((ast = ast_channel_alloc(0))) {
1771 int vmlen = strlen(emailbody)*3 + 200;
1772 if ((passdata = alloca(vmlen))) {
1773 memset(passdata, 0, vmlen);
1774 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1775 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1776 fprintf(p, "%s\n",passdata);
1777 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1778 ast_channel_free(ast);
1779 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1781 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1783 "in mailbox %s from %s, on %s so you might\n"
1784 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1785 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1787 if (attach_user_voicemail) {
1788 /* Eww. We want formats to tell us their own MIME type */
1789 char *ctype = "audio/x-";
1790 if (!strcasecmp(format, "ogg"))
1791 ctype = "application/";
1793 fprintf(p, "--%s\n", bound);
1794 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1795 fprintf(p, "Content-Transfer-Encoding: base64\n");
1796 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1797 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1799 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1800 base_encode(fname, p);
1801 fprintf(p, "\n\n--%s--\n.\n", bound);
1804 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1805 ast_safe_system(tmp2);
1806 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1811 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)
1814 char host[MAXHOSTNAMELEN]="";
1817 char tmp[80] = "/tmp/astmail-XXXXXX";
1820 FILE *p = vm_mkftemp(tmp);
1823 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1826 gethostname(host, sizeof(host)-1);
1827 if (strchr(srcemail, '@'))
1828 ast_copy_string(who, srcemail, sizeof(who));
1830 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1832 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1833 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1834 fprintf(p, "Date: %s\n", date);
1836 if (*pagerfromstring) {
1837 struct ast_channel *ast;
1838 if ((ast = ast_channel_alloc(0))) {
1840 int vmlen = strlen(fromstring)*3 + 200;
1841 if ((passdata = alloca(vmlen))) {
1842 memset(passdata, 0, vmlen);
1843 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1844 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1845 fprintf(p, "From: %s <%s>\n",passdata,who);
1847 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1848 ast_channel_free(ast);
1849 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1851 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1852 fprintf(p, "To: %s\n", pager);
1854 struct ast_channel *ast;
1855 if ((ast = ast_channel_alloc(0))) {
1857 int vmlen = strlen(pagersubject)*3 + 200;
1858 if ((passdata = alloca(vmlen))) {
1859 memset(passdata, 0, vmlen);
1860 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1861 pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
1862 fprintf(p, "Subject: %s\n\n",passdata);
1863 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1864 ast_channel_free(ast);
1865 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1867 fprintf(p, "Subject: New VM\n\n");
1868 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1870 struct ast_channel *ast;
1871 if ((ast = ast_channel_alloc(0))) {
1873 int vmlen = strlen(pagerbody)*3 + 200;
1874 if ((passdata = alloca(vmlen))) {
1875 memset(passdata, 0, vmlen);
1876 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1877 pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
1878 fprintf(p, "%s\n",passdata);
1879 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1880 ast_channel_free(ast);
1881 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1883 fprintf(p, "New %s long msg in box %s\n"
1884 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1887 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1888 ast_safe_system(tmp2);
1889 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1894 static int get_date(char *s, int len)
1899 localtime_r(&t,&tm);
1900 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1903 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1907 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
1909 if (ast_fileexists(fn, NULL, NULL) > 0) {
1910 res = ast_streamfile(chan, fn, chan->language);
1915 res = ast_waitstream(chan, ecodes);
1921 /* Dispose just in case */
1923 res = ast_streamfile(chan, "vm-theperson", chan->language);
1926 res = ast_waitstream(chan, ecodes);
1929 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1934 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1936 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1939 res = ast_waitstream(chan, ecodes);
1943 static void free_user(struct ast_vm_user *vmu)
1945 if (ast_test_flag(vmu, VM_ALLOCED))
1949 static void free_zone(struct vm_zone *z)
1954 static char *mbox(int id)
1982 #ifdef USE_ODBC_STORAGE
1983 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
1997 /* If no mailbox, return immediately */
1998 if (ast_strlen_zero(mailbox))
2001 ast_copy_string(tmp, mailbox, sizeof(tmp));
2003 context = strchr(tmp, '@');
2008 context = "default";
2011 obj = fetch_odbc_obj(odbc_database, 0);
2013 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2014 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2015 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2018 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir LIKE '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2019 res = SQLPrepare(stmt, sql, SQL_NTS);
2020 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2021 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2022 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2025 res = odbc_smart_execute(obj, stmt);
2026 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2027 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2028 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2031 res = SQLFetch(stmt);
2032 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2033 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2034 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2037 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2038 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2039 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2040 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2043 *newmsgs = atoi(rowdata);
2044 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2046 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2047 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2048 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2051 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "Old");
2052 res = SQLPrepare(stmt, sql, SQL_NTS);
2053 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2054 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2055 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2058 res = odbc_smart_execute(obj, stmt);
2059 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2060 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2061 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2064 res = SQLFetch(stmt);
2065 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2066 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2067 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2070 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2071 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2072 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2073 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2076 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2077 *oldmsgs = atoi(rowdata);
2080 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2086 static int has_voicemail(const char *mailbox, const char *folder)
2097 /* If no mailbox, return immediately */
2098 if (ast_strlen_zero(mailbox))
2101 ast_copy_string(tmp, mailbox, sizeof(tmp));
2103 context = strchr(tmp, '@');
2108 context = "default";
2111 obj = fetch_odbc_obj(odbc_database, 0);
2113 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2114 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2115 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2118 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2119 res = SQLPrepare(stmt, sql, SQL_NTS);
2120 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2121 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2122 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2125 res = odbc_smart_execute(obj, stmt);
2126 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2127 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2128 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2131 res = SQLFetch(stmt);
2132 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2133 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2134 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2137 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2138 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2139 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2140 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2143 nummsgs = atoi(rowdata);
2144 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2146 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2157 static int has_voicemail(const char *mailbox, const char *folder)
2168 /* If no mailbox, return immediately */
2169 if (ast_strlen_zero(mailbox))
2171 if (strchr(mailbox, ',')) {
2172 ast_copy_string(tmp, mailbox, sizeof(tmp));
2175 while((cur = strsep(&mb, ","))) {
2176 if (!ast_strlen_zero(cur)) {
2177 if (has_voicemail(cur, folder))
2183 ast_copy_string(tmp, mailbox, sizeof(tmp));
2184 context = strchr(tmp, '@');
2189 context = "default";
2190 snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
2194 while ((de = readdir(dir))) {
2195 if (!strncasecmp(de->d_name, "msg", 3))
2205 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2217 /* If no mailbox, return immediately */
2218 if (ast_strlen_zero(mailbox))
2220 if (strchr(mailbox, ',')) {
2224 ast_copy_string(tmp, mailbox, sizeof(tmp));
2226 while((cur = strsep(&mb, ", "))) {
2227 if (!ast_strlen_zero(cur)) {
2228 if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2240 ast_copy_string(tmp, mailbox, sizeof(tmp));
2241 context = strchr(tmp, '@');
2246 context = "default";
2248 snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
2251 while ((de = readdir(dir))) {
2252 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2253 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2261 snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
2264 while ((de = readdir(dir))) {
2265 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2266 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2278 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
2280 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)
2282 char fromdir[256], todir[256], frompath[256], topath[256];
2283 char *frombox = mbox(imbox);
2286 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2288 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2290 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2291 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2293 if (vm_lock_path(todir))
2294 return ERROR_LOCK_PATH;
2298 make_file(topath, sizeof(topath), todir, recipmsgnum);
2299 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2302 } while (recipmsgnum < recip->maxmsg);
2303 if (recipmsgnum < recip->maxmsg) {
2304 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2306 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2308 ast_unlock_path(todir);
2309 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2314 static void run_externnotify(char *context, char *extension)
2316 char arguments[255];
2317 char ext_context[256] = "";
2318 int newvoicemails = 0, oldvoicemails = 0;
2320 struct ast_smdi_mwi_message *mwi_msg;
2323 if (!ast_strlen_zero(context))
2324 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2326 ast_copy_string(ext_context, extension, sizeof(ext_context));
2329 if (!strcasecmp(externnotify, "smdi")) {
2330 if (ast_app_has_voicemail(ext_context, NULL))
2331 ast_smdi_mwi_set(smdi_iface, extension);
2333 ast_smdi_mwi_unset(smdi_iface, extension);
2335 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2336 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2337 if (!strncmp(mwi_msg->cause, "INV", 3))
2338 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2339 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2340 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2341 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2342 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2344 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2346 } else if (!ast_strlen_zero(externnotify)) {
2348 if (!ast_strlen_zero(externnotify)) {
2350 if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
2351 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2353 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2354 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2355 ast_safe_system(arguments);
2360 struct leave_vm_options {
2362 signed char record_gain;
2365 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2367 char tmptxtfile[256], txtfile[256];
2378 char prefile[256]="";
2379 char tempfile[256]="";
2380 char ext_context[256] = "";
2383 char ecodes[16] = "#";
2384 char tmp[256] = "", *tmpptr;
2385 struct ast_vm_user *vmu;
2386 struct ast_vm_user svm;
2387 const char *category = NULL;
2389 ast_copy_string(tmp, ext, sizeof(tmp));
2391 context = strchr(tmp, '@');
2395 tmpptr = strchr(context, '&');
2397 tmpptr = strchr(ext, '&');
2405 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2407 if (!(vmu = find_user(&svm, context, ext))) {
2408 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2409 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2410 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2411 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2415 /* Setup pre-file if appropriate */
2416 if (strcmp(vmu->context, "default"))
2417 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2419 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2420 if (ast_test_flag(options, OPT_BUSY_GREETING))
2421 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2422 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2423 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2424 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2425 RETRIEVE(tempfile, -1);
2426 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2427 ast_copy_string(prefile, tempfile, sizeof(prefile));
2428 DISPOSE(tempfile, -1);
2429 /* It's easier just to try to make it than to check for its existence */
2430 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2432 /* Check current or macro-calling context for special extensions */
2433 if (!ast_strlen_zero(vmu->exit)) {
2434 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2435 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2436 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
2437 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2438 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2439 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2443 if (!ast_strlen_zero(vmu->exit)) {
2444 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2445 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2446 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2447 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2448 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2449 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2453 /* Play the beginning intro if desired */
2454 if (!ast_strlen_zero(prefile)) {
2455 RETRIEVE(prefile, -1);
2456 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2457 if (ast_streamfile(chan, prefile, chan->language) > -1)
2458 res = ast_waitstream(chan, ecodes);
2460 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2461 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2463 DISPOSE(prefile, -1);
2465 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2467 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2472 /* On a '#' we skip the instructions */
2473 ast_set_flag(options, OPT_SILENT);
2476 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2477 res = ast_streamfile(chan, INTRO, chan->language);
2479 res = ast_waitstream(chan, ecodes);
2481 ast_set_flag(options, OPT_SILENT);
2486 ast_stopstream(chan);
2487 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2488 other than the operator -- an automated attendant or mailbox login for example */
2490 chan->exten[0] = 'a';
2491 chan->exten[1] = '\0';
2492 if (!ast_strlen_zero(vmu->exit)) {
2493 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2494 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2495 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2499 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2502 /* Check for a '0' here */
2505 if (ast_test_flag(vmu, VM_OPERATOR)) {
2506 chan->exten[0] = 'o';
2507 chan->exten[1] = '\0';
2508 if (!ast_strlen_zero(vmu->exit)) {
2509 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2510 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2511 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2513 ast_play_and_wait(chan, "transfer");
2516 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2519 ast_play_and_wait(chan, "vm-sorry");
2520 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2526 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2529 /* The meat of recording the message... All the announcements and beeps have been played*/
2530 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2531 if (!ast_strlen_zero(fmt)) {
2534 if (vm_lock_path(dir)) {
2536 return ERROR_LOCK_PATH;
2540 * This operation can be very expensive if done say over NFS or if the mailbox has 100+ messages
2541 * in the folder. So we should get this first so we don't cut off the first few seconds of the
2545 make_file(fn, sizeof(fn), dir, msgnum);
2546 if (!EXISTS(dir,msgnum,fn,chan->language))
2549 } while (msgnum < vmu->maxmsg);
2551 /* Now play the beep once we have the message number for our next message. */
2553 /* Unless we're *really* silent, try to send the beep */
2554 res = ast_streamfile(chan, "beep", chan->language);
2556 res = ast_waitstream(chan, "");
2558 if (msgnum < vmu->maxmsg) {
2559 /* assign a variable with the name of the voicemail file */
2560 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
2562 /* Store information */
2563 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2564 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s.txt.tmp", fn);
2565 txt = fopen(tmptxtfile, "w+");
2567 get_date(date, sizeof(date));
2570 "; Message Information file\n"
2589 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2590 date, (long)time(NULL),
2591 category ? category : "");
2593 ast_log(LOG_WARNING, "Error opening text file for output\n");
2594 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2603 fprintf(txt, "duration=%d\n", duration);
2605 rename(tmptxtfile, txtfile);
2608 if (duration < vmminmessage) {
2609 if (option_verbose > 2)
2610 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2611 DELETE(dir,msgnum,fn);
2612 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2613 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2616 /* Are there to be more recipients of this message? */
2618 struct ast_vm_user recipu, *recip;
2619 char *exten, *context;
2621 exten = strsep(&tmpptr, "&");
2622 context = strchr(exten, '@');
2627 if ((recip = find_user(&recipu, context, exten))) {
2628 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2632 if (ast_fileexists(fn, NULL, NULL)) {
2633 STORE(dir, vmu->mailbox, vmu->context, msgnum);
2634 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2635 DISPOSE(dir, msgnum);
2637 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
2639 ast_unlock_path(dir);
2640 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2642 res = ast_waitstream(chan, "");
2643 ast_log(LOG_WARNING, "No more messages possible\n");
2644 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2647 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2654 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
2656 /* we know max messages, so stop process when number is hit */
2662 if (vm_lock_path(dir))
2663 return ERROR_LOCK_PATH;
2665 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
2666 make_file(sfn, sizeof(sfn), dir, x);
2667 if (EXISTS(dir, x, sfn, NULL)) {
2670 make_file(dfn, sizeof(dfn), dir, dest);
2671 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
2677 ast_unlock_path(dir);
2683 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
2686 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2690 static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *context, char *username, int box)
2695 char *dbox = mbox(box);
2697 make_file(sfn, sizeof(sfn), dir, msg);
2698 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
2700 if (vm_lock_path(ddir))
2701 return ERROR_LOCK_PATH;
2703 for (x = 0; x < vmu->maxmsg; x++) {
2704 make_file(dfn, sizeof(dfn), ddir, x);
2705 if (!EXISTS(ddir, x, dfn, NULL))
2708 if (x >= vmu->maxmsg) {
2709 ast_unlock_path(ddir);
2712 if (strcmp(sfn, dfn)) {
2713 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
2715 ast_unlock_path(ddir);
2720 static int adsi_logo(unsigned char *buf)
2723 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2724 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2728 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2730 unsigned char buf[256];
2736 bytes += adsi_data_mode(buf + bytes);
2737 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2740 bytes += adsi_logo(buf);
2741 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2743 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
2745 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2746 bytes += adsi_data_mode(buf + bytes);
2747 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2749 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2751 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2752 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2753 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2754 bytes += adsi_voice_mode(buf + bytes, 0);
2755 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2762 bytes += adsi_logo(buf);
2763 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
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);
2769 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2770 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2771 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2772 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2773 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2774 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 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_voice_mode(buf + bytes, 0);
2783 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2784 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2788 /* These buttons we load but don't use yet */
2789 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2790 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2791 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2792 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2793 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2794 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2795 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2798 /* Add another dot */
2800 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2801 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2802 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2807 snprintf(num, sizeof(num), "%d", x);
2808 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2810 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2811 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2814 /* Add another dot */
2816 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2817 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2818 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2821 if (adsi_end_download(chan)) {
2823 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2824 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2825 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2826 bytes += adsi_voice_mode(buf + bytes, 0);
2827 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2831 bytes += adsi_download_disconnect(buf + bytes);
2832 bytes += adsi_voice_mode(buf + bytes, 0);
2833 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2835 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2840 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2841 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2843 ast_log(LOG_DEBUG, "Restarting session...\n");
2846 /* Load the session now */
2847 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2849 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2851 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2853 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2857 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2860 if (!adsi_available(chan))
2862 x = adsi_load_session(chan, adsifdn, adsiver, 1);
2866 if (adsi_load_vmail(chan, useadsi)) {
2867 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2874 static void adsi_login(struct ast_channel *chan)
2876 unsigned char buf[256];
2878 unsigned char keys[8];
2880 if (!adsi_available(chan))
2885 /* Set one key for next */
2886 keys[3] = ADSI_KEY_APPS + 3;
2888 bytes += adsi_logo(buf + bytes);
2889 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2890 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2891 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2892 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2893 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2894 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2895 bytes += adsi_set_keys(buf + bytes, keys);
2896 bytes += adsi_voice_mode(buf + bytes, 0);
2897 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2900 static void adsi_password(struct ast_channel *chan)
2902 unsigned char buf[256];
2904 unsigned char keys[8];
2906 if (!adsi_available(chan))
2911 /* Set one key for next */
2912 keys[3] = ADSI_KEY_APPS + 3;
2914 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2915 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2916 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2917 bytes += adsi_set_keys(buf + bytes, keys);
2918 bytes += adsi_voice_mode(buf + bytes, 0);
2919 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2922 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2924 unsigned char buf[256];
2926 unsigned char keys[8];
2929 if (!adsi_available(chan))
2933 y = ADSI_KEY_APPS + 12 + start + x;
2934 if (y > ADSI_KEY_APPS + 12 + 4)
2936 keys[x] = ADSI_KEY_SKT | y;
2938 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2942 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2943 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2944 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2945 bytes += adsi_set_keys(buf + bytes, keys);
2946 bytes += adsi_voice_mode(buf + bytes, 0);
2948 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2951 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2954 unsigned char buf[256];
2955 char buf1[256], buf2[256];
2961 char datetime[21]="";
2964 unsigned char keys[8];
2968 if (!adsi_available(chan))
2971 /* Retrieve important info */
2972 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
2973 f = fopen(fn2, "r");
2976 fgets((char *)buf, sizeof(buf), f);
2979 stringp = (char *)buf;
2980 strsep(&stringp, "=");
2981 val = strsep(&stringp, "=");
2982 if (!ast_strlen_zero(val)) {
2983 if (!strcmp((char *)buf, "callerid"))
2984 ast_copy_string(cid, val, sizeof(cid));
2985 if (!strcmp((char *)buf, "origdate"))
2986 ast_copy_string(datetime, val, sizeof(datetime));
2992 /* New meaning for keys */
2994 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2999 /* No prev key, provide "Folder" instead */
3000 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3002 if (vms->curmsg >= vms->lastmsg) {
3003 /* If last message ... */
3005 /* but not only message, provide "Folder" instead */
3006 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3007 bytes += adsi_voice_mode(buf + bytes, 0);
3010 /* Otherwise if only message, leave blank */
3015 if (!ast_strlen_zero(cid)) {
3016 ast_callerid_parse(cid, &name, &num);
3020 name = "Unknown Caller";
3022 /* If deleted, show "undeleted" */
3024 if (vms->deleted[vms->curmsg])
3025 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3028 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);