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
80 #define VOICEMAIL_DIR_MODE 0770
81 #define VOICEMAIL_FILE_MODE 0660
83 #define VOICEMAIL_CONFIG "voicemail.conf"
84 #define ASTERISK_USERNAME "asterisk"
86 /* Default mail command to mail voicemail. Change it with the
87 mailcmd= command in voicemail.conf */
88 #define SENDMAIL "/usr/sbin/sendmail -t"
90 #define INTRO "vm-intro"
93 #define MAXMSGLIMIT 9999
95 #define BASEMAXINLINE 256
96 #define BASELINELEN 72
97 #define BASEMAXINLINE 256
100 #define MAX_DATETIME_FORMAT 512
101 #define MAX_NUM_CID_CONTEXTS 10
103 #define VM_REVIEW (1 << 0)
104 #define VM_OPERATOR (1 << 1)
105 #define VM_SAYCID (1 << 2)
106 #define VM_SVMAIL (1 << 3)
107 #define VM_ENVELOPE (1 << 4)
108 #define VM_SAYDURATION (1 << 5)
109 #define VM_SKIPAFTERCMD (1 << 6)
110 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
111 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
112 #define VM_PBXSKIP (1 << 9)
113 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
114 #define VM_ATTACH (1 << 11)
115 #define VM_DELETE (1 << 12)
116 #define VM_ALLOCED (1 << 13)
117 #define VM_SEARCH (1 << 14)
119 #define ERROR_LOCK_PATH -100
122 OPT_SILENT = (1 << 0),
123 OPT_BUSY_GREETING = (1 << 1),
124 OPT_UNAVAIL_GREETING = (1 << 2),
125 OPT_RECORDGAIN = (1 << 3),
126 OPT_PREPEND_MAILBOX = (1 << 4),
127 OPT_PRIORITY_JUMP = (1 << 5),
128 OPT_AUTOPLAY = (1 << 6),
132 OPT_ARG_RECORDGAIN = 0,
133 OPT_ARG_PLAYFOLDER = 1,
134 /* This *must* be the last value in this enum! */
135 OPT_ARG_ARRAY_SIZE = 2,
138 AST_APP_OPTIONS(vm_app_options, {
139 AST_APP_OPTION('s', OPT_SILENT),
140 AST_APP_OPTION('b', OPT_BUSY_GREETING),
141 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
142 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
143 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
144 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
145 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
148 static int load_config(void);
150 /*! \page vmlang Voicemail Language Syntaxes Supported
152 \par Syntaxes supported, not really language codes.
159 \arg \b pt - Portuguese
161 \arg \b no - Norwegian
164 German requires the following additional soundfile:
165 \arg \b 1F einE (feminine)
167 Spanish requires the following additional soundfile:
168 \arg \b 1M un (masculine)
170 Dutch, Portuguese & Spanish require the following additional soundfiles:
171 \arg \b vm-INBOXs singular of 'new'
172 \arg \b vm-Olds singular of 'old/heard/read'
175 \arg \b vm-INBOX nieuwe (nl)
176 \arg \b vm-Old oude (nl)
179 \arg \b vm-nytt singular of 'new'
180 \arg \b vm-nya plural of 'new'
181 \arg \b vm-gammalt singular of 'old'
182 \arg \b vm-gamla plural of 'old'
183 \arg \b digits/ett 'one', not always same as 'digits/1'
186 \arg \b vm-ny singular of 'new'
187 \arg \b vm-nye plural of 'new'
188 \arg \b vm-gammel singular of 'old'
189 \arg \b vm-gamle plural of 'old'
197 Italian requires the following additional soundfile:
201 \arg \b vm-nuovi new plural
202 \arg \b vm-vecchio old
203 \arg \b vm-vecchi old plural
205 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
206 spelled among others when you have to change folder. For the above reasons, vm-INBOX
207 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
216 unsigned char iobuf[BASEMAXINLINE];
219 /*! Structure for linked list of users */
221 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
222 char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */
223 char password[80]; /*!< Secret pin code, numbers only */
224 char fullname[80]; /*!< Full name, for directory app */
225 char email[80]; /*!< E-mail address */
226 char pager[80]; /*!< E-mail address to pager (no attachment) */
227 char serveremail[80]; /*!< From: Mail address */
228 char mailcmd[160]; /*!< Configurable mail command */
229 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
230 char zonetag[80]; /*!< Time zone */
233 char uniqueid[20]; /*!< Unique integer identifier */
235 unsigned int flags; /*!< VM_ flags */
237 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
238 struct ast_vm_user *next;
244 char msg_format[512];
245 struct vm_zone *next;
264 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
265 int option, signed char record_gain);
266 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
267 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
268 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
269 signed char record_gain);
270 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
271 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
273 static void apply_options(struct ast_vm_user *vmu, const char *options);
275 #ifdef USE_ODBC_STORAGE
276 static char odbc_database[80];
277 static char odbc_table[80];
278 #define RETRIEVE(a,b) retrieve_file(a,b)
279 #define DISPOSE(a,b) remove_file(a,b)
280 #define STORE(a,b,c,d) store_file(a,b,c,d)
281 #define EXISTS(a,b,c,d) (message_exists(a,b))
282 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
283 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
284 #define DELETE(a,b,c) (delete_file(a,b))
286 #define RETRIEVE(a,b)
288 #define STORE(a,b,c,d)
289 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
290 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
291 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
292 #define DELETE(a,b,c) (vm_delete(c))
295 static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
297 static char ext_pass_cmd[128];
299 static char *tdesc = "Comedian Mail (Voicemail System)";
301 static char *addesc = "Comedian Mail";
303 static char *synopsis_vm =
304 "Leave a Voicemail message";
306 static char *descrip_vm =
307 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
308 "application allows the calling party to leave a message for the specified\n"
309 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
310 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
311 "specified mailbox does not exist.\n"
312 " The Voicemail application will exit if any of the following DTMF digits are\n"
314 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
315 " * - Jump to the 'a' extension in the current dialplan context.\n"
316 " This application will set the following channel variable upon completion:\n"
317 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
318 " application. The possible values are:\n"
319 " SUCCESS | USEREXIT | FAILED\n\n"
321 " b - Play the 'busy' greeting to the calling party.\n"
322 " g(#) - Use the specified amount of gain when recording the voicemail\n"
323 " message. The units are whole-number decibels (dB).\n"
324 " s - Skip the playback of instructions for leaving a message to the\n"
326 " u - Play the 'unavailble greeting.\n"
327 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
330 static char *synopsis_vmain =
331 "Check Voicemail messages";
333 static char *descrip_vmain =
334 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
335 "calling party to check voicemail messages. A specific mailbox, and optional\n"
336 "corresponding context, may be specified. If a mailbox is not provided, the\n"
337 "calling party will be prompted to enter one. If a context is not specified,\n"
338 "the 'default' context will be used.\n\n"
340 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
341 " is entered by the caller.\n"
342 " g(#) - Use the specified amount of gain when recording a voicemail\n"
343 " message. The units are whole-number decibels (dB).\n"
344 " s - Skip checking the passcode for the mailbox.\n"
345 " a(#) - Skip folder prompt and go directly to folder specified.\n"
346 " Defaults to INBOX\n";
348 static char *synopsis_vm_box_exists =
349 "Check to see if Voicemail mailbox exists";
351 static char *descrip_vm_box_exists =
352 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
353 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
355 " This application will set the following channel variable upon completion:\n"
356 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
357 " MailboxExists application. Possible values include:\n"
358 " SUCCESS | FAILED\n\n"
360 " j - Jump to priority n+101 if the mailbox is found.\n";
362 static char *synopsis_vmauthenticate =
363 "Authenticate with Voicemail passwords";
365 static char *descrip_vmauthenticate =
366 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
367 "same way as the Authenticate application, but the passwords are taken from\n"
369 " If the mailbox is specified, only that mailbox's password will be considered\n"
370 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
371 "be set with the authenticated mailbox.\n\n"
373 " s - Skip playing the initial prompts.\n";
375 /* Leave a message */
376 static char *app = "VoiceMail";
378 /* Check mail, control, etc */
379 static char *app2 = "VoiceMailMain";
381 static char *app3 = "MailboxExists";
382 static char *app4 = "VMAuthenticate";
384 AST_MUTEX_DEFINE_STATIC(vmlock);
385 struct ast_vm_user *users;
386 struct ast_vm_user *usersl;
387 struct vm_zone *zones = NULL;
388 struct vm_zone *zonesl = NULL;
389 static int maxsilence;
391 static int silencethreshold = 128;
392 static char serveremail[80];
393 static char mailcmd[160]; /* Configurable mail cmd */
394 static char externnotify[160];
396 static char vmfmts[80];
397 static int vmminmessage;
398 static int vmmaxmessage;
401 static int maxlogins;
403 static struct ast_flags globalflags = {0};
405 static int saydurationminfo;
407 static char dialcontext[AST_MAX_CONTEXT];
408 static char callcontext[AST_MAX_CONTEXT];
409 static char exitcontext[AST_MAX_CONTEXT];
411 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
414 static char *emailbody = NULL;
415 static char *emailsubject = NULL;
416 static char *pagerbody = NULL;
417 static char *pagersubject = NULL;
418 static char fromstring[100];
419 static char pagerfromstring[100];
420 static char emailtitle[100];
421 static char charset[32] = "ISO-8859-1";
423 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
424 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
425 static int adsiver = 1;
426 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
432 static void populate_defaults(struct ast_vm_user *vmu)
434 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
435 if (saydurationminfo)
436 vmu->saydurationm = saydurationminfo;
438 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
440 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
442 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
444 vmu->maxmsg = maxmsg;
447 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
450 if (!strcasecmp(var, "attach")) {
451 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
452 } else if (!strcasecmp(var, "serveremail")) {
453 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
454 } else if (!strcasecmp(var, "language")) {
455 ast_copy_string(vmu->language, value, sizeof(vmu->language));
456 } else if (!strcasecmp(var, "tz")) {
457 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
458 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
459 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
460 } else if (!strcasecmp(var, "saycid")){
461 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
462 } else if (!strcasecmp(var,"sendvoicemail")){
463 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
464 } else if (!strcasecmp(var, "review")){
465 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
466 } else if (!strcasecmp(var, "operator")){
467 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
468 } else if (!strcasecmp(var, "envelope")){
469 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
470 } else if (!strcasecmp(var, "sayduration")){
471 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
472 } else if (!strcasecmp(var, "saydurationm")){
473 if (sscanf(value, "%d", &x) == 1) {
474 vmu->saydurationm = x;
476 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
478 } else if (!strcasecmp(var, "forcename")){
479 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
480 } else if (!strcasecmp(var, "forcegreetings")){
481 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
482 } else if (!strcasecmp(var, "callback")) {
483 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
484 } else if (!strcasecmp(var, "dialout")) {
485 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
486 } else if (!strcasecmp(var, "exitcontext")) {
487 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
488 } else if (!strcasecmp(var, "maxmsg")) {
489 vmu->maxmsg = atoi(value);
490 if (vmu->maxmsg <= 0) {
491 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
492 vmu->maxmsg = MAXMSG;
493 } else if (vmu->maxmsg > MAXMSGLIMIT) {
494 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
495 vmu->maxmsg = MAXMSGLIMIT;
497 } else if (!strcasecmp(var, "options")) {
498 apply_options(vmu, value);
502 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
505 if (!ast_strlen_zero(vmu->uniqueid)) {
506 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
508 ast_copy_string(vmu->password, password, sizeof(vmu->password));
518 static void apply_options(struct ast_vm_user *vmu, const char *options)
519 { /* Destructively Parse options and apply */
523 stringp = ast_strdupa(options);
524 while ((s = strsep(&stringp, "|"))) {
526 if ((var = strsep(&value, "=")) && value) {
527 apply_option(vmu, var, value);
532 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
534 struct ast_variable *var, *tmp;
535 struct ast_vm_user *retval;
537 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
539 ast_set_flag(retval, VM_ALLOCED);
541 memset(retval, 0, sizeof(*retval));
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)))
596 /* Make a copy, so that on a reload, we have no race */
597 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
598 memcpy(vmu, cur, sizeof(*vmu));
599 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
603 vmu = find_user_realtime(ivm, context, mailbox);
604 ast_mutex_unlock(&vmlock);
608 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
610 /* This function could be made to generate one from a database, too */
611 struct ast_vm_user *cur;
613 ast_mutex_lock(&vmlock);
616 if ((!context || !strcasecmp(context, cur->context)) &&
617 (!strcasecmp(mailbox, cur->mailbox)))
622 ast_copy_string(cur->password, newpass, sizeof(cur->password));
625 ast_mutex_unlock(&vmlock);
629 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
631 /* There's probably a better way of doing this. */
632 /* That's why I've put the password change in a separate function. */
633 /* This could also be done with a database function */
640 char currcontext[256] ="";
641 char tmpin[AST_CONFIG_MAX_PATH];
642 char tmpout[AST_CONFIG_MAX_PATH];
645 if (!change_password_realtime(vmu, newpassword))
648 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
649 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
650 configin = fopen(tmpin,"r");
652 configout = fopen(tmpout,"w+");
655 if (!configin || !configout) {
659 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
663 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
667 while (!feof(configin)) {
668 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
670 /* Read in the line */
671 fgets(inbuf, sizeof(inbuf), configin);
674 if (ast_strlen_zero(inbuf)) {
675 fprintf(configout, "\n");
679 /* Make a backup of it */
680 ast_copy_string(orig, inbuf, sizeof(orig));
683 Read the file line by line, split each line into a comment and command section
684 only parse the command portion of the line
686 if (inbuf[strlen(inbuf) - 1] == '\n')
687 inbuf[strlen(inbuf) - 1] = '\0';
689 if ((comment = strchr(inbuf, ';')))
690 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
692 if (ast_strlen_zero(inbuf)) {
693 fprintf(configout, "%s", orig);
697 /* Check for a context, first '[' to first ']' */
698 if ((tmpctx = strchr(inbuf, '['))) {
699 tmpctxend = strchr(tmpctx, ']');
702 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
703 fprintf(configout, "%s", orig);
708 /* This isn't a context line, check for MBX => PSWD... */
710 if ((pass = strchr(user, '='))) {
711 /* We have a line in the form of aaaaa=aaaaaa */
714 user = ast_strip(user);
719 pass = ast_skip_blanks(pass);
722 Since no whitespace allowed in fields, or more correctly white space
723 inside the fields is there for a purpose, we can just terminate pass
724 at the comma or EOL whichever comes first.
726 if ((rest = strchr(pass, ',')))
732 /* Compare user, pass AND context */
733 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
734 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
735 !strcasecmp(currcontext, vmu->context)) {
736 /* This is the line */
738 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
740 fprintf(configout, "%s => %s", user, newpassword);
742 /* If there was a comment on the line print it out */
744 fprintf(configout, ";%s\n", comment);
746 fprintf(configout, "\n");
749 /* Put it back like it was */
750 fprintf(configout, "%s", orig);
756 stat(tmpin, &statbuf);
757 chmod(tmpout, statbuf.st_mode);
758 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
760 rename(tmpout, tmpin);
761 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
762 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
765 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
768 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
769 if (!ast_safe_system(buf))
770 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
773 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
775 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, mailbox);
778 static int make_file(char *dest, int len, char *dir, int num)
780 return snprintf(dest, len, "%s/msg%04d", dir, num);
783 /*! \brief basically mkdir -p $dest/$context/$ext/$mailbox
784 * \param dest String. base directory.
785 * \param context String. Ignored if is null or empty string.
786 * \param ext String. Ignored if is null or empty string.
787 * \param mailbox String. Ignored if is null or empty string.
788 * \return 0 on failure, 1 on success.
790 static int create_dirpath(char *dest, int len, char *context, char *ext, char *mailbox)
792 mode_t mode = VOICEMAIL_DIR_MODE;
794 if(context && context[0] != '\0') {
795 make_dir(dest, len, context, "", "");
796 if(mkdir(dest, mode) && errno != EEXIST) {
797 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
801 if(ext && ext[0] != '\0') {
802 make_dir(dest, len, context, ext, "");
803 if(mkdir(dest, mode) && errno != EEXIST) {
804 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
808 if(mailbox && mailbox[0] != '\0') {
809 make_dir(dest, len, context, ext, mailbox);
810 if(mkdir(dest, mode) && errno != EEXIST) {
811 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
818 /* only return failure if ast_lock_path returns 'timeout',
819 not if the path does not exist or any other reason
821 static int vm_lock_path(const char *path)
823 switch (ast_lock_path(path)) {
824 case AST_LOCK_TIMEOUT:
832 #ifdef USE_ODBC_STORAGE
833 static int retrieve_file(char *dir, int msgnum)
840 SQLSMALLINT colcount=0;
847 SQLSMALLINT datatype;
848 SQLSMALLINT decimaldigits;
849 SQLSMALLINT nullable;
858 obj = fetch_odbc_obj(odbc_database, 0);
860 ast_copy_string(fmt, vmfmts, sizeof(fmt));
861 c = strchr(fmt, '|');
864 if (!strcasecmp(fmt, "wav49"))
866 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
868 make_file(fn, sizeof(fn), dir, msgnum);
870 ast_copy_string(fn, dir, sizeof(fn));
871 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
872 f = fopen(full_fn, "w+");
873 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
874 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
875 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
876 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
879 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
880 res = SQLPrepare(stmt, sql, SQL_NTS);
881 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
882 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
883 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
886 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
887 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
888 res = odbc_smart_execute(obj, stmt);
889 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
890 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
891 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
894 res = SQLFetch(stmt);
895 if (res == SQL_NO_DATA) {
896 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
899 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
900 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
901 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
904 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
906 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
907 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
910 res = SQLNumResultCols(stmt, &colcount);
911 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
912 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
913 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
917 fprintf(f, "[message]\n");
918 for (x=0;x<colcount;x++) {
920 collen = sizeof(coltitle);
921 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
922 &datatype, &colsize, &decimaldigits, &nullable);
923 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
924 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
925 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
928 if (!strcasecmp(coltitle, "recording")) {
929 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
931 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
934 lseek(fd, fdlen - 1, SEEK_SET);
935 if (write(fd, tmp, 1) != 1) {
940 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
943 memset(fdm, 0, fdlen);
944 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
945 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
946 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
947 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
952 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
953 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
954 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
955 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
958 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
959 fprintf(f, "%s=%s\n", coltitle, rowdata);
962 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
964 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
975 static int remove_file(char *dir, int msgnum)
982 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
983 make_file(fn, sizeof(fn), dir, msgnum);
985 ast_copy_string(fn, dir, sizeof(fn));
986 ast_filedelete(fn, NULL);
987 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
992 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1001 obj = fetch_odbc_obj(odbc_database, 0);
1003 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1004 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1005 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1008 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1009 res = SQLPrepare(stmt, sql, SQL_NTS);
1010 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1011 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1012 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1015 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1016 res = odbc_smart_execute(obj, stmt);
1017 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1018 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1019 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1022 res = SQLFetch(stmt);
1023 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1024 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1025 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1028 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1029 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1030 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1031 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1034 if (sscanf(rowdata, "%d", &x) != 1)
1035 ast_log(LOG_WARNING, "Failed to read message count!\n");
1036 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1038 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1043 static int message_exists(char *dir, int msgnum)
1053 obj = fetch_odbc_obj(odbc_database, 0);
1055 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1056 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1057 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1058 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1061 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1062 res = SQLPrepare(stmt, sql, SQL_NTS);
1063 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1064 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1065 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1068 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1069 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1070 res = odbc_smart_execute(obj, stmt);
1071 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1072 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1073 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1076 res = SQLFetch(stmt);
1077 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1078 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1079 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1082 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1083 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1084 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1085 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1088 if (sscanf(rowdata, "%d", &x) != 1)
1089 ast_log(LOG_WARNING, "Failed to read message count!\n");
1090 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1092 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1097 static int count_messages(struct ast_vm_user *vmu, char *dir)
1099 return last_message_index(vmu, dir) + 1;
1102 static void delete_file(char *sdir, int smsg)
1110 obj = fetch_odbc_obj(odbc_database, 0);
1112 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1113 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1114 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1115 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1118 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1119 res = SQLPrepare(stmt, sql, SQL_NTS);
1120 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1121 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1122 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1125 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1126 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1127 res = odbc_smart_execute(obj, stmt);
1128 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1129 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1130 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1133 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1135 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1140 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1149 delete_file(ddir, dmsg);
1150 obj = fetch_odbc_obj(odbc_database, 0);
1152 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1153 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1154 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1155 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1156 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1159 #ifdef EXTENDED_ODBC_STORAGE
1160 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);
1162 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);
1164 res = SQLPrepare(stmt, sql, SQL_NTS);
1165 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1166 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1167 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1170 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1171 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1172 #ifdef EXTENDED_ODBC_STORAGE
1173 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1174 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1175 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1176 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1178 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1179 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1181 res = odbc_smart_execute(obj, stmt);
1182 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1183 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1184 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1187 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1189 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1194 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1209 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1210 char *category = "";
1211 struct ast_config *cfg=NULL;
1214 delete_file(dir, msgnum);
1215 obj = fetch_odbc_obj(odbc_database, 0);
1217 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1218 c = strchr(fmt, '|');
1221 if (!strcasecmp(fmt, "wav49"))
1223 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1225 make_file(fn, sizeof(fn), dir, msgnum);
1227 ast_copy_string(fn, dir, sizeof(fn));
1228 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1229 cfg = ast_config_load(full_fn);
1230 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1231 fd = open(full_fn, O_RDWR);
1233 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1237 context = ast_variable_retrieve(cfg, "message", "context");
1238 if (!context) context = "";
1239 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1240 if (!macrocontext) macrocontext = "";
1241 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1242 if (!callerid) callerid = "";
1243 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1244 if (!origtime) origtime = "";
1245 duration = ast_variable_retrieve(cfg, "message", "duration");
1246 if (!duration) duration = "";
1247 category = ast_variable_retrieve(cfg, "message", "category");
1248 if (!category) category = "";
1250 fdlen = lseek(fd, 0, SEEK_END);
1251 lseek(fd, 0, SEEK_SET);
1252 printf("Length is %d\n", fdlen);
1253 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1255 ast_log(LOG_WARNING, "Memory map failed!\n");
1258 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1259 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1260 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1263 if (!ast_strlen_zero(category))
1264 #ifdef EXTENDED_ODBC_STORAGE
1265 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1267 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
1270 #ifdef EXTENDED_ODBC_STORAGE
1271 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1273 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
1275 res = SQLPrepare(stmt, sql, SQL_NTS);
1276 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1277 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1278 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1281 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1282 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1283 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1284 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1285 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1286 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1287 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1288 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1289 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1290 #ifdef EXTENDED_ODBC_STORAGE
1291 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1292 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1293 if (!ast_strlen_zero(category))
1294 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1296 if (!ast_strlen_zero(category))
1297 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1299 res = odbc_smart_execute(obj, stmt);
1300 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1301 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1302 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1305 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1307 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1310 ast_config_destroy(cfg);
1318 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1327 delete_file(ddir, dmsg);
1328 obj = fetch_odbc_obj(odbc_database, 0);
1330 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1331 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1332 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1333 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1334 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1337 #ifdef EXTENDED_ODBC_STORAGE
1338 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1340 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
1342 res = SQLPrepare(stmt, sql, SQL_NTS);
1343 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1344 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1345 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1348 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1349 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1350 #ifdef EXTENDED_ODBC_STORAGE
1351 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1352 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1353 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1354 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1356 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1357 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1359 res = odbc_smart_execute(obj, stmt);
1360 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1361 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1362 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1365 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1367 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1374 static int count_messages(struct ast_vm_user *vmu, char *dir)
1376 /* Find all .txt files - even if they are not in sequence from 0000 */
1380 struct dirent *vment = NULL;
1382 if (vm_lock_path(dir))
1383 return ERROR_LOCK_PATH;
1385 if ((vmdir = opendir(dir))) {
1386 while ((vment = readdir(vmdir))) {
1387 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1392 ast_unlock_path(dir);
1397 static void rename_file(char *sfn, char *dfn)
1401 ast_filerename(sfn,dfn,NULL);
1402 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1403 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1407 static int copy(char *infile, char *outfile)
1415 #ifdef HARDLINK_WHEN_POSSIBLE
1416 /* Hard link if possible; saves disk space & is faster */
1417 if (link(infile, outfile)) {
1419 if ((ifd = open(infile, O_RDONLY)) < 0) {
1420 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1423 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1424 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1429 len = read(ifd, buf, sizeof(buf));
1431 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1437 res = write(ofd, buf, len);
1438 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1439 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1449 #ifdef HARDLINK_WHEN_POSSIBLE
1451 /* Hard link succeeded */
1457 static void copy_file(char *frompath, char *topath)
1459 char frompath2[256],topath2[256];
1460 ast_filecopy(frompath, topath, NULL);
1461 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1462 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1463 copy(frompath2, topath2);
1467 * A negative return value indicates an error.
1469 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1474 if (vm_lock_path(dir))
1475 return ERROR_LOCK_PATH;
1477 for (x = 0; x < vmu->maxmsg; x++) {
1478 make_file(fn, sizeof(fn), dir, x);
1479 if (ast_fileexists(fn, NULL, NULL) < 1)
1482 ast_unlock_path(dir);
1487 static int vm_delete(char *file)
1492 txtsize = (strlen(file) + 5)*sizeof(char);
1493 txt = alloca(txtsize);
1494 /* Sprintf here would safe because we alloca'd exactly the right length,
1495 * but trying to eliminate all sprintf's anyhow
1497 snprintf(txt, txtsize, "%s.txt", file);
1499 return ast_filedelete(file, NULL);
1505 inbuf(struct baseio *bio, FILE *fi)
1512 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1527 inchar(struct baseio *bio, FILE *fi)
1529 if (bio->iocp>=bio->iolen) {
1530 if (!inbuf(bio, fi))
1534 return bio->iobuf[bio->iocp++];
1538 ochar(struct baseio *bio, int c, FILE *so)
1540 if (bio->linelength>=BASELINELEN) {
1541 if (fputs(eol,so)==EOF)
1547 if (putc(((unsigned char)c),so)==EOF)
1555 static int base_encode(char *filename, FILE *so)
1557 unsigned char dtable[BASEMAXINLINE];
1562 memset(&bio, 0, sizeof(bio));
1563 bio.iocp = BASEMAXINLINE;
1565 if (!(fi = fopen(filename, "rb"))) {
1566 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1570 for (i= 0;i<9;i++) {
1573 dtable[26+i]= 'a'+i;
1574 dtable[26+i+9]= 'j'+i;
1576 for (i= 0;i<8;i++) {
1577 dtable[i+18]= 'S'+i;
1578 dtable[26+i+18]= 's'+i;
1580 for (i= 0;i<10;i++) {
1581 dtable[52+i]= '0'+i;
1587 unsigned char igroup[3],ogroup[4];
1590 igroup[0]= igroup[1]= igroup[2]= 0;
1592 for (n= 0;n<3;n++) {
1593 if ((c = inchar(&bio, fi)) == EOF) {
1598 igroup[n]= (unsigned char)c;
1602 ogroup[0]= dtable[igroup[0]>>2];
1603 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1604 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1605 ogroup[3]= dtable[igroup[2]&0x3F];
1615 ochar(&bio, ogroup[i], so);
1619 if (fputs(eol,so)==EOF)
1627 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)
1630 /* Prepare variables for substition in email body and subject */
1631 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1632 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1633 snprintf(passdata, passdatasize, "%d", msgnum);
1634 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1635 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1636 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1637 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1638 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1639 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1640 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1641 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1645 * fill in *tm for current time according to the proper timezone, if any.
1646 * Return tm so it can be used as a function argument.
1648 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1650 const struct vm_zone *z = NULL;
1651 time_t t = time(NULL);
1653 /* Does this user have a timezone specified? */
1654 if (!ast_strlen_zero(vmu->zonetag)) {
1655 /* Find the zone in the list */
1656 for (z = zones; z ; z = z->next)
1657 if (!strcmp(z->name, vmu->zonetag))
1660 ast_localtime(&t, tm, z ? z->timezone : NULL);
1664 /* same as mkstemp, but return a FILE * */
1665 static FILE *vm_mkftemp(char *template)
1668 int pfd = mkstemp(template);
1670 p = fdopen(pfd, "w");
1679 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)
1683 char host[MAXHOSTNAMELEN] = "";
1688 char tmp[80] = "/tmp/astmail-XXXXXX";
1692 if (vmu && ast_strlen_zero(vmu->email)) {
1693 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1696 if (!strcmp(format, "wav49"))
1698 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));
1699 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1701 p = vm_mkftemp(tmp);
1703 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1706 gethostname(host, sizeof(host)-1);
1707 if (strchr(srcemail, '@'))
1708 ast_copy_string(who, srcemail, sizeof(who));
1710 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1712 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1713 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1714 fprintf(p, "Date: %s\n", date);
1716 /* Set date format for voicemail mail */
1717 strftime(date, sizeof(date), emaildateformat, &tm);
1720 struct ast_channel *ast;
1721 if ((ast = ast_channel_alloc(0))) {
1723 int vmlen = strlen(fromstring)*3 + 200;
1724 if ((passdata = alloca(vmlen))) {
1725 memset(passdata, 0, vmlen);
1726 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1727 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1728 fprintf(p, "From: %s <%s>\n",passdata,who);
1729 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1730 ast_channel_free(ast);
1731 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1733 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1734 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1737 struct ast_channel *ast;
1738 if ((ast = ast_channel_alloc(0))) {
1740 int vmlen = strlen(emailsubject)*3 + 200;
1741 if ((passdata = alloca(vmlen))) {
1742 memset(passdata, 0, vmlen);
1743 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1744 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1745 fprintf(p, "Subject: %s\n",passdata);
1746 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1747 ast_channel_free(ast);
1748 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1751 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1753 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1754 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1756 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1757 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
1758 fprintf(p, "MIME-Version: 1.0\n");
1759 if (attach_user_voicemail) {
1760 /* Something unique. */
1761 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
1763 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1765 fprintf(p, "--%s\n", bound);
1767 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1769 struct ast_channel *ast;
1770 if ((ast = ast_channel_alloc(0))) {
1772 int vmlen = strlen(emailbody)*3 + 200;
1773 if ((passdata = alloca(vmlen))) {
1774 memset(passdata, 0, vmlen);
1775 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1776 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1777 fprintf(p, "%s\n",passdata);
1778 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1779 ast_channel_free(ast);
1780 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1782 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1784 "in mailbox %s from %s, on %s so you might\n"
1785 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1786 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1788 if (attach_user_voicemail) {
1789 /* Eww. We want formats to tell us their own MIME type */
1790 char *ctype = "audio/x-";
1791 if (!strcasecmp(format, "ogg"))
1792 ctype = "application/";
1794 fprintf(p, "--%s\n", bound);
1795 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1796 fprintf(p, "Content-Transfer-Encoding: base64\n");
1797 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1798 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1800 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1801 base_encode(fname, p);
1802 fprintf(p, "\n\n--%s--\n.\n", bound);
1805 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1806 ast_safe_system(tmp2);
1807 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1812 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)
1815 char host[MAXHOSTNAMELEN]="";
1818 char tmp[80] = "/tmp/astmail-XXXXXX";
1821 FILE *p = vm_mkftemp(tmp);
1824 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1827 gethostname(host, sizeof(host)-1);
1828 if (strchr(srcemail, '@'))
1829 ast_copy_string(who, srcemail, sizeof(who));
1831 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1833 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1834 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1835 fprintf(p, "Date: %s\n", date);
1837 if (*pagerfromstring) {
1838 struct ast_channel *ast;
1839 if ((ast = ast_channel_alloc(0))) {
1841 int vmlen = strlen(fromstring)*3 + 200;
1842 if ((passdata = alloca(vmlen))) {
1843 memset(passdata, 0, vmlen);
1844 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1845 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1846 fprintf(p, "From: %s <%s>\n",passdata,who);
1848 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1849 ast_channel_free(ast);
1850 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1852 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1853 fprintf(p, "To: %s\n", pager);
1855 struct ast_channel *ast;
1856 if ((ast = ast_channel_alloc(0))) {
1858 int vmlen = strlen(pagersubject)*3 + 200;
1859 if ((passdata = alloca(vmlen))) {
1860 memset(passdata, 0, vmlen);
1861 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1862 pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
1863 fprintf(p, "Subject: %s\n\n",passdata);
1864 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1865 ast_channel_free(ast);
1866 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1868 fprintf(p, "Subject: New VM\n\n");
1869 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1871 struct ast_channel *ast;
1872 if ((ast = ast_channel_alloc(0))) {
1874 int vmlen = strlen(pagerbody)*3 + 200;
1875 if ((passdata = alloca(vmlen))) {
1876 memset(passdata, 0, vmlen);
1877 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1878 pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
1879 fprintf(p, "%s\n",passdata);
1880 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1881 ast_channel_free(ast);
1882 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1884 fprintf(p, "New %s long msg in box %s\n"
1885 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1888 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1889 ast_safe_system(tmp2);
1890 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1895 static int get_date(char *s, int len)
1900 localtime_r(&t,&tm);
1901 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1904 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1908 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
1910 if (ast_fileexists(fn, NULL, NULL) > 0) {
1911 res = ast_streamfile(chan, fn, chan->language);
1916 res = ast_waitstream(chan, ecodes);
1922 /* Dispose just in case */
1924 res = ast_streamfile(chan, "vm-theperson", chan->language);
1927 res = ast_waitstream(chan, ecodes);
1930 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1935 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1937 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1940 res = ast_waitstream(chan, ecodes);
1944 static void free_user(struct ast_vm_user *vmu)
1946 if (ast_test_flag(vmu, VM_ALLOCED))
1950 static void free_zone(struct vm_zone *z)
1955 static char *mbox(int id)
1983 #ifdef USE_ODBC_STORAGE
1984 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
1998 /* If no mailbox, return immediately */
1999 if (ast_strlen_zero(mailbox))
2002 ast_copy_string(tmp, mailbox, sizeof(tmp));
2004 context = strchr(tmp, '@');
2009 context = "default";
2012 obj = fetch_odbc_obj(odbc_database, 0);
2014 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2015 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2016 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2019 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir LIKE '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2020 res = SQLPrepare(stmt, sql, SQL_NTS);
2021 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2022 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2023 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2026 res = odbc_smart_execute(obj, stmt);
2027 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2028 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2029 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2032 res = SQLFetch(stmt);
2033 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2034 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2035 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2038 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2039 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2040 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2041 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2044 *newmsgs = atoi(rowdata);
2045 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2047 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2048 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2049 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2052 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "Old");
2053 res = SQLPrepare(stmt, sql, SQL_NTS);
2054 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2055 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2056 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2059 res = odbc_smart_execute(obj, stmt);
2060 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2061 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2062 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2065 res = SQLFetch(stmt);
2066 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2067 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2068 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2071 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2072 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2073 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2074 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2077 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2078 *oldmsgs = atoi(rowdata);
2081 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2087 static int has_voicemail(const char *mailbox, const char *folder)
2098 /* If no mailbox, return immediately */
2099 if (ast_strlen_zero(mailbox))
2102 ast_copy_string(tmp, mailbox, sizeof(tmp));
2104 context = strchr(tmp, '@');
2109 context = "default";
2112 obj = fetch_odbc_obj(odbc_database, 0);
2114 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2115 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2116 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2119 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2120 res = SQLPrepare(stmt, sql, SQL_NTS);
2121 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2122 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2123 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2126 res = odbc_smart_execute(obj, stmt);
2127 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2128 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2129 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2132 res = SQLFetch(stmt);
2133 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2134 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2135 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2138 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2139 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2140 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2141 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2144 nummsgs = atoi(rowdata);
2145 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2147 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2158 static int has_voicemail(const char *mailbox, const char *folder)
2169 /* If no mailbox, return immediately */
2170 if (ast_strlen_zero(mailbox))
2172 if (strchr(mailbox, ',')) {
2173 ast_copy_string(tmp, mailbox, sizeof(tmp));
2176 while((cur = strsep(&mb, ","))) {
2177 if (!ast_strlen_zero(cur)) {
2178 if (has_voicemail(cur, folder))
2184 ast_copy_string(tmp, mailbox, sizeof(tmp));
2185 context = strchr(tmp, '@');
2190 context = "default";
2191 snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
2195 while ((de = readdir(dir))) {
2196 if (!strncasecmp(de->d_name, "msg", 3))
2206 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2219 /* If no mailbox, return immediately */
2220 if (ast_strlen_zero(mailbox))
2222 if (strchr(mailbox, ',')) {
2224 ast_copy_string(tmp, mailbox, sizeof(tmp));
2227 while((cur = strsep(&mb, ", "))) {
2228 if (!ast_strlen_zero(cur)) {
2229 if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2241 ast_copy_string(tmp, mailbox, sizeof(tmp));
2242 context = strchr(tmp, '@');
2247 context = "default";
2249 snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
2252 while ((de = readdir(dir))) {
2253 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2254 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2262 snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
2265 while ((de = readdir(dir))) {
2266 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2267 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2279 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
2281 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)
2283 char fromdir[256], todir[256], frompath[256], topath[256];
2284 char *frombox = mbox(imbox);
2287 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2289 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2291 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2292 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2294 if (vm_lock_path(todir))
2295 return ERROR_LOCK_PATH;
2299 make_file(topath, sizeof(topath), todir, recipmsgnum);
2300 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2303 } while (recipmsgnum < recip->maxmsg);
2304 if (recipmsgnum < recip->maxmsg) {
2305 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2307 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2309 ast_unlock_path(todir);
2310 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2315 static void run_externnotify(char *context, char *extension)
2317 char arguments[255];
2318 char ext_context[256] = "";
2319 int newvoicemails = 0, oldvoicemails = 0;
2321 if (!ast_strlen_zero(context))
2322 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2324 ast_copy_string(ext_context, extension, sizeof(ext_context));
2326 if (!ast_strlen_zero(externnotify)) {
2327 if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
2328 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2330 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2331 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2332 ast_safe_system(arguments);
2337 struct leave_vm_options {
2339 signed char record_gain;
2342 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2355 char prefile[256]="";
2356 char tempfile[256]="";
2357 char ext_context[256] = "";
2360 char ecodes[16] = "#";
2361 char tmp[256] = "", *tmpptr;
2362 struct ast_vm_user *vmu;
2363 struct ast_vm_user svm;
2364 const char *category = NULL;
2366 ast_copy_string(tmp, ext, sizeof(tmp));
2368 context = strchr(tmp, '@');
2372 tmpptr = strchr(context, '&');
2374 tmpptr = strchr(ext, '&');
2382 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2384 if (!(vmu = find_user(&svm, context, ext))) {
2385 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2386 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2387 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2388 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2392 /* Setup pre-file if appropriate */
2393 if (strcmp(vmu->context, "default"))
2394 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2396 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2397 if (ast_test_flag(options, OPT_BUSY_GREETING))
2398 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2399 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2400 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2401 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2402 RETRIEVE(tempfile, -1);
2403 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2404 ast_copy_string(prefile, tempfile, sizeof(prefile));
2405 DISPOSE(tempfile, -1);
2406 /* It's easier just to try to make it than to check for its existence */
2407 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2409 /* Check current or macro-calling context for special extensions */
2410 if (!ast_strlen_zero(vmu->exit)) {
2411 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2412 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2413 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
2414 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2415 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2416 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2420 if (!ast_strlen_zero(vmu->exit)) {
2421 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2422 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2423 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2424 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2425 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2426 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2430 /* Play the beginning intro if desired */
2431 if (!ast_strlen_zero(prefile)) {
2432 RETRIEVE(prefile, -1);
2433 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2434 if (ast_streamfile(chan, prefile, chan->language) > -1)
2435 res = ast_waitstream(chan, ecodes);
2437 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2438 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2440 DISPOSE(prefile, -1);
2442 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2444 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2449 /* On a '#' we skip the instructions */
2450 ast_set_flag(options, OPT_SILENT);
2453 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2454 res = ast_streamfile(chan, INTRO, chan->language);
2456 res = ast_waitstream(chan, ecodes);
2458 ast_set_flag(options, OPT_SILENT);
2463 ast_stopstream(chan);
2464 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2465 other than the operator -- an automated attendant or mailbox login for example */
2467 chan->exten[0] = 'a';
2468 chan->exten[1] = '\0';
2469 if (!ast_strlen_zero(vmu->exit)) {
2470 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2471 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2472 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2476 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2479 /* Check for a '0' here */
2482 if (ast_test_flag(vmu, VM_OPERATOR)) {
2483 chan->exten[0] = 'o';
2484 chan->exten[1] = '\0';
2485 if (!ast_strlen_zero(vmu->exit)) {
2486 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2487 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2488 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2490 ast_play_and_wait(chan, "transfer");
2493 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2496 ast_play_and_wait(chan, "vm-sorry");
2497 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2503 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2506 /* The meat of recording the message... All the announcements and beeps have been played*/
2507 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2508 if (!ast_strlen_zero(fmt)) {
2511 if (vm_lock_path(dir)) {
2513 return ERROR_LOCK_PATH;
2517 * This operation can be very expensive if done say over NFS or if the mailbox has 100+ messages
2518 * in the mailbox. So we should get this first so we don't cut off the first few seconds of the
2522 make_file(fn, sizeof(fn), dir, msgnum);
2523 if (!EXISTS(dir,msgnum,fn,chan->language))
2526 } while (msgnum < vmu->maxmsg);
2528 /* Now play the beep once we have the message number for our next message. */
2530 /* Unless we're *really* silent, try to send the beep */
2531 res = ast_streamfile(chan, "beep", chan->language);
2533 res = ast_waitstream(chan, "");
2535 if (msgnum < vmu->maxmsg) {
2536 /* assign a variable with the name of the voicemail file */
2537 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
2539 /* Store information */
2540 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2541 txt = fopen(txtfile, "w+");
2543 get_date(date, sizeof(date));
2546 "; Message Information file\n"
2565 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2566 date, (long)time(NULL),
2567 category ? category : "");
2569 ast_log(LOG_WARNING, "Error opening text file for output\n");
2570 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2579 fprintf(txt, "duration=%d\n", duration);
2583 if (duration < vmminmessage) {
2584 if (option_verbose > 2)
2585 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2586 DELETE(dir,msgnum,fn);
2587 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2588 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2591 /* Are there to be more recipients of this message? */
2593 struct ast_vm_user recipu, *recip;
2594 char *exten, *context;
2596 exten = strsep(&tmpptr, "&");
2597 context = strchr(exten, '@');
2602 if ((recip = find_user(&recipu, context, exten))) {
2603 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2607 if (ast_fileexists(fn, NULL, NULL)) {
2608 STORE(dir, vmu->mailbox, vmu->context, msgnum);
2609 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2610 DISPOSE(dir, msgnum);
2612 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
2614 ast_unlock_path(dir);
2615 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2617 res = ast_waitstream(chan, "");
2618 ast_log(LOG_WARNING, "No more messages possible\n");
2619 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2622 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2629 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
2631 /* we know max messages, so stop process when number is hit */
2637 if (vm_lock_path(dir))
2638 return ERROR_LOCK_PATH;
2640 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
2641 make_file(sfn, sizeof(sfn), dir, x);
2642 if (EXISTS(dir, x, sfn, NULL)) {
2645 make_file(dfn, sizeof(dfn), dir, dest);
2646 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
2652 ast_unlock_path(dir);
2658 static int say_and_wait(struct ast_channel *chan, int num, char *language)
2661 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2665 static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *context, char *username, int box)
2670 char *dbox = mbox(box);
2672 make_file(sfn, sizeof(sfn), dir, msg);
2673 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
2675 if (vm_lock_path(ddir))
2676 return ERROR_LOCK_PATH;
2678 for (x = 0; x < vmu->maxmsg; x++) {
2679 make_file(dfn, sizeof(dfn), ddir, x);
2680 if (!EXISTS(ddir, x, dfn, NULL))
2683 if (x >= vmu->maxmsg) {
2684 ast_unlock_path(ddir);
2687 if (strcmp(sfn, dfn)) {
2688 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
2690 ast_unlock_path(ddir);
2695 static int adsi_logo(unsigned char *buf)
2698 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2699 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2703 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2705 unsigned char buf[256];
2711 bytes += adsi_data_mode(buf + bytes);
2712 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2715 bytes += adsi_logo(buf);
2716 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2718 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
2720 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2721 bytes += adsi_data_mode(buf + bytes);
2722 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2724 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2726 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2727 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2728 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2729 bytes += adsi_voice_mode(buf + bytes, 0);
2730 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2737 bytes += adsi_logo(buf);
2738 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2739 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
2740 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2741 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2744 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2745 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2746 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2747 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2748 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2749 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2750 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2753 /* Add another dot */
2755 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
2756 bytes += adsi_voice_mode(buf + bytes, 0);
2758 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2759 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2763 /* These buttons we load but don't use yet */
2764 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2765 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2766 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2767 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2768 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2769 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2770 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2773 /* Add another dot */
2775 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2776 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2777 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2782 snprintf(num, sizeof(num), "%d", x);
2783 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2785 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2786 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2789 /* Add another dot */
2791 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2792 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2793 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2796 if (adsi_end_download(chan)) {
2798 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2799 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2800 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2801 bytes += adsi_voice_mode(buf + bytes, 0);
2802 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2806 bytes += adsi_download_disconnect(buf + bytes);
2807 bytes += adsi_voice_mode(buf + bytes, 0);
2808 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2810 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2815 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2816 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2818 ast_log(LOG_DEBUG, "Restarting session...\n");
2821 /* Load the session now */
2822 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2824 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2826 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2828 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2832 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2835 if (!adsi_available(chan))
2837 x = adsi_load_session(chan, adsifdn, adsiver, 1);
2841 if (adsi_load_vmail(chan, useadsi)) {
2842 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2849 static void adsi_login(struct ast_channel *chan)
2851 unsigned char buf[256];
2853 unsigned char keys[8];
2855 if (!adsi_available(chan))
2860 /* Set one key for next */
2861 keys[3] = ADSI_KEY_APPS + 3;
2863 bytes += adsi_logo(buf + bytes);
2864 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2865 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2866 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2867 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2868 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2869 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2870 bytes += adsi_set_keys(buf + bytes, keys);
2871 bytes += adsi_voice_mode(buf + bytes, 0);
2872 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2875 static void adsi_password(struct ast_channel *chan)
2877 unsigned char buf[256];
2879 unsigned char keys[8];
2881 if (!adsi_available(chan))
2886 /* Set one key for next */
2887 keys[3] = ADSI_KEY_APPS + 3;
2889 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2890 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2891 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2892 bytes += adsi_set_keys(buf + bytes, keys);
2893 bytes += adsi_voice_mode(buf + bytes, 0);
2894 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2897 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2899 unsigned char buf[256];
2901 unsigned char keys[8];
2904 if (!adsi_available(chan))
2908 y = ADSI_KEY_APPS + 12 + start + x;
2909 if (y > ADSI_KEY_APPS + 12 + 4)
2911 keys[x] = ADSI_KEY_SKT | y;
2913 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2917 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2918 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2919 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2920 bytes += adsi_set_keys(buf + bytes, keys);
2921 bytes += adsi_voice_mode(buf + bytes, 0);
2923 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2926 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2929 unsigned char buf[256];
2930 char buf1[256], buf2[256];
2936 char datetime[21]="";
2939 unsigned char keys[8];
2943 if (!adsi_available(chan))
2946 /* Retrieve important info */
2947 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
2948 f = fopen(fn2, "r");
2951 fgets((char *)buf, sizeof(buf), f);
2954 stringp = (char *)buf;
2955 strsep(&stringp, "=");
2956 val = strsep(&stringp, "=");
2957 if (!ast_strlen_zero(val)) {
2958 if (!strcmp((char *)buf, "callerid"))
2959 ast_copy_string(cid, val, sizeof(cid));
2960 if (!strcmp((char *)buf, "origdate"))
2961 ast_copy_string(datetime, val, sizeof(datetime));
2967 /* New meaning for keys */
2969 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2974 /* No prev key, provide "Folder" instead */
2975 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2977 if (vms->curmsg >= vms->lastmsg) {
2978 /* If last message ... */
2980 /* but not only message, provide "Folder" instead */
2981 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2982 bytes += adsi_voice_mode(buf + bytes, 0);
2985 /* Otherwise if only message, leave blank */
2990 if (!ast_strlen_zero(cid)) {
2991 ast_callerid_parse(cid, &name, &num);
2995 name = "Unknown Caller";
2997 /* If deleted, show "undeleted" */
2999 if (vms->deleted[vms->curmsg])
3000 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3003 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3004 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3005 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3006 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3008 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3009 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3010 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3011 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3012 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3013 bytes += adsi_set_keys(buf + bytes, keys);
3014 bytes += adsi_voice_mode(buf + bytes, 0);
3016 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3019 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3022 unsigned char buf[256];
3023 unsigned char keys[8];
3027 if (!adsi_available(chan))
3030 /* New meaning for keys */
3032 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);