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 * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
28 * \ingroup applications
29 * \note This module requires res_adsi to load. This needs to be optional
34 * \note This file is now almost impossible to work with, due to all #ifdefs.
35 * Feels like the database code before realtime. Someone - please come up
36 * with a plan to clean this up.
40 <depend>res_adsi</depend>
44 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o">
45 <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
46 <depend>unixodbc</depend>
47 <conflict>IMAP_STORAGE</conflict>
48 <defaultenabled>no</defaultenabled>
50 <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
51 <depend>imap_tk</depend>
52 <conflict>ODBC_STORAGE</conflict>
54 <defaultenabled>no</defaultenabled>
61 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
71 #include <sys/types.h>
85 #include "asterisk/lock.h"
86 #include "asterisk/file.h"
87 #include "asterisk/logger.h"
88 #include "asterisk/channel.h"
89 #include "asterisk/pbx.h"
90 #include "asterisk/options.h"
91 #include "asterisk/config.h"
92 #include "asterisk/say.h"
93 #include "asterisk/module.h"
94 #include "asterisk/adsi.h"
95 #include "asterisk/app.h"
96 #include "asterisk/manager.h"
97 #include "asterisk/dsp.h"
98 #include "asterisk/localtime.h"
99 #include "asterisk/cli.h"
100 #include "asterisk/utils.h"
101 #include "asterisk/stringfields.h"
102 #include "asterisk/smdi.h"
105 #include "asterisk/res_odbc.h"
109 static char imapserver[48];
110 static char imapport[8];
111 static char imapflags[128];
112 static char imapfolder[64];
113 static char authuser[32];
114 static char authpassword[42];
116 static int expungeonhangup = 1;
117 AST_MUTEX_DEFINE_STATIC(delimiter_lock);
118 static char delimiter = '\0';
123 /* Forward declarations for IMAP */
124 static int init_mailstream(struct vm_state *vms, int box);
125 static void write_file char *filename, char *buffer, unsigned long len);
126 static void display_body(BODY *body, char *pfx, long i);
127 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
128 static void vm_imap_delete(int msgnum, struct vm_state *vms);
129 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
130 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
131 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
132 static void vmstate_insert(struct vm_state *vms);
133 static void vmstate_delete(struct vm_state *vms);
134 static void set_update(MAILSTREAM * stream);
135 static void init_vm_state(struct vm_state *vms);
136 static void check_msgArray(struct vm_state *vms);
137 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
138 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
139 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
140 static void get_mailbox_delimiter(MAILSTREAM *stream);
141 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
142 static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int target);
143 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms);
148 struct vm_state *vms;
149 AST_LIST_ENTRY(vmstate) list;
152 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
156 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
158 #define COMMAND_TIMEOUT 5000
159 /* Don't modify these here; set your umask at runtime instead */
160 #define VOICEMAIL_DIR_MODE 0777
161 #define VOICEMAIL_FILE_MODE 0666
162 #define CHUNKSIZE 65536
164 #define VOICEMAIL_CONFIG "voicemail.conf"
165 #define ASTERISK_USERNAME "asterisk"
167 /* Default mail command to mail voicemail. Change it with the
168 mailcmd= command in voicemail.conf */
169 #define SENDMAIL "/usr/sbin/sendmail -t"
171 #define INTRO "vm-intro"
174 #define MAXMSGLIMIT 9999
176 #define BASEMAXINLINE 256
177 #define BASELINELEN 72
178 #define BASEMAXINLINE 256
181 #define MAX_DATETIME_FORMAT 512
182 #define MAX_NUM_CID_CONTEXTS 10
184 #define VM_REVIEW (1 << 0)
185 #define VM_OPERATOR (1 << 1)
186 #define VM_SAYCID (1 << 2)
187 #define VM_SVMAIL (1 << 3)
188 #define VM_ENVELOPE (1 << 4)
189 #define VM_SAYDURATION (1 << 5)
190 #define VM_SKIPAFTERCMD (1 << 6)
191 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
192 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
193 #define VM_PBXSKIP (1 << 9)
194 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
195 #define VM_ATTACH (1 << 11)
196 #define VM_DELETE (1 << 12)
197 #define VM_ALLOCED (1 << 13)
198 #define VM_SEARCH (1 << 14)
199 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
200 #define ERROR_LOCK_PATH -100
204 OPT_SILENT = (1 << 0),
205 OPT_BUSY_GREETING = (1 << 1),
206 OPT_UNAVAIL_GREETING = (1 << 2),
207 OPT_RECORDGAIN = (1 << 3),
208 OPT_PREPEND_MAILBOX = (1 << 4),
209 OPT_PRIORITY_JUMP = (1 << 5),
210 OPT_AUTOPLAY = (1 << 6),
214 OPT_ARG_RECORDGAIN = 0,
215 OPT_ARG_PLAYFOLDER = 1,
216 /* This *must* be the last value in this enum! */
217 OPT_ARG_ARRAY_SIZE = 2,
220 AST_APP_OPTIONS(vm_app_options, {
221 AST_APP_OPTION('s', OPT_SILENT),
222 AST_APP_OPTION('b', OPT_BUSY_GREETING),
223 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
224 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
225 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
226 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
227 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
230 static int load_config(void);
232 /*! \page vmlang Voicemail Language Syntaxes Supported
234 \par Syntaxes supported, not really language codes.
241 \arg \b pt - Portuguese
242 \arg \b pt_BR - Portuguese (Brazil)
244 \arg \b no - Norwegian
247 German requires the following additional soundfile:
248 \arg \b 1F einE (feminine)
250 Spanish requires the following additional soundfile:
251 \arg \b 1M un (masculine)
253 Dutch, Portuguese & Spanish require the following additional soundfiles:
254 \arg \b vm-INBOXs singular of 'new'
255 \arg \b vm-Olds singular of 'old/heard/read'
258 \arg \b vm-INBOX nieuwe (nl)
259 \arg \b vm-Old oude (nl)
262 \arg \b vm-new-a 'new', feminine singular accusative
263 \arg \b vm-new-e 'new', feminine plural accusative
264 \arg \b vm-new-ych 'new', feminine plural genitive
265 \arg \b vm-old-a 'old', feminine singular accusative
266 \arg \b vm-old-e 'old', feminine plural accusative
267 \arg \b vm-old-ych 'old', feminine plural genitive
268 \arg \b digits/1-a 'one', not always same as 'digits/1'
269 \arg \b digits/2-ie 'two', not always same as 'digits/2'
272 \arg \b vm-nytt singular of 'new'
273 \arg \b vm-nya plural of 'new'
274 \arg \b vm-gammalt singular of 'old'
275 \arg \b vm-gamla plural of 'old'
276 \arg \b digits/ett 'one', not always same as 'digits/1'
279 \arg \b vm-ny singular of 'new'
280 \arg \b vm-nye plural of 'new'
281 \arg \b vm-gammel singular of 'old'
282 \arg \b vm-gamle plural of 'old'
290 Italian requires the following additional soundfile:
294 \arg \b vm-nuovi new plural
295 \arg \b vm-vecchio old
296 \arg \b vm-vecchi old plural
298 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
299 spelled among others when you have to change folder. For the above reasons, vm-INBOX
300 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
309 unsigned char iobuf[BASEMAXINLINE];
312 /*! Structure for linked list of users */
314 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
315 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
316 char password[80]; /*!< Secret pin code, numbers only */
317 char fullname[80]; /*!< Full name, for directory app */
318 char email[80]; /*!< E-mail address */
319 char pager[80]; /*!< E-mail address to pager (no attachment) */
320 char serveremail[80]; /*!< From: Mail address */
321 char mailcmd[160]; /*!< Configurable mail command */
322 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
323 char zonetag[80]; /*!< Time zone */
326 char uniqueid[20]; /*!< Unique integer identifier */
328 char attachfmt[20]; /*!< Attachment format */
329 unsigned int flags; /*!< VM_ flags */
331 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
332 int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
334 char imapuser[80]; /*!< IMAP server login */
335 char imappassword[80]; /*!< IMAP server password if authpassword not defined */
337 double volgain; /*!< Volume gain for voicemails sent via email */
338 AST_LIST_ENTRY(ast_vm_user) list;
341 /*! Voicemail time zones */
343 AST_LIST_ENTRY(vm_zone) list;
346 char msg_format[512];
349 /*! Voicemail mailbox state */
353 char curdir[PATH_MAX];
354 char vmbox[PATH_MAX];
366 int updated; /*!< decremented on each mail check until 1 -allows delay */
368 MAILSTREAM *mailstream;
370 char imapuser[80]; /*!< IMAP server login */
372 unsigned int quota_limit;
373 unsigned int quota_usage;
374 struct vm_state *persist_vms;
380 static char odbc_database[80];
381 static char odbc_table[80];
382 #define RETRIEVE(a,b) retrieve_file(a,b)
383 #define DISPOSE(a,b) remove_file(a,b)
384 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
385 #define EXISTS(a,b,c,d) (message_exists(a,b))
386 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
387 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
388 #define DELETE(a,b,c) (delete_file(a,b))
391 #define RETRIEVE(a,b)
393 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
394 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
395 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
396 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
397 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
398 #define DELETE(a,b,c) (vm_delete(c))
400 #define RETRIEVE(a,b)
402 #define STORE(a,b,c,d,e,f,g,h,i)
403 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
404 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
405 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
406 #define DELETE(a,b,c) (vm_delete(c))
410 static char VM_SPOOL_DIR[PATH_MAX];
412 static char ext_pass_cmd[128];
414 #define PWDCHANGE_INTERNAL (1 << 1)
415 #define PWDCHANGE_EXTERNAL (1 << 2)
416 static int pwdchange = PWDCHANGE_INTERNAL;
419 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
420 #elifdef IMAP_STORAGE
421 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
423 #define tdesc "Comedian Mail (Voicemail System)"
426 static char userscontext[AST_MAX_EXTENSION] = "default";
428 static char *addesc = "Comedian Mail";
430 static char *synopsis_vm = "Leave a Voicemail message";
432 static char *descrip_vm =
433 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
434 "application allows the calling party to leave a message for the specified\n"
435 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
436 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
437 "specified mailbox does not exist.\n"
438 " The Voicemail application will exit if any of the following DTMF digits are\n"
440 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
441 " * - Jump to the 'a' extension in the current dialplan context.\n"
442 " This application will set the following channel variable upon completion:\n"
443 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
444 " application. The possible values are:\n"
445 " SUCCESS | USEREXIT | FAILED\n\n"
447 " b - Play the 'busy' greeting to the calling party.\n"
448 " g(#) - Use the specified amount of gain when recording the voicemail\n"
449 " message. The units are whole-number decibels (dB).\n"
450 " s - Skip the playback of instructions for leaving a message to the\n"
452 " u - Play the 'unavailble greeting.\n"
453 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
456 static char *synopsis_vmain = "Check Voicemail messages";
458 static char *descrip_vmain =
459 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
460 "calling party to check voicemail messages. A specific mailbox, and optional\n"
461 "corresponding context, may be specified. If a mailbox is not provided, the\n"
462 "calling party will be prompted to enter one. If a context is not specified,\n"
463 "the 'default' context will be used.\n\n"
465 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
466 " is entered by the caller.\n"
467 " g(#) - Use the specified amount of gain when recording a voicemail\n"
468 " message. The units are whole-number decibels (dB).\n"
469 " s - Skip checking the passcode for the mailbox.\n"
470 " a(#) - Skip folder prompt and go directly to folder specified.\n"
471 " Defaults to INBOX\n";
473 static char *synopsis_vm_box_exists =
474 "Check to see if Voicemail mailbox exists";
476 static char *descrip_vm_box_exists =
477 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
478 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
480 " This application will set the following channel variable upon completion:\n"
481 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
482 " MailboxExists application. Possible values include:\n"
483 " SUCCESS | FAILED\n\n"
485 " j - Jump to priority n+101 if the mailbox is found.\n";
487 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
489 static char *descrip_vmauthenticate =
490 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
491 "same way as the Authenticate application, but the passwords are taken from\n"
493 " If the mailbox is specified, only that mailbox's password will be considered\n"
494 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
495 "be set with the authenticated mailbox.\n\n"
497 " s - Skip playing the initial prompts.\n";
499 /* Leave a message */
500 static char *app = "VoiceMail";
502 /* Check mail, control, etc */
503 static char *app2 = "VoiceMailMain";
505 static char *app3 = "MailboxExists";
506 static char *app4 = "VMAuthenticate";
508 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
509 static AST_LIST_HEAD_STATIC(zones, vm_zone);
510 static int maxsilence;
512 static int silencethreshold = 128;
513 static char serveremail[80];
514 static char mailcmd[160]; /* Configurable mail cmd */
515 static char externnotify[160];
516 static struct ast_smdi_interface *smdi_iface = NULL;
517 static char vmfmts[80];
518 static double volgain;
519 static int vmminsecs;
520 static int vmmaxsecs;
523 static int maxlogins;
525 /* custom password sounds */
526 static char vm_password[80] = "vm-password";
527 static char vm_newpassword[80] = "vm-newpassword";
528 static char vm_passchanged[80] = "vm-passchanged";
529 static char vm_reenterpassword[80] = "vm-reenterpassword";
530 static char vm_mismatch[80] = "vm-mismatch";
532 static struct ast_flags globalflags = {0};
534 static int saydurationminfo;
536 static char dialcontext[AST_MAX_CONTEXT];
537 static char callcontext[AST_MAX_CONTEXT];
538 static char exitcontext[AST_MAX_CONTEXT];
540 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
543 static char *emailbody = NULL;
544 static char *emailsubject = NULL;
545 static char *pagerbody = NULL;
546 static char *pagersubject = NULL;
547 static char fromstring[100];
548 static char pagerfromstring[100];
549 static char emailtitle[100];
550 static char charset[32] = "ISO-8859-1";
552 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
553 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
554 static int adsiver = 1;
555 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
557 /* Forward declarations - generic */
558 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
559 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
560 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
561 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
562 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
563 signed char record_gain, struct vm_state *vms);
564 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
565 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
566 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
567 static void make_email_file(FILE *p, 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, struct ast_channel *chan, const char *category, int imap);
568 static void apply_options(struct ast_vm_user *vmu, const char *options);
570 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
571 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
576 static void populate_defaults(struct ast_vm_user *vmu)
578 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
579 if (saydurationminfo)
580 vmu->saydurationm = saydurationminfo;
582 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
584 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
586 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
588 vmu->maxsecs = vmmaxsecs;
590 vmu->maxmsg = maxmsg;
591 vmu->volgain = volgain;
594 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
597 if (!strcasecmp(var, "attach")) {
598 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
599 } else if (!strcasecmp(var, "attachfmt")) {
600 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
601 } else if (!strcasecmp(var, "serveremail")) {
602 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
603 } else if (!strcasecmp(var, "language")) {
604 ast_copy_string(vmu->language, value, sizeof(vmu->language));
605 } else if (!strcasecmp(var, "tz")) {
606 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
608 } else if (!strcasecmp(var, "imapuser")) {
609 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
610 } else if (!strcasecmp(var, "imappassword")) {
611 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
613 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
614 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
615 } else if (!strcasecmp(var, "saycid")){
616 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
617 } else if (!strcasecmp(var,"sendvoicemail")){
618 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
619 } else if (!strcasecmp(var, "review")){
620 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
621 } else if (!strcasecmp(var, "tempgreetwarn")){
622 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
623 } else if (!strcasecmp(var, "operator")){
624 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
625 } else if (!strcasecmp(var, "envelope")){
626 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
627 } else if (!strcasecmp(var, "sayduration")){
628 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
629 } else if (!strcasecmp(var, "saydurationm")){
630 if (sscanf(value, "%d", &x) == 1) {
631 vmu->saydurationm = x;
633 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
635 } else if (!strcasecmp(var, "forcename")){
636 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
637 } else if (!strcasecmp(var, "forcegreetings")){
638 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
639 } else if (!strcasecmp(var, "callback")) {
640 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
641 } else if (!strcasecmp(var, "dialout")) {
642 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
643 } else if (!strcasecmp(var, "exitcontext")) {
644 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
645 } else if (!strcasecmp(var, "maxmessage")) {
646 if (vmu->maxsecs <= 0) {
647 ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %i\n", value, vmmaxsecs);
648 vmu->maxsecs = vmmaxsecs;
650 vmu->maxsecs = atoi(value);
652 } else if (!strcasecmp(var, "maxmsg")) {
653 vmu->maxmsg = atoi(value);
654 if (vmu->maxmsg <= 0) {
655 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
656 vmu->maxmsg = MAXMSG;
657 } else if (vmu->maxmsg > MAXMSGLIMIT) {
658 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
659 vmu->maxmsg = MAXMSGLIMIT;
661 } else if (!strcasecmp(var, "volgain")) {
662 sscanf(value, "%lf", &vmu->volgain);
663 } else if (!strcasecmp(var, "options")) {
664 apply_options(vmu, value);
668 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
671 if (!ast_strlen_zero(vmu->uniqueid)) {
672 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
674 ast_copy_string(vmu->password, password, sizeof(vmu->password));
684 static void apply_options(struct ast_vm_user *vmu, const char *options)
685 { /* Destructively Parse options and apply */
689 stringp = ast_strdupa(options);
690 while ((s = strsep(&stringp, "|"))) {
692 if ((var = strsep(&value, "=")) && value) {
693 apply_option(vmu, var, value);
698 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
700 struct ast_variable *tmp;
703 if (!strcasecmp(tmp->name, "vmsecret")) {
704 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
705 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
706 if (ast_strlen_zero(retval->password))
707 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
708 } else if (!strcasecmp(tmp->name, "uniqueid")) {
709 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
710 } else if (!strcasecmp(tmp->name, "pager")) {
711 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
712 } else if (!strcasecmp(tmp->name, "email")) {
713 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
714 } else if (!strcasecmp(tmp->name, "fullname")) {
715 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
716 } else if (!strcasecmp(tmp->name, "context")) {
717 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
719 } else if (!strcasecmp(tmp->name, "imapuser")) {
720 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
721 } else if (!strcasecmp(tmp->name, "imappassword")) {
722 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
725 apply_option(retval, tmp->name, tmp->value);
730 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
732 struct ast_variable *var;
733 struct ast_vm_user *retval;
735 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
737 ast_set_flag(retval, VM_ALLOCED);
739 memset(retval, 0, sizeof(*retval));
741 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
742 populate_defaults(retval);
743 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
744 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
746 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
748 apply_options_full(retval, var);
749 ast_variables_destroy(var);
759 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
761 /* This function could be made to generate one from a database, too */
762 struct ast_vm_user *vmu=NULL, *cur;
763 AST_LIST_LOCK(&users);
765 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
768 AST_LIST_TRAVERSE(&users, cur, list) {
769 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
771 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
775 /* Make a copy, so that on a reload, we have no race */
776 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
777 memcpy(vmu, cur, sizeof(*vmu));
778 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
779 AST_LIST_NEXT(vmu, list) = NULL;
782 vmu = find_user_realtime(ivm, context, mailbox);
783 AST_LIST_UNLOCK(&users);
787 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
789 /* This function could be made to generate one from a database, too */
790 struct ast_vm_user *cur;
792 AST_LIST_LOCK(&users);
793 AST_LIST_TRAVERSE(&users, cur, list) {
794 if ((!context || !strcasecmp(context, cur->context)) &&
795 (!strcasecmp(mailbox, cur->mailbox)))
799 ast_copy_string(cur->password, newpass, sizeof(cur->password));
802 AST_LIST_UNLOCK(&users);
806 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
808 struct ast_config *cfg=NULL;
809 struct ast_variable *var=NULL;
810 struct ast_category *cat=NULL;
811 char *category=NULL, *value=NULL, *new=NULL;
812 const char *tmp=NULL;
814 if (!change_password_realtime(vmu, newpassword))
817 /* check voicemail.conf */
818 if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
819 while ((category = ast_category_browse(cfg, category))) {
820 if (!strcasecmp(category, vmu->context)) {
821 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
822 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
825 value = strstr(tmp,",");
827 ast_log(LOG_WARNING, "variable has bad format.\n");
830 new = alloca((strlen(value)+strlen(newpassword)+1));
831 sprintf(new,"%s%s", newpassword, value);
832 if (!(cat = ast_category_get(cfg, category))) {
833 ast_log(LOG_WARNING, "Failed to get category structure.\n");
836 ast_variable_update(cat, vmu->mailbox, new, NULL);
839 /* save the results */
840 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
841 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
842 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
846 /* check users.conf and update the password stored for the mailbox*/
847 /* if no vmsecret entry exists create one. */
848 if ((cfg = ast_config_load_with_comments("users.conf"))) {
849 if (option_debug > 3)
850 ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
851 while ((category = ast_category_browse(cfg, category))) {
852 if (option_debug > 3)
853 ast_log(LOG_DEBUG, "users.conf: %s\n", category);
854 if (!strcasecmp(category, vmu->mailbox)) {
855 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
856 if (option_debug > 3)
857 ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
858 var = ast_variable_new("vmsecret", newpassword);
860 new = alloca(strlen(newpassword)+1);
861 sprintf(new, "%s", newpassword);
862 if (!(cat = ast_category_get(cfg, category))) {
863 if (option_debug > 3)
864 ast_log(LOG_DEBUG, "failed to get category!\n");
868 ast_variable_update(cat, "vmsecret", new, NULL);
870 ast_variable_append(cat, var);
873 /* save the results and clean things up */
874 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
875 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
876 config_text_file_save("users.conf", cfg, "AppVoicemail");
880 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
883 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
884 if (!ast_safe_system(buf))
885 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
888 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
890 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
894 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
896 if (mkdir(dir, 01777) && (errno != EEXIST)) {
897 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
898 return sprintf(dest, "%s/msg%04d", dir, num);
900 /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
901 return sprintf(dest, "%s/msg%04d", dir, num);
904 static void vm_imap_delete(int msgnum, struct vm_state *vms)
906 unsigned long messageNum = 0;
909 /* find real message number based on msgnum */
910 /* this may be an index into vms->msgArray based on the msgnum. */
912 messageNum = vms->msgArray[msgnum];
913 if (messageNum == 0) {
914 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
918 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
920 sprintf (arg,"%lu",messageNum);
921 mail_setflag (vms->mailstream,arg,"\\DELETED");
925 static int make_file(char *dest, int len, char *dir, int num)
927 return snprintf(dest, len, "%s/msg%04d", dir, num);
930 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
931 * \param dest String. base directory.
932 * \param len Length of dest.
933 * \param context String. Ignored if is null or empty string.
934 * \param ext String. Ignored if is null or empty string.
935 * \param folder String. Ignored if is null or empty string.
936 * \return 0 on failure, 1 on success.
938 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
940 mode_t mode = VOICEMAIL_DIR_MODE;
942 if (!ast_strlen_zero(context)) {
943 make_dir(dest, len, context, "", "");
944 if (mkdir(dest, mode) && errno != EEXIST) {
945 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
949 if (!ast_strlen_zero(ext)) {
950 make_dir(dest, len, context, ext, "");
951 if (mkdir(dest, mode) && errno != EEXIST) {
952 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
956 if (!ast_strlen_zero(folder)) {
957 make_dir(dest, len, context, ext, folder);
958 if (mkdir(dest, mode) && errno != EEXIST) {
959 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
966 /*! \brief Lock file path
967 only return failure if ast_lock_path returns 'timeout',
968 not if the path does not exist or any other reason
970 static int vm_lock_path(const char *path)
972 switch (ast_lock_path(path)) {
973 case AST_LOCK_TIMEOUT:
982 static int retrieve_file(char *dir, int msgnum)
989 SQLSMALLINT colcount=0;
996 SQLSMALLINT datatype;
997 SQLSMALLINT decimaldigits;
998 SQLSMALLINT nullable;
1004 char full_fn[PATH_MAX];
1007 struct odbc_obj *obj;
1008 obj = ast_odbc_request_obj(odbc_database, 0);
1010 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1011 c = strchr(fmt, '|');
1014 if (!strcasecmp(fmt, "wav49"))
1016 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1018 make_file(fn, sizeof(fn), dir, msgnum);
1020 ast_copy_string(fn, dir, sizeof(fn));
1021 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1023 if (!(f = fopen(full_fn, "w+"))) {
1024 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1028 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1029 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1030 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1031 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1032 ast_odbc_release_obj(obj);
1035 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1036 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1037 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1038 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1039 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1040 ast_odbc_release_obj(obj);
1043 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1044 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1045 res = ast_odbc_smart_execute(obj, stmt);
1046 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1047 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1048 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1049 ast_odbc_release_obj(obj);
1052 res = SQLFetch(stmt);
1053 if (res == SQL_NO_DATA) {
1054 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1055 ast_odbc_release_obj(obj);
1058 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1059 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1060 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1061 ast_odbc_release_obj(obj);
1064 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1066 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1067 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1068 ast_odbc_release_obj(obj);
1071 res = SQLNumResultCols(stmt, &colcount);
1072 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1073 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1074 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1075 ast_odbc_release_obj(obj);
1079 fprintf(f, "[message]\n");
1080 for (x=0;x<colcount;x++) {
1082 collen = sizeof(coltitle);
1083 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
1084 &datatype, &colsize, &decimaldigits, &nullable);
1085 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1086 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1087 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1088 ast_odbc_release_obj(obj);
1091 if (!strcasecmp(coltitle, "recording")) {
1093 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1097 lseek(fd, fdlen - 1, SEEK_SET);
1098 if (write(fd, tmp, 1) != 1) {
1103 /* Read out in small chunks */
1104 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1105 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == (void *)-1) {
1106 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1107 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1108 ast_odbc_release_obj(obj);
1111 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1112 munmap(fdm, CHUNKSIZE);
1113 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1114 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1116 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1117 ast_odbc_release_obj(obj);
1122 truncate(full_fn, fdlen);
1125 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1126 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1127 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1128 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1129 ast_odbc_release_obj(obj);
1132 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1133 fprintf(f, "%s=%s\n", coltitle, rowdata);
1136 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1137 ast_odbc_release_obj(obj);
1139 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1148 static int remove_file(char *dir, int msgnum)
1151 char full_fn[PATH_MAX];
1155 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1156 make_file(fn, sizeof(fn), dir, msgnum);
1158 ast_copy_string(fn, dir, sizeof(fn));
1159 ast_filedelete(fn, NULL);
1160 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1165 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1173 struct odbc_obj *obj;
1174 obj = ast_odbc_request_obj(odbc_database, 0);
1176 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1177 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1178 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1179 ast_odbc_release_obj(obj);
1182 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1183 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1184 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1185 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1186 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1187 ast_odbc_release_obj(obj);
1190 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1191 res = ast_odbc_smart_execute(obj, stmt);
1192 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1193 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1194 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1195 ast_odbc_release_obj(obj);
1198 res = SQLFetch(stmt);
1199 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1200 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1201 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1202 ast_odbc_release_obj(obj);
1205 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1206 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1207 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1208 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1209 ast_odbc_release_obj(obj);
1212 if (sscanf(rowdata, "%d", &x) != 1)
1213 ast_log(LOG_WARNING, "Failed to read message count!\n");
1214 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1215 ast_odbc_release_obj(obj);
1217 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1222 static int message_exists(char *dir, int msgnum)
1231 struct odbc_obj *obj;
1232 obj = ast_odbc_request_obj(odbc_database, 0);
1234 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1235 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1236 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1237 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1238 ast_odbc_release_obj(obj);
1241 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1242 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1243 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1244 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1245 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1246 ast_odbc_release_obj(obj);
1249 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1250 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1251 res = ast_odbc_smart_execute(obj, stmt);
1252 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1253 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1254 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1255 ast_odbc_release_obj(obj);
1258 res = SQLFetch(stmt);
1259 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1260 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1261 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1262 ast_odbc_release_obj(obj);
1265 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1266 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1267 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1268 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1269 ast_odbc_release_obj(obj);
1272 if (sscanf(rowdata, "%d", &x) != 1)
1273 ast_log(LOG_WARNING, "Failed to read message count!\n");
1274 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1275 ast_odbc_release_obj(obj);
1277 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1282 static int count_messages(struct ast_vm_user *vmu, char *dir)
1284 return last_message_index(vmu, dir) + 1;
1287 static void delete_file(char *sdir, int smsg)
1294 struct odbc_obj *obj;
1295 obj = ast_odbc_request_obj(odbc_database, 0);
1297 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1298 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1299 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1300 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1301 ast_odbc_release_obj(obj);
1304 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1305 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1306 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1307 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1308 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1309 ast_odbc_release_obj(obj);
1312 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1313 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1314 res = ast_odbc_smart_execute(obj, stmt);
1315 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1316 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1317 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1318 ast_odbc_release_obj(obj);
1321 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1322 ast_odbc_release_obj(obj);
1324 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1329 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1336 struct odbc_obj *obj;
1338 delete_file(ddir, dmsg);
1339 obj = ast_odbc_request_obj(odbc_database, 0);
1341 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1342 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1343 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1344 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1345 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1346 ast_odbc_release_obj(obj);
1349 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);
1350 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1351 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1352 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1353 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1354 ast_odbc_release_obj(obj);
1357 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1358 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1359 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1360 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1361 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1362 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1363 res = ast_odbc_smart_execute(obj, stmt);
1364 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1365 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1366 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1367 ast_odbc_release_obj(obj);
1370 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1371 ast_odbc_release_obj(obj);
1373 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1378 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1390 char full_fn[PATH_MAX];
1393 const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1394 const char *category = "";
1395 struct ast_config *cfg=NULL;
1396 struct odbc_obj *obj;
1398 delete_file(dir, msgnum);
1399 obj = ast_odbc_request_obj(odbc_database, 0);
1401 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1402 c = strchr(fmt, '|');
1405 if (!strcasecmp(fmt, "wav49"))
1407 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1409 make_file(fn, sizeof(fn), dir, msgnum);
1411 ast_copy_string(fn, dir, sizeof(fn));
1412 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1413 cfg = ast_config_load(full_fn);
1414 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1415 fd = open(full_fn, O_RDWR);
1417 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1418 ast_odbc_release_obj(obj);
1422 context = ast_variable_retrieve(cfg, "message", "context");
1423 if (!context) context = "";
1424 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1425 if (!macrocontext) macrocontext = "";
1426 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1427 if (!callerid) callerid = "";
1428 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1429 if (!origtime) origtime = "";
1430 duration = ast_variable_retrieve(cfg, "message", "duration");
1431 if (!duration) duration = "";
1432 category = ast_variable_retrieve(cfg, "message", "category");
1433 if (!category) category = "";
1435 fdlen = lseek(fd, 0, SEEK_END);
1436 lseek(fd, 0, SEEK_SET);
1437 printf("Length is %zd\n", fdlen);
1438 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1440 ast_log(LOG_WARNING, "Memory map failed!\n");
1441 ast_odbc_release_obj(obj);
1444 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1445 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1446 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1447 ast_odbc_release_obj(obj);
1450 if (!ast_strlen_zero(category))
1451 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1453 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1454 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1455 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1456 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1457 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1458 ast_odbc_release_obj(obj);
1461 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1462 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1463 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1464 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1465 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1466 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1467 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1468 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1469 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1470 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1471 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1472 if (!ast_strlen_zero(category))
1473 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1474 res = ast_odbc_smart_execute(obj, stmt);
1475 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1476 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1477 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1478 ast_odbc_release_obj(obj);
1481 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1482 ast_odbc_release_obj(obj);
1484 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1487 ast_config_destroy(cfg);
1495 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1502 struct odbc_obj *obj;
1504 delete_file(ddir, dmsg);
1505 obj = ast_odbc_request_obj(odbc_database, 0);
1507 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1508 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1509 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1510 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1511 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1512 ast_odbc_release_obj(obj);
1515 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1516 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1517 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1518 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1519 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1520 ast_odbc_release_obj(obj);
1523 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1524 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1525 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1526 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1527 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1528 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1529 res = ast_odbc_smart_execute(obj, stmt);
1530 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1531 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1532 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1533 ast_odbc_release_obj(obj);
1536 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1537 ast_odbc_release_obj(obj);
1539 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1545 #ifndef IMAP_STORAGE
1546 static int count_messages(struct ast_vm_user *vmu, char *dir)
1548 /* Find all .txt files - even if they are not in sequence from 0000 */
1552 struct dirent *vment = NULL;
1554 if (vm_lock_path(dir))
1555 return ERROR_LOCK_PATH;
1557 if ((vmdir = opendir(dir))) {
1558 while ((vment = readdir(vmdir))) {
1559 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1564 ast_unlock_path(dir);
1569 static void rename_file(char *sfn, char *dfn)
1571 char stxt[PATH_MAX];
1572 char dtxt[PATH_MAX];
1573 ast_filerename(sfn,dfn,NULL);
1574 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1575 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1579 static int copy(char *infile, char *outfile)
1587 #ifdef HARDLINK_WHEN_POSSIBLE
1588 /* Hard link if possible; saves disk space & is faster */
1589 if (link(infile, outfile)) {
1591 if ((ifd = open(infile, O_RDONLY)) < 0) {
1592 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1595 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1596 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1601 len = read(ifd, buf, sizeof(buf));
1603 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1609 res = write(ofd, buf, len);
1610 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1611 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1621 #ifdef HARDLINK_WHEN_POSSIBLE
1623 /* Hard link succeeded */
1629 static void copy_file(char *frompath, char *topath)
1631 char frompath2[PATH_MAX], topath2[PATH_MAX];
1632 ast_filecopy(frompath, topath, NULL);
1633 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1634 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1635 copy(frompath2, topath2);
1640 * A negative return value indicates an error.
1641 * \note Should always be called with a lock already set on dir.
1643 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1646 unsigned char map[MAXMSGLIMIT] = "";
1648 struct dirent *msgdirent;
1651 /* Reading the entire directory into a file map scales better than
1652 * doing a stat repeatedly on a predicted sequence. I suspect this
1653 * is partially due to stat(2) internally doing a readdir(2) itself to
1654 * find each file. */
1655 msgdir = opendir(dir);
1656 while ((msgdirent = readdir(msgdir))) {
1657 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1662 for (x = 0; x < vmu->maxmsg; x++) {
1670 static int vm_delete(char *file)
1675 txtsize = (strlen(file) + 5)*sizeof(char);
1676 txt = alloca(txtsize);
1677 /* Sprintf here would safe because we alloca'd exactly the right length,
1678 * but trying to eliminate all sprintf's anyhow
1680 snprintf(txt, txtsize, "%s.txt", file);
1682 return ast_filedelete(file, NULL);
1687 static int inbuf(struct baseio *bio, FILE *fi)
1694 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1708 static int inchar(struct baseio *bio, FILE *fi)
1710 if (bio->iocp>=bio->iolen) {
1711 if (!inbuf(bio, fi))
1715 return bio->iobuf[bio->iocp++];
1718 static int ochar(struct baseio *bio, int c, FILE *so)
1720 if (bio->linelength >= BASELINELEN) {
1721 if (fputs(eol,so) == EOF)
1727 if (putc(((unsigned char)c),so) == EOF)
1735 static int base_encode(char *filename, FILE *so)
1737 unsigned char dtable[BASEMAXINLINE];
1742 memset(&bio, 0, sizeof(bio));
1743 bio.iocp = BASEMAXINLINE;
1745 if (!(fi = fopen(filename, "rb"))) {
1746 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1750 for (i= 0; i<9; i++) {
1753 dtable[26+i]= 'a'+i;
1754 dtable[26+i+9]= 'j'+i;
1756 for (i= 0; i<8; i++) {
1757 dtable[i+18]= 'S'+i;
1758 dtable[26+i+18]= 's'+i;
1760 for (i= 0; i<10; i++) {
1761 dtable[52+i]= '0'+i;
1767 unsigned char igroup[3], ogroup[4];
1770 igroup[0]= igroup[1]= igroup[2]= 0;
1772 for (n= 0;n<3;n++) {
1773 if ((c = inchar(&bio, fi)) == EOF) {
1778 igroup[n]= (unsigned char)c;
1782 ogroup[0]= dtable[igroup[0]>>2];
1783 ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1784 ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1785 ogroup[3]= dtable[igroup[2]&0x3F];
1795 ochar(&bio, ogroup[i], so);
1799 if (fputs(eol,so) == EOF)
1807 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)
1810 /* Prepare variables for substitution in email body and subject */
1811 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1812 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1813 snprintf(passdata, passdatasize, "%d", msgnum);
1814 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1815 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1816 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1817 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1818 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1819 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1820 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1821 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1824 static char *quote(const char *from, char *to, size_t len)
1828 for (; ptr < to + len - 1; from++) {
1831 else if (*from == '\0')
1835 if (ptr < to + len - 1)
1842 * fill in *tm for current time according to the proper timezone, if any.
1843 * Return tm so it can be used as a function argument.
1845 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1847 const struct vm_zone *z = NULL;
1848 time_t t = time(NULL);
1850 /* Does this user have a timezone specified? */
1851 if (!ast_strlen_zero(vmu->zonetag)) {
1852 /* Find the zone in the list */
1853 AST_LIST_LOCK(&zones);
1854 AST_LIST_TRAVERSE(&zones, z, list) {
1855 if (!strcmp(z->name, vmu->zonetag))
1858 AST_LIST_UNLOCK(&zones);
1860 ast_localtime(&t, tm, z ? z->timezone : NULL);
1864 /*! \brief same as mkstemp, but return a FILE * */
1865 static FILE *vm_mkftemp(char *template)
1868 int pfd = mkstemp(template);
1870 p = fdopen(pfd, "w+");
1879 static void make_email_file(FILE *p, 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, struct ast_channel *chan, const char *category, int imap)
1882 char host[MAXHOSTNAMELEN] = "";
1890 size_t len_passdata;
1892 gethostname(host, sizeof(host)-1);
1893 if (strchr(srcemail, '@'))
1894 ast_copy_string(who, srcemail, sizeof(who));
1896 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1897 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1898 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1899 fprintf(p, "Date: %s\r\n", date);
1901 /* Set date format for voicemail mail */
1902 strftime(date, sizeof(date), emaildateformat, &tm);
1904 if (!ast_strlen_zero(fromstring)) {
1905 struct ast_channel *ast;
1906 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1908 int vmlen = strlen(fromstring)*3 + 200;
1909 if ((passdata = alloca(vmlen))) {
1910 memset(passdata, 0, vmlen);
1911 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1912 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1913 len_passdata = strlen(passdata) * 2 + 3;
1914 passdata2 = alloca(len_passdata);
1915 fprintf(p, "From: %s <%s>\r\n", quote(passdata, passdata2, len_passdata), who);
1917 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1918 ast_channel_free(ast);
1920 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1922 fprintf(p, "From: Asterisk PBX <%s>\r\n", who);
1923 len_passdata = strlen(vmu->fullname) * 2 + 3;
1924 passdata2 = alloca(len_passdata);
1925 fprintf(p, "To: %s <%s>\r\n", quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1926 if (!ast_strlen_zero(emailsubject)) {
1927 struct ast_channel *ast;
1928 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1930 int vmlen = strlen(emailsubject) * 3 + 200;
1931 if ((passdata = alloca(vmlen))) {
1932 memset(passdata, 0, vmlen);
1933 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1934 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1935 fprintf(p, "Subject: %s\r\n", passdata);
1937 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1938 ast_channel_free(ast);
1940 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1941 } else if (!ast_strlen_zero(emailtitle)) {
1942 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1944 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1945 fprintf(p, "Subject: New message %d in mailbox %s\r\n", msgnum + 1, mailbox);
1947 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\r\n", msgnum + 1, mailbox);
1948 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\r\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1950 /* additional information needed for IMAP searching */
1951 fprintf(p, "X-Asterisk-VM-Message-Num: %d\r\n", msgnum + 1);
1952 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\r\n", ext); */
1953 fprintf(p, "X-Asterisk-VM-Server-Name: %s\r\n", fromstring);
1954 fprintf(p, "X-Asterisk-VM-Context: %s\r\n", context);
1955 fprintf(p, "X-Asterisk-VM-Extension: %s\r\n", mailbox);
1956 fprintf(p, "X-Asterisk-VM-Priority: %d\r\n", chan->priority);
1957 fprintf(p, "X-Asterisk-VM-Caller-channel: %s\r\n", chan->name);
1958 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s\r\n", cidnum);
1959 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s\r\n", cidname);
1960 fprintf(p, "X-Asterisk-VM-Duration: %d\r\n", duration);
1961 if (!ast_strlen_zero(category))
1962 fprintf(p, "X-Asterisk-VM-Category: %s\r\n", category);
1963 fprintf(p, "X-Asterisk-VM-Orig-date: %s\r\n", date);
1964 fprintf(p, "X-Asterisk-VM-Orig-time: %ld\r\n", (long)time(NULL));
1966 if (!ast_strlen_zero(cidnum))
1967 fprintf(p, "X-Asterisk-CallerID: %s\r\n", cidnum);
1968 if (!ast_strlen_zero(cidname))
1969 fprintf(p, "X-Asterisk-CallerIDName: %s\r\n", cidname);
1970 fprintf(p, "MIME-Version: 1.0\r\n");
1971 if (attach_user_voicemail) {
1972 /* Something unique. */
1973 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1975 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\r\n\r\n\r\n", bound);
1977 fprintf(p, "--%s\r\n", bound);
1979 fprintf(p, "Content-Type: text/plain; charset=%s\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", charset);
1981 struct ast_channel *ast;
1982 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1984 int vmlen = strlen(emailbody)*3 + 200;
1985 if ((passdata = alloca(vmlen))) {
1986 memset(passdata, 0, vmlen);
1987 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1988 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1989 fprintf(p, "%s\r\n", passdata);
1991 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1992 ast_channel_free(ast);
1994 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1996 fprintf(p, "Dear %s:\r\n\r\n\tJust wanted to let you know you were just left a %s long message (number %d)\r\n"
1998 "in mailbox %s from %s, on %s so you might\r\n"
1999 "want to check it when you get a chance. Thanks!\r\n\r\n\t\t\t\t--Asterisk\r\n\r\n", vmu->fullname,
2000 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2002 if (attach_user_voicemail) {
2003 /* Eww. We want formats to tell us their own MIME type */
2004 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2005 char tmpdir[256], newtmp[256];
2008 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2009 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2010 tmpfd = mkstemp(newtmp);
2011 if (option_debug > 2)
2012 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
2013 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2014 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2015 ast_safe_system(tmpcmd);
2017 if (option_debug > 2)
2018 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2020 fprintf(p, "--%s\r\n", bound);
2021 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\r\n", ctype, format, msgnum, format);
2022 fprintf(p, "Content-Transfer-Encoding: base64\r\n");
2023 fprintf(p, "Content-Description: Voicemail sound attachment.\r\n");
2024 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\r\n\r\n", msgnum, format);
2025 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2026 base_encode(fname, p);
2027 /* only attach if necessary */
2028 if (imap && !strcmp(format, "gsm")) {
2029 fprintf(p, "--%s\r\n", bound);
2030 fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\r\n", msgnum, format);
2031 fprintf(p, "Content-Transfer-Encoding: base64\r\n");
2032 fprintf(p, "Content-Description: Voicemail sound attachment.\r\n");
2033 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\r\n\r\n", msgnum);
2034 snprintf(fname, sizeof(fname), "%s.gsm", attach);
2035 base_encode(fname, p);
2037 fprintf(p, "\r\n\r\n--%s--\r\n.\r\n", bound);
2044 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, struct ast_channel *chan, const char *category)
2047 char tmp[80] = "/tmp/astmail-XXXXXX";
2050 if (vmu && ast_strlen_zero(vmu->email)) {
2051 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
2054 if (!strcmp(format, "wav49"))
2056 if (option_debug > 2)
2057 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));
2058 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2060 if ((p = vm_mkftemp(tmp)) == NULL) {
2061 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2064 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2066 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2067 ast_safe_system(tmp2);
2069 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2074 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)
2077 char host[MAXHOSTNAMELEN] = "";
2080 char tmp[80] = "/tmp/astmail-XXXXXX";
2081 char tmp2[PATH_MAX];
2085 if ((p = vm_mkftemp(tmp)) == NULL) {
2086 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2089 gethostname(host, sizeof(host)-1);
2090 if (strchr(srcemail, '@'))
2091 ast_copy_string(who, srcemail, sizeof(who));
2093 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2094 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2095 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2096 fprintf(p, "Date: %s\n", date);
2098 if (*pagerfromstring) {
2099 struct ast_channel *ast;
2100 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2102 int vmlen = strlen(fromstring)*3 + 200;
2103 if ((passdata = alloca(vmlen))) {
2104 memset(passdata, 0, vmlen);
2105 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2106 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2107 fprintf(p, "From: %s <%s>\n", passdata, who);
2109 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2110 ast_channel_free(ast);
2112 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2114 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2115 fprintf(p, "To: %s\n", pager);
2117 struct ast_channel *ast;
2118 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2120 int vmlen = strlen(pagersubject) * 3 + 200;
2121 if ((passdata = alloca(vmlen))) {
2122 memset(passdata, 0, vmlen);
2123 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2124 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2125 fprintf(p, "Subject: %s\n\n", passdata);
2127 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2128 ast_channel_free(ast);
2130 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2132 fprintf(p, "Subject: New VM\n\n");
2134 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2136 struct ast_channel *ast;
2137 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2139 int vmlen = strlen(pagerbody)*3 + 200;
2140 if ((passdata = alloca(vmlen))) {
2141 memset(passdata, 0, vmlen);
2142 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2143 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2144 fprintf(p, "%s\n", passdata);
2146 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2147 ast_channel_free(ast);
2149 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2151 fprintf(p, "New %s long msg in box %s\n"
2152 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2155 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2156 ast_safe_system(tmp2);
2158 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2162 static int get_date(char *s, int len)
2167 localtime_r(&t,&tm);
2168 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2171 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2175 char dest[PATH_MAX];
2177 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2179 if (!(res = create_dirpath(dest, sizeof(dest), context, ext, "greet"))) {
2180 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2185 if (ast_fileexists(fn, NULL, NULL) > 0) {
2186 res = ast_stream_and_wait(chan, fn, ecodes);
2192 /* Dispose just in case */
2194 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2197 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2201 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2205 static void free_user(struct ast_vm_user *vmu)
2207 if (ast_test_flag(vmu, VM_ALLOCED))
2211 static void free_zone(struct vm_zone *z)
2216 static const char *mbox(int id)
2218 static const char *msgs[] = {
2230 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2234 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2241 char tmp[PATH_MAX] = "";
2242 struct odbc_obj *obj;
2250 /* If no mailbox, return immediately */
2251 if (ast_strlen_zero(mailbox))
2254 ast_copy_string(tmp, mailbox, sizeof(tmp));
2256 context = strchr(tmp, '@');
2261 context = "default";
2263 obj = ast_odbc_request_obj(odbc_database, 0);
2265 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2266 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2267 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2268 ast_odbc_release_obj(obj);
2271 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2272 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2273 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2274 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2275 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2276 ast_odbc_release_obj(obj);
2279 res = ast_odbc_smart_execute(obj, stmt);
2280 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2281 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2282 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2283 ast_odbc_release_obj(obj);
2286 res = SQLFetch(stmt);
2287 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2288 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2289 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2290 ast_odbc_release_obj(obj);
2293 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2294 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2295 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2296 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2297 ast_odbc_release_obj(obj);
2300 *newmsgs = atoi(rowdata);
2301 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2303 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2304 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2305 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2306 ast_odbc_release_obj(obj);
2309 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2310 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2311 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2312 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2313 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2314 ast_odbc_release_obj(obj);
2317 res = ast_odbc_smart_execute(obj, stmt);
2318 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2319 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2320 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2321 ast_odbc_release_obj(obj);
2324 res = SQLFetch(stmt);
2325 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2326 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2327 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2328 ast_odbc_release_obj(obj);
2331 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2332 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2333 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2334 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2335 ast_odbc_release_obj(obj);
2338 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2339 ast_odbc_release_obj(obj);
2340 *oldmsgs = atoi(rowdata);
2343 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2349 static int messagecount(const char *context, const char *mailbox, const char *folder)
2351 struct odbc_obj *obj = NULL;
2354 SQLHSTMT stmt = NULL;
2359 /* If no mailbox, return immediately */
2360 if (ast_strlen_zero(mailbox))
2363 obj = ast_odbc_request_obj(odbc_database, 0);
2365 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2366 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2367 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2370 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2371 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2372 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2373 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2374 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2377 res = ast_odbc_smart_execute(obj, stmt);
2378 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2379 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2380 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2383 res = SQLFetch(stmt);
2384 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2385 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2386 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2389 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2390 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2391 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2392 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2395 nummsgs = atoi(rowdata);
2396 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2398 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2402 ast_odbc_release_obj(obj);
2406 static int has_voicemail(const char *mailbox, const char *folder)
2408 char *context, tmp[256];
2409 ast_copy_string(tmp, mailbox, sizeof(tmp));
2410 if ((context = strchr(tmp, '@')))
2413 context = "default";
2415 if (messagecount(context, tmp, folder))
2421 #elif defined(IMAP_STORAGE)
2423 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms)
2425 char *myserveremail = serveremail;
2430 char tmp[80] = "/tmp/astmail-XXXXXX";
2435 /* Attach only the first format */
2436 fmt = ast_strdupa(fmt);
2438 strsep(&stringp, "|");
2440 if (!ast_strlen_zero(vmu->serveremail))
2441 myserveremail = vmu->serveremail;
2443 make_file(fn, sizeof(fn), dir, msgnum);
2445 if (ast_strlen_zero(vmu->email))
2446 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2448 if (!strcmp(fmt, "wav49"))
2450 if(option_debug > 2)
2451 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2453 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2455 if (!(p = vm_mkftemp(tmp))) {
2456 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2460 make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, fmt, duration, 1, chan, NULL, 1);
2461 /* read mail file to memory */
2464 if (!(buf = ast_malloc(len+1))) {
2465 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2468 fread(buf, len, 1, p);
2469 ((char *)buf)[len] = '\0';
2470 INIT(&str, mail_string, buf, len);
2471 imap_mailbox_name(mailbox, vms, 0, 1);
2472 if(!mail_append(vms->mailstream, mailbox, &str))
2473 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2477 if(option_debug > 2)
2478 ast_log(LOG_DEBUG, "%s stored\n", fn);
2483 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2488 struct ast_vm_user *vmu;
2489 struct vm_state *vms_p;
2502 if(option_debug > 2)
2503 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2505 /* If no mailbox, return immediately */
2506 if (ast_strlen_zero(mailbox))
2509 if (strchr(mailbox, ',')) {
2511 ast_copy_string(tmp, mailbox, sizeof(tmp));
2514 while((cur = strsep(&mb, ", "))) {
2515 if (!ast_strlen_zero(cur)) {
2516 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2529 ast_copy_string(tmp, mailbox, sizeof(tmp));
2531 if ((context = strchr(tmp, '@'))) {
2536 context = "default";
2537 mailboxnc = (char *)mailbox;
2540 /* We have to get the user before we can open the stream! */
2541 /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2542 if (!(vmu = find_user(NULL, context, mailboxnc))) {
2543 ast_log(LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2547 /* No IMAP account available */
2548 if (vmu->imapuser[0] == '\0') {
2549 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2553 /* check if someone is accessing this box right now... */
2554 if ((vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1)) || (vms_p = get_vm_state_by_mailbox(mailboxnc, 1))) {
2555 if(option_debug > 2)
2556 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2557 *newmsgs = vms_p->newmessages;
2558 *oldmsgs = vms_p->oldmessages;
2562 /* add one if not there... */
2563 if (!(vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0)) && !(vms_p = get_vm_state_by_mailbox(mailboxnc, 0))) {
2564 if(option_debug > 2)
2565 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2566 if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
2568 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2569 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2570 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2571 if(option_debug > 2)
2572 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2574 /* set mailbox to INBOX! */
2575 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2576 init_vm_state(vms_p);
2577 vmstate_insert(vms_p);
2580 /* If no mailstream exists yet and even after attempting to initialize it fails, bail out */
2581 if (!vms_p->mailstream) {
2582 ret = init_mailstream(vms_p, 0);
2583 if (!vms_p->mailstream) {
2584 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2589 if (!ret && vms_p->updated == 1) {
2591 pgm = mail_newsearchpgm();
2592 hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (char *)mailboxnc);
2598 vms_p->vmArrayIndex = 0;
2599 mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2600 *newmsgs = vms_p->vmArrayIndex;
2601 vms_p->newmessages = vms_p->vmArrayIndex;
2602 mail_free_searchpgm(&pgm);
2605 pgm = mail_newsearchpgm ();
2606 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2612 vms_p->vmArrayIndex = 0;
2613 mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2614 *oldmsgs = vms_p->vmArrayIndex;
2615 vms_p->oldmessages = vms_p->vmArrayIndex;
2616 mail_free_searchpgm(&pgm);
2620 if (vms_p->updated == 1) { /* changes, so we did the searches above */
2622 } else if (vms_p->updated > 1) { /* decrement delay count */
2624 } else { /* no changes, so don't search */
2625 mail_ping(vms_p->mailstream);
2626 /* Keep the old data */
2627 *newmsgs = vms_p->newmessages;
2628 *oldmsgs = vms_p->oldmessages;
2634 static int has_voicemail(const char *mailbox, const char *folder)
2636 int newmsgs, oldmsgs;
2638 if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2639 return folder? oldmsgs: newmsgs;
2644 static int messagecount(const char *context, const char *mailbox, const char *folder)
2646 int newmsgs, oldmsgs;
2649 if (ast_strlen_zero(mailbox))
2651 sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2653 if(inboxcount(tmp, &newmsgs, &oldmsgs))
2654 return folder? oldmsgs: newmsgs;
2660 #ifndef IMAP_STORAGE
2661 /* copy message only used by file storage */
2662 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, char *dir)
2664 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2665 const char *frombox = mbox(imbox);
2668 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2670 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2673 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2675 ast_copy_string(fromdir, dir, sizeof(fromdir));
2677 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2678 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2680 if (vm_lock_path(todir))
2681 return ERROR_LOCK_PATH;
2683 recipmsgnum = last_message_index(recip, todir) + 1;
2684 if (recipmsgnum < recip->maxmsg) {
2685 make_file(topath, sizeof(topath), todir, recipmsgnum);
2686 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2688 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2690 ast_unlock_path(todir);
2691 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2696 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2697 static int messagecount(const char *context, const char *mailbox, const char *folder)
2699 return __has_voicemail(context, mailbox, folder, 0);
2703 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2710 /* If no mailbox, return immediately */
2711 if (ast_strlen_zero(mailbox))
2714 if (ast_strlen_zero(folder))
2716 if (ast_strlen_zero(context))
2717 context = "default";
2719 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2721 if (!(dir = opendir(fn)))
2724 while ((de = readdir(dir))) {
2725 if (!strncasecmp(de->d_name, "msg", 3)) {
2729 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2740 static int has_voicemail(const char *mailbox, const char *folder)
2742 char tmp[256], *tmp2 = tmp, *mbox, *context;
2743 ast_copy_string(tmp, mailbox, sizeof(tmp));
2744 while ((mbox = strsep(&tmp2, ","))) {
2745 if ((context = strchr(mbox, '@')))
2748 context = "default";
2749 if (__has_voicemail(context, mbox, folder, 1))
2756 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2761 /* If no mailbox, return immediately */
2762 if (ast_strlen_zero(mailbox))
2770 if (strchr(mailbox, ',')) {
2774 ast_copy_string(tmp, mailbox, sizeof(tmp));
2776 while ((cur = strsep(&mb, ", "))) {
2777 if (!ast_strlen_zero(cur)) {
2778 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2791 ast_copy_string(tmp, mailbox, sizeof(tmp));
2793 if ((context = strchr(tmp, '@')))
2796 context = "default";
2799 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2801 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2808 static void run_externnotify(char *context, char *extension)
2810 char arguments[255];
2811 char ext_context[256] = "";
2812 int newvoicemails = 0, oldvoicemails = 0;
2813 struct ast_smdi_mwi_message *mwi_msg;
2815 if (!ast_strlen_zero(context))
2816 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2818 ast_copy_string(ext_context, extension, sizeof(ext_context));
2821 if (ast_app_has_voicemail(ext_context, NULL))
2822 ast_smdi_mwi_set(smdi_iface, extension);
2824 ast_smdi_mwi_unset(smdi_iface, extension);
2826 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2827 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2828 if (!strncmp(mwi_msg->cause, "INV", 3))
2829 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2830 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2831 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2832 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2833 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2836 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2840 if (!ast_strlen_zero(externnotify)) {
2841 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2842 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2844 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2846 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2847 ast_safe_system(arguments);
2852 struct leave_vm_options {
2854 signed char record_gain;
2857 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2860 int newmsgs, oldmsgs;
2861 struct vm_state *vms = NULL;
2863 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2874 char dir[PATH_MAX], tmpdir[PATH_MAX];
2875 char dest[PATH_MAX];
2877 char prefile[PATH_MAX] = "";
2878 char tempfile[PATH_MAX] = "";
2879 char ext_context[256] = "";
2882 char ecodes[16] = "#";
2883 char tmp[256] = "", *tmpptr;
2884 struct ast_vm_user *vmu;
2885 struct ast_vm_user svm;
2886 const char *category = NULL;
2888 ast_copy_string(tmp, ext, sizeof(tmp));
2890 if ((context = strchr(tmp, '@'))) {
2892 tmpptr = strchr(context, '&');
2894 tmpptr = strchr(ext, '&');
2900 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2902 if(option_debug > 2)
2903 ast_log(LOG_DEBUG, "Before find_user\n");
2904 if (!(vmu = find_user(&svm, context, ext))) {
2905 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2906 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2907 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2908 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2911 /* Setup pre-file if appropriate */
2912 if (strcmp(vmu->context, "default"))
2913 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2915 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2916 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
2917 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
2918 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2919 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
2920 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
2921 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2923 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2924 if (!(res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
2925 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
2928 RETRIEVE(tempfile, -1);
2929 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2930 ast_copy_string(prefile, tempfile, sizeof(prefile));
2931 DISPOSE(tempfile, -1);
2932 /* It's easier just to try to make it than to check for its existence */
2933 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2934 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2936 /* Check current or macro-calling context for special extensions */
2937 if (ast_test_flag(vmu, VM_OPERATOR)) {
2938 if (!ast_strlen_zero(vmu->exit)) {
2939 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2940 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2943 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2944 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2947 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2948 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2953 if (!ast_strlen_zero(vmu->exit)) {
2954 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2955 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2956 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2957 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2958 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2959 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2963 /* Play the beginning intro if desired */
2964 if (!ast_strlen_zero(prefile)) {
2965 RETRIEVE(prefile, -1);
2966 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2967 if (ast_streamfile(chan, prefile, chan->language) > -1)
2968 res = ast_waitstream(chan, ecodes);
2971 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2972 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2974 DISPOSE(prefile, -1);
2977 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2979 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2984 /* On a '#' we skip the instructions */
2985 ast_set_flag(options, OPT_SILENT);
2988 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2989 res = ast_stream_and_wait(chan, INTRO, ecodes);
2991 ast_set_flag(options, OPT_SILENT);
2996 ast_stopstream(chan);
2997 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2998 other than the operator -- an automated attendant or mailbox login for example */
3000 chan->exten[0] = 'a';
3001 chan->exten[1] = '\0';
3002 if (!ast_strlen_zero(vmu->exit)) {
3003 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3004 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3005 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3009 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3013 /* Check for a '0' here */
3016 if (ouseexten || ousemacro) {
3017 chan->exten[0] = 'o';
3018 chan->exten[1] = '\0';
3019 if (!ast_strlen_zero(vmu->exit)) {
3020 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3021 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3022 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3024 ast_play_and_wait(chan, "transfer");
3027 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3033 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3036 /* The meat of recording the message... All the announcements and beeps have been played*/
3037 ast_copy_string(fmt, vmfmts, sizeof(fmt));
3038 if (!ast_strlen_zero(fmt)) {
3042 /* Is ext a mailbox? */
3043 /* must open stream for this user to get info! */
3044 vms = get_vm_state_by_mailbox(ext,0);
3046 if(option_debug > 2)
3047 ast_log(LOG_DEBUG, "Using&n