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 if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
676 /* Make a backup of it */
677 ast_copy_string(orig, inbuf, sizeof(orig));
680 Read the file line by line, split each line into a comment and command section
681 only parse the command portion of the line
683 if (inbuf[strlen(inbuf) - 1] == '\n')
684 inbuf[strlen(inbuf) - 1] = '\0';
686 if ((comment = strchr(inbuf, ';')))
687 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
689 if (ast_strlen_zero(inbuf)) {
690 fprintf(configout, "%s", orig);
694 /* Check for a context, first '[' to first ']' */
695 if ((tmpctx = strchr(inbuf, '['))) {
696 tmpctxend = strchr(tmpctx, ']');
699 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
700 fprintf(configout, "%s", orig);
705 /* This isn't a context line, check for MBX => PSWD... */
707 if ((pass = strchr(user, '='))) {
708 /* We have a line in the form of aaaaa=aaaaaa */
711 user = ast_strip(user);
716 pass = ast_skip_blanks(pass);
719 Since no whitespace allowed in fields, or more correctly white space
720 inside the fields is there for a purpose, we can just terminate pass
721 at the comma or EOL whichever comes first.
723 if ((rest = strchr(pass, ',')))
729 /* Compare user, pass AND context */
730 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
731 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
732 !strcasecmp(currcontext, vmu->context)) {
733 /* This is the line */
735 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
737 fprintf(configout, "%s => %s", user, newpassword);
739 /* If there was a comment on the line print it out */
741 fprintf(configout, ";%s\n", comment);
743 fprintf(configout, "\n");
746 /* Put it back like it was */
747 fprintf(configout, "%s", orig);
753 stat(tmpin, &statbuf);
754 chmod(tmpout, statbuf.st_mode);
755 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
757 rename(tmpout, tmpin);
758 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
759 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
762 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
765 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
766 if (!ast_safe_system(buf))
767 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
770 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
772 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
775 static int make_file(char *dest, int len, char *dir, int num)
777 return snprintf(dest, len, "%s/msg%04d", dir, num);
780 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
781 * \param dest String. base directory.
782 * \param context String. Ignored if is null or empty string.
783 * \param ext String. Ignored if is null or empty string.
784 * \param folder String. Ignored if is null or empty string.
785 * \return 0 on failure, 1 on success.
787 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
789 mode_t mode = VOICEMAIL_DIR_MODE;
791 if (!ast_strlen_zero(context)) {
792 make_dir(dest, len, context, "", "");
793 if(mkdir(dest, mode) && errno != EEXIST) {
794 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
798 if (!ast_strlen_zero(ext)) {
799 make_dir(dest, len, context, ext, "");
800 if(mkdir(dest, mode) && errno != EEXIST) {
801 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
805 if (!ast_strlen_zero(folder)) {
806 make_dir(dest, len, context, ext, folder);
807 if(mkdir(dest, mode) && errno != EEXIST) {
808 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
815 /* only return failure if ast_lock_path returns 'timeout',
816 not if the path does not exist or any other reason
818 static int vm_lock_path(const char *path)
820 switch (ast_lock_path(path)) {
821 case AST_LOCK_TIMEOUT:
829 #ifdef USE_ODBC_STORAGE
830 static int retrieve_file(char *dir, int msgnum)
837 SQLSMALLINT colcount=0;
844 SQLSMALLINT datatype;
845 SQLSMALLINT decimaldigits;
846 SQLSMALLINT nullable;
854 struct odbc_obj *obj;
855 obj = odbc_request_obj(odbc_database, 0);
857 ast_copy_string(fmt, vmfmts, sizeof(fmt));
858 c = strchr(fmt, '|');
861 if (!strcasecmp(fmt, "wav49"))
863 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
865 make_file(fn, sizeof(fn), dir, msgnum);
867 ast_copy_string(fn, dir, sizeof(fn));
868 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
869 f = fopen(full_fn, "w+");
870 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
871 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
872 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
873 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
874 odbc_release_obj(obj);
877 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
878 res = SQLPrepare(stmt, sql, SQL_NTS);
879 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
880 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
881 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
882 odbc_release_obj(obj);
885 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
886 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
887 res = odbc_smart_execute(obj, stmt);
888 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
889 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
890 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
891 odbc_release_obj(obj);
894 res = SQLFetch(stmt);
895 if (res == SQL_NO_DATA) {
896 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
897 odbc_release_obj(obj);
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);
903 odbc_release_obj(obj);
906 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
908 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
909 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
910 odbc_release_obj(obj);
913 res = SQLNumResultCols(stmt, &colcount);
914 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
915 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
916 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
917 odbc_release_obj(obj);
921 fprintf(f, "[message]\n");
922 for (x=0;x<colcount;x++) {
924 collen = sizeof(coltitle);
925 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
926 &datatype, &colsize, &decimaldigits, &nullable);
927 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
928 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
929 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
930 odbc_release_obj(obj);
933 if (!strcasecmp(coltitle, "recording")) {
934 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
936 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
939 lseek(fd, fdlen - 1, SEEK_SET);
940 if (write(fd, tmp, 1) != 1) {
945 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
948 memset(fdm, 0, fdlen);
949 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
950 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
951 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
952 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
953 odbc_release_obj(obj);
958 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
959 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
960 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
961 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
962 odbc_release_obj(obj);
965 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
966 fprintf(f, "%s=%s\n", coltitle, rowdata);
969 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
970 odbc_release_obj(obj);
972 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
983 static int remove_file(char *dir, int msgnum)
990 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
991 make_file(fn, sizeof(fn), dir, msgnum);
993 ast_copy_string(fn, dir, sizeof(fn));
994 ast_filedelete(fn, NULL);
995 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1000 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1008 struct odbc_obj *obj;
1009 obj = odbc_request_obj(odbc_database, 0);
1011 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1012 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1013 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1014 odbc_release_obj(obj);
1017 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1018 res = SQLPrepare(stmt, sql, SQL_NTS);
1019 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1020 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1021 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1022 odbc_release_obj(obj);
1025 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1026 res = odbc_smart_execute(obj, stmt);
1027 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1028 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1029 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1030 odbc_release_obj(obj);
1033 res = SQLFetch(stmt);
1034 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1035 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1036 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1037 odbc_release_obj(obj);
1040 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1041 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1042 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1043 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1044 odbc_release_obj(obj);
1047 if (sscanf(rowdata, "%d", &x) != 1)
1048 ast_log(LOG_WARNING, "Failed to read message count!\n");
1049 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1050 odbc_release_obj(obj);
1052 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1057 static int message_exists(char *dir, int msgnum)
1066 struct odbc_obj *obj;
1067 obj = odbc_request_obj(odbc_database, 0);
1069 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1070 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1071 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1072 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1073 odbc_release_obj(obj);
1076 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1077 res = SQLPrepare(stmt, sql, SQL_NTS);
1078 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1079 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1080 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1081 odbc_release_obj(obj);
1084 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1085 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1086 res = odbc_smart_execute(obj, stmt);
1087 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1088 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1089 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1090 odbc_release_obj(obj);
1093 res = SQLFetch(stmt);
1094 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1095 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1096 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1097 odbc_release_obj(obj);
1100 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1101 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1102 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1103 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1104 odbc_release_obj(obj);
1107 if (sscanf(rowdata, "%d", &x) != 1)
1108 ast_log(LOG_WARNING, "Failed to read message count!\n");
1109 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1110 odbc_release_obj(obj);
1112 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1117 static int count_messages(struct ast_vm_user *vmu, char *dir)
1119 return last_message_index(vmu, dir) + 1;
1122 static void delete_file(char *sdir, int smsg)
1129 struct odbc_obj *obj;
1130 obj = odbc_request_obj(odbc_database, 0);
1132 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1133 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1134 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1135 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1136 odbc_release_obj(obj);
1139 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1140 res = SQLPrepare(stmt, sql, SQL_NTS);
1141 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1142 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1143 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1144 odbc_release_obj(obj);
1147 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1148 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1149 res = odbc_smart_execute(obj, stmt);
1150 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1151 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1152 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1153 odbc_release_obj(obj);
1156 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1157 odbc_release_obj(obj);
1159 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1164 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1171 struct odbc_obj *obj;
1173 delete_file(ddir, dmsg);
1174 obj = odbc_request_obj(odbc_database, 0);
1176 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1177 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1178 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1179 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1180 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1181 odbc_release_obj(obj);
1184 #ifdef EXTENDED_ODBC_STORAGE
1185 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);
1187 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);
1189 res = SQLPrepare(stmt, sql, SQL_NTS);
1190 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1191 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1192 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1193 odbc_release_obj(obj);
1196 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1197 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1198 #ifdef EXTENDED_ODBC_STORAGE
1199 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1200 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1201 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1202 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1204 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1205 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1207 res = odbc_smart_execute(obj, stmt);
1208 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1209 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1210 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1211 odbc_release_obj(obj);
1214 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1215 odbc_release_obj(obj);
1217 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1222 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1237 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1238 char *category = "";
1239 struct ast_config *cfg=NULL;
1240 struct odbc_obj *obj;
1242 delete_file(dir, msgnum);
1243 obj = odbc_request_obj(odbc_database, 0);
1245 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1246 c = strchr(fmt, '|');
1249 if (!strcasecmp(fmt, "wav49"))
1251 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1253 make_file(fn, sizeof(fn), dir, msgnum);
1255 ast_copy_string(fn, dir, sizeof(fn));
1256 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1257 cfg = ast_config_load(full_fn);
1258 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1259 fd = open(full_fn, O_RDWR);
1261 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1262 odbc_release_obj(obj);
1266 context = ast_variable_retrieve(cfg, "message", "context");
1267 if (!context) context = "";
1268 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1269 if (!macrocontext) macrocontext = "";
1270 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1271 if (!callerid) callerid = "";
1272 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1273 if (!origtime) origtime = "";
1274 duration = ast_variable_retrieve(cfg, "message", "duration");
1275 if (!duration) duration = "";
1276 category = ast_variable_retrieve(cfg, "message", "category");
1277 if (!category) category = "";
1279 fdlen = lseek(fd, 0, SEEK_END);
1280 lseek(fd, 0, SEEK_SET);
1281 printf("Length is %d\n", fdlen);
1282 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1284 ast_log(LOG_WARNING, "Memory map failed!\n");
1285 odbc_release_obj(obj);
1288 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1289 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1290 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1291 odbc_release_obj(obj);
1294 if (!ast_strlen_zero(category))
1295 #ifdef EXTENDED_ODBC_STORAGE
1296 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1298 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
1301 #ifdef EXTENDED_ODBC_STORAGE
1302 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1304 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
1306 res = SQLPrepare(stmt, sql, SQL_NTS);
1307 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1308 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1309 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1310 odbc_release_obj(obj);
1313 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1314 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1315 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1316 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1317 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1318 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1319 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1320 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1321 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1322 #ifdef EXTENDED_ODBC_STORAGE
1323 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1324 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1325 if (!ast_strlen_zero(category))
1326 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1328 if (!ast_strlen_zero(category))
1329 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1331 res = odbc_smart_execute(obj, stmt);
1332 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1333 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1334 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1335 odbc_release_obj(obj);
1338 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1339 odbc_release_obj(obj);
1341 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1344 ast_config_destroy(cfg);
1352 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1359 struct odbc_obj *obj;
1361 delete_file(ddir, dmsg);
1362 obj = odbc_request_obj(odbc_database, 0);
1364 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1365 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1366 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1367 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1368 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1369 odbc_release_obj(obj);
1372 #ifdef EXTENDED_ODBC_STORAGE
1373 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1375 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
1377 res = SQLPrepare(stmt, sql, SQL_NTS);
1378 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1379 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1380 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1381 odbc_release_obj(obj);
1384 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1385 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1386 #ifdef EXTENDED_ODBC_STORAGE
1387 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1388 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1389 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1390 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1392 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1393 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1395 res = odbc_smart_execute(obj, stmt);
1396 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1397 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1398 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1399 odbc_release_obj(obj);
1402 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1403 odbc_release_obj(obj);
1405 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1412 static int count_messages(struct ast_vm_user *vmu, char *dir)
1414 /* Find all .txt files - even if they are not in sequence from 0000 */
1418 struct dirent *vment = NULL;
1420 if (vm_lock_path(dir))
1421 return ERROR_LOCK_PATH;
1423 if ((vmdir = opendir(dir))) {
1424 while ((vment = readdir(vmdir))) {
1425 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1430 ast_unlock_path(dir);
1435 static void rename_file(char *sfn, char *dfn)
1439 ast_filerename(sfn,dfn,NULL);
1440 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1441 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1445 static int copy(char *infile, char *outfile)
1453 #ifdef HARDLINK_WHEN_POSSIBLE
1454 /* Hard link if possible; saves disk space & is faster */
1455 if (link(infile, outfile)) {
1457 if ((ifd = open(infile, O_RDONLY)) < 0) {
1458 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1461 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1462 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1467 len = read(ifd, buf, sizeof(buf));
1469 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1475 res = write(ofd, buf, len);
1476 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1477 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1487 #ifdef HARDLINK_WHEN_POSSIBLE
1489 /* Hard link succeeded */
1495 static void copy_file(char *frompath, char *topath)
1497 char frompath2[256],topath2[256];
1498 ast_filecopy(frompath, topath, NULL);
1499 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1500 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1501 copy(frompath2, topath2);
1505 * A negative return value indicates an error.
1507 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1512 if (vm_lock_path(dir))
1513 return ERROR_LOCK_PATH;
1515 for (x = 0; x < vmu->maxmsg; x++) {
1516 make_file(fn, sizeof(fn), dir, x);
1517 if (ast_fileexists(fn, NULL, NULL) < 1)
1520 ast_unlock_path(dir);
1525 static int vm_delete(char *file)
1530 txtsize = (strlen(file) + 5)*sizeof(char);
1531 txt = alloca(txtsize);
1532 /* Sprintf here would safe because we alloca'd exactly the right length,
1533 * but trying to eliminate all sprintf's anyhow
1535 snprintf(txt, txtsize, "%s.txt", file);
1537 return ast_filedelete(file, NULL);
1543 inbuf(struct baseio *bio, FILE *fi)
1550 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1565 inchar(struct baseio *bio, FILE *fi)
1567 if (bio->iocp>=bio->iolen) {
1568 if (!inbuf(bio, fi))
1572 return bio->iobuf[bio->iocp++];
1576 ochar(struct baseio *bio, int c, FILE *so)
1578 if (bio->linelength>=BASELINELEN) {
1579 if (fputs(eol,so)==EOF)
1585 if (putc(((unsigned char)c),so)==EOF)
1593 static int base_encode(char *filename, FILE *so)
1595 unsigned char dtable[BASEMAXINLINE];
1600 memset(&bio, 0, sizeof(bio));
1601 bio.iocp = BASEMAXINLINE;
1603 if (!(fi = fopen(filename, "rb"))) {
1604 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1608 for (i= 0;i<9;i++) {
1611 dtable[26+i]= 'a'+i;
1612 dtable[26+i+9]= 'j'+i;
1614 for (i= 0;i<8;i++) {
1615 dtable[i+18]= 'S'+i;
1616 dtable[26+i+18]= 's'+i;
1618 for (i= 0;i<10;i++) {
1619 dtable[52+i]= '0'+i;
1625 unsigned char igroup[3],ogroup[4];
1628 igroup[0]= igroup[1]= igroup[2]= 0;
1630 for (n= 0;n<3;n++) {
1631 if ((c = inchar(&bio, fi)) == EOF) {
1636 igroup[n]= (unsigned char)c;
1640 ogroup[0]= dtable[igroup[0]>>2];
1641 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1642 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1643 ogroup[3]= dtable[igroup[2]&0x3F];
1653 ochar(&bio, ogroup[i], so);
1657 if (fputs(eol,so)==EOF)
1665 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)
1668 /* Prepare variables for substition in email body and subject */
1669 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1670 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1671 snprintf(passdata, passdatasize, "%d", msgnum);
1672 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1673 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1674 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1675 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1676 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1677 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1678 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1679 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1683 * fill in *tm for current time according to the proper timezone, if any.
1684 * Return tm so it can be used as a function argument.
1686 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1688 const struct vm_zone *z = NULL;
1689 time_t t = time(NULL);
1691 /* Does this user have a timezone specified? */
1692 if (!ast_strlen_zero(vmu->zonetag)) {
1693 /* Find the zone in the list */
1694 AST_LIST_LOCK(&zones);
1695 AST_LIST_TRAVERSE(&zones, z, list) {
1696 if (!strcmp(z->name, vmu->zonetag))
1699 AST_LIST_UNLOCK(&zones);
1701 ast_localtime(&t, tm, z ? z->timezone : NULL);
1705 /* same as mkstemp, but return a FILE * */
1706 static FILE *vm_mkftemp(char *template)
1709 int pfd = mkstemp(template);
1711 p = fdopen(pfd, "w");
1720 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)
1724 char host[MAXHOSTNAMELEN] = "";
1729 char tmp[80] = "/tmp/astmail-XXXXXX";
1733 if (vmu && ast_strlen_zero(vmu->email)) {
1734 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1737 if (!strcmp(format, "wav49"))
1739 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));
1740 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1742 if ((p = vm_mkftemp(tmp)) == NULL) {
1743 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1746 gethostname(host, sizeof(host)-1);
1747 if (strchr(srcemail, '@'))
1748 ast_copy_string(who, srcemail, sizeof(who));
1750 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1752 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1753 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1754 fprintf(p, "Date: %s\n", date);
1756 /* Set date format for voicemail mail */
1757 strftime(date, sizeof(date), emaildateformat, &tm);
1760 struct ast_channel *ast;
1761 if ((ast = ast_channel_alloc(0))) {
1763 int vmlen = strlen(fromstring)*3 + 200;
1764 if ((passdata = alloca(vmlen))) {
1765 memset(passdata, 0, vmlen);
1766 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1767 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1768 fprintf(p, "From: %s <%s>\n",passdata,who);
1769 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1770 ast_channel_free(ast);
1771 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1773 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1774 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1777 struct ast_channel *ast;
1778 if ((ast = ast_channel_alloc(0))) {
1780 int vmlen = strlen(emailsubject)*3 + 200;
1781 if ((passdata = alloca(vmlen))) {
1782 memset(passdata, 0, vmlen);
1783 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1784 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1785 fprintf(p, "Subject: %s\n",passdata);
1786 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1787 ast_channel_free(ast);
1788 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1791 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1793 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1794 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1796 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1797 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1798 fprintf(p, "MIME-Version: 1.0\n");
1799 if (attach_user_voicemail) {
1800 /* Something unique. */
1801 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1803 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1805 fprintf(p, "--%s\n", bound);
1807 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1809 struct ast_channel *ast;
1810 if ((ast = ast_channel_alloc(0))) {
1812 int vmlen = strlen(emailbody)*3 + 200;
1813 if ((passdata = alloca(vmlen))) {
1814 memset(passdata, 0, vmlen);
1815 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1816 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1817 fprintf(p, "%s\n",passdata);
1818 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1819 ast_channel_free(ast);
1820 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1822 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1824 "in mailbox %s from %s, on %s so you might\n"
1825 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1826 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1828 if (attach_user_voicemail) {
1829 /* Eww. We want formats to tell us their own MIME type */
1830 char *ctype = "audio/x-";
1831 if (!strcasecmp(format, "ogg"))
1832 ctype = "application/";
1834 fprintf(p, "--%s\n", bound);
1835 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1836 fprintf(p, "Content-Transfer-Encoding: base64\n");
1837 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1838 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1840 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1841 base_encode(fname, p);
1842 fprintf(p, "\n\n--%s--\n.\n", bound);
1845 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1846 ast_safe_system(tmp2);
1847 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1852 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)
1855 char host[MAXHOSTNAMELEN]="";
1858 char tmp[80] = "/tmp/astmail-XXXXXX";
1863 if ((p = vm_mkftemp(tmp)) == NULL) {
1864 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1867 gethostname(host, sizeof(host)-1);
1868 if (strchr(srcemail, '@'))
1869 ast_copy_string(who, srcemail, sizeof(who));
1871 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1873 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1874 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1875 fprintf(p, "Date: %s\n", date);
1877 if (*pagerfromstring) {
1878 struct ast_channel *ast;
1879 if ((ast = ast_channel_alloc(0))) {
1881 int vmlen = strlen(fromstring)*3 + 200;
1882 if ((passdata = alloca(vmlen))) {
1883 memset(passdata, 0, vmlen);
1884 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1885 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1886 fprintf(p, "From: %s <%s>\n",passdata,who);
1888 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1889 ast_channel_free(ast);
1890 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1892 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1893 fprintf(p, "To: %s\n", pager);
1895 struct ast_channel *ast;
1896 if ((ast = ast_channel_alloc(0))) {
1898 int vmlen = strlen(pagersubject)*3 + 200;
1899 if ((passdata = alloca(vmlen))) {
1900 memset(passdata, 0, vmlen);
1901 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1902 pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
1903 fprintf(p, "Subject: %s\n\n",passdata);
1904 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1905 ast_channel_free(ast);
1906 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1908 fprintf(p, "Subject: New VM\n\n");
1909 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1911 struct ast_channel *ast;
1912 if ((ast = ast_channel_alloc(0))) {
1914 int vmlen = strlen(pagerbody)*3 + 200;
1915 if ((passdata = alloca(vmlen))) {
1916 memset(passdata, 0, vmlen);
1917 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1918 pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
1919 fprintf(p, "%s\n",passdata);
1920 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1921 ast_channel_free(ast);
1922 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1924 fprintf(p, "New %s long msg in box %s\n"
1925 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1928 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1929 ast_safe_system(tmp2);
1930 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1935 static int get_date(char *s, int len)
1940 localtime_r(&t,&tm);
1941 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1944 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1948 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
1950 if (ast_fileexists(fn, NULL, NULL) > 0) {
1951 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
1957 /* Dispose just in case */
1959 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
1962 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1966 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
1970 static void free_user(struct ast_vm_user *vmu)
1972 if (ast_test_flag(vmu, VM_ALLOCED))
1976 static void free_zone(struct vm_zone *z)
1981 static const char *mbox(int id)
1983 static const char *msgs[] = {
1995 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
1998 #ifdef USE_ODBC_STORAGE
1999 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2007 struct odbc_obj *obj;
2014 /* If no mailbox, return immediately */
2015 if (ast_strlen_zero(mailbox))
2018 ast_copy_string(tmp, mailbox, sizeof(tmp));
2020 context = strchr(tmp, '@');
2025 context = "default";
2027 obj = odbc_request_obj(odbc_database, 0);
2029 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2030 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2031 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2032 odbc_release_obj(obj);
2035 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir LIKE '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2036 res = SQLPrepare(stmt, sql, SQL_NTS);
2037 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2038 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2039 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2040 odbc_release_obj(obj);
2043 res = odbc_smart_execute(obj, stmt);
2044 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2045 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2046 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2047 odbc_release_obj(obj);
2050 res = SQLFetch(stmt);
2051 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2052 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2053 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2054 odbc_release_obj(obj);
2057 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2058 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2059 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2060 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2061 odbc_release_obj(obj);
2064 *newmsgs = atoi(rowdata);
2065 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2067 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2068 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2069 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2070 odbc_release_obj(obj);
2073 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "Old");
2074 res = SQLPrepare(stmt, sql, SQL_NTS);
2075 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2076 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2077 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2078 odbc_release_obj(obj);
2081 res = odbc_smart_execute(obj, stmt);
2082 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2083 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2084 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2085 odbc_release_obj(obj);
2088 res = SQLFetch(stmt);
2089 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2090 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2091 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2092 odbc_release_obj(obj);
2095 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2096 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2097 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2098 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2099 odbc_release_obj(obj);
2102 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2103 odbc_release_obj(obj);
2104 *oldmsgs = atoi(rowdata);
2107 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2113 static int has_voicemail(const char *mailbox, const char *folder)
2115 struct odbc_obj *obj;
2125 /* If no mailbox, return immediately */
2126 if (ast_strlen_zero(mailbox))
2129 ast_copy_string(tmp, mailbox, sizeof(tmp));
2131 context = strchr(tmp, '@');
2136 context = "default";
2138 obj = odbc_request_obj(odbc_database, 0);
2140 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2141 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2142 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2143 odbc_release_obj(obj);
2146 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2147 res = SQLPrepare(stmt, sql, SQL_NTS);
2148 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2149 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2150 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2151 odbc_release_obj(obj);
2154 res = odbc_smart_execute(obj, stmt);
2155 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2156 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2157 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2158 odbc_release_obj(obj);
2161 res = SQLFetch(stmt);
2162 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2163 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2164 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2165 odbc_release_obj(obj);
2168 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2169 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2170 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2171 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2172 odbc_release_obj(obj);
2175 nummsgs = atoi(rowdata);
2176 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2177 odbc_release_obj(obj);
2179 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2190 static int has_voicemail(const char *mailbox, const char *folder)
2201 /* If no mailbox, return immediately */
2202 if (ast_strlen_zero(mailbox))
2204 if (strchr(mailbox, ',')) {
2205 ast_copy_string(tmp, mailbox, sizeof(tmp));
2208 while((cur = strsep(&mb, ","))) {
2209 if (!ast_strlen_zero(cur)) {
2210 if (has_voicemail(cur, folder))
2216 ast_copy_string(tmp, mailbox, sizeof(tmp));
2217 context = strchr(tmp, '@');
2222 context = "default";
2223 snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
2227 while ((de = readdir(dir))) {
2228 if (!strncasecmp(de->d_name, "msg", 3))
2238 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2250 /* If no mailbox, return immediately */
2251 if (ast_strlen_zero(mailbox))
2253 if (strchr(mailbox, ',')) {
2257 ast_copy_string(tmp, mailbox, sizeof(tmp));
2259 while((cur = strsep(&mb, ", "))) {
2260 if (!ast_strlen_zero(cur)) {
2261 if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2273 ast_copy_string(tmp, mailbox, sizeof(tmp));
2274 context = strchr(tmp, '@');
2279 context = "default";
2281 snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
2284 while ((de = readdir(dir))) {
2285 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2286 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2294 snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
2297 while ((de = readdir(dir))) {
2298 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2299 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2311 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
2313 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)
2315 char fromdir[256], todir[256], frompath[256], topath[256];
2316 const char *frombox = mbox(imbox);
2319 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2321 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2323 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2324 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2326 if (vm_lock_path(todir))
2327 return ERROR_LOCK_PATH;
2331 make_file(topath, sizeof(topath), todir, recipmsgnum);
2332 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2335 } while (recipmsgnum < recip->maxmsg);
2336 if (recipmsgnum < recip->maxmsg) {
2337 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2339 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2341 ast_unlock_path(todir);
2342 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2347 static void run_externnotify(char *context, char *extension)
2349 char arguments[255];
2350 char ext_context[256] = "";
2351 int newvoicemails = 0, oldvoicemails = 0;
2353 struct ast_smdi_mwi_message *mwi_msg;
2356 if (!ast_strlen_zero(context))
2357 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2359 ast_copy_string(ext_context, extension, sizeof(ext_context));
2362 if (!strcasecmp(externnotify, "smdi")) {
2363 if (ast_app_has_voicemail(ext_context, NULL))
2364 ast_smdi_mwi_set(smdi_iface, extension);
2366 ast_smdi_mwi_unset(smdi_iface, extension);
2368 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2369 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2370 if (!strncmp(mwi_msg->cause, "INV", 3))
2371 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2372 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2373 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2374 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2375 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2377 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2379 } else if (!ast_strlen_zero(externnotify)) {
2381 if (!ast_strlen_zero(externnotify)) {
2383 if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
2384 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2386 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2387 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2388 ast_safe_system(arguments);
2393 struct leave_vm_options {
2395 signed char record_gain;
2398 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2400 char tmptxtfile[256], txtfile[256];
2412 char prefile[256]="";
2413 char tempfile[256]="";
2414 char ext_context[256] = "";
2417 char ecodes[16] = "#";
2418 char tmp[256] = "", *tmpptr;
2419 struct ast_vm_user *vmu;
2420 struct ast_vm_user svm;
2421 const char *category = NULL;
2423 ast_copy_string(tmp, ext, sizeof(tmp));
2425 context = strchr(tmp, '@');
2428 tmpptr = strchr(context, '&');
2430 tmpptr = strchr(ext, '&');
2436 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2438 if (!(vmu = find_user(&svm, context, ext))) {
2439 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2440 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2441 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2442 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2446 /* Setup pre-file if appropriate */
2447 if (strcmp(vmu->context, "default"))
2448 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2450 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2451 if (ast_test_flag(options, OPT_BUSY_GREETING))
2452 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2453 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2454 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2455 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2456 RETRIEVE(tempfile, -1);
2457 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2458 ast_copy_string(prefile, tempfile, sizeof(prefile));
2459 DISPOSE(tempfile, -1);
2460 /* It's easier just to try to make it than to check for its existence */
2461 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2463 /* Check current or macro-calling context for special extensions */
2464 if (ast_test_flag(vmu, VM_OPERATOR)) {
2465 if (!ast_strlen_zero(vmu->exit)) {
2466 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2467 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2470 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2471 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2474 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2475 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2480 if (!ast_strlen_zero(vmu->exit)) {
2481 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2482 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2483 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2484 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2485 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2486 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2490 /* Play the beginning intro if desired */
2491 if (!ast_strlen_zero(prefile)) {
2492 RETRIEVE(prefile, -1);
2493 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2494 if (ast_streamfile(chan, prefile, chan->language) > -1)
2495 res = ast_waitstream(chan, ecodes);
2497 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2498 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2500 DISPOSE(prefile, -1);
2502 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2504 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2509 /* On a '#' we skip the instructions */
2510 ast_set_flag(options, OPT_SILENT);
2513 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2514 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2516 ast_set_flag(options, OPT_SILENT);
2521 ast_stopstream(chan);
2522 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2523 other than the operator -- an automated attendant or mailbox login for example */
2525 chan->exten[0] = 'a';
2526 chan->exten[1] = '\0';
2527 if (!ast_strlen_zero(vmu->exit)) {
2528 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2529 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2530 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2534 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2538 /* Check for a '0' here */
2541 if(ouseexten || ousemacro) {
2542 chan->exten[0] = 'o';
2543 chan->exten[1] = '\0';
2544 if (!ast_strlen_zero(vmu->exit)) {
2545 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2546 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2547 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2549 ast_play_and_wait(chan, "transfer");
2552 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2558 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2561 /* The meat of recording the message... All the announcements and beeps have been played*/
2562 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2563 if (!ast_strlen_zero(fmt)) {
2566 if (vm_lock_path(dir)) {
2568 return ERROR_LOCK_PATH;
2572 * This operation can be very expensive if done say over NFS or if the mailbox has 100+ messages
2573 * in the folder. So we should get this first so we don't cut off the first few seconds of the
2577 make_file(fn, sizeof(fn), dir, msgnum);
2578 if (!EXISTS(dir,msgnum,fn,chan->language))
2581 } while (msgnum < vmu->maxmsg);
2583 /* Now play the beep once we have the message number for our next message. */
2585 /* Unless we're *really* silent, try to send the beep */
2586 res = ast_stream_and_wait(chan, "beep", chan->language, "");
2588 if (msgnum < vmu->maxmsg) {
2589 /* assign a variable with the name of the voicemail file */
2590 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
2592 /* Store information */
2593 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2594 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s.txt.tmp", fn);
2595 txt = fopen(tmptxtfile, "w+");
2597 get_date(date, sizeof(date));
2600 "; Message Information file\n"
2619 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2620 date, (long)time(NULL),
2621 category ? category : "");
2623 ast_log(LOG_WARNING, "Error opening text file for output\n");
2624 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2626 if (txt && EXISTS(dir,msgnum,fn,chan->language)) {
2628 rename(tmptxtfile, txtfile);
2629 } else if (txt && !EXISTS(dir,msgnum,fn,chan->language)) {
2631 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
2640 fprintf(txt, "duration=%d\n", duration);
2642 rename(tmptxtfile, txtfile);
2645 if (duration < vmminmessage) {
2646 if (option_verbose > 2)
2647 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2648 DELETE(dir,msgnum,fn);
2649 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2650 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2653 /* Are there to be more recipients of this message? */
2655 struct ast_vm_user recipu, *recip;
2656 char *exten, *context;
2658 exten = strsep(&tmpptr, "&");
2659 context = strchr(exten, '@');
2664 if ((recip = find_user(&recipu, context, exten))) {
2665 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2669 if (ast_fileexists(fn, NULL, NULL)) {
2670 STORE(dir, vmu->mailbox, vmu->context, msgnum);
2671 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2672 DISPOSE(dir, msgnum);
2674 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
2676 ast_unlock_path(dir);
2677 res = ast_stream_and_wait(chan, "vm-mailboxfull", chan->language, "");
2678 ast_log(LOG_WARNING, "No more messages possible\n");
2679 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2682 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2689 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
2691 /* we know max messages, so stop process when number is hit */
2697 if (vm_lock_path(dir))
2698 return ERROR_LOCK_PATH;
2700 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
2701 make_file(sfn, sizeof(sfn), dir, x);
2702 if (EXISTS(dir, x, sfn, NULL)) {
2705 make_file(dfn, sizeof(dfn), dir, dest);
2706 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
2712 ast_unlock_path(dir);
2718 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
2721 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2725 static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *context, char *username, int box)
2730 const char *dbox = mbox(box);
2732 make_file(sfn, sizeof(sfn), dir, msg);
2733 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
2735 if (vm_lock_path(ddir))
2736 return ERROR_LOCK_PATH;
2738 for (x = 0; x < vmu->maxmsg; x++) {
2739 make_file(dfn, sizeof(dfn), ddir, x);
2740 if (!EXISTS(ddir, x, dfn, NULL))
2743 if (x >= vmu->maxmsg) {
2744 ast_unlock_path(ddir);
2747 if (strcmp(sfn, dfn)) {
2748 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
2750 ast_unlock_path(ddir);
2755 static int adsi_logo(unsigned char *buf)
2758 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2759 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2763 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2765 unsigned char buf[256];
2771 bytes += adsi_data_mode(buf + bytes);
2772 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2775 bytes += adsi_logo(buf);
2776 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2778 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
2780 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2781 bytes += adsi_data_mode(buf + bytes);
2782 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2784 if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2786 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2787 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2788 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2789 bytes += adsi_voice_mode(buf + bytes, 0);
2790 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2797 bytes += adsi_logo(buf);
2798 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2799 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
2800 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2801 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2804 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2805 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2806 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2807 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2808 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2809 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2810 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2813 /* Add another dot */
2815 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
2816 bytes += adsi_voice_mode(buf + bytes, 0);
2818 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2819 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2823 /* These buttons we load but don't use yet */
2824 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2825 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2826 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2827 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2828 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2829 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2830 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2833 /* Add another dot */
2835 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
2836 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2837 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2842 snprintf(num, sizeof(num), "%d", x);
2843 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2845 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2846 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2849 /* Add another dot */
2851 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
2852 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2853 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2856 if (adsi_end_download(chan)) {
2858 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2859 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2860 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2861 bytes += adsi_voice_mode(buf + bytes, 0);
2862 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2866 bytes += adsi_download_disconnect(buf + bytes);
2867 bytes += adsi_voice_mode(buf + bytes, 0);
2868 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2870 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2875 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
2876 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2878 ast_log(LOG_DEBUG, "Restarting session...\n");
2881 /* Load the session now */
2882 if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2884 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2886 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2888 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2892 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2895 if (!adsi_available(chan))
2897 x = adsi_load_session(chan, adsifdn, adsiver, 1);
2901 if (adsi_load_vmail(chan, useadsi)) {
2902 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2909 static void adsi_login(struct ast_channel *chan)
2911 unsigned char buf[256];
2913 unsigned char keys[8];
2915 if (!adsi_available(chan))
2920 /* Set one key for next */
2921 keys[3] = ADSI_KEY_APPS + 3;
2923 bytes += adsi_logo(buf + bytes);
2924 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2925 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2926 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2927 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2928 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2929 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2930 bytes += adsi_set_keys(buf + bytes, keys);
2931 bytes += adsi_voice_mode(buf + bytes, 0);
2932 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2935 static void adsi_password(struct ast_channel *chan)
2937 unsigned char buf[256];
2939 unsigned char keys[8];
2941 if (!adsi_available(chan))
2946 /* Set one key for next */
2947 keys[3] = ADSI_KEY_APPS + 3;
2949 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2950 bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2951 bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2952 bytes += adsi_set_keys(buf + bytes, keys);
2953 bytes += adsi_voice_mode(buf + bytes, 0);
2954 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2957 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2959 unsigned char buf[256];
2961 unsigned char keys[8];
2964 if (!adsi_available(chan))
2968 y = ADSI_KEY_APPS + 12 + start + x;
2969 if (y > ADSI_KEY_APPS + 12 + 4)
2971 keys[x] = ADSI_KEY_SKT | y;
2973 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2977 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2978 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2979 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2980 bytes += adsi_set_keys(buf + bytes, keys);
2981 bytes += adsi_voice_mode(buf + bytes, 0);
2983 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2986 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2989 unsigned char buf[256];
2990 char buf1[256], buf2[256];
2996 char datetime[21]="";
2999 unsigned char keys[8];
3003 if (!adsi_available(chan))
3006 /* Retrieve important info */
3007 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
3008 f = fopen(fn2, "r");
3011 fgets((char *)buf, sizeof(buf), f);
3014 stringp = (char *)buf;
3015 strsep(&stringp, "=");