2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Comedian Mail - Voicemail System
23 * \author Mark Spencer <markster@digium.com>
27 * \ingroup applications
31 * 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr)
32 * George Konstantoulakis <gkon@inaccessnetworks.com>
34 * 05-10-2005 : Support for Swedish and Norwegian added by Daniel Nylander, http://www.danielnylander.se/
36 * 05-11-2005 : An option for maximum number of messsages per mailbox added by GDS Partners (www.gdspartners.com)
37 * 07-11-2005 : An issue with voicemail synchronization has been fixed by GDS Partners (www.gdspartners.com)
38 * Stojan Sljivic <stojan.sljivic@gdspartners.com>
50 #include <sys/types.h>
57 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
59 #include "asterisk/lock.h"
60 #include "asterisk/file.h"
61 #include "asterisk/logger.h"
62 #include "asterisk/channel.h"
63 #include "asterisk/pbx.h"
64 #include "asterisk/options.h"
65 #include "asterisk/config.h"
66 #include "asterisk/say.h"
67 #include "asterisk/module.h"
68 #include "asterisk/adsi.h"
69 #include "asterisk/app.h"
70 #include "asterisk/manager.h"
71 #include "asterisk/dsp.h"
72 #include "asterisk/localtime.h"
73 #include "asterisk/cli.h"
74 #include "asterisk/utils.h"
75 #include "asterisk/stringfields.h"
77 #include "asterisk/smdi.h"
78 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
80 #ifdef USE_ODBC_STORAGE
81 #include "asterisk/res_odbc.h"
84 #define COMMAND_TIMEOUT 5000
85 #define VOICEMAIL_DIR_MODE 0770
86 #define VOICEMAIL_FILE_MODE 0660
88 #define VOICEMAIL_CONFIG "voicemail.conf"
89 #define ASTERISK_USERNAME "asterisk"
91 /* Default mail command to mail voicemail. Change it with the
92 mailcmd= command in voicemail.conf */
93 #define SENDMAIL "/usr/sbin/sendmail -t"
95 #define INTRO "vm-intro"
98 #define MAXMSGLIMIT 9999
100 #define BASEMAXINLINE 256
101 #define BASELINELEN 72
102 #define BASEMAXINLINE 256
105 #define MAX_DATETIME_FORMAT 512
106 #define MAX_NUM_CID_CONTEXTS 10
108 #define VM_REVIEW (1 << 0)
109 #define VM_OPERATOR (1 << 1)
110 #define VM_SAYCID (1 << 2)
111 #define VM_SVMAIL (1 << 3)
112 #define VM_ENVELOPE (1 << 4)
113 #define VM_SAYDURATION (1 << 5)
114 #define VM_SKIPAFTERCMD (1 << 6)
115 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
116 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
117 #define VM_PBXSKIP (1 << 9)
118 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
119 #define VM_ATTACH (1 << 11)
120 #define VM_DELETE (1 << 12)
121 #define VM_ALLOCED (1 << 13)
122 #define VM_SEARCH (1 << 14)
124 #define ERROR_LOCK_PATH -100
127 OPT_SILENT = (1 << 0),
128 OPT_BUSY_GREETING = (1 << 1),
129 OPT_UNAVAIL_GREETING = (1 << 2),
130 OPT_RECORDGAIN = (1 << 3),
131 OPT_PREPEND_MAILBOX = (1 << 4),
132 OPT_PRIORITY_JUMP = (1 << 5),
133 OPT_AUTOPLAY = (1 << 6),
137 OPT_ARG_RECORDGAIN = 0,
138 OPT_ARG_PLAYFOLDER = 1,
139 /* This *must* be the last value in this enum! */
140 OPT_ARG_ARRAY_SIZE = 2,
143 AST_APP_OPTIONS(vm_app_options, {
144 AST_APP_OPTION('s', OPT_SILENT),
145 AST_APP_OPTION('b', OPT_BUSY_GREETING),
146 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
147 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
148 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
149 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
150 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
153 static int load_config(void);
155 /*! \page vmlang Voicemail Language Syntaxes Supported
157 \par Syntaxes supported, not really language codes.
164 \arg \b pt - Portuguese
166 \arg \b no - Norwegian
169 German requires the following additional soundfile:
170 \arg \b 1F einE (feminine)
172 Spanish requires the following additional soundfile:
173 \arg \b 1M un (masculine)
175 Dutch, Portuguese & Spanish require the following additional soundfiles:
176 \arg \b vm-INBOXs singular of 'new'
177 \arg \b vm-Olds singular of 'old/heard/read'
180 \arg \b vm-INBOX nieuwe (nl)
181 \arg \b vm-Old oude (nl)
184 \arg \b vm-nytt singular of 'new'
185 \arg \b vm-nya plural of 'new'
186 \arg \b vm-gammalt singular of 'old'
187 \arg \b vm-gamla plural of 'old'
188 \arg \b digits/ett 'one', not always same as 'digits/1'
191 \arg \b vm-ny singular of 'new'
192 \arg \b vm-nye plural of 'new'
193 \arg \b vm-gammel singular of 'old'
194 \arg \b vm-gamle plural of 'old'
202 Italian requires the following additional soundfile:
206 \arg \b vm-nuovi new plural
207 \arg \b vm-vecchio old
208 \arg \b vm-vecchi old plural
210 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
211 spelled among others when you have to change folder. For the above reasons, vm-INBOX
212 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
221 unsigned char iobuf[BASEMAXINLINE];
224 /*! Structure for linked list of users */
226 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
227 char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */
228 char password[80]; /*!< Secret pin code, numbers only */
229 char fullname[80]; /*!< Full name, for directory app */
230 char email[80]; /*!< E-mail address */
231 char pager[80]; /*!< E-mail address to pager (no attachment) */
232 char serveremail[80]; /*!< From: Mail address */
233 char mailcmd[160]; /*!< Configurable mail command */
234 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
235 char zonetag[80]; /*!< Time zone */
238 char uniqueid[20]; /*!< Unique integer identifier */
240 char attachfmt[20]; /*!< Attachment format */
241 unsigned int flags; /*!< VM_ flags */
243 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
244 AST_LIST_ENTRY(ast_vm_user) list;
248 AST_LIST_ENTRY(vm_zone) list;
251 char msg_format[512];
270 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
271 int option, signed char record_gain);
272 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
273 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
274 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
275 signed char record_gain);
276 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
277 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
279 static void apply_options(struct ast_vm_user *vmu, const char *options);
281 #ifdef USE_ODBC_STORAGE
282 static char odbc_database[80];
283 static char odbc_table[80];
284 #define RETRIEVE(a,b) retrieve_file(a,b)
285 #define DISPOSE(a,b) remove_file(a,b)
286 #define STORE(a,b,c,d) store_file(a,b,c,d)
287 #define EXISTS(a,b,c,d) (message_exists(a,b))
288 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
289 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
290 #define DELETE(a,b,c) (delete_file(a,b))
292 #define RETRIEVE(a,b)
294 #define STORE(a,b,c,d)
295 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
296 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
297 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
298 #define DELETE(a,b,c) (vm_delete(c))
301 static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
303 static char ext_pass_cmd[128];
305 static char *tdesc = "Comedian Mail (Voicemail System)";
307 static char *addesc = "Comedian Mail";
309 static char *synopsis_vm =
310 "Leave a Voicemail message";
312 static char *descrip_vm =
313 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
314 "application allows the calling party to leave a message for the specified\n"
315 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
316 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
317 "specified mailbox does not exist.\n"
318 " The Voicemail application will exit if any of the following DTMF digits are\n"
320 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
321 " * - Jump to the 'a' extension in the current dialplan context.\n"
322 " This application will set the following channel variable upon completion:\n"
323 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
324 " application. The possible values are:\n"
325 " SUCCESS | USEREXIT | FAILED\n\n"
327 " b - Play the 'busy' greeting to the calling party.\n"
328 " g(#) - Use the specified amount of gain when recording the voicemail\n"
329 " message. The units are whole-number decibels (dB).\n"
330 " s - Skip the playback of instructions for leaving a message to the\n"
332 " u - Play the 'unavailable greeting.\n"
333 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
336 static char *synopsis_vmain =
337 "Check Voicemail messages";
339 static char *descrip_vmain =
340 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
341 "calling party to check voicemail messages. A specific mailbox, and optional\n"
342 "corresponding context, may be specified. If a mailbox is not provided, the\n"
343 "calling party will be prompted to enter one. If a context is not specified,\n"
344 "the 'default' context will be used.\n\n"
346 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
347 " is entered by the caller.\n"
348 " g(#) - Use the specified amount of gain when recording a voicemail\n"
349 " message. The units are whole-number decibels (dB).\n"
350 " s - Skip checking the passcode for the mailbox.\n"
351 " a(#) - Skip folder prompt and go directly to folder specified.\n"
352 " Defaults to INBOX\n";
354 static char *synopsis_vm_box_exists =
355 "Check to see if Voicemail mailbox exists";
357 static char *descrip_vm_box_exists =
358 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
359 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
361 " This application will set the following channel variable upon completion:\n"
362 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
363 " MailboxExists application. Possible values include:\n"
364 " SUCCESS | FAILED\n\n"
366 " j - Jump to priority n+101 if the mailbox is found.\n";
368 static char *synopsis_vmauthenticate =
369 "Authenticate with Voicemail passwords";
371 static char *descrip_vmauthenticate =
372 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
373 "same way as the Authenticate application, but the passwords are taken from\n"
375 " If the mailbox is specified, only that mailbox's password will be considered\n"
376 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
377 "be set with the authenticated mailbox.\n\n"
379 " s - Skip playing the initial prompts.\n";
381 /* Leave a message */
382 static char *app = "VoiceMail";
384 /* Check mail, control, etc */
385 static char *app2 = "VoiceMailMain";
387 static char *app3 = "MailboxExists";
388 static char *app4 = "VMAuthenticate";
390 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
391 static AST_LIST_HEAD_STATIC(zones, vm_zone);
392 static int maxsilence;
394 static int silencethreshold = 128;
395 static char serveremail[80];
396 static char mailcmd[160]; /* Configurable mail cmd */
397 static char externnotify[160];
399 static struct ast_smdi_interface *smdi_iface = NULL;
401 static char vmfmts[80];
402 static int vmminmessage;
403 static int vmmaxmessage;
406 static int maxlogins;
408 static struct ast_flags globalflags = {0};
410 static int saydurationminfo;
412 static char dialcontext[AST_MAX_CONTEXT];
413 static char callcontext[AST_MAX_CONTEXT];
414 static char exitcontext[AST_MAX_CONTEXT];
416 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
419 static char *emailbody = NULL;
420 static char *emailsubject = NULL;
421 static char *pagerbody = NULL;
422 static char *pagersubject = NULL;
423 static char fromstring[100];
424 static char pagerfromstring[100];
425 static char emailtitle[100];
426 static char charset[32] = "ISO-8859-1";
428 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
429 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
430 static int adsiver = 1;
431 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
435 static void populate_defaults(struct ast_vm_user *vmu)
437 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
438 if (saydurationminfo)
439 vmu->saydurationm = saydurationminfo;
441 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
443 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
445 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
447 vmu->maxmsg = maxmsg;
450 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
453 if (!strcasecmp(var, "attach")) {
454 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
455 } else if (!strcasecmp(var, "attachfmt")) {
456 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
457 } else if (!strcasecmp(var, "serveremail")) {
458 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
459 } else if (!strcasecmp(var, "language")) {
460 ast_copy_string(vmu->language, value, sizeof(vmu->language));
461 } else if (!strcasecmp(var, "tz")) {
462 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
463 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
464 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
465 } else if (!strcasecmp(var, "saycid")){
466 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
467 } else if (!strcasecmp(var,"sendvoicemail")){
468 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
469 } else if (!strcasecmp(var, "review")){
470 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
471 } else if (!strcasecmp(var, "operator")){
472 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
473 } else if (!strcasecmp(var, "envelope")){
474 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
475 } else if (!strcasecmp(var, "sayduration")){
476 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
477 } else if (!strcasecmp(var, "saydurationm")){
478 if (sscanf(value, "%d", &x) == 1) {
479 vmu->saydurationm = x;
481 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
483 } else if (!strcasecmp(var, "forcename")){
484 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
485 } else if (!strcasecmp(var, "forcegreetings")){
486 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
487 } else if (!strcasecmp(var, "callback")) {
488 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
489 } else if (!strcasecmp(var, "dialout")) {
490 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
491 } else if (!strcasecmp(var, "exitcontext")) {
492 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
493 } else if (!strcasecmp(var, "maxmsg")) {
494 vmu->maxmsg = atoi(value);
495 if (vmu->maxmsg <= 0) {
496 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
497 vmu->maxmsg = MAXMSG;
498 } else if (vmu->maxmsg > MAXMSGLIMIT) {
499 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
500 vmu->maxmsg = MAXMSGLIMIT;
502 } else if (!strcasecmp(var, "options")) {
503 apply_options(vmu, value);
507 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
510 if (!ast_strlen_zero(vmu->uniqueid)) {
511 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
513 ast_copy_string(vmu->password, password, sizeof(vmu->password));
523 static void apply_options(struct ast_vm_user *vmu, const char *options)
524 { /* Destructively Parse options and apply */
528 stringp = ast_strdupa(options);
529 while ((s = strsep(&stringp, "|"))) {
531 if ((var = strsep(&value, "=")) && value) {
532 apply_option(vmu, var, value);
537 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
539 struct ast_variable *var, *tmp;
540 struct ast_vm_user *retval;
542 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
544 ast_set_flag(retval, VM_ALLOCED);
546 memset(retval, 0, sizeof(*retval));
548 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
549 populate_defaults(retval);
550 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
551 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
553 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
557 printf("%s => %s\n", tmp->name, tmp->value);
558 if (!strcasecmp(tmp->name, "password")) {
559 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
560 } else if (!strcasecmp(tmp->name, "uniqueid")) {
561 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
562 } else if (!strcasecmp(tmp->name, "pager")) {
563 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
564 } else if (!strcasecmp(tmp->name, "email")) {
565 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
566 } else if (!strcasecmp(tmp->name, "fullname")) {
567 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
568 } else if (!strcasecmp(tmp->name, "context")) {
569 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
571 apply_option(retval, tmp->name, tmp->value);
583 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
585 /* This function could be made to generate one from a database, too */
586 struct ast_vm_user *vmu=NULL, *cur;
587 AST_LIST_LOCK(&users);
589 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
592 AST_LIST_TRAVERSE(&users, cur, list) {
593 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
595 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
599 /* Make a copy, so that on a reload, we have no race */
600 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
601 memcpy(vmu, cur, sizeof(*vmu));
602 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
603 AST_LIST_NEXT(vmu, list) = NULL;
606 vmu = find_user_realtime(ivm, context, mailbox);
607 AST_LIST_UNLOCK(&users);
611 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
613 /* This function could be made to generate one from a database, too */
614 struct ast_vm_user *cur;
616 AST_LIST_LOCK(&users);
617 AST_LIST_TRAVERSE(&users, cur, list) {
618 if ((!context || !strcasecmp(context, cur->context)) &&
619 (!strcasecmp(mailbox, cur->mailbox)))
623 ast_copy_string(cur->password, newpass, sizeof(cur->password));
626 AST_LIST_UNLOCK(&users);
630 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
632 /* There's probably a better way of doing this. */
633 /* That's why I've put the password change in a separate function. */
634 /* This could also be done with a database function */
641 char currcontext[256] ="";
642 char tmpin[AST_CONFIG_MAX_PATH];
643 char tmpout[AST_CONFIG_MAX_PATH];
646 if (!change_password_realtime(vmu, newpassword))
649 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
650 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
651 configin = fopen(tmpin,"r");
653 configout = fopen(tmpout,"w+");
656 if (!configin || !configout) {
660 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
664 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
668 while (!feof(configin)) {
669 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
671 /* Read in the line */
672 fgets(inbuf, sizeof(inbuf), configin);
675 if (ast_strlen_zero(inbuf)) {
676 fprintf(configout, "\n");
680 /* Make a backup of it */
681 ast_copy_string(orig, inbuf, sizeof(orig));
684 Read the file line by line, split each line into a comment and command section
685 only parse the command portion of the line
687 if (inbuf[strlen(inbuf) - 1] == '\n')
688 inbuf[strlen(inbuf) - 1] = '\0';
690 if ((comment = strchr(inbuf, ';')))
691 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
693 if (ast_strlen_zero(inbuf)) {
694 fprintf(configout, "%s", orig);
698 /* Check for a context, first '[' to first ']' */
699 if ((tmpctx = strchr(inbuf, '['))) {
700 tmpctxend = strchr(tmpctx, ']');
703 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
704 fprintf(configout, "%s", orig);
709 /* This isn't a context line, check for MBX => PSWD... */
711 if ((pass = strchr(user, '='))) {
712 /* We have a line in the form of aaaaa=aaaaaa */
715 user = ast_strip(user);
720 pass = ast_skip_blanks(pass);
723 Since no whitespace allowed in fields, or more correctly white space
724 inside the fields is there for a purpose, we can just terminate pass
725 at the comma or EOL whichever comes first.
727 if ((rest = strchr(pass, ',')))
733 /* Compare user, pass AND context */
734 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
735 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
736 !strcasecmp(currcontext, vmu->context)) {
737 /* This is the line */
739 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
741 fprintf(configout, "%s => %s", user, newpassword);
743 /* If there was a comment on the line print it out */
745 fprintf(configout, ";%s\n", comment);
747 fprintf(configout, "\n");
750 /* Put it back like it was */
751 fprintf(configout, "%s", orig);
757 stat(tmpin, &statbuf);
758 chmod(tmpout, statbuf.st_mode);
759 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
761 rename(tmpout, tmpin);
762 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
763 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
766 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
769 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
770 if (!ast_safe_system(buf))
771 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
774 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
776 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
779 static int make_file(char *dest, int len, char *dir, int num)
781 return snprintf(dest, len, "%s/msg%04d", dir, num);
784 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
785 * \param dest String. base directory.
786 * \param context String. Ignored if is null or empty string.
787 * \param ext String. Ignored if is null or empty string.
788 * \param folder String. Ignored if is null or empty string.
789 * \return 0 on failure, 1 on success.
791 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
793 mode_t mode = VOICEMAIL_DIR_MODE;
795 if (!ast_strlen_zero(context)) {
796 make_dir(dest, len, context, "", "");
797 if(mkdir(dest, mode) && errno != EEXIST) {
798 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
802 if (!ast_strlen_zero(ext)) {
803 make_dir(dest, len, context, ext, "");
804 if(mkdir(dest, mode) && errno != EEXIST) {
805 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
809 if (!ast_strlen_zero(folder)) {
810 make_dir(dest, len, context, ext, folder);
811 if(mkdir(dest, mode) && errno != EEXIST) {
812 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
819 /* only return failure if ast_lock_path returns 'timeout',
820 not if the path does not exist or any other reason
822 static int vm_lock_path(const char *path)
824 switch (ast_lock_path(path)) {
825 case AST_LOCK_TIMEOUT:
833 #ifdef USE_ODBC_STORAGE
834 static int retrieve_file(char *dir, int msgnum)
841 SQLSMALLINT colcount=0;
848 SQLSMALLINT datatype;
849 SQLSMALLINT decimaldigits;
850 SQLSMALLINT nullable;
858 struct odbc_obj *obj;
859 obj = odbc_request_obj(odbc_database, 0);
861 ast_copy_string(fmt, vmfmts, sizeof(fmt));
862 c = strchr(fmt, '|');
865 if (!strcasecmp(fmt, "wav49"))
867 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
869 make_file(fn, sizeof(fn), dir, msgnum);
871 ast_copy_string(fn, dir, sizeof(fn));
872 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
873 f = fopen(full_fn, "w+");
874 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
875 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
876 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
877 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
878 odbc_release_obj(obj);
881 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
882 res = SQLPrepare(stmt, sql, SQL_NTS);
883 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
884 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
885 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
886 odbc_release_obj(obj);
889 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
890 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
891 res = odbc_smart_execute(obj, stmt);
892 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
893 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
894 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
895 odbc_release_obj(obj);
898 res = SQLFetch(stmt);
899 if (res == SQL_NO_DATA) {
900 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
901 odbc_release_obj(obj);
904 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
905 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
906 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
907 odbc_release_obj(obj);
910 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
912 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
913 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
914 odbc_release_obj(obj);
917 res = SQLNumResultCols(stmt, &colcount);
918 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
919 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
920 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
921 odbc_release_obj(obj);
925 fprintf(f, "[message]\n");
926 for (x=0;x<colcount;x++) {
928 collen = sizeof(coltitle);
929 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
930 &datatype, &colsize, &decimaldigits, &nullable);
931 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
932 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
933 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
934 odbc_release_obj(obj);
937 if (!strcasecmp(coltitle, "recording")) {
938 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
940 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
943 lseek(fd, fdlen - 1, SEEK_SET);
944 if (write(fd, tmp, 1) != 1) {
949 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
952 memset(fdm, 0, fdlen);
953 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
954 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
955 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
956 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
957 odbc_release_obj(obj);
962 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
963 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
964 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
965 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
966 odbc_release_obj(obj);
969 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
970 fprintf(f, "%s=%s\n", coltitle, rowdata);
973 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
974 odbc_release_obj(obj);
976 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
987 static int remove_file(char *dir, int msgnum)
994 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
995 make_file(fn, sizeof(fn), dir, msgnum);
997 ast_copy_string(fn, dir, sizeof(fn));
998 ast_filedelete(fn, NULL);
999 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1004 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1012 struct odbc_obj *obj;
1013 obj = odbc_request_obj(odbc_database, 0);
1015 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1016 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1017 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1018 odbc_release_obj(obj);
1021 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1022 res = SQLPrepare(stmt, sql, SQL_NTS);
1023 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1024 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1025 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1026 odbc_release_obj(obj);
1029 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1030 res = odbc_smart_execute(obj, stmt);
1031 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1032 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1033 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1034 odbc_release_obj(obj);
1037 res = SQLFetch(stmt);
1038 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1039 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1040 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1041 odbc_release_obj(obj);
1044 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1045 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1046 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1047 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1048 odbc_release_obj(obj);
1051 if (sscanf(rowdata, "%d", &x) != 1)
1052 ast_log(LOG_WARNING, "Failed to read message count!\n");
1053 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1054 odbc_release_obj(obj);
1056 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1061 static int message_exists(char *dir, int msgnum)
1070 struct odbc_obj *obj;
1071 obj = odbc_request_obj(odbc_database, 0);
1073 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1074 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1075 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1076 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1077 odbc_release_obj(obj);
1080 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1081 res = SQLPrepare(stmt, sql, SQL_NTS);
1082 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1083 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1084 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1085 odbc_release_obj(obj);
1088 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1089 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1090 res = odbc_smart_execute(obj, stmt);
1091 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1092 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1093 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1094 odbc_release_obj(obj);
1097 res = SQLFetch(stmt);
1098 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1099 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1100 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1101 odbc_release_obj(obj);
1104 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1105 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1106 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1107 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1108 odbc_release_obj(obj);
1111 if (sscanf(rowdata, "%d", &x) != 1)
1112 ast_log(LOG_WARNING, "Failed to read message count!\n");
1113 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1114 odbc_release_obj(obj);
1116 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1121 static int count_messages(struct ast_vm_user *vmu, char *dir)
1123 return last_message_index(vmu, dir) + 1;
1126 static void delete_file(char *sdir, int smsg)
1133 struct odbc_obj *obj;
1134 obj = odbc_request_obj(odbc_database, 0);
1136 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1137 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1138 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1139 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1140 odbc_release_obj(obj);
1143 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1144 res = SQLPrepare(stmt, sql, SQL_NTS);
1145 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1146 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1147 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1148 odbc_release_obj(obj);
1151 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1152 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1153 res = odbc_smart_execute(obj, stmt);
1154 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1155 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1156 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1157 odbc_release_obj(obj);
1160 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1161 odbc_release_obj(obj);
1163 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1168 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1175 struct odbc_obj *obj;
1177 delete_file(ddir, dmsg);
1178 obj = odbc_request_obj(odbc_database, 0);
1180 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1181 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1182 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1183 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1184 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1185 odbc_release_obj(obj);
1188 #ifdef EXTENDED_ODBC_STORAGE
1189 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);
1191 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);
1193 res = SQLPrepare(stmt, sql, SQL_NTS);
1194 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1195 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1196 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1197 odbc_release_obj(obj);
1200 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1201 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1202 #ifdef EXTENDED_ODBC_STORAGE
1203 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1204 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1205 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1206 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1208 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1209 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1211 res = odbc_smart_execute(obj, stmt);
1212 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1213 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1214 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1215 odbc_release_obj(obj);
1218 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1219 odbc_release_obj(obj);
1221 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1226 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1241 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1242 char *category = "";
1243 struct ast_config *cfg=NULL;
1244 struct odbc_obj *obj;
1246 delete_file(dir, msgnum);
1247 obj = odbc_request_obj(odbc_database, 0);
1249 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1250 c = strchr(fmt, '|');
1253 if (!strcasecmp(fmt, "wav49"))
1255 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1257 make_file(fn, sizeof(fn), dir, msgnum);
1259 ast_copy_string(fn, dir, sizeof(fn));
1260 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1261 cfg = ast_config_load(full_fn);
1262 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1263 fd = open(full_fn, O_RDWR);
1265 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1266 odbc_release_obj(obj);
1270 context = ast_variable_retrieve(cfg, "message", "context");
1271 if (!context) context = "";
1272 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1273 if (!macrocontext) macrocontext = "";
1274 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1275 if (!callerid) callerid = "";
1276 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1277 if (!origtime) origtime = "";
1278 duration = ast_variable_retrieve(cfg, "message", "duration");
1279 if (!duration) duration = "";
1280 category = ast_variable_retrieve(cfg, "message", "category");
1281 if (!category) category = "";
1283 fdlen = lseek(fd, 0, SEEK_END);
1284 lseek(fd, 0, SEEK_SET);
1285 printf("Length is %d\n", fdlen);
1286 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1288 ast_log(LOG_WARNING, "Memory map failed!\n");
1289 odbc_release_obj(obj);
1292 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1293 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1294 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1295 odbc_release_obj(obj);
1298 if (!ast_strlen_zero(category))
1299 #ifdef EXTENDED_ODBC_STORAGE
1300 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1302 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
1305 #ifdef EXTENDED_ODBC_STORAGE
1306 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1308 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
1310 res = SQLPrepare(stmt, sql, SQL_NTS);
1311 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1312 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1313 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1314 odbc_release_obj(obj);
1317 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1318 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1319 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1320 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1321 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1322 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1323 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1324 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1325 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1326 #ifdef EXTENDED_ODBC_STORAGE
1327 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1328 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1329 if (!ast_strlen_zero(category))
1330 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1332 if (!ast_strlen_zero(category))
1333 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1335 res = odbc_smart_execute(obj, stmt);
1336 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1337 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1338 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1339 odbc_release_obj(obj);
1342 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1343 odbc_release_obj(obj);
1345 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1348 ast_config_destroy(cfg);
1356 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1363 struct odbc_obj *obj;
1365 delete_file(ddir, dmsg);
1366 obj = odbc_request_obj(odbc_database, 0);
1368 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1369 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1370 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1371 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1372 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1373 odbc_release_obj(obj);
1376 #ifdef EXTENDED_ODBC_STORAGE
1377 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1379 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
1381 res = SQLPrepare(stmt, sql, SQL_NTS);
1382 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1383 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1384 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1385 odbc_release_obj(obj);
1388 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1389 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1390 #ifdef EXTENDED_ODBC_STORAGE
1391 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1392 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1393 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1394 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1396 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1397 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1399 res = odbc_smart_execute(obj, stmt);
1400 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1401 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1402 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1403 odbc_release_obj(obj);
1406 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1407 odbc_release_obj(obj);
1409 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1416 static int count_messages(struct ast_vm_user *vmu, char *dir)
1418 /* Find all .txt files - even if they are not in sequence from 0000 */
1422 struct dirent *vment = NULL;
1424 if (vm_lock_path(dir))
1425 return ERROR_LOCK_PATH;
1427 if ((vmdir = opendir(dir))) {
1428 while ((vment = readdir(vmdir))) {
1429 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1434 ast_unlock_path(dir);
1439 static void rename_file(char *sfn, char *dfn)
1443 ast_filerename(sfn,dfn,NULL);
1444 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1445 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1449 static int copy(char *infile, char *outfile)
1457 #ifdef HARDLINK_WHEN_POSSIBLE
1458 /* Hard link if possible; saves disk space & is faster */
1459 if (link(infile, outfile)) {
1461 if ((ifd = open(infile, O_RDONLY)) < 0) {
1462 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1465 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1466 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1471 len = read(ifd, buf, sizeof(buf));
1473 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1479 res = write(ofd, buf, len);
1480 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1481 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1491 #ifdef HARDLINK_WHEN_POSSIBLE
1493 /* Hard link succeeded */
1499 static void copy_file(char *frompath, char *topath)
1501 char frompath2[256],topath2[256];
1502 ast_filecopy(frompath, topath, NULL);
1503 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1504 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1505 copy(frompath2, topath2);
1509 * A negative return value indicates an error.
1511 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1516 if (vm_lock_path(dir))
1517 return ERROR_LOCK_PATH;
1519 for (x = 0; x < vmu->maxmsg; x++) {
1520 make_file(fn, sizeof(fn), dir, x);
1521 if (ast_fileexists(fn, NULL, NULL) < 1)
1524 ast_unlock_path(dir);
1529 static int vm_delete(char *file)
1534 txtsize = (strlen(file) + 5)*sizeof(char);
1535 txt = alloca(txtsize);
1536 /* Sprintf here would safe because we alloca'd exactly the right length,
1537 * but trying to eliminate all sprintf's anyhow
1539 snprintf(txt, txtsize, "%s.txt", file);
1541 return ast_filedelete(file, NULL);
1547 inbuf(struct baseio *bio, FILE *fi)
1554 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1569 inchar(struct baseio *bio, FILE *fi)
1571 if (bio->iocp>=bio->iolen) {
1572 if (!inbuf(bio, fi))
1576 return bio->iobuf[bio->iocp++];
1580 ochar(struct baseio *bio, int c, FILE *so)
1582 if (bio->linelength>=BASELINELEN) {
1583 if (fputs(eol,so)==EOF)
1589 if (putc(((unsigned char)c),so)==EOF)
1597 static int base_encode(char *filename, FILE *so)
1599 unsigned char dtable[BASEMAXINLINE];
1604 memset(&bio, 0, sizeof(bio));
1605 bio.iocp = BASEMAXINLINE;
1607 if (!(fi = fopen(filename, "rb"))) {
1608 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1612 for (i= 0;i<9;i++) {
1615 dtable[26+i]= 'a'+i;
1616 dtable[26+i+9]= 'j'+i;
1618 for (i= 0;i<8;i++) {
1619 dtable[i+18]= 'S'+i;
1620 dtable[26+i+18]= 's'+i;
1622 for (i= 0;i<10;i++) {
1623 dtable[52+i]= '0'+i;
1629 unsigned char igroup[3],ogroup[4];
1632 igroup[0]= igroup[1]= igroup[2]= 0;
1634 for (n= 0;n<3;n++) {
1635 if ((c = inchar(&bio, fi)) == EOF) {
1640 igroup[n]= (unsigned char)c;
1644 ogroup[0]= dtable[igroup[0]>>2];
1645 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1646 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1647 ogroup[3]= dtable[igroup[2]&0x3F];
1657 ochar(&bio, ogroup[i], so);
1661 if (fputs(eol,so)==EOF)
1669 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)
1672 /* Prepare variables for substition in email body and subject */
1673 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1674 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1675 snprintf(passdata, passdatasize, "%d", msgnum);
1676 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1677 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1678 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1679 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1680 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1681 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1682 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1683 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1687 * fill in *tm for current time according to the proper timezone, if any.
1688 * Return tm so it can be used as a function argument.
1690 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1692 const struct vm_zone *z = NULL;
1693 time_t t = time(NULL);
1695 /* Does this user have a timezone specified? */
1696 if (!ast_strlen_zero(vmu->zonetag)) {
1697 /* Find the zone in the list */
1698 AST_LIST_LOCK(&zones);
1699 AST_LIST_TRAVERSE(&zones, z, list) {
1700 if (!strcmp(z->name, vmu->zonetag))
1703 AST_LIST_UNLOCK(&zones);
1705 ast_localtime(&t, tm, z ? z->timezone : NULL);
1709 /* same as mkstemp, but return a FILE * */
1710 static FILE *vm_mkftemp(char *template)
1713 int pfd = mkstemp(template);
1715 p = fdopen(pfd, "w");
1724 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)
1728 char host[MAXHOSTNAMELEN] = "";
1733 char tmp[80] = "/tmp/astmail-XXXXXX";
1737 if (vmu && ast_strlen_zero(vmu->email)) {
1738 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1741 if (!strcmp(format, "wav49"))
1743 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));
1744 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1746 if ((p = vm_mkftemp(tmp)) == NULL) {
1747 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1750 gethostname(host, sizeof(host)-1);
1751 if (strchr(srcemail, '@'))
1752 ast_copy_string(who, srcemail, sizeof(who));
1754 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1756 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1757 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1758 fprintf(p, "Date: %s\n", date);
1760 /* Set date format for voicemail mail */
1761 strftime(date, sizeof(date), emaildateformat, &tm);
1764 struct ast_channel *ast;
1765 if ((ast = ast_channel_alloc(0))) {
1767 int vmlen = strlen(fromstring)*3 + 200;
1768 if ((passdata = alloca(vmlen))) {
1769 memset(passdata, 0, vmlen);
1770 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1771 pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1772 fprintf(p, "From: %s <%s>\n",passdata,who);
1773 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1774 ast_channel_free(ast);
1775 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1777 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1778 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1781 struct ast_channel *ast;
1782 if ((ast = ast_channel_alloc(0))) {
1784 int vmlen = strlen(emailsubject)*3 + 200;
1785 if ((passdata = alloca(vmlen))) {
1786 memset(passdata, 0, vmlen);
1787 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1788 pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1789 fprintf(p, "Subject: %s\n",passdata);
1790 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1791 ast_channel_free(ast);
1792 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1795 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1797 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1798 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1800 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1801 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1802 fprintf(p, "MIME-Version: 1.0\n");
1803 if (attach_user_voicemail) {
1804 /* Something unique. */
1805 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1807 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1809 fprintf(p, "--%s\n", bound);
1811 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1813 struct ast_channel *ast;
1814 if ((ast = ast_channel_alloc(0))) {
1816 int vmlen = strlen(emailbody)*3 + 200;
1817 if ((passdata = alloca(vmlen))) {
1818 memset(passdata, 0, vmlen);
1819 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1820 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1821 fprintf(p, "%s\n",passdata);
1822 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1823 ast_channel_free(ast);
1824 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1826 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1828 "in mailbox %s from %s, on %s so you might\n"
1829 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1830 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1832 if (attach_user_voicemail) {
1833 /* Eww. We want formats to tell us their own MIME type */
1834 char *ctype = "audio/x-";
1835 if (!strcasecmp(format, "ogg"))
1836 ctype = "application/";
1838 fprintf(p, "--%s\n", bound);
1839 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1840 fprintf(p, "Content-Transfer-Encoding: base64\n");
1841 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1842 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1844 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1845 base_encode(fname, p);
1846 fprintf(p, "\n\n--%s--\n.\n", bound);
1849 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1850 ast_safe_system(tmp2);
1851 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1856 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)
1859 char host[MAXHOSTNAMELEN]="";
1862 char tmp[80] = "/tmp/astmail-XXXXXX";
1867 if ((p = vm_mkftemp(tmp)) == NULL) {
1868 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1871 gethostname(host, sizeof(host)-1);
1872 if (strchr(srcemail, '@'))
1873 ast_copy_string(who, srcemail, sizeof(who));
1875 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1877 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1878 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1879 fprintf(p, "Date: %s\n", date);
1881 if (*pagerfromstring) {
1882 struct ast_channel *ast;
1883 if ((ast = ast_channel_alloc(0))) {
1885 int vmlen = strlen(fromstring)*3 + 200;
1886 if ((passdata = alloca(vmlen))) {
1887 memset(passdata, 0, vmlen);
1888 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1889 pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1890 fprintf(p, "From: %s <%s>\n",passdata,who);
1892 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1893 ast_channel_free(ast);
1894 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1896 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1897 fprintf(p, "To: %s\n", pager);
1899 struct ast_channel *ast;
1900 if ((ast = ast_channel_alloc(0))) {
1902 int vmlen = strlen(pagersubject)*3 + 200;
1903 if ((passdata = alloca(vmlen))) {
1904 memset(passdata, 0, vmlen);
1905 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1906 pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
1907 fprintf(p, "Subject: %s\n\n",passdata);
1908 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1909 ast_channel_free(ast);
1910 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1912 fprintf(p, "Subject: New VM\n\n");
1913 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1915 struct ast_channel *ast;
1916 if ((ast = ast_channel_alloc(0))) {
1918 int vmlen = strlen(pagerbody)*3 + 200;
1919 if ((passdata = alloca(vmlen))) {
1920 memset(passdata, 0, vmlen);
1921 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1922 pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
1923 fprintf(p, "%s\n",passdata);
1924 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1925 ast_channel_free(ast);
1926 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1928 fprintf(p, "New %s long msg in box %s\n"
1929 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1932 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1933 ast_safe_system(tmp2);
1934 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1939 static int get_date(char *s, int len)
1944 localtime_r(&t,&tm);
1945 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1948 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1952 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
1954 if (ast_fileexists(fn, NULL, NULL) > 0) {
1955 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
1961 /* Dispose just in case */
1963 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
1966 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1970 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
1974 static void free_user(struct ast_vm_user *vmu)
1976 if (ast_test_flag(vmu, VM_ALLOCED))
1980 static void free_zone(struct vm_zone *z)
1985 static const char *mbox(int id)
1987 static const char *msgs[] = {
1999 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2002 #ifdef USE_ODBC_STORAGE
2003 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2011 struct odbc_obj *obj;
2018 /* If no mailbox, return immediately */
2019 if (ast_strlen_zero(mailbox))
2022 ast_copy_string(tmp, mailbox, sizeof(tmp));
2024 context = strchr(tmp, '@');
2029 context = "default";
2031 obj = odbc_request_obj(odbc_database, 0);
2033 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2034 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2035 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2036 odbc_release_obj(obj);
2039 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir LIKE '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2040 res = SQLPrepare(stmt, sql, SQL_NTS);
2041 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2042 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2043 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2044 odbc_release_obj(obj);
2047 res = odbc_smart_execute(obj, stmt);
2048 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2049 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2050 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2051 odbc_release_obj(obj);
2054 res = SQLFetch(stmt);
2055 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2056 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2057 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2058 odbc_release_obj(obj);
2061 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2062 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2063 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2064 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2065 odbc_release_obj(obj);
2068 *newmsgs = atoi(rowdata);
2069 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2071 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2072 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2073 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2074 odbc_release_obj(obj);
2077 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "Old");
2078 res = SQLPrepare(stmt, sql, SQL_NTS);
2079 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2080 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2081 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2082 odbc_release_obj(obj);
2085 res = odbc_smart_execute(obj, stmt);
2086 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2087 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2088 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2089 odbc_release_obj(obj);
2092 res = SQLFetch(stmt);
2093 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2094 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2095 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2096 odbc_release_obj(obj);
2099 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2100 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2101 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2102 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2103 odbc_release_obj(obj);
2106 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2107 odbc_release_obj(obj);
2108 *oldmsgs = atoi(rowdata);
2111 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2117 static int has_voicemail(const char *mailbox, const char *folder)
2119 struct odbc_obj *obj;
2129 /* If no mailbox, return immediately */
2130 if (ast_strlen_zero(mailbox))
2133 ast_copy_string(tmp, mailbox, sizeof(tmp));
2135 context = strchr(tmp, '@');
2140 context = "default";
2142 obj = odbc_request_obj(odbc_database, 0);
2144 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2145 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2146 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2147 odbc_release_obj(obj);
2150 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2151 res = SQLPrepare(stmt, sql, SQL_NTS);
2152 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2153 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2154 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2155 odbc_release_obj(obj);
2158 res = odbc_smart_execute(obj, stmt);
2159 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2160 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2161 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2162 odbc_release_obj(obj);
2165 res = SQLFetch(stmt);
2166 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2167 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2168 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2169 odbc_release_obj(obj);
2172 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2173 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2174 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2175 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2176 odbc_release_obj(obj);
2179 nummsgs = atoi(rowdata);
2180 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2181 odbc_release_obj(obj);
2183 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2194 static int has_voicemail(const char *mailbox, const char *folder)
2205 /* If no mailbox, return immediately */
2206 if (ast_strlen_zero(mailbox))
2208 if (strchr(mailbox, ',')) {
2209 ast_copy_string(tmp, mailbox, sizeof(tmp));
2212 while((cur = strsep(&mb, ","))) {
2213 if (!ast_strlen_zero(cur)) {
2214 if (has_voicemail(cur, folder))
2220 ast_copy_string(tmp, mailbox, sizeof(tmp));
2221 context = strchr(tmp, '@');
2226 context = "default";
2227 snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
2231 while ((de = readdir(dir))) {
2232 if (!strncasecmp(de->d_name, "msg", 3))
2242 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2254 /* If no mailbox, return immediately */
2255 if (ast_strlen_zero(mailbox))
2257 if (strchr(mailbox, ',')) {
2261 ast_copy_string(tmp, mailbox, sizeof(tmp));
2263 while((cur = strsep(&mb, ", "))) {
2264 if (!ast_strlen_zero(cur)) {
2265 if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2277 ast_copy_string(tmp, mailbox, sizeof(tmp));
2278 context = strchr(tmp, '@');
2283 context = "default";
2285 snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
2288 while ((de = readdir(dir))) {
2289 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2290 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2298 snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
2301 while ((de = readdir(dir))) {
2302 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2303 !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2315 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
2317 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)
2319 char fromdir[256], todir[256], frompath[256], topath[256];
2320 const char *frombox = mbox(imbox);
2323 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2325 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2327 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2328 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2330 if (vm_lock_path(todir))
2331 return ERROR_LOCK_PATH;
2335 make_file(topath, sizeof(topath), todir, recipmsgnum);
2336 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2339 } while (recipmsgnum < recip->maxmsg);
2340 if (recipmsgnum < recip->maxmsg) {
2341 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2343 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2345 ast_unlock_path(todir);
2346 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2351 static void run_externnotify(char *context, char *extension)
2353 char arguments[255];
2354 char ext_context[256] = "";
2355 int newvoicemails = 0, oldvoicemails = 0;
2357 struct ast_smdi_mwi_message *mwi_msg;
2360 if (!ast_strlen_zero(context))
2361 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2363 ast_copy_string(ext_context, extension, sizeof(ext_context));
2366 if (!strcasecmp(externnotify, "smdi")) {
2367 if (ast_app_has_voicemail(ext_context, NULL))
2368 ast_smdi_mwi_set(smdi_iface, extension);
2370 ast_smdi_mwi_unset(smdi_iface, extension);
2372 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2373 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2374 if (!strncmp(mwi_msg->cause, "INV", 3))
2375 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2376 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2377 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2378 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2379 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2381 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2383 } else if (!ast_strlen_zero(externnotify)) {
2385 if (!ast_strlen_zero(externnotify)) {
2387 if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
2388 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2390 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2391 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2392 ast_safe_system(arguments);
2397 struct leave_vm_options {
2399 signed char record_gain;
2402 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2404 char tmptxtfile[256], txtfile[256];
2415 char prefile[256]="";
2416 char tempfile[256]="";
2417 char ext_context[256] = "";
2420 char ecodes[16] = "#";
2421 char tmp[256] = "", *tmpptr;
2422 struct ast_vm_user *vmu;
2423 struct ast_vm_user svm;
2424 const char *category = NULL;
2426 ast_copy_string(tmp, ext, sizeof(tmp));
2428 context = strchr(tmp, '@');
2431 tmpptr = strchr(context, '&');
2433 tmpptr = strchr(ext, '&');
2439 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2441 if (!(vmu = find_user(&svm, context, ext))) {
2442 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2443 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2444 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2445 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2449 /* Setup pre-file if appropriate */
2450 if (strcmp(vmu->context, "default"))
2451 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2453 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2454 if (ast_test_flag(options, OPT_BUSY_GREETING))
2455 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2456 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2457 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2458 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2459 RETRIEVE(tempfile, -1);
2460 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2461 ast_copy_string(prefile, tempfile, sizeof(prefile));
2462 DISPOSE(tempfile, -1);
2463 /* It's easier just to try to make it than to check for its existence */
2464 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2466 /* Check current or macro-calling context for special extensions */
2467 if (!ast_strlen_zero(vmu->exit)) {
2468 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2469 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);
2472 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2473 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2477 if (!ast_strlen_zero(vmu->exit)) {
2478 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2479 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2480 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2481 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2482 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2483 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2487 /* Play the beginning intro if desired */
2488 if (!ast_strlen_zero(prefile)) {
2489 RETRIEVE(prefile, -1);
2490 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2491 if (ast_streamfile(chan, prefile, chan->language) > -1)
2492 res = ast_waitstream(chan, ecodes);
2494 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2495 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2497 DISPOSE(prefile, -1);
2499 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2501 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2506 /* On a '#' we skip the instructions */
2507 ast_set_flag(options, OPT_SILENT);
2510 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2511 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2513 ast_set_flag(options, OPT_SILENT);
2518 ast_stopstream(chan);
2519 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2520 other than the operator -- an automated attendant or mailbox login for example */
2522 chan->exten[0] = 'a';
2523 chan->exten[1] = '\0';
2524 if (!ast_strlen_zero(vmu->exit)) {
2525 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2526 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2527 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2531 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2534 /* Check for a '0' here */
2537 if (ast_test_flag(vmu, VM_OPERATOR)) {
2538 chan->exten[0] = 'o';
2539 chan->exten[1] = '\0';
2540 if (!ast_strlen_zero(vmu->exit)) {
2541 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2542 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2543 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2545 ast_play_and_wait(chan, "transfer");
2548 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2551 ast_play_and_wait(chan, "vm-sorry");
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, "=");
3016 val = strsep(&stringp, "=");
3017 if (!ast_strlen_zero(val)) {
3018 if (!strcmp((char *)buf, "callerid"))
3019 ast_copy_string(cid, val, sizeof(cid));
3020 if (!strcmp((char *)buf, "origdate"))
3021 ast_copy_string(datetime, val, sizeof(datetime));
3027 /* New meaning for keys */
3029 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3034 /* No prev key, provide "Folder" instead */
3035 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3037 if (vms->curmsg >= vms->lastmsg) {
3038 /* If last message ... */
3040 /* but not only message, provide "Folder" instead */
3041 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3042 bytes += adsi_voice_mode(buf + bytes, 0);
3045 /* Otherwise if only message, leave blank */
3050 if (!ast_strlen_zero(cid)) {
3051 ast_callerid_parse(cid, &name, &num);
3055 name = "Unknown Caller";
3057 /* If deleted, show "undeleted" */
3059 if (vms->deleted[vms->curmsg])
3060 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3063 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3064 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3065 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3066 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3068 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3069 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3070 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3071 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3072 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3073 bytes += adsi_set_keys(buf + bytes, keys);
3074 bytes += adsi_voice_mode(buf + bytes, 0);
3076 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3079 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3082 unsigned char buf[256];
3083 unsigned char keys[8];
3087 if (!adsi_available(chan))
3090 /* New meaning for keys */
3092 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3098 /* No prev key, provide "Folder" instead */
3099 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3101 if (vms->curmsg >= vms->lastmsg) {
3102 /* If last message ... */
3104 /* but not only message, provide "Folder" instead */
3105 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3107 /* Otherwise if only message, leave blank */
3112 /* If deleted, show "undeleted" */
3113 if (vms->deleted[vms->curmsg])
3114 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3117 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3118 bytes += adsi_set_keys(buf + bytes, keys);
3119 bytes += adsi_voice_mode(buf + bytes, 0);
3121 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3124 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
3126 unsigned char buf[256] = "";
3127 char buf1[256] = "", buf2[256] = "";
3129 unsigned char keys[8];
3132 char *newm = (vms->newmessages == 1) ? "message" : "messages";
3133 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
3134 if (!adsi_available(chan))
3136 if (vms->newmessages) {
3137 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
3138 if (vms->oldmessages) {
3139 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
3140 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
3142 snprintf(buf2, sizeof(buf2), "%s.", newm);
3144 } else if (vms->oldmessages) {
3145 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
3146 snprintf(buf2, sizeof(buf2), "%s.", oldm);
3148 strcpy(buf1, "You have no messages.");
3152 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3153 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3154 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3157 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3161 /* Don't let them listen if there are none */
3162 if (vms->lastmsg < 0)
3164 bytes += adsi_set_keys(buf + bytes, keys);
3166 bytes += adsi_voice_mode(buf + bytes, 0);
3168 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3171 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
3173 unsigned char buf[256] = "";
3174 char buf1[256] = "", buf2[256] = "";
3176 unsigned char keys[8];
3179 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
3181 if (!adsi_available(chan))
3184 /* Original command keys */
3186 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3191 if ((vms->lastmsg + 1) < 1)
3194 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
3195 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
3197 if (vms->lastmsg + 1)
3198 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
3200 strcpy(buf2, "no messages.");
3201 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3202 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3203 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
3204 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3205 bytes += adsi_set_keys(buf + bytes, keys);
3207 bytes += adsi_voice_mode(buf + bytes, 0);
3209 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3214 static void adsi_clear(struct ast_channel *chan)
3218 if (!adsi_available(chan))
3220 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3221 bytes += adsi_voice_mode(buf + bytes, 0);
3223 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3227 static void adsi_goodbye(struct ast_channel *chan)
3229 unsigned char buf[256];
3232 if (!adsi_available(chan))
3234 bytes += adsi_logo(buf + bytes);
3235 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
3236 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
3237 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3238 bytes += adsi_voice_mode(buf + bytes, 0);
3240 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3243 /*--- get_folder: Folder menu ---*/
3244 /* Plays "press 1 for INBOX messages" etc
3245 Should possibly be internationalized
3247 static int get_folder(struct ast_channel *chan, int start)
3252 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
3255 for (x = start; x< 5; x++) { /* For all folders */
3256 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
3258 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
3261 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
3262 d = vm_play_folder_name(chan, fn);
3265 d = ast_waitfordigit(chan, 500);
3269 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
3272 d = ast_waitfordigit(chan, 4000);
3276 static int get_folder2(struct ast_channel *chan, char *fn, int start)
3279 res = ast_play_and_wait(chan, fn); /* Folder name */
3280 while (((res < '0') || (res > '9')) &&
3281 (res != '#') && (res >= 0)) {
3282 res = get_folder(chan, 0);
3287 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts,
3288 char *context, signed char record_gain)
3293 signed char zero_gain = 0;
3295 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
3300 /* prepend a message to the current message and return */
3303 snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
3305 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
3306 cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
3308 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
3318 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
3319 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3321 cmd = ast_play_and_wait(chan,"vm-starmain");
3322 /* "press star to return to the main menu" */
3324 cmd = ast_waitfordigit(chan,6000);
3331 if (cmd == 't' || cmd == 'S')
3336 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
3338 char todir[256], fn[256], ext_context[256], *stringp;
3339 int newmsgs = 0, oldmsgs = 0;
3340 const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3342 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
3343 make_file(fn, sizeof(fn), todir, msgnum);
3344 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
3346 if (!ast_strlen_zero(vmu->attachfmt)) {
3347 fmt = vmu->attachfmt;
3350 /* Attach only the first format */
3351 if ((fmt = ast_strdupa(fmt))) {
3353 strsep(&stringp, "|");
3355 if (!ast_strlen_zero(vmu->email)) {
3356 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3357 char *myserveremail = serveremail;
3358 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
3359 if (!ast_strlen_zero(vmu->serveremail))
3360 myserveremail = vmu->serveremail;
3361 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, category);
3364 if (!ast_strlen_zero(vmu->pager)) {
3365 char *myserveremail = serveremail;
3366 if (!ast_strlen_zero(vmu->serveremail))
3367 myserveremail = vmu->serveremail;
3368 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
3372 if (ast_test_flag(vmu, VM_DELETE)) {
3373 DELETE(todir, msgnum, fn);
3376 /* Leave voicemail for someone */
3377 if (ast_app_has_voicemail(ext_context, NULL)) {
3378 ast_app_messagecount(ext_context, &newmsgs, &oldmsgs);
3380 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
3381 run_externnotify(vmu->context, vmu->mailbox);
3385 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender,
3386 char *fmt, int flag, signed char record_gain)
3388 char username[70]="";
3393 struct ast_config *mif;
3397 char ext_context[256]="";
3398 int res = 0, cmd = 0;
3399 struct ast_vm_user *receiver = NULL, *vmtmp;
3400 AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
3403 int saved_messages = 0, found = 0;
3404 int valid_extensions = 0;
3406 while (!res && !valid_extensions) {
3407 int use_directory = 0;
3408 if(ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
3412 while((cmd >= 0) && !done ){
3429 /* Press 1 to enter an extension press 2 to use the directory */
3430 cmd = ast_play_and_wait(chan,"vm-forward");
3432 cmd = ast_waitfordigit(chan,3000);
3443 if( cmd<0 || cmd=='t' )
3447 if (use_directory) {
3448 /* use app_directory */
3450 char old_context[sizeof(chan->context)];
3451 char old_exten[sizeof(chan->exten)];
3453 struct ast_app* app;
3456 app = pbx_findapp("Directory");
3458 /* make mackup copies */
3459 memcpy(old_context, chan->context, sizeof(chan->context));