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>
40 * 12-04-2006 : Support for Polish added by DIR (www.dir.pl)
41 * Bartosz Supczinski <Bartosz.Supczinski@dir.pl>
52 #include <sys/types.h>
59 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
61 #include "asterisk/lock.h"
62 #include "asterisk/file.h"
63 #include "asterisk/logger.h"
64 #include "asterisk/channel.h"
65 #include "asterisk/pbx.h"
66 #include "asterisk/options.h"
67 #include "asterisk/config.h"
68 #include "asterisk/say.h"
69 #include "asterisk/module.h"
70 #include "asterisk/adsi.h"
71 #include "asterisk/app.h"
72 #include "asterisk/manager.h"
73 #include "asterisk/dsp.h"
74 #include "asterisk/localtime.h"
75 #include "asterisk/cli.h"
76 #include "asterisk/utils.h"
77 #include "asterisk/stringfields.h"
79 #include "asterisk/smdi.h"
80 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
82 #ifdef USE_ODBC_STORAGE
83 #include "asterisk/res_odbc.h"
86 #define COMMAND_TIMEOUT 5000
87 #define VOICEMAIL_DIR_MODE 0770
88 #define VOICEMAIL_FILE_MODE 0660
90 #define VOICEMAIL_CONFIG "voicemail.conf"
91 #define ASTERISK_USERNAME "asterisk"
93 /* Default mail command to mail voicemail. Change it with the
94 mailcmd= command in voicemail.conf */
95 #define SENDMAIL "/usr/sbin/sendmail -t"
97 #define INTRO "vm-intro"
100 #define MAXMSGLIMIT 9999
102 #define BASEMAXINLINE 256
103 #define BASELINELEN 72
104 #define BASEMAXINLINE 256
107 #define MAX_DATETIME_FORMAT 512
108 #define MAX_NUM_CID_CONTEXTS 10
110 #define VM_REVIEW (1 << 0)
111 #define VM_OPERATOR (1 << 1)
112 #define VM_SAYCID (1 << 2)
113 #define VM_SVMAIL (1 << 3)
114 #define VM_ENVELOPE (1 << 4)
115 #define VM_SAYDURATION (1 << 5)
116 #define VM_SKIPAFTERCMD (1 << 6)
117 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
118 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
119 #define VM_PBXSKIP (1 << 9)
120 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
121 #define VM_ATTACH (1 << 11)
122 #define VM_DELETE (1 << 12)
123 #define VM_ALLOCED (1 << 13)
124 #define VM_SEARCH (1 << 14)
125 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
126 #define ERROR_LOCK_PATH -100
129 OPT_SILENT = (1 << 0),
130 OPT_BUSY_GREETING = (1 << 1),
131 OPT_UNAVAIL_GREETING = (1 << 2),
132 OPT_RECORDGAIN = (1 << 3),
133 OPT_PREPEND_MAILBOX = (1 << 4),
134 OPT_PRIORITY_JUMP = (1 << 5),
135 OPT_AUTOPLAY = (1 << 6),
139 OPT_ARG_RECORDGAIN = 0,
140 OPT_ARG_PLAYFOLDER = 1,
141 /* This *must* be the last value in this enum! */
142 OPT_ARG_ARRAY_SIZE = 2,
145 AST_APP_OPTIONS(vm_app_options, {
146 AST_APP_OPTION('s', OPT_SILENT),
147 AST_APP_OPTION('b', OPT_BUSY_GREETING),
148 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
149 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
150 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
151 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
152 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
155 static int load_config(void);
157 /*! \page vmlang Voicemail Language Syntaxes Supported
159 \par Syntaxes supported, not really language codes.
167 \arg \b pt - Portuguese
169 \arg \b no - Norwegian
172 German requires the following additional soundfile:
173 \arg \b 1F einE (feminine)
175 Spanish requires the following additional soundfile:
176 \arg \b 1M un (masculine)
178 Dutch, Portuguese & Spanish require the following additional soundfiles:
179 \arg \b vm-INBOXs singular of 'new'
180 \arg \b vm-Olds singular of 'old/heard/read'
183 \arg \b vm-INBOX nieuwe (nl)
184 \arg \b vm-Old oude (nl)
187 \arg \b vm-new-a 'new', feminine singular accusative
188 \arg \b vm-new-e 'new', feminine plural accusative
189 \arg \b vm-new-ych 'new', feminine plural genitive
190 \arg \b vm-old-a 'old', feminine singular accusative
191 \arg \b vm-old-e 'old', feminine plural accusative
192 \arg \b vm-old-ych 'old', feminine plural genitive
193 \arg \b digits/1-a 'one', not always same as 'digits/1'
194 \arg \b digits/2-ie 'two', not always same as 'digits/2'
197 \arg \b vm-nytt singular of 'new'
198 \arg \b vm-nya plural of 'new'
199 \arg \b vm-gammalt singular of 'old'
200 \arg \b vm-gamla plural of 'old'
201 \arg \b digits/ett 'one', not always same as 'digits/1'
204 \arg \b vm-ny singular of 'new'
205 \arg \b vm-nye plural of 'new'
206 \arg \b vm-gammel singular of 'old'
207 \arg \b vm-gamle plural of 'old'
215 Italian requires the following additional soundfile:
219 \arg \b vm-nuovi new plural
220 \arg \b vm-vecchio old
221 \arg \b vm-vecchi old plural
223 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
224 spelled among others when you have to change folder. For the above reasons, vm-INBOX
225 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
234 unsigned char iobuf[BASEMAXINLINE];
237 /*! Structure for linked list of users */
239 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
240 char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */
241 char password[80]; /*!< Secret pin code, numbers only */
242 char fullname[80]; /*!< Full name, for directory app */
243 char email[80]; /*!< E-mail address */
244 char pager[80]; /*!< E-mail address to pager (no attachment) */
245 char serveremail[80]; /*!< From: Mail address */
246 char mailcmd[160]; /*!< Configurable mail command */
247 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
248 char zonetag[80]; /*!< Time zone */
251 char uniqueid[20]; /*!< Unique integer identifier */
253 char attachfmt[20]; /*!< Attachment format */
254 unsigned int flags; /*!< VM_ flags */
256 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
257 AST_LIST_ENTRY(ast_vm_user) list;
261 AST_LIST_ENTRY(vm_zone) list;
264 char msg_format[512];
283 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
284 int option, signed char record_gain);
285 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
286 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
287 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
288 signed char record_gain);
289 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
290 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
292 static void apply_options(struct ast_vm_user *vmu, const char *options);
294 #ifdef USE_ODBC_STORAGE
295 static char odbc_database[80];
296 static char odbc_table[80];
297 #define RETRIEVE(a,b) retrieve_file(a,b)
298 #define DISPOSE(a,b) remove_file(a,b)
299 #define STORE(a,b,c,d) store_file(a,b,c,d)
300 #define EXISTS(a,b,c,d) (message_exists(a,b))
301 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
302 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
303 #define DELETE(a,b,c) (delete_file(a,b))
305 #define RETRIEVE(a,b)
307 #define STORE(a,b,c,d)
308 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
309 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
310 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
311 #define DELETE(a,b,c) (vm_delete(c))
314 static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
316 static char ext_pass_cmd[128];
318 static char *tdesc = "Comedian Mail (Voicemail System)";
320 static char *addesc = "Comedian Mail";
322 static char *synopsis_vm =
323 "Leave a Voicemail message";
325 static char *descrip_vm =
326 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
327 "application allows the calling party to leave a message for the specified\n"
328 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
329 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
330 "specified mailbox does not exist.\n"
331 " The Voicemail application will exit if any of the following DTMF digits are\n"
333 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
334 " * - Jump to the 'a' extension in the current dialplan context.\n"
335 " This application will set the following channel variable upon completion:\n"
336 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
337 " application. The possible values are:\n"
338 " SUCCESS | USEREXIT | FAILED\n\n"
340 " b - Play the 'busy' greeting to the calling party.\n"
341 " g(#) - Use the specified amount of gain when recording the voicemail\n"
342 " message. The units are whole-number decibels (dB).\n"
343 " s - Skip the playback of instructions for leaving a message to the\n"
345 " u - Play the 'unavailable greeting.\n"
346 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
349 static char *synopsis_vmain =
350 "Check Voicemail messages";
352 static char *descrip_vmain =
353 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
354 "calling party to check voicemail messages. A specific mailbox, and optional\n"
355 "corresponding context, may be specified. If a mailbox is not provided, the\n"
356 "calling party will be prompted to enter one. If a context is not specified,\n"
357 "the 'default' context will be used.\n\n"
359 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
360 " is entered by the caller.\n"
361 " g(#) - Use the specified amount of gain when recording a voicemail\n"
362 " message. The units are whole-number decibels (dB).\n"
363 " s - Skip checking the passcode for the mailbox.\n"
364 " a(#) - Skip folder prompt and go directly to folder specified.\n"
365 " Defaults to INBOX\n";
367 static char *synopsis_vm_box_exists =
368 "Check to see if Voicemail mailbox exists";
370 static char *descrip_vm_box_exists =
371 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
372 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
374 " This application will set the following channel variable upon completion:\n"
375 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
376 " MailboxExists application. Possible values include:\n"
377 " SUCCESS | FAILED\n\n"
379 " j - Jump to priority n+101 if the mailbox is found.\n";
381 static char *synopsis_vmauthenticate =
382 "Authenticate with Voicemail passwords";
384 static char *descrip_vmauthenticate =
385 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
386 "same way as the Authenticate application, but the passwords are taken from\n"
388 " If the mailbox is specified, only that mailbox's password will be considered\n"
389 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
390 "be set with the authenticated mailbox.\n\n"
392 " s - Skip playing the initial prompts.\n";
394 /* Leave a message */
395 static char *app = "VoiceMail";
397 /* Check mail, control, etc */
398 static char *app2 = "VoiceMailMain";
400 static char *app3 = "MailboxExists";
401 static char *app4 = "VMAuthenticate";
403 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
404 static AST_LIST_HEAD_STATIC(zones, vm_zone);
405 static int maxsilence;
407 static int silencethreshold = 128;
408 static char serveremail[80];
409 static char mailcmd[160]; /* Configurable mail cmd */
410 static char externnotify[160];
412 static struct ast_smdi_interface *smdi_iface = NULL;
414 static char vmfmts[80];
415 static int vmminmessage;
416 static int vmmaxmessage;
419 static int maxlogins;
421 static struct ast_flags globalflags = {0};
423 static int saydurationminfo;
425 static char dialcontext[AST_MAX_CONTEXT];
426 static char callcontext[AST_MAX_CONTEXT];
427 static char exitcontext[AST_MAX_CONTEXT];
429 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
432 static char *emailbody = NULL;
433 static char *emailsubject = NULL;
434 static char *pagerbody = NULL;
435 static char *pagersubject = NULL;
436 static char fromstring[100];
437 static char pagerfromstring[100];
438 static char emailtitle[100];
439 static char charset[32] = "ISO-8859-1";
441 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
442 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
443 static int adsiver = 1;
444 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
448 static void populate_defaults(struct ast_vm_user *vmu)
450 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
451 if (saydurationminfo)
452 vmu->saydurationm = saydurationminfo;
454 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
456 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
458 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
460 vmu->maxmsg = maxmsg;
463 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
466 if (!strcasecmp(var, "attach")) {
467 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
468 } else if (!strcasecmp(var, "attachfmt")) {
469 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
470 } else if (!strcasecmp(var, "serveremail")) {
471 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
472 } else if (!strcasecmp(var, "language")) {
473 ast_copy_string(vmu->language, value, sizeof(vmu->language));
474 } else if (!strcasecmp(var, "tz")) {
475 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
476 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
477 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
478 } else if (!strcasecmp(var, "saycid")){
479 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
480 } else if (!strcasecmp(var,"sendvoicemail")){
481 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
482 } else if (!strcasecmp(var, "review")){
483 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
484 } else if (!strcasecmp(var, "tempgreetwarn")){
485 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
486 } else if (!strcasecmp(var, "operator")){
487 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
488 } else if (!strcasecmp(var, "envelope")){
489 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
490 } else if (!strcasecmp(var, "sayduration")){
491 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
492 } else if (!strcasecmp(var, "saydurationm")){
493 if (sscanf(value, "%d", &x) == 1) {
494 vmu->saydurationm = x;
496 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
498 } else if (!strcasecmp(var, "forcename")){
499 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
500 } else if (!strcasecmp(var, "forcegreetings")){
501 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
502 } else if (!strcasecmp(var, "callback")) {
503 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
504 } else if (!strcasecmp(var, "dialout")) {
505 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
506 } else if (!strcasecmp(var, "exitcontext")) {
507 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
508 } else if (!strcasecmp(var, "maxmsg")) {
509 vmu->maxmsg = atoi(value);
510 if (vmu->maxmsg <= 0) {
511 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
512 vmu->maxmsg = MAXMSG;
513 } else if (vmu->maxmsg > MAXMSGLIMIT) {
514 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
515 vmu->maxmsg = MAXMSGLIMIT;
517 } else if (!strcasecmp(var, "options")) {
518 apply_options(vmu, value);
522 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
525 if (!ast_strlen_zero(vmu->uniqueid)) {
526 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
528 ast_copy_string(vmu->password, password, sizeof(vmu->password));
538 static void apply_options(struct ast_vm_user *vmu, const char *options)
539 { /* Destructively Parse options and apply */
543 stringp = ast_strdupa(options);
544 while ((s = strsep(&stringp, "|"))) {
546 if ((var = strsep(&value, "=")) && value) {
547 apply_option(vmu, var, value);
552 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
554 struct ast_variable *var, *tmp;
555 struct ast_vm_user *retval;
557 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
559 ast_set_flag(retval, VM_ALLOCED);
561 memset(retval, 0, sizeof(*retval));
563 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
564 populate_defaults(retval);
565 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
566 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
568 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
572 printf("%s => %s\n", tmp->name, tmp->value);
573 if (!strcasecmp(tmp->name, "password")) {
574 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
575 } else if (!strcasecmp(tmp->name, "uniqueid")) {
576 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
577 } else if (!strcasecmp(tmp->name, "pager")) {
578 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
579 } else if (!strcasecmp(tmp->name, "email")) {
580 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
581 } else if (!strcasecmp(tmp->name, "fullname")) {
582 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
583 } else if (!strcasecmp(tmp->name, "context")) {
584 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
586 apply_option(retval, tmp->name, tmp->value);
598 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
600 /* This function could be made to generate one from a database, too */
601 struct ast_vm_user *vmu=NULL, *cur;
602 AST_LIST_LOCK(&users);
604 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
607 AST_LIST_TRAVERSE(&users, cur, list) {
608 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
610 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
614 /* Make a copy, so that on a reload, we have no race */
615 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
616 memcpy(vmu, cur, sizeof(*vmu));
617 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
618 AST_LIST_NEXT(vmu, list) = NULL;
621 vmu = find_user_realtime(ivm, context, mailbox);
622 AST_LIST_UNLOCK(&users);
626 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
628 /* This function could be made to generate one from a database, too */
629 struct ast_vm_user *cur;
631 AST_LIST_LOCK(&users);
632 AST_LIST_TRAVERSE(&users, cur, list) {
633 if ((!context || !strcasecmp(context, cur->context)) &&
634 (!strcasecmp(mailbox, cur->mailbox)))
638 ast_copy_string(cur->password, newpass, sizeof(cur->password));
641 AST_LIST_UNLOCK(&users);
645 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
647 /* There's probably a better way of doing this. */
648 /* That's why I've put the password change in a separate function. */
649 /* This could also be done with a database function */
656 char currcontext[256] ="";
657 char tmpin[AST_CONFIG_MAX_PATH];
658 char tmpout[AST_CONFIG_MAX_PATH];
661 if (!change_password_realtime(vmu, newpassword))
664 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
665 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
666 configin = fopen(tmpin,"r");
668 configout = fopen(tmpout,"w+");
671 if (!configin || !configout) {
675 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
679 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
683 while (!feof(configin)) {
684 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
686 /* Read in the line */
687 if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
691 /* Make a backup of it */
692 ast_copy_string(orig, inbuf, sizeof(orig));
695 Read the file line by line, split each line into a comment and command section
696 only parse the command portion of the line
698 if (inbuf[strlen(inbuf) - 1] == '\n')
699 inbuf[strlen(inbuf) - 1] = '\0';
701 if ((comment = strchr(inbuf, ';')))
702 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
704 if (ast_strlen_zero(inbuf)) {
705 fprintf(configout, "%s", orig);
709 /* Check for a context, first '[' to first ']' */
710 if ((tmpctx = strchr(inbuf, '['))) {
711 tmpctxend = strchr(tmpctx, ']');
714 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
715 fprintf(configout, "%s", orig);
720 /* This isn't a context line, check for MBX => PSWD... */
722 if ((pass = strchr(user, '='))) {
723 /* We have a line in the form of aaaaa=aaaaaa */
726 user = ast_strip(user);
731 pass = ast_skip_blanks(pass);
734 Since no whitespace allowed in fields, or more correctly white space
735 inside the fields is there for a purpose, we can just terminate pass
736 at the comma or EOL whichever comes first.
738 if ((rest = strchr(pass, ',')))
744 /* Compare user, pass AND context */
745 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
746 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
747 !strcasecmp(currcontext, vmu->context)) {
748 /* This is the line */
750 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
752 fprintf(configout, "%s => %s", user, newpassword);
754 /* If there was a comment on the line print it out */
756 fprintf(configout, ";%s\n", comment);
758 fprintf(configout, "\n");
761 /* Put it back like it was */
762 fprintf(configout, "%s", orig);
768 stat(tmpin, &statbuf);
769 chmod(tmpout, statbuf.st_mode);
770 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
772 rename(tmpout, tmpin);
773 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
774 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
777 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
780 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
781 if (!ast_safe_system(buf))
782 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
785 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
787 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
790 static int make_file(char *dest, int len, char *dir, int num)
792 return snprintf(dest, len, "%s/msg%04d", dir, num);
795 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
796 * \param dest String. base directory.
797 * \param context String. Ignored if is null or empty string.
798 * \param ext String. Ignored if is null or empty string.
799 * \param folder String. Ignored if is null or empty string.
800 * \return 0 on failure, 1 on success.
802 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
804 mode_t mode = VOICEMAIL_DIR_MODE;
806 if (!ast_strlen_zero(context)) {
807 make_dir(dest, len, context, "", "");
808 if(mkdir(dest, mode) && errno != EEXIST) {
809 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
813 if (!ast_strlen_zero(ext)) {
814 make_dir(dest, len, context, ext, "");
815 if(mkdir(dest, mode) && errno != EEXIST) {
816 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
820 if (!ast_strlen_zero(folder)) {
821 make_dir(dest, len, context, ext, folder);
822 if(mkdir(dest, mode) && errno != EEXIST) {
823 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
830 /* only return failure if ast_lock_path returns 'timeout',
831 not if the path does not exist or any other reason
833 static int vm_lock_path(const char *path)
835 switch (ast_lock_path(path)) {
836 case AST_LOCK_TIMEOUT:
844 #ifdef USE_ODBC_STORAGE
845 static int retrieve_file(char *dir, int msgnum)
852 SQLSMALLINT colcount=0;
859 SQLSMALLINT datatype;
860 SQLSMALLINT decimaldigits;
861 SQLSMALLINT nullable;
869 struct odbc_obj *obj;
870 obj = odbc_request_obj(odbc_database, 0);
872 ast_copy_string(fmt, vmfmts, sizeof(fmt));
873 c = strchr(fmt, '|');
876 if (!strcasecmp(fmt, "wav49"))
878 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
880 make_file(fn, sizeof(fn), dir, msgnum);
882 ast_copy_string(fn, dir, sizeof(fn));
883 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
884 f = fopen(full_fn, "w+");
885 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
886 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
887 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
888 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
889 odbc_release_obj(obj);
892 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
893 res = SQLPrepare(stmt, sql, SQL_NTS);
894 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
895 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
896 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
897 odbc_release_obj(obj);
900 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
901 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
902 res = odbc_smart_execute(obj, stmt);
903 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
904 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
905 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
906 odbc_release_obj(obj);
909 res = SQLFetch(stmt);
910 if (res == SQL_NO_DATA) {
911 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
912 odbc_release_obj(obj);
915 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
916 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
917 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
918 odbc_release_obj(obj);
921 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
923 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
924 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
925 odbc_release_obj(obj);
928 res = SQLNumResultCols(stmt, &colcount);
929 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
930 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
931 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
932 odbc_release_obj(obj);
936 fprintf(f, "[message]\n");
937 for (x=0;x<colcount;x++) {
939 collen = sizeof(coltitle);
940 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
941 &datatype, &colsize, &decimaldigits, &nullable);
942 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
943 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
944 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
945 odbc_release_obj(obj);
948 if (!strcasecmp(coltitle, "recording")) {
949 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
951 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
954 lseek(fd, fdlen - 1, SEEK_SET);
955 if (write(fd, tmp, 1) != 1) {
960 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
963 memset(fdm, 0, fdlen);
964 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
965 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
966 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
967 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
968 odbc_release_obj(obj);
973 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
974 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
975 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
976 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
977 odbc_release_obj(obj);
980 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
981 fprintf(f, "%s=%s\n", coltitle, rowdata);
984 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
985 odbc_release_obj(obj);
987 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
998 static int remove_file(char *dir, int msgnum)
1005 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1006 make_file(fn, sizeof(fn), dir, msgnum);
1008 ast_copy_string(fn, dir, sizeof(fn));
1009 ast_filedelete(fn, NULL);
1010 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1015 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1023 struct odbc_obj *obj;
1024 obj = odbc_request_obj(odbc_database, 0);
1026 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1027 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1028 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1029 odbc_release_obj(obj);
1032 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1033 res = SQLPrepare(stmt, sql, SQL_NTS);
1034 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1035 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1036 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1037 odbc_release_obj(obj);
1040 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1041 res = odbc_smart_execute(obj, stmt);
1042 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1043 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1044 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1045 odbc_release_obj(obj);
1048 res = SQLFetch(stmt);
1049 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1050 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1051 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1052 odbc_release_obj(obj);
1055 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1056 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1057 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1058 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1059 odbc_release_obj(obj);
1062 if (sscanf(rowdata, "%d", &x) != 1)
1063 ast_log(LOG_WARNING, "Failed to read message count!\n");
1064 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1065 odbc_release_obj(obj);
1067 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1072 static int message_exists(char *dir, int msgnum)
1081 struct odbc_obj *obj;
1082 obj = odbc_request_obj(odbc_database, 0);
1084 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1085 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1086 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1087 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1088 odbc_release_obj(obj);
1091 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1092 res = SQLPrepare(stmt, sql, SQL_NTS);
1093 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1094 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1095 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1096 odbc_release_obj(obj);
1099 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1100 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1101 res = odbc_smart_execute(obj, stmt);
1102 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1103 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1104 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1105 odbc_release_obj(obj);
1108 res = SQLFetch(stmt);
1109 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1110 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1111 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1112 odbc_release_obj(obj);
1115 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1116 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1117 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1118 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1119 odbc_release_obj(obj);
1122 if (sscanf(rowdata, "%d", &x) != 1)
1123 ast_log(LOG_WARNING, "Failed to read message count!\n");
1124 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1125 odbc_release_obj(obj);
1127 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1132 static int count_messages(struct ast_vm_user *vmu, char *dir)
1134 return last_message_index(vmu, dir) + 1;
1137 static void delete_file(char *sdir, int smsg)
1144 struct odbc_obj *obj;
1145 obj = odbc_request_obj(odbc_database, 0);
1147 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1148 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1149 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1150 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1151 odbc_release_obj(obj);
1154 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1155 res = SQLPrepare(stmt, sql, SQL_NTS);
1156 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1157 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1158 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1159 odbc_release_obj(obj);
1162 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1163 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1164 res = odbc_smart_execute(obj, stmt);
1165 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1166 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1167 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1168 odbc_release_obj(obj);
1171 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1172 odbc_release_obj(obj);
1174 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1179 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1186 struct odbc_obj *obj;
1188 delete_file(ddir, dmsg);
1189 obj = odbc_request_obj(odbc_database, 0);
1191 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1192 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1193 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1194 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1195 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1196 odbc_release_obj(obj);
1199 #ifdef EXTENDED_ODBC_STORAGE
1200 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);
1202 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);
1204 res = SQLPrepare(stmt, sql, SQL_NTS);
1205 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1206 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1207 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1208 odbc_release_obj(obj);
1211 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1212 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1213 #ifdef EXTENDED_ODBC_STORAGE
1214 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1215 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1216 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1217 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1219 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1220 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1222 res = odbc_smart_execute(obj, stmt);
1223 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1224 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1225 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1226 odbc_release_obj(obj);
1229 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1230 odbc_release_obj(obj);
1232 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1237 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1252 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1253 char *category = "";
1254 struct ast_config *cfg=NULL;
1255 struct odbc_obj *obj;
1257 delete_file(dir, msgnum);
1258 obj = odbc_request_obj(odbc_database, 0);
1260 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1261 c = strchr(fmt, '|');
1264 if (!strcasecmp(fmt, "wav49"))
1266 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1268 make_file(fn, sizeof(fn), dir, msgnum);
1270 ast_copy_string(fn, dir, sizeof(fn));
1271 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1272 cfg = ast_config_load(full_fn);
1273 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1274 fd = open(full_fn, O_RDWR);
1276 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1277 odbc_release_obj(obj);
1281 context = ast_variable_retrieve(cfg, "message", "context");
1282 if (!context) context = "";
1283 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1284 if (!macrocontext) macrocontext = "";
1285 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1286 if (!callerid) callerid = "";
1287 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1288 if (!origtime) origtime = "";
1289 duration = ast_variable_retrieve(cfg, "message", "duration");
1290 if (!duration) duration = "";
1291 category = ast_variable_retrieve(cfg, "message", "category");
1292 if (!category) category = "";
1294 fdlen = lseek(fd, 0, SEEK_END);
1295 lseek(fd, 0, SEEK_SET);
1296 printf("Length is %d\n", fdlen);
1297 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1299 ast_log(LOG_WARNING, "Memory map failed!\n");
1300 odbc_release_obj(obj);
1303 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1304 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1305 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1306 odbc_release_obj(obj);
1309 if (!ast_strlen_zero(category))
1310 #ifdef EXTENDED_ODBC_STORAGE
1311 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1313 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
1316 #ifdef EXTENDED_ODBC_STORAGE
1317 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1319 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
1321 res = SQLPrepare(stmt, sql, SQL_NTS);
1322 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1323 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1324 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1325 odbc_release_obj(obj);
1328 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1329 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1330 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1331 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1332 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1333 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1334 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1335 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1336 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1337 #ifdef EXTENDED_ODBC_STORAGE
1338 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1339 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1340 if (!ast_strlen_zero(category))
1341 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1343 if (!ast_strlen_zero(category))
1344 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1346 res = odbc_smart_execute(obj, stmt);
1347 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1348 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1349 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1350 odbc_release_obj(obj);
1353 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1354 odbc_release_obj(obj);
1356 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1359 ast_config_destroy(cfg);
1367 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1374 struct odbc_obj *obj;
1376 delete_file(ddir, dmsg);
1377 obj = odbc_request_obj(odbc_database, 0);
1379 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1380 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1381 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1382 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1383 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1384 odbc_release_obj(obj);
1387 #ifdef EXTENDED_ODBC_STORAGE
1388 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1390 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
1392 res = SQLPrepare(stmt, sql, SQL_NTS);
1393 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1394 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1395 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1396 odbc_release_obj(obj);
1399 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1400 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1401 #ifdef EXTENDED_ODBC_STORAGE
1402 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1403 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1404 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1405 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1407 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1408 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1410 res = odbc_smart_execute(obj, stmt);
1411 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1412 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1413 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1414 odbc_release_obj(obj);
1417 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1418 odbc_release_obj(obj);
1420 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1427 static int count_messages(struct ast_vm_user *vmu, char *dir)
1429 /* Find all .txt files - even if they are not in sequence from 0000 */
1433 struct dirent *vment = NULL;
1435 if (vm_lock_path(dir))
1436 return ERROR_LOCK_PATH;
1438 if ((vmdir = opendir(dir))) {
1439 while ((vment = readdir(vmdir))) {
1440 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1445 ast_unlock_path(dir);
1450 static void rename_file(char *sfn, char *dfn)
1454 ast_filerename(sfn,dfn,NULL);
1455 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1456 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1460 static int copy(char *infile, char *outfile)
1468 #ifdef HARDLINK_WHEN_POSSIBLE
1469 /* Hard link if possible; saves disk space & is faster */
1470 if (link(infile, outfile)) {
1472 if ((ifd = open(infile, O_RDONLY)) < 0) {
1473 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1476 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1477 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1482 len = read(ifd, buf, sizeof(buf));
1484 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1490 res = write(ofd, buf, len);
1491 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1492 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1502 #ifdef HARDLINK_WHEN_POSSIBLE
1504 /* Hard link succeeded */
1510 static void copy_file(char *frompath, char *topath)
1512 char frompath2[256],topath2[256];
1513 ast_filecopy(frompath, topath, NULL);
1514 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1515 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1516 copy(frompath2, topath2);
1520 * A negative return value indicates an error.
1522 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1527 if (vm_lock_path(dir))
1528 return ERROR_LOCK_PATH;
1530 for (x = 0; x < vmu->maxmsg; x++) {
1531 make_file(fn, sizeof(fn), dir, x);
1532 if (ast_fileexists(fn, NULL, NULL) < 1)
1535 ast_unlock_path(dir);
1540 static int vm_delete(char *file)
1545 txtsize = (strlen(file) + 5)*sizeof(char);
1546 txt = alloca(txtsize);
1547 /* Sprintf here would safe because we alloca'd exactly the right length,
1548 * but trying to eliminate all sprintf's anyhow
1550 snprintf(txt, txtsize, "%s.txt", file);
1552 return ast_filedelete(file, NULL);
1558 inbuf(struct baseio *bio, FILE *fi)
1565 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1580 inchar(struct baseio *bio, FILE *fi)
1582 if (bio->iocp>=bio->iolen) {
1583 if (!inbuf(bio, fi))
1587 return bio->iobuf[bio->iocp++];
1591 ochar(struct baseio *bio, int c, FILE *so)
1593 if (bio->linelength>=BASELINELEN) {
1594 if (fputs(eol,so)==EOF)
1600 if (putc(((unsigned char)c),so)==EOF)
1608 static int base_encode(char *filename, FILE *so)
1610 unsigned char dtable[BASEMAXINLINE];
1615 memset(&bio, 0, sizeof(bio));
1616 bio.iocp = BASEMAXINLINE;
1618 if (!(fi = fopen(filename, "rb"))) {
1619 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1623 for (i= 0;i<9;i++) {
1626 dtable[26+i]= 'a'+i;
1627 dtable[26+i+9]= 'j'+i;
1629 for (i= 0;i<8;i++) {
1630 dtable[i+18]= 'S'+i;
1631 dtable[26+i+18]= 's'+i;
1633 for (i= 0;i<10;i++) {
1634 dtable[52+i]= '0'+i;
1640 unsigned char igroup[3],ogroup[4];
1643 igroup[0]= igroup[1]= igroup[2]= 0;
1645 for (n= 0;n<3;n++) {
1646 if ((c = inchar(&bio, fi)) == EOF) {
1651 igroup[n]= (unsigned char)c;
1655 ogroup[0]= dtable[igroup[0]>>2];
1656 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1657 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1658 ogroup[3]= dtable[igroup[2]&0x3F];
1668 ochar(&bio, ogroup[i], so);
1672 if (fputs(eol,so)==EOF)
1680 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)
1683 /* Prepare variables for substition in email body and subject */
1684 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1685 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1686 snprintf(passdata, passdatasize, "%d", msgnum);
1687 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1688 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1689 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1690 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1691 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1692 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1693 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1694 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1698 * fill in *tm for current time according to the proper timezone, if any.
1699 * Return tm so it can be used as a function argument.
1701 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1703 const struct vm_zone *z = NULL;
1704 time_t t = time(NULL);
1706 /* Does this user have a timezone specified? */
1707 if (!ast_strlen_zero(vmu->zonetag)) {
1708 /* Find the zone in the list */
1709 AST_LIST_LOCK(&zones);
1710 AST_LIST_TRAVERSE(&zones, z, list) {
1711 if (!strcmp(z->name, vmu->zonetag))
1714 AST_LIST_UNLOCK(&zones);
1716 ast_localtime(&t, tm, z ? z->timezone : NULL);
1720 /* same as mkstemp, but return a FILE * */
1721 static FILE *vm_mkftemp(char *template)
1724 int pfd = mkstemp(template);
1726 p = fdopen(pfd, "w");
1735 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)
1739 char host[MAXHOSTNAMELEN] = "";
1744 char tmp[80] = "/tmp/astmail-XXXXXX";
1748 if (vmu && ast_strlen_zero(vmu->email)) {
1749 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1752 if (!strcmp(format, "wav49"))
1754 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));
1755 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1757 if ((p = vm_mkftemp(tmp)) == NULL) {
1758 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1761 gethostname(host, sizeof(host)-1);
1762 if (strchr(srcemail, '@'))
1763 ast_copy_string(who, srcemail, sizeof(who));
1765 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1767 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1768 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1769 fprintf(p, "Date: %s\n", date);
1771 /* Set date format for voicemail mail */
1772 strftime(date, sizeof(date), emaildateformat, &tm);
1775 struct ast_channel *ast;
1776 if ((ast = ast_channel_alloc(0))) {
1778 int vmlen = strlen(fromstring)*3 + 200;
1779 if ((passdata = alloca(vmlen))) {
1780 memset(passdata, 0, vmlen);
1781 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1782 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1783 fprintf(p, "From: %s <%s>\n",passdata,who);
1784 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1785 ast_channel_free(ast);
1786 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1788 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1789 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1792 struct ast_channel *ast;
1793 if ((ast = ast_channel_alloc(0))) {
1795 int vmlen = strlen(emailsubject)*3 + 200;
1796 if ((passdata = alloca(vmlen))) {
1797 memset(passdata, 0, vmlen);
1798 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1799 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1800 fprintf(p, "Subject: %s\n",passdata);
1801 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1802 ast_channel_free(ast);
1803 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1806 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1808 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1809 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1811 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1812 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1813 fprintf(p, "MIME-Version: 1.0\n");
1814 if (attach_user_voicemail) {
1815 /* Something unique. */
1816 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1818 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1820 fprintf(p, "--%s\n", bound);
1822 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1824 struct ast_channel *ast;
1825 if ((ast = ast_channel_alloc(0))) {
1827 int vmlen = strlen(emailbody)*3 + 200;
1828 if ((passdata = alloca(vmlen))) {
1829 memset(passdata, 0, vmlen);
1830 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1831 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1832 fprintf(p, "%s\n",passdata);
1833 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1834 ast_channel_free(ast);
1835 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1837 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1839 "in mailbox %s from %s, on %s so you might\n"
1840 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1841 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1843 if (attach_user_voicemail) {
1844 /* Eww. We want formats to tell us their own MIME type */
1845 char *ctype = "audio/x-";
1846 if (!strcasecmp(format, "ogg"))
1847 ctype = "application/";
1849 fprintf(p, "--%s\n", bound);
1850 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1851 fprintf(p, "Content-Transfer-Encoding: base64\n");
1852 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1853 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1855 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1856 base_encode(fname, p);
1857 fprintf(p, "\n\n--%s--\n.\n", bound);
1860 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1861 ast_safe_system(tmp2);
1862 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1867 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)
1870 char host[MAXHOSTNAMELEN]="";
1873 char tmp[80] = "/tmp/astmail-XXXXXX";
1878 if ((p = vm_mkftemp(tmp)) == NULL) {
1879 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1882 gethostname(host, sizeof(host)-1);
1883 if (strchr(srcemail, '@'))
1884 ast_copy_string(who, srcemail, sizeof(who));
1886 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1888 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1889 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1890 fprintf(p, "Date: %s\n", date);
1892 if (*pagerfromstring) {
1893 struct ast_channel *ast;
1894 if ((ast = ast_channel_alloc(0))) {
1896 int vmlen = strlen(fromstring)*3 + 200;
1897 if ((passdata = alloca(vmlen))) {
1898 memset(passdata, 0, vmlen);
1899 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1900 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1901 fprintf(p, "From: %s <%s>\n",passdata,who);
1903 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1904 ast_channel_free(ast);
1905 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1907 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1908 fprintf(p, "To: %s\n", pager);
1910 struct ast_channel *ast;
1911 if ((ast = ast_channel_alloc(0))) {
1913 int vmlen = strlen(pagersubject)*3 + 200;
1914 if ((passdata = alloca(vmlen))) {
1915 memset(passdata, 0, vmlen);
1916 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1917 pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
1918 fprintf(p, "Subject: %s\n\n",passdata);
1919 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1920 ast_channel_free(ast);
1921 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1923 fprintf(p, "Subject: New VM\n\n");
1924 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1926 struct ast_channel *ast;
1927 if ((ast = ast_channel_alloc(0))) {
1929 int vmlen = strlen(pagerbody)*3 + 200;
1930 if ((passdata = alloca(vmlen))) {
1931 memset(passdata, 0, vmlen);
1932 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1933 pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
1934 fprintf(p, "%s\n",passdata);
1935 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1936 ast_channel_free(ast);
1937 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1939 fprintf(p, "New %s long msg in box %s\n"
1940 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1943 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1944 ast_safe_system(tmp2);
1945 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1950 static int get_date(char *s, int len)
1955 localtime_r(&t,&tm);
1956 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1959 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1963 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
1965 if (ast_fileexists(fn, NULL, NULL) > 0) {
1966 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
1972 /* Dispose just in case */
1974 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
1977 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1981 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
1985 static void free_user(struct ast_vm_user *vmu)
1987 if (ast_test_flag(vmu, VM_ALLOCED))
1991 static void free_zone(struct vm_zone *z)
1996 static const char *mbox(int id)
1998 static const char *msgs[] = {
2010 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2013 #ifdef USE_ODBC_STORAGE
2014 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2022 struct odbc_obj *obj;
2029 /* If no mailbox, return immediately */
2030 if (ast_strlen_zero(mailbox))
2033 ast_copy_string(tmp, mailbox, sizeof(tmp));
2035 context = strchr(tmp, '@');
2040 context = "default";
2042 obj = odbc_request_obj(odbc_database, 0);
2044 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2045 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2046 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2047 odbc_release_obj(obj);
2050 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2051 res = SQLPrepare(stmt, sql, SQL_NTS);
2052 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2053 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2054 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2055 odbc_release_obj(obj);
2058 res = odbc_smart_execute(obj, stmt);
2059 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2060 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2061 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2062 odbc_release_obj(obj);
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);
2069 odbc_release_obj(obj);
2072 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2073 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2074 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2075 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2076 odbc_release_obj(obj);
2079 *newmsgs = atoi(rowdata);
2080 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2082 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2083 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2084 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2085 odbc_release_obj(obj);
2088 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2089 res = SQLPrepare(stmt, sql, SQL_NTS);
2090 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2091 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2092 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2093 odbc_release_obj(obj);
2096 res = odbc_smart_execute(obj, stmt);
2097 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2098 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2099 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2100 odbc_release_obj(obj);
2103 res = SQLFetch(stmt);
2104 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2105 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2106 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2107 odbc_release_obj(obj);
2110 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2111 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2112 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2113 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2114 odbc_release_obj(obj);
2117 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2118 odbc_release_obj(obj);
2119 *oldmsgs = atoi(rowdata);
2122 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2128 static int has_voicemail(const char *mailbox, const char *folder)
2130 struct odbc_obj *obj;
2140 /* If no mailbox, return immediately */
2141 if (ast_strlen_zero(mailbox))
2144 ast_copy_string(tmp, mailbox, sizeof(tmp));
2146 context = strchr(tmp, '@');
2151 context = "default";
2153 obj = odbc_request_obj(odbc_database, 0);
2155 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2156 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2157 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2158 odbc_release_obj(obj);
2161 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2162 res = SQLPrepare(stmt, sql, SQL_NTS);
2163 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2164 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2165 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2166 odbc_release_obj(obj);
2169 res = odbc_smart_execute(obj, stmt);
2170 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2171 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2172 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2173 odbc_release_obj(obj);
2176 res = SQLFetch(stmt);
2177 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2178 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2179 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2180 odbc_release_obj(obj);
2183 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2184 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2185 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2186 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2187 odbc_release_obj(obj);
2190 nummsgs = atoi(rowdata);
2191 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2192 odbc_release_obj(obj);
2194 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2205 static int has_voicemail(const char *mailbox, const char *folder)
2216 /* If no mailbox, return immediately */
2217 if (ast_strlen_zero(mailbox))
2219 if (strchr(mailbox, ',')) {
2220 ast_copy_string(tmp, mailbox, sizeof(tmp));
2223 while((cur = strsep(&mb, ","))) {
2224 if (!ast_strlen_zero(cur)) {
2225 if (has_voicemail(cur, folder))
2231 ast_copy_string(tmp, mailbox, sizeof(tmp));
2232 context = strchr(tmp, '@');
2237 context = "default";
2238 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
2242 while ((de = readdir(dir))) {
2243 if (!strncasecmp(de->d_name, "msg", 3))
2253 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2265 /* If no mailbox, return immediately */
2266 if (ast_strlen_zero(mailbox))
2268 if (strchr(mailbox, ',')) {
2272 ast_copy_string(tmp, mailbox, sizeof(tmp));
2274 while((cur = strsep(&mb, ", "))) {
2275 if (!ast_strlen_zero(cur)) {
2276 if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2288 ast_copy_string(tmp, mailbox, sizeof(tmp));
2289 context = strchr(tmp, '@');
2294 context = "default";
2296 snprintf(fn, sizeof(fn), "%s%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
2299 while ((de = readdir(dir))) {
2300 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2301 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2309 snprintf(fn, sizeof(fn), "%s%s/%s/Old", VM_SPOOL_DIR, context, tmp);
2312 while ((de = readdir(dir))) {
2313 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2314 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2326 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
2328 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)
2330 char fromdir[256], todir[256], frompath[256], topath[256];
2331 const char *frombox = mbox(imbox);
2334 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2336 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2338 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2339 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2341 if (vm_lock_path(todir))
2342 return ERROR_LOCK_PATH;
2346 make_file(topath, sizeof(topath), todir, recipmsgnum);
2347 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2350 } while (recipmsgnum < recip->maxmsg);
2351 if (recipmsgnum < recip->maxmsg) {
2352 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2354 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2356 ast_unlock_path(todir);
2357 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2362 static void run_externnotify(char *context, char *extension)
2364 char arguments[255];
2365 char ext_context[256] = "";
2366 int newvoicemails = 0, oldvoicemails = 0;
2368 struct ast_smdi_mwi_message *mwi_msg;
2371 if (!ast_strlen_zero(context))
2372 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2374 ast_copy_string(ext_context, extension, sizeof(ext_context));
2377 if (!strcasecmp(externnotify, "smdi")) {
2378 if (ast_app_has_voicemail(ext_context, NULL))
2379 ast_smdi_mwi_set(smdi_iface, extension);
2381 ast_smdi_mwi_unset(smdi_iface, extension);
2383 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2384 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2385 if (!strncmp(mwi_msg->cause, "INV", 3))
2386 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2387 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2388 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2389 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2390 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2392 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2394 } else if (!ast_strlen_zero(externnotify)) {
2396 if (!ast_strlen_zero(externnotify)) {
2398 if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
2399 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2401 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2402 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2403 ast_safe_system(arguments);
2408 struct leave_vm_options {
2410 signed char record_gain;
2413 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2415 char tmptxtfile[256], txtfile[256];
2427 char prefile[256]="";
2428 char tempfile[256]="";
2429 char ext_context[256] = "";
2432 char ecodes[16] = "#";
2433 char tmp[256] = "", *tmpptr;
2434 struct ast_vm_user *vmu;
2435 struct ast_vm_user svm;
2436 const char *category = NULL;
2438 ast_copy_string(tmp, ext, sizeof(tmp));
2440 context = strchr(tmp, '@');
2443 tmpptr = strchr(context, '&');
2445 tmpptr = strchr(ext, '&');
2451 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2453 if (!(vmu = find_user(&svm, context, ext))) {
2454 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2455 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2456 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2457 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2461 /* Setup pre-file if appropriate */
2462 if (strcmp(vmu->context, "default"))
2463 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2465 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2466 if (ast_test_flag(options, OPT_BUSY_GREETING))
2467 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2468 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2469 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2470 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2471 RETRIEVE(tempfile, -1);
2472 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2473 ast_copy_string(prefile, tempfile, sizeof(prefile));
2474 DISPOSE(tempfile, -1);
2475 /* It's easier just to try to make it than to check for its existence */
2476 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2478 /* Check current or macro-calling context for special extensions */
2479 if (ast_test_flag(vmu, VM_OPERATOR)) {
2480 if (!ast_strlen_zero(vmu->exit)) {
2481 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2482 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2485 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2486 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2489 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2490 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2495 if (!ast_strlen_zero(vmu->exit)) {
2496 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2497 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2498 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2499 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2500 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2501 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2505 /* Play the beginning intro if desired */
2506 if (!ast_strlen_zero(prefile)) {
2507 RETRIEVE(prefile, -1);
2508 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2509 if (ast_streamfile(chan, prefile, chan->language) > -1)
2510 res = ast_waitstream(chan, ecodes);
2512 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2513 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2515 DISPOSE(prefile, -1);
2517 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2519 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2524 /* On a '#' we skip the instructions */
2525 ast_set_flag(options, OPT_SILENT);
2528 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2529 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2531 ast_set_flag(options, OPT_SILENT);
2536 ast_stopstream(chan);
2537 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2538 other than the operator -- an automated attendant or mailbox login for example */
2540 chan->exten[0] = 'a';
2541 chan->exten[1] = '\0';
2542 if (!ast_strlen_zero(vmu->exit)) {
2543 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2544 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2545 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2549 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2553 /* Check for a '0' here */
2556 if(ouseexten || ousemacro) {
2557 chan->exten[0] = 'o';
2558 chan->exten[1] = '\0';
2559 if (!ast_strlen_zero(vmu->exit)) {
2560 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2561 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2562 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2564 ast_play_and_wait(chan, "transfer");
2567 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2573 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2576 /* The meat of recording the message... All the announcements and beeps have been played*/
2577 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2578 if (!ast_strlen_zero(fmt)) {
2581 if (vm_lock_path(dir)) {
2583 return ERROR_LOCK_PATH;
2587 * This operation can be very expensive if done say over NFS or if the mailbox has 100+ messages
2588 * in the folder. So we should get this first so we don't cut off the first few seconds of the
2592 make_file(fn, sizeof(fn), dir, msgnum);
2593 if (!EXISTS(dir,msgnum,fn,chan->language))
2596 } while (msgnum < vmu->maxmsg);
2598 /* Now play the beep once we have the message number for our next message. */
2600 /* Unless we're *really* silent, try to send the beep */
2601 res = ast_stream_and_wait(chan, "beep", chan->language, "");
2603 if (msgnum < vmu->maxmsg) {
2604 /* assign a variable with the name of the voicemail file */
2605 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
2607 /* Store information */
2608 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2609 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s.txt.tmp", fn);
2610 txt = fopen(tmptxtfile, "w+");
2612 get_date(date, sizeof(date));
2615 "; Message Information file\n"
2634 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2635 date, (long)time(NULL),
2636 category ? category : "");
2638 ast_log(LOG_WARNING, "Error opening text file for output\n");
2639 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2641 if (txt && EXISTS(dir,msgnum,fn,chan->language)) {
2643 rename(tmptxtfile, txtfile);
2644 } else if (txt && !EXISTS(dir,msgnum,fn,chan->language)) {
2646 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
2655 fprintf(txt, "duration=%d\n", duration);
2657 rename(tmptxtfile, txtfile);
2660 if (duration < vmminmessage) {
2661 if (option_verbose > 2)
2662 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2663 DELETE(dir,msgnum,fn);
2664 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2665 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2668 /* Are there to be more recipients of this message? */
2670 struct ast_vm_user recipu, *recip;
2671 char *exten, *context;
2673 exten = strsep(&tmpptr, "&");
2674 context = strchr(exten, '@');
2679 if ((recip = find_user(&recipu, context, exten))) {
2680 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2684 if (ast_fileexists(fn, NULL, NULL)) {
2685 STORE(dir, vmu->mailbox, vmu->context, msgnum);
2686 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2687 DISPOSE(dir, msgnum);
2689 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
2691 ast_unlock_path(dir);
2692 res = ast_stream_and_wait(chan, "vm-mailboxfull", chan->language, "");
2693 ast_log(LOG_WARNING, "No more messages possible\n");
2694 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2697 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2704 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
2706 /* we know max messages, so stop process when number is hit */
2712 if (vm_lock_path(dir))
2713 return ERROR_LOCK_PATH;
2715 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
2716 make_file(sfn, sizeof(sfn), dir, x);
2717 if (EXISTS(dir, x, sfn, NULL)) {
2720 make_file(dfn, sizeof(dfn), dir, dest);
2721 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
2727 ast_unlock_path(dir);
2733 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
2736 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2740 static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *context, char *username, int box)
2745 const char *dbox = mbox(box);
2747 make_file(sfn, sizeof(sfn), dir, msg);
2748 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
2750 if (vm_lock_path(ddir))
2751 return ERROR_LOCK_PATH;
2753 for (x = 0; x < vmu->maxmsg; x++) {
2754 make_file(dfn, sizeof(dfn), ddir, x);
2755 if (!EXISTS(ddir, x, dfn, NULL))
2758 if (x >= vmu->maxmsg) {
2759 ast_unlock_path(ddir);
2762 if (strcmp(sfn, dfn)) {
2763 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
2765 ast_unlock_path(ddir);
2770 static int adsi_logo(unsigned char *buf)
2773 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2774 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2778 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2780 unsigned char buf[256];
2786 bytes += adsi_data_mode(buf + bytes);
2787 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2790 bytes += adsi_logo(buf);
2791 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2793 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
2795 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2796 bytes += adsi_data_mode(buf + bytes);
2797 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2799 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2801 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2802 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2803 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2804 bytes += adsi_voice_mode(buf + bytes, 0);
2805 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2812 bytes += adsi_logo(buf);
2813 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2814 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
2815 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2816 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2819 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2820 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2821 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2822 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2823 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2824 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2825 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2828 /* Add another dot */
2830 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
2831 bytes += adsi_voice_mode(buf + bytes, 0);
2833 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2834 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2838 /* These buttons we load but don't use yet */
2839 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2840 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2841 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2842 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2843 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2844 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2845 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2848 /* Add another dot */
2850 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2851 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2852 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2857 snprintf(num, sizeof(num), "%d", x);
2858 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2860 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2861 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2864 /* Add another dot */
2866 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2867 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2868 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2871 if (adsi_end_download(chan)) {
2873 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2874 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2875 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2876 bytes += adsi_voice_mode(buf + bytes, 0);
2877 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2881 bytes += adsi_download_disconnect(buf + bytes);
2882 bytes += adsi_voice_mode(buf + bytes, 0);
2883 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2885 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2890 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2891 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2893 ast_log(LOG_DEBUG, "Restarting session...\n");
2896 /* Load the session now */
2897 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2899 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2901 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2903 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2907 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2910 if (!adsi_available(chan))
2912 x = adsi_load_session(chan, adsifdn, adsiver, 1);
2916 if (adsi_load_vmail(chan, useadsi)) {
2917 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2924 static void adsi_login(struct ast_channel *chan)
2926 unsigned char buf[256];
2928 unsigned char keys[8];
2930 if (!adsi_available(chan))
2935 /* Set one key for next */
2936 keys[3] = ADSI_KEY_APPS + 3;
2938 bytes += adsi_logo(buf + bytes);
2939 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2940 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2941 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2942 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2943 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2944 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2945 bytes += adsi_set_keys(buf + bytes, keys);
2946 bytes += adsi_voice_mode(buf + bytes, 0);
2947 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2950 static void adsi_password(struct ast_channel *chan)
2952 unsigned char buf[256];
2954 unsigned char keys[8];
2956 if (!adsi_available(chan))
2961 /* Set one key for next */
2962 keys[3] = ADSI_KEY_APPS + 3;
2964 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2965 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2966 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2967 bytes += adsi_set_keys(buf + bytes, keys);
2968 bytes += adsi_voice_mode(buf + bytes, 0);
2969 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2972 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2974 unsigned char buf[256];
2976 unsigned char keys[8];
2979 if (!adsi_available(chan))
2983 y = ADSI_KEY_APPS + 12 + start + x;
2984 if (y > ADSI_KEY_APPS + 12 + 4)
2986 keys[x] = ADSI_KEY_SKT | y;
2988 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2992 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2993 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2994 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2995 bytes += adsi_set_keys(buf + bytes, keys);
2996 bytes += adsi_voice_mode(buf + bytes, 0);
2998 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3001 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3004 unsigned char buf[256];
3005 char buf1[256], buf2[256];
3011 char datetime[21]="";
3014 unsigned char keys[8];