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 char attachfmt[20]; /*!< Attachment format */
241 unsigned int flags; /*!< VM_ flags */
243 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
244 AST_LIST_ENTRY(ast_vm_user) list;
248 AST_LIST_ENTRY(vm_zone) list;
251 char msg_format[512];
270 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
271 int option, signed char record_gain);
272 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
273 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
274 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
275 signed char record_gain);
276 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
277 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
279 static void apply_options(struct ast_vm_user *vmu, const char *options);
281 #ifdef USE_ODBC_STORAGE
282 static char odbc_database[80];
283 static char odbc_table[80];
284 #define RETRIEVE(a,b) retrieve_file(a,b)
285 #define DISPOSE(a,b) remove_file(a,b)
286 #define STORE(a,b,c,d) store_file(a,b,c,d)
287 #define EXISTS(a,b,c,d) (message_exists(a,b))
288 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
289 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
290 #define DELETE(a,b,c) (delete_file(a,b))
292 #define RETRIEVE(a,b)
294 #define STORE(a,b,c,d)
295 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
296 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
297 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
298 #define DELETE(a,b,c) (vm_delete(c))
301 static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
303 static char ext_pass_cmd[128];
305 static char *tdesc = "Comedian Mail (Voicemail System)";
307 static char *addesc = "Comedian Mail";
309 static char *synopsis_vm =
310 "Leave a Voicemail message";
312 static char *descrip_vm =
313 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
314 "application allows the calling party to leave a message for the specified\n"
315 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
316 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
317 "specified mailbox does not exist.\n"
318 " The Voicemail application will exit if any of the following DTMF digits are\n"
320 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
321 " * - Jump to the 'a' extension in the current dialplan context.\n"
322 " This application will set the following channel variable upon completion:\n"
323 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
324 " application. The possible values are:\n"
325 " SUCCESS | USEREXIT | FAILED\n\n"
327 " b - Play the 'busy' greeting to the calling party.\n"
328 " g(#) - Use the specified amount of gain when recording the voicemail\n"
329 " message. The units are whole-number decibels (dB).\n"
330 " s - Skip the playback of instructions for leaving a message to the\n"
332 " u - Play the 'unavailable greeting.\n"
333 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
336 static char *synopsis_vmain =
337 "Check Voicemail messages";
339 static char *descrip_vmain =
340 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
341 "calling party to check voicemail messages. A specific mailbox, and optional\n"
342 "corresponding context, may be specified. If a mailbox is not provided, the\n"
343 "calling party will be prompted to enter one. If a context is not specified,\n"
344 "the 'default' context will be used.\n\n"
346 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
347 " is entered by the caller.\n"
348 " g(#) - Use the specified amount of gain when recording a voicemail\n"
349 " message. The units are whole-number decibels (dB).\n"
350 " s - Skip checking the passcode for the mailbox.\n"
351 " a(#) - Skip folder prompt and go directly to folder specified.\n"
352 " Defaults to INBOX\n";
354 static char *synopsis_vm_box_exists =
355 "Check to see if Voicemail mailbox exists";
357 static char *descrip_vm_box_exists =
358 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
359 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
361 " This application will set the following channel variable upon completion:\n"
362 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
363 " MailboxExists application. Possible values include:\n"
364 " SUCCESS | FAILED\n\n"
366 " j - Jump to priority n+101 if the mailbox is found.\n";
368 static char *synopsis_vmauthenticate =
369 "Authenticate with Voicemail passwords";
371 static char *descrip_vmauthenticate =
372 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
373 "same way as the Authenticate application, but the passwords are taken from\n"
375 " If the mailbox is specified, only that mailbox's password will be considered\n"
376 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
377 "be set with the authenticated mailbox.\n\n"
379 " s - Skip playing the initial prompts.\n";
381 /* Leave a message */
382 static char *app = "VoiceMail";
384 /* Check mail, control, etc */
385 static char *app2 = "VoiceMailMain";
387 static char *app3 = "MailboxExists";
388 static char *app4 = "VMAuthenticate";
390 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
391 static AST_LIST_HEAD_STATIC(zones, vm_zone);
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, "attachfmt")) {
456 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
457 } else if (!strcasecmp(var, "serveremail")) {
458 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
459 } else if (!strcasecmp(var, "language")) {
460 ast_copy_string(vmu->language, value, sizeof(vmu->language));
461 } else if (!strcasecmp(var, "tz")) {
462 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
463 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
464 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
465 } else if (!strcasecmp(var, "saycid")){
466 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
467 } else if (!strcasecmp(var,"sendvoicemail")){
468 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
469 } else if (!strcasecmp(var, "review")){
470 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
471 } else if (!strcasecmp(var, "operator")){
472 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
473 } else if (!strcasecmp(var, "envelope")){
474 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
475 } else if (!strcasecmp(var, "sayduration")){
476 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
477 } else if (!strcasecmp(var, "saydurationm")){
478 if (sscanf(value, "%d", &x) == 1) {
479 vmu->saydurationm = x;
481 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
483 } else if (!strcasecmp(var, "forcename")){
484 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
485 } else if (!strcasecmp(var, "forcegreetings")){
486 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
487 } else if (!strcasecmp(var, "callback")) {
488 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
489 } else if (!strcasecmp(var, "dialout")) {
490 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
491 } else if (!strcasecmp(var, "exitcontext")) {
492 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
493 } else if (!strcasecmp(var, "maxmsg")) {
494 vmu->maxmsg = atoi(value);
495 if (vmu->maxmsg <= 0) {
496 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
497 vmu->maxmsg = MAXMSG;
498 } else if (vmu->maxmsg > MAXMSGLIMIT) {
499 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
500 vmu->maxmsg = MAXMSGLIMIT;
502 } else if (!strcasecmp(var, "options")) {
503 apply_options(vmu, value);
507 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
510 if (!ast_strlen_zero(vmu->uniqueid)) {
511 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
513 ast_copy_string(vmu->password, password, sizeof(vmu->password));
523 static void apply_options(struct ast_vm_user *vmu, const char *options)
524 { /* Destructively Parse options and apply */
528 stringp = ast_strdupa(options);
529 while ((s = strsep(&stringp, "|"))) {
531 if ((var = strsep(&value, "=")) && value) {
532 apply_option(vmu, var, value);
537 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
539 struct ast_variable *var, *tmp;
540 struct ast_vm_user *retval;
542 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
544 ast_set_flag(retval, VM_ALLOCED);
546 memset(retval, 0, sizeof(*retval));
548 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
549 populate_defaults(retval);
550 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
551 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
553 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
557 printf("%s => %s\n", tmp->name, tmp->value);
558 if (!strcasecmp(tmp->name, "password")) {
559 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
560 } else if (!strcasecmp(tmp->name, "uniqueid")) {
561 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
562 } else if (!strcasecmp(tmp->name, "pager")) {
563 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
564 } else if (!strcasecmp(tmp->name, "email")) {
565 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
566 } else if (!strcasecmp(tmp->name, "fullname")) {
567 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
568 } else if (!strcasecmp(tmp->name, "context")) {
569 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
571 apply_option(retval, tmp->name, tmp->value);
583 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
585 /* This function could be made to generate one from a database, too */
586 struct ast_vm_user *vmu=NULL, *cur;
587 AST_LIST_LOCK(&users);
589 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
592 AST_LIST_TRAVERSE(&users, cur, list) {
593 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
595 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
599 /* Make a copy, so that on a reload, we have no race */
600 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
601 memcpy(vmu, cur, sizeof(*vmu));
602 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
603 AST_LIST_NEXT(vmu, list) = NULL;
606 vmu = find_user_realtime(ivm, context, mailbox);
607 AST_LIST_UNLOCK(&users);
611 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
613 /* This function could be made to generate one from a database, too */
614 struct ast_vm_user *cur;
616 AST_LIST_LOCK(&users);
617 AST_LIST_TRAVERSE(&users, cur, list) {
618 if ((!context || !strcasecmp(context, cur->context)) &&
619 (!strcasecmp(mailbox, cur->mailbox)))
623 ast_copy_string(cur->password, newpass, sizeof(cur->password));
626 AST_LIST_UNLOCK(&users);
630 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
632 /* There's probably a better way of doing this. */
633 /* That's why I've put the password change in a separate function. */
634 /* This could also be done with a database function */
641 char currcontext[256] ="";
642 char tmpin[AST_CONFIG_MAX_PATH];
643 char tmpout[AST_CONFIG_MAX_PATH];
646 if (!change_password_realtime(vmu, newpassword))
649 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
650 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
651 configin = fopen(tmpin,"r");
653 configout = fopen(tmpout,"w+");
656 if (!configin || !configout) {
660 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
664 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
668 while (!feof(configin)) {
669 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
671 /* Read in the line */
672 fgets(inbuf, sizeof(inbuf), configin);
675 if (ast_strlen_zero(inbuf)) {
676 fprintf(configout, "\n");
680 /* Make a backup of it */
681 ast_copy_string(orig, inbuf, sizeof(orig));
684 Read the file line by line, split each line into a comment and command section
685 only parse the command portion of the line
687 if (inbuf[strlen(inbuf) - 1] == '\n')
688 inbuf[strlen(inbuf) - 1] = '\0';
690 if ((comment = strchr(inbuf, ';')))
691 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
693 if (ast_strlen_zero(inbuf)) {
694 fprintf(configout, "%s", orig);
698 /* Check for a context, first '[' to first ']' */
699 if ((tmpctx = strchr(inbuf, '['))) {
700 tmpctxend = strchr(tmpctx, ']');
703 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
704 fprintf(configout, "%s", orig);
709 /* This isn't a context line, check for MBX => PSWD... */
711 if ((pass = strchr(user, '='))) {
712 /* We have a line in the form of aaaaa=aaaaaa */
715 user = ast_strip(user);
720 pass = ast_skip_blanks(pass);
723 Since no whitespace allowed in fields, or more correctly white space
724 inside the fields is there for a purpose, we can just terminate pass
725 at the comma or EOL whichever comes first.
727 if ((rest = strchr(pass, ',')))
733 /* Compare user, pass AND context */
734 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
735 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
736 !strcasecmp(currcontext, vmu->context)) {
737 /* This is the line */
739 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
741 fprintf(configout, "%s => %s", user, newpassword);
743 /* If there was a comment on the line print it out */
745 fprintf(configout, ";%s\n", comment);
747 fprintf(configout, "\n");
750 /* Put it back like it was */
751 fprintf(configout, "%s", orig);
757 stat(tmpin, &statbuf);
758 chmod(tmpout, statbuf.st_mode);
759 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
761 rename(tmpout, tmpin);
762 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
763 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
766 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
769 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
770 if (!ast_safe_system(buf))
771 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
774 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
776 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
779 static int make_file(char *dest, int len, char *dir, int num)
781 return snprintf(dest, len, "%s/msg%04d", dir, num);
784 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
785 * \param dest String. base directory.
786 * \param context String. Ignored if is null or empty string.
787 * \param ext String. Ignored if is null or empty string.
788 * \param folder String. Ignored if is null or empty string.
789 * \return 0 on failure, 1 on success.
791 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
793 mode_t mode = VOICEMAIL_DIR_MODE;
795 if (!ast_strlen_zero(context)) {
796 make_dir(dest, len, context, "", "");
797 if(mkdir(dest, mode) && errno != EEXIST) {
798 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
802 if (!ast_strlen_zero(ext)) {
803 make_dir(dest, len, context, ext, "");
804 if(mkdir(dest, mode) && errno != EEXIST) {
805 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
809 if (!ast_strlen_zero(folder)) {
810 make_dir(dest, len, context, ext, folder);
811 if(mkdir(dest, mode) && errno != EEXIST) {
812 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
819 /* only return failure if ast_lock_path returns 'timeout',
820 not if the path does not exist or any other reason
822 static int vm_lock_path(const char *path)
824 switch (ast_lock_path(path)) {
825 case AST_LOCK_TIMEOUT:
833 #ifdef USE_ODBC_STORAGE
834 static int retrieve_file(char *dir, int msgnum)
841 SQLSMALLINT colcount=0;
848 SQLSMALLINT datatype;
849 SQLSMALLINT decimaldigits;
850 SQLSMALLINT nullable;
859 obj = fetch_odbc_obj(odbc_database, 0);
861 ast_copy_string(fmt, vmfmts, sizeof(fmt));
862 c = strchr(fmt, '|');
865 if (!strcasecmp(fmt, "wav49"))
867 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
869 make_file(fn, sizeof(fn), dir, msgnum);
871 ast_copy_string(fn, dir, sizeof(fn));
872 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
873 f = fopen(full_fn, "w+");
874 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
875 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
876 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
877 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
880 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
881 res = SQLPrepare(stmt, sql, SQL_NTS);
882 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
883 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
884 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
887 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
888 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
889 res = odbc_smart_execute(obj, stmt);
890 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
891 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
892 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
895 res = SQLFetch(stmt);
896 if (res == SQL_NO_DATA) {
897 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
900 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
901 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
902 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
905 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
907 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
908 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
911 res = SQLNumResultCols(stmt, &colcount);
912 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
913 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
914 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
918 fprintf(f, "[message]\n");
919 for (x=0;x<colcount;x++) {
921 collen = sizeof(coltitle);
922 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
923 &datatype, &colsize, &decimaldigits, &nullable);
924 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
925 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
926 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
929 if (!strcasecmp(coltitle, "recording")) {
930 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
932 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
935 lseek(fd, fdlen - 1, SEEK_SET);
936 if (write(fd, tmp, 1) != 1) {
941 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
944 memset(fdm, 0, fdlen);
945 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
946 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
947 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
948 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
953 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
954 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
955 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
956 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
959 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
960 fprintf(f, "%s=%s\n", coltitle, rowdata);
963 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
965 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
976 static int remove_file(char *dir, int msgnum)
983 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
984 make_file(fn, sizeof(fn), dir, msgnum);
986 ast_copy_string(fn, dir, sizeof(fn));
987 ast_filedelete(fn, NULL);
988 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
993 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1002 obj = fetch_odbc_obj(odbc_database, 0);
1004 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1005 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1006 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1009 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1010 res = SQLPrepare(stmt, sql, SQL_NTS);
1011 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1012 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1013 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1016 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1017 res = odbc_smart_execute(obj, stmt);
1018 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1019 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1020 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1023 res = SQLFetch(stmt);
1024 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1025 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1026 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1029 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1030 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1031 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1032 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1035 if (sscanf(rowdata, "%d", &x) != 1)
1036 ast_log(LOG_WARNING, "Failed to read message count!\n");
1037 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1039 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1044 static int message_exists(char *dir, int msgnum)
1054 obj = fetch_odbc_obj(odbc_database, 0);
1056 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1057 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1058 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1059 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1062 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1063 res = SQLPrepare(stmt, sql, SQL_NTS);
1064 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1065 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1066 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1069 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1070 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1071 res = odbc_smart_execute(obj, stmt);
1072 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1073 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1074 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1077 res = SQLFetch(stmt);
1078 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1079 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1080 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1083 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1084 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1085 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1086 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1089 if (sscanf(rowdata, "%d", &x) != 1)
1090 ast_log(LOG_WARNING, "Failed to read message count!\n");
1091 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1093 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1098 static int count_messages(struct ast_vm_user *vmu, char *dir)
1100 return last_message_index(vmu, dir) + 1;
1103 static void delete_file(char *sdir, int smsg)
1111 obj = fetch_odbc_obj(odbc_database, 0);
1113 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1114 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1115 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1116 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1119 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1120 res = SQLPrepare(stmt, sql, SQL_NTS);
1121 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1122 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1123 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1126 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1127 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1128 res = odbc_smart_execute(obj, stmt);
1129 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1130 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1131 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1134 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1136 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1141 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1150 delete_file(ddir, dmsg);
1151 obj = fetch_odbc_obj(odbc_database, 0);
1153 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1154 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1155 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1156 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1157 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1160 #ifdef EXTENDED_ODBC_STORAGE
1161 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);
1163 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);
1165 res = SQLPrepare(stmt, sql, SQL_NTS);
1166 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1167 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1168 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1171 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1172 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1173 #ifdef EXTENDED_ODBC_STORAGE
1174 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1175 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1176 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1177 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1179 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1180 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1182 res = odbc_smart_execute(obj, stmt);
1183 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1184 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1185 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1188 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1190 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1195 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1210 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1211 char *category = "";
1212 struct ast_config *cfg=NULL;
1215 delete_file(dir, msgnum);
1216 obj = fetch_odbc_obj(odbc_database, 0);
1218 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1219 c = strchr(fmt, '|');
1222 if (!strcasecmp(fmt, "wav49"))
1224 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1226 make_file(fn, sizeof(fn), dir, msgnum);
1228 ast_copy_string(fn, dir, sizeof(fn));
1229 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1230 cfg = ast_config_load(full_fn);
1231 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1232 fd = open(full_fn, O_RDWR);
1234 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1238 context = ast_variable_retrieve(cfg, "message", "context");
1239 if (!context) context = "";
1240 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1241 if (!macrocontext) macrocontext = "";
1242 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1243 if (!callerid) callerid = "";
1244 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1245 if (!origtime) origtime = "";
1246 duration = ast_variable_retrieve(cfg, "message", "duration");
1247 if (!duration) duration = "";
1248 category = ast_variable_retrieve(cfg, "message", "category");
1249 if (!category) category = "";
1251 fdlen = lseek(fd, 0, SEEK_END);
1252 lseek(fd, 0, SEEK_SET);
1253 printf("Length is %d\n", fdlen);
1254 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1256 ast_log(LOG_WARNING, "Memory map failed!\n");
1259 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1260 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1261 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1264 if (!ast_strlen_zero(category))
1265 #ifdef EXTENDED_ODBC_STORAGE
1266 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1268 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
1271 #ifdef EXTENDED_ODBC_STORAGE
1272 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1274 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
1276 res = SQLPrepare(stmt, sql, SQL_NTS);
1277 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1278 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1279 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1282 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1283 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1284 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1285 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1286 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1287 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1288 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1289 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1290 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1291 #ifdef EXTENDED_ODBC_STORAGE
1292 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1293 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1294 if (!ast_strlen_zero(category))
1295 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1297 if (!ast_strlen_zero(category))
1298 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1300 res = odbc_smart_execute(obj, stmt);
1301 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1302 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1303 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1306 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1308 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1311 ast_config_destroy(cfg);
1319 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1328 delete_file(ddir, dmsg);
1329 obj = fetch_odbc_obj(odbc_database, 0);
1331 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1332 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1333 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1334 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1335 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1338 #ifdef EXTENDED_ODBC_STORAGE
1339 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1341 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
1343 res = SQLPrepare(stmt, sql, SQL_NTS);
1344 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1345 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1346 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1349 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1350 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1351 #ifdef EXTENDED_ODBC_STORAGE
1352 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1353 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1354 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1355 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1357 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1358 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1360 res = odbc_smart_execute(obj, stmt);
1361 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1362 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1363 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1366 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1368 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1375 static int count_messages(struct ast_vm_user *vmu, char *dir)
1377 /* Find all .txt files - even if they are not in sequence from 0000 */
1381 struct dirent *vment = NULL;
1383 if (vm_lock_path(dir))
1384 return ERROR_LOCK_PATH;
1386 if ((vmdir = opendir(dir))) {
1387 while ((vment = readdir(vmdir))) {
1388 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1393 ast_unlock_path(dir);
1398 static void rename_file(char *sfn, char *dfn)
1402 ast_filerename(sfn,dfn,NULL);
1403 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1404 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1408 static int copy(char *infile, char *outfile)
1416 #ifdef HARDLINK_WHEN_POSSIBLE
1417 /* Hard link if possible; saves disk space & is faster */
1418 if (link(infile, outfile)) {
1420 if ((ifd = open(infile, O_RDONLY)) < 0) {
1421 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1424 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1425 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1430 len = read(ifd, buf, sizeof(buf));
1432 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1438 res = write(ofd, buf, len);
1439 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1440 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1450 #ifdef HARDLINK_WHEN_POSSIBLE
1452 /* Hard link succeeded */
1458 static void copy_file(char *frompath, char *topath)
1460 char frompath2[256],topath2[256];
1461 ast_filecopy(frompath, topath, NULL);
1462 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1463 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1464 copy(frompath2, topath2);
1468 * A negative return value indicates an error.
1470 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1475 if (vm_lock_path(dir))
1476 return ERROR_LOCK_PATH;
1478 for (x = 0; x < vmu->maxmsg; x++) {
1479 make_file(fn, sizeof(fn), dir, x);
1480 if (ast_fileexists(fn, NULL, NULL) < 1)
1483 ast_unlock_path(dir);
1488 static int vm_delete(char *file)
1493 txtsize = (strlen(file) + 5)*sizeof(char);
1494 txt = alloca(txtsize);
1495 /* Sprintf here would safe because we alloca'd exactly the right length,
1496 * but trying to eliminate all sprintf's anyhow
1498 snprintf(txt, txtsize, "%s.txt", file);
1500 return ast_filedelete(file, NULL);
1506 inbuf(struct baseio *bio, FILE *fi)
1513 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1528 inchar(struct baseio *bio, FILE *fi)
1530 if (bio->iocp>=bio->iolen) {
1531 if (!inbuf(bio, fi))
1535 return bio->iobuf[bio->iocp++];
1539 ochar(struct baseio *bio, int c, FILE *so)
1541 if (bio->linelength>=BASELINELEN) {
1542 if (fputs(eol,so)==EOF)
1548 if (putc(((unsigned char)c),so)==EOF)
1556 static int base_encode(char *filename, FILE *so)
1558 unsigned char dtable[BASEMAXINLINE];
1563 memset(&bio, 0, sizeof(bio));
1564 bio.iocp = BASEMAXINLINE;
1566 if (!(fi = fopen(filename, "rb"))) {
1567 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1571 for (i= 0;i<9;i++) {
1574 dtable[26+i]= 'a'+i;
1575 dtable[26+i+9]= 'j'+i;
1577 for (i= 0;i<8;i++) {
1578 dtable[i+18]= 'S'+i;
1579 dtable[26+i+18]= 's'+i;
1581 for (i= 0;i<10;i++) {
1582 dtable[52+i]= '0'+i;
1588 unsigned char igroup[3],ogroup[4];
1591 igroup[0]= igroup[1]= igroup[2]= 0;
1593 for (n= 0;n<3;n++) {
1594 if ((c = inchar(&bio, fi)) == EOF) {
1599 igroup[n]= (unsigned char)c;
1603 ogroup[0]= dtable[igroup[0]>>2];
1604 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1605 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1606 ogroup[3]= dtable[igroup[2]&0x3F];
1616 ochar(&bio, ogroup[i], so);
1620 if (fputs(eol,so)==EOF)
1628 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)
1631 /* Prepare variables for substition in email body and subject */
1632 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1633 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1634 snprintf(passdata, passdatasize, "%d", msgnum);
1635 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1636 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1637 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1638 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1639 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1640 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1641 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1642 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1646 * fill in *tm for current time according to the proper timezone, if any.
1647 * Return tm so it can be used as a function argument.
1649 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1651 const struct vm_zone *z = NULL;
1652 time_t t = time(NULL);
1654 /* Does this user have a timezone specified? */
1655 if (!ast_strlen_zero(vmu->zonetag)) {
1656 /* Find the zone in the list */
1657 AST_LIST_LOCK(&zones);
1658 AST_LIST_TRAVERSE(&zones, z, list) {
1659 if (!strcmp(z->name, vmu->zonetag))
1662 AST_LIST_UNLOCK(&zones);
1664 ast_localtime(&t, tm, z ? z->timezone : NULL);
1668 /* same as mkstemp, but return a FILE * */
1669 static FILE *vm_mkftemp(char *template)
1672 int pfd = mkstemp(template);
1674 p = fdopen(pfd, "w");
1683 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)
1687 char host[MAXHOSTNAMELEN] = "";
1692 char tmp[80] = "/tmp/astmail-XXXXXX";
1696 if (vmu && ast_strlen_zero(vmu->email)) {
1697 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1700 if (!strcmp(format, "wav49"))
1702 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));
1703 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1705 if ((p = vm_mkftemp(tmp)) == NULL) {
1706 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1709 gethostname(host, sizeof(host)-1);
1710 if (strchr(srcemail, '@'))
1711 ast_copy_string(who, srcemail, sizeof(who));
1713 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1715 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1716 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1717 fprintf(p, "Date: %s\n", date);
1719 /* Set date format for voicemail mail */
1720 strftime(date, sizeof(date), emaildateformat, &tm);
1723 struct ast_channel *ast;
1724 if ((ast = ast_channel_alloc(0))) {
1726 int vmlen = strlen(fromstring)*3 + 200;
1727 if ((passdata = alloca(vmlen))) {
1728 memset(passdata, 0, vmlen);
1729 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1730 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1731 fprintf(p, "From: %s <%s>\n",passdata,who);
1732 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1733 ast_channel_free(ast);
1734 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1736 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1737 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1740 struct ast_channel *ast;
1741 if ((ast = ast_channel_alloc(0))) {
1743 int vmlen = strlen(emailsubject)*3 + 200;
1744 if ((passdata = alloca(vmlen))) {
1745 memset(passdata, 0, vmlen);
1746 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1747 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1748 fprintf(p, "Subject: %s\n",passdata);
1749 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1750 ast_channel_free(ast);
1751 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1754 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1756 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1757 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1759 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1760 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1761 fprintf(p, "MIME-Version: 1.0\n");
1762 if (attach_user_voicemail) {
1763 /* Something unique. */
1764 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1766 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1768 fprintf(p, "--%s\n", bound);
1770 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1772 struct ast_channel *ast;
1773 if ((ast = ast_channel_alloc(0))) {
1775 int vmlen = strlen(emailbody)*3 + 200;
1776 if ((passdata = alloca(vmlen))) {
1777 memset(passdata, 0, vmlen);
1778 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1779 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1780 fprintf(p, "%s\n",passdata);
1781 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1782 ast_channel_free(ast);
1783 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1785 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1787 "in mailbox %s from %s, on %s so you might\n"
1788 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1789 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1791 if (attach_user_voicemail) {
1792 /* Eww. We want formats to tell us their own MIME type */
1793 char *ctype = "audio/x-";
1794 if (!strcasecmp(format, "ogg"))
1795 ctype = "application/";
1797 fprintf(p, "--%s\n", bound);
1798 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1799 fprintf(p, "Content-Transfer-Encoding: base64\n");
1800 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1801 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1803 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1804 base_encode(fname, p);
1805 fprintf(p, "\n\n--%s--\n.\n", bound);
1808 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1809 ast_safe_system(tmp2);
1810 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1815 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)
1818 char host[MAXHOSTNAMELEN]="";
1821 char tmp[80] = "/tmp/astmail-XXXXXX";
1826 if ((p = vm_mkftemp(tmp)) == NULL) {
1827 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1830 gethostname(host, sizeof(host)-1);
1831 if (strchr(srcemail, '@'))
1832 ast_copy_string(who, srcemail, sizeof(who));
1834 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1836 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1837 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1838 fprintf(p, "Date: %s\n", date);
1840 if (*pagerfromstring) {
1841 struct ast_channel *ast;
1842 if ((ast = ast_channel_alloc(0))) {
1844 int vmlen = strlen(fromstring)*3 + 200;
1845 if ((passdata = alloca(vmlen))) {
1846 memset(passdata, 0, vmlen);
1847 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1848 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1849 fprintf(p, "From: %s <%s>\n",passdata,who);
1851 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1852 ast_channel_free(ast);
1853 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1855 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1856 fprintf(p, "To: %s\n", pager);
1858 struct ast_channel *ast;
1859 if ((ast = ast_channel_alloc(0))) {
1861 int vmlen = strlen(pagersubject)*3 + 200;
1862 if ((passdata = alloca(vmlen))) {
1863 memset(passdata, 0, vmlen);
1864 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1865 pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
1866 fprintf(p, "Subject: %s\n\n",passdata);
1867 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1868 ast_channel_free(ast);
1869 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1871 fprintf(p, "Subject: New VM\n\n");
1872 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1874 struct ast_channel *ast;
1875 if ((ast = ast_channel_alloc(0))) {
1877 int vmlen = strlen(pagerbody)*3 + 200;
1878 if ((passdata = alloca(vmlen))) {
1879 memset(passdata, 0, vmlen);
1880 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1881 pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
1882 fprintf(p, "%s\n",passdata);
1883 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1884 ast_channel_free(ast);
1885 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1887 fprintf(p, "New %s long msg in box %s\n"
1888 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1891 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1892 ast_safe_system(tmp2);
1893 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1898 static int get_date(char *s, int len)
1903 localtime_r(&t,&tm);
1904 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1907 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1911 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
1913 if (ast_fileexists(fn, NULL, NULL) > 0) {
1914 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
1920 /* Dispose just in case */
1922 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
1925 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1929 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
1933 static void free_user(struct ast_vm_user *vmu)
1935 if (ast_test_flag(vmu, VM_ALLOCED))
1939 static void free_zone(struct vm_zone *z)
1944 static const char *mbox(int id)
1946 static const char *msgs[] = {
1958 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
1961 #ifdef USE_ODBC_STORAGE
1962 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
1976 /* If no mailbox, return immediately */
1977 if (ast_strlen_zero(mailbox))
1980 ast_copy_string(tmp, mailbox, sizeof(tmp));
1982 context = strchr(tmp, '@');
1987 context = "default";
1990 obj = fetch_odbc_obj(odbc_database, 0);
1992 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1993 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1994 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1997 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir LIKE '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
1998 res = SQLPrepare(stmt, sql, SQL_NTS);
1999 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2000 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2001 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2004 res = odbc_smart_execute(obj, stmt);
2005 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2006 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2007 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2010 res = SQLFetch(stmt);
2011 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2012 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2013 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2016 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2017 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2018 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2019 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2022 *newmsgs = atoi(rowdata);
2023 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2025 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2026 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2027 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2030 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "Old");
2031 res = SQLPrepare(stmt, sql, SQL_NTS);
2032 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2033 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2034 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2037 res = odbc_smart_execute(obj, stmt);
2038 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2039 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2040 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2043 res = SQLFetch(stmt);
2044 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2045 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2046 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2049 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2050 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2051 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2052 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2055 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2056 *oldmsgs = atoi(rowdata);
2059 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2065 static int has_voicemail(const char *mailbox, const char *folder)
2076 /* If no mailbox, return immediately */
2077 if (ast_strlen_zero(mailbox))
2080 ast_copy_string(tmp, mailbox, sizeof(tmp));
2082 context = strchr(tmp, '@');
2087 context = "default";
2090 obj = fetch_odbc_obj(odbc_database, 0);
2092 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2093 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2094 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2097 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2098 res = SQLPrepare(stmt, sql, SQL_NTS);
2099 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2100 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2101 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2104 res = odbc_smart_execute(obj, stmt);
2105 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2106 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2107 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2110 res = SQLFetch(stmt);
2111 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2112 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2113 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2116 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2117 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2118 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2119 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2122 nummsgs = atoi(rowdata);
2123 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2125 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2136 static int has_voicemail(const char *mailbox, const char *folder)
2147 /* If no mailbox, return immediately */
2148 if (ast_strlen_zero(mailbox))
2150 if (strchr(mailbox, ',')) {
2151 ast_copy_string(tmp, mailbox, sizeof(tmp));
2154 while((cur = strsep(&mb, ","))) {
2155 if (!ast_strlen_zero(cur)) {
2156 if (has_voicemail(cur, folder))
2162 ast_copy_string(tmp, mailbox, sizeof(tmp));
2163 context = strchr(tmp, '@');
2168 context = "default";
2169 snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
2173 while ((de = readdir(dir))) {
2174 if (!strncasecmp(de->d_name, "msg", 3))
2184 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2196 /* If no mailbox, return immediately */
2197 if (ast_strlen_zero(mailbox))
2199 if (strchr(mailbox, ',')) {
2203 ast_copy_string(tmp, mailbox, sizeof(tmp));
2205 while((cur = strsep(&mb, ", "))) {
2206 if (!ast_strlen_zero(cur)) {
2207 if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2219 ast_copy_string(tmp, mailbox, sizeof(tmp));
2220 context = strchr(tmp, '@');
2225 context = "default";
2227 snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
2230 while ((de = readdir(dir))) {
2231 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2232 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2240 snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
2243 while ((de = readdir(dir))) {
2244 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2245 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2257 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
2259 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)
2261 char fromdir[256], todir[256], frompath[256], topath[256];
2262 const char *frombox = mbox(imbox);
2265 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2267 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2269 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2270 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2272 if (vm_lock_path(todir))
2273 return ERROR_LOCK_PATH;
2277 make_file(topath, sizeof(topath), todir, recipmsgnum);
2278 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2281 } while (recipmsgnum < recip->maxmsg);
2282 if (recipmsgnum < recip->maxmsg) {
2283 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2285 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2287 ast_unlock_path(todir);
2288 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2293 static void run_externnotify(char *context, char *extension)
2295 char arguments[255];
2296 char ext_context[256] = "";
2297 int newvoicemails = 0, oldvoicemails = 0;
2299 struct ast_smdi_mwi_message *mwi_msg;
2302 if (!ast_strlen_zero(context))
2303 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2305 ast_copy_string(ext_context, extension, sizeof(ext_context));
2308 if (!strcasecmp(externnotify, "smdi")) {
2309 if (ast_app_has_voicemail(ext_context, NULL))
2310 ast_smdi_mwi_set(smdi_iface, extension);
2312 ast_smdi_mwi_unset(smdi_iface, extension);
2314 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2315 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2316 if (!strncmp(mwi_msg->cause, "INV", 3))
2317 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2318 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2319 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2320 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2321 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2323 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2325 } else if (!ast_strlen_zero(externnotify)) {
2327 if (!ast_strlen_zero(externnotify)) {
2329 if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
2330 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2332 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2333 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2334 ast_safe_system(arguments);
2339 struct leave_vm_options {
2341 signed char record_gain;
2344 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2346 char tmptxtfile[256], txtfile[256];
2357 char prefile[256]="";
2358 char tempfile[256]="";
2359 char ext_context[256] = "";
2362 char ecodes[16] = "#";
2363 char tmp[256] = "", *tmpptr;
2364 struct ast_vm_user *vmu;
2365 struct ast_vm_user svm;
2366 const char *category = NULL;
2368 ast_copy_string(tmp, ext, sizeof(tmp));
2370 context = strchr(tmp, '@');
2373 tmpptr = strchr(context, '&');
2375 tmpptr = strchr(ext, '&');
2381 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2383 if (!(vmu = find_user(&svm, context, ext))) {
2384 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2385 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2386 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2387 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2391 /* Setup pre-file if appropriate */
2392 if (strcmp(vmu->context, "default"))
2393 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2395 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2396 if (ast_test_flag(options, OPT_BUSY_GREETING))
2397 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2398 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2399 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2400 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2401 RETRIEVE(tempfile, -1);
2402 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2403 ast_copy_string(prefile, tempfile, sizeof(prefile));
2404 DISPOSE(tempfile, -1);
2405 /* It's easier just to try to make it than to check for its existence */
2406 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2408 /* Check current or macro-calling context for special extensions */
2409 if (!ast_strlen_zero(vmu->exit)) {
2410 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2411 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2412 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
2413 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2414 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2415 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2419 if (!ast_strlen_zero(vmu->exit)) {
2420 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2421 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2422 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2423 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2424 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2425 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2429 /* Play the beginning intro if desired */
2430 if (!ast_strlen_zero(prefile)) {
2431 RETRIEVE(prefile, -1);
2432 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2433 if (ast_streamfile(chan, prefile, chan->language) > -1)
2434 res = ast_waitstream(chan, ecodes);
2436 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2437 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2439 DISPOSE(prefile, -1);
2441 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2443 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2448 /* On a '#' we skip the instructions */
2449 ast_set_flag(options, OPT_SILENT);
2452 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2453 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2455 ast_set_flag(options, OPT_SILENT);
2460 ast_stopstream(chan);
2461 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2462 other than the operator -- an automated attendant or mailbox login for example */
2464 chan->exten[0] = 'a';
2465 chan->exten[1] = '\0';
2466 if (!ast_strlen_zero(vmu->exit)) {
2467 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2468 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2469 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2473 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2476 /* Check for a '0' here */
2479 if (ast_test_flag(vmu, VM_OPERATOR)) {
2480 chan->exten[0] = 'o';
2481 chan->exten[1] = '\0';
2482 if (!ast_strlen_zero(vmu->exit)) {
2483 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2484 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2485 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2487 ast_play_and_wait(chan, "transfer");
2490 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2493 ast_play_and_wait(chan, "vm-sorry");
2494 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2500 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2503 /* The meat of recording the message... All the announcements and beeps have been played*/
2504 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2505 if (!ast_strlen_zero(fmt)) {
2508 if (vm_lock_path(dir)) {
2510 return ERROR_LOCK_PATH;
2514 * This operation can be very expensive if done say over NFS or if the mailbox has 100+ messages
2515 * in the folder. So we should get this first so we don't cut off the first few seconds of the
2519 make_file(fn, sizeof(fn), dir, msgnum);
2520 if (!EXISTS(dir,msgnum,fn,chan->language))
2523 } while (msgnum < vmu->maxmsg);
2525 /* Now play the beep once we have the message number for our next message. */
2527 /* Unless we're *really* silent, try to send the beep */
2528 res = ast_stream_and_wait(chan, "beep", chan->language, "");
2530 if (msgnum < vmu->maxmsg) {
2531 /* assign a variable with the name of the voicemail file */
2532 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
2534 /* Store information */
2535 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2536 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s.txt.tmp", fn);
2537 txt = fopen(tmptxtfile, "w+");
2539 get_date(date, sizeof(date));
2542 "; Message Information file\n"
2561 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2562 date, (long)time(NULL),
2563 category ? category : "");
2565 ast_log(LOG_WARNING, "Error opening text file for output\n");
2566 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2570 rename(tmptxtfile, txtfile);
2577 fprintf(txt, "duration=%d\n", duration);
2579 rename(tmptxtfile, txtfile);
2582 if (duration < vmminmessage) {
2583 if (option_verbose > 2)
2584 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2585 DELETE(dir,msgnum,fn);
2586 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2587 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2590 /* Are there to be more recipients of this message? */
2592 struct ast_vm_user recipu, *recip;
2593 char *exten, *context;
2595 exten = strsep(&tmpptr, "&");
2596 context = strchr(exten, '@');
2601 if ((recip = find_user(&recipu, context, exten))) {
2602 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2606 if (ast_fileexists(fn, NULL, NULL)) {
2607 STORE(dir, vmu->mailbox, vmu->context, msgnum);
2608 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2609 DISPOSE(dir, msgnum);
2611 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
2613 ast_unlock_path(dir);
2614 res = ast_stream_and_wait(chan, "vm-mailboxfull", chan->language, "");
2615 ast_log(LOG_WARNING, "No more messages possible\n");
2616 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2619 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2626 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
2628 /* we know max messages, so stop process when number is hit */
2634 if (vm_lock_path(dir))
2635 return ERROR_LOCK_PATH;
2637 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
2638 make_file(sfn, sizeof(sfn), dir, x);
2639 if (EXISTS(dir, x, sfn, NULL)) {
2642 make_file(dfn, sizeof(dfn), dir, dest);
2643 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
2649 ast_unlock_path(dir);
2655 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
2658 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2662 static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *context, char *username, int box)
2667 const char *dbox = mbox(box);
2669 make_file(sfn, sizeof(sfn), dir, msg);
2670 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
2672 if (vm_lock_path(ddir))
2673 return ERROR_LOCK_PATH;
2675 for (x = 0; x < vmu->maxmsg; x++) {
2676 make_file(dfn, sizeof(dfn), ddir, x);
2677 if (!EXISTS(ddir, x, dfn, NULL))
2680 if (x >= vmu->maxmsg) {
2681 ast_unlock_path(ddir);
2684 if (strcmp(sfn, dfn)) {
2685 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
2687 ast_unlock_path(ddir);
2692 static int adsi_logo(unsigned char *buf)
2695 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2696 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2700 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2702 unsigned char buf[256];
2708 bytes += adsi_data_mode(buf + bytes);
2709 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2712 bytes += adsi_logo(buf);
2713 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2715 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
2717 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2718 bytes += adsi_data_mode(buf + bytes);
2719 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2721 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2723 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2724 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2725 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2726 bytes += adsi_voice_mode(buf + bytes, 0);
2727 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2734 bytes += adsi_logo(buf);
2735 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2736 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
2737 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2738 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2741 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2742 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2743 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2744 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2745 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2746 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2747 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2750 /* Add another dot */
2752 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
2753 bytes += adsi_voice_mode(buf + bytes, 0);
2755 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2756 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2760 /* These buttons we load but don't use yet */
2761 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2762 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2763 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2764 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2765 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2766 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2767 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2770 /* Add another dot */
2772 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2773 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2774 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2779 snprintf(num, sizeof(num), "%d", x);
2780 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2782 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2783 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2786 /* Add another dot */
2788 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2789 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2790 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2793 if (adsi_end_download(chan)) {
2795 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2796 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2797 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2798 bytes += adsi_voice_mode(buf + bytes, 0);
2799 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2803 bytes += adsi_download_disconnect(buf + bytes);
2804 bytes += adsi_voice_mode(buf + bytes, 0);
2805 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2807 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2812 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2813 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2815 ast_log(LOG_DEBUG, "Restarting session...\n");
2818 /* Load the session now */
2819 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2821 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2823 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2825 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2829 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2832 if (!adsi_available(chan))
2834 x = adsi_load_session(chan, adsifdn, adsiver, 1);
2838 if (adsi_load_vmail(chan, useadsi)) {
2839 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2846 static void adsi_login(struct ast_channel *chan)
2848 unsigned char buf[256];
2850 unsigned char keys[8];
2852 if (!adsi_available(chan))
2857 /* Set one key for next */
2858 keys[3] = ADSI_KEY_APPS + 3;
2860 bytes += adsi_logo(buf + bytes);
2861 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2862 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2863 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2864 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2865 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2866 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2867 bytes += adsi_set_keys(buf + bytes, keys);
2868 bytes += adsi_voice_mode(buf + bytes, 0);
2869 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2872 static void adsi_password(struct ast_channel *chan)
2874 unsigned char buf[256];
2876 unsigned char keys[8];
2878 if (!adsi_available(chan))
2883 /* Set one key for next */
2884 keys[3] = ADSI_KEY_APPS + 3;
2886 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2887 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2888 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2889 bytes += adsi_set_keys(buf + bytes, keys);
2890 bytes += adsi_voice_mode(buf + bytes, 0);
2891 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2894 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2896 unsigned char buf[256];
2898 unsigned char keys[8];
2901 if (!adsi_available(chan))
2905 y = ADSI_KEY_APPS + 12 + start + x;
2906 if (y > ADSI_KEY_APPS + 12 + 4)
2908 keys[x] = ADSI_KEY_SKT | y;
2910 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2914 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2915 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2916 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2917 bytes += adsi_set_keys(buf + bytes, keys);
2918 bytes += adsi_voice_mode(buf + bytes, 0);
2920 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2923 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2926 unsigned char buf[256];
2927 char buf1[256], buf2[256];
2933 char datetime[21]="";
2936 unsigned char keys[8];
2940 if (!adsi_available(chan))
2943 /* Retrieve important info */
2944 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
2945 f = fopen(fn2, "r");
2948 fgets((char *)buf, sizeof(buf), f);
2951 stringp = (char *)buf;
2952 strsep(&stringp, "=");
2953 val = strsep(&stringp, "=");
2954 if (!ast_strlen_zero(val)) {
2955 if (!strcmp((char *)buf, "callerid"))
2956 ast_copy_string(cid, val, sizeof(cid));
2957 if (!strcmp((char *)buf, "origdate"))
2958 ast_copy_string(datetime, val, sizeof(datetime));
2964 /* New meaning for keys */
2966 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2971 /* No prev key, provide "Folder" instead */
2972 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2974 if (vms->curmsg >= vms->lastmsg) {
2975 /* If last message ... */
2977 /* but not only message, provide "Folder" instead */
2978 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2979 bytes += adsi_voice_mode(buf + bytes, 0);
2982 /* Otherwise if only message, leave blank */
2987 if (!ast_strlen_zero(cid)) {
2988 ast_callerid_parse(cid, &name, &num);
2992 name = "Unknown Caller";
2994 /* If deleted, show "undeleted" */
2996 if (vms->deleted[vms->curmsg])
2997 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3000 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3001 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3002 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3003 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3005 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3006 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3007 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3008 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3009 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3010 bytes += adsi_set_keys(buf + bytes, keys);
3011 bytes += adsi_voice_mode(buf + bytes, 0);
3013 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3016 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3019 unsigned char buf[256];
3020 unsigned char keys[8];
3024 if (!adsi_available(chan))
3027 /* New meaning for keys */