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>
25 * \extref Unixodbc - http://www.unixodbc.org
26 * \extref A source distribution of University of Washington's IMAP
27 c-client (http://www.washington.edu/imap/
31 * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
32 * \ingroup applications
33 * \note This module requires res_adsi to load. This needs to be optional
38 * \note This file is now almost impossible to work with, due to all \#ifdefs.
39 * Feels like the database code before realtime. Someone - please come up
40 * with a plan to clean this up.
44 <depend>res_smdi</depend>
48 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o">
49 <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
50 <depend>unixodbc</depend>
52 <conflict>IMAP_STORAGE</conflict>
53 <defaultenabled>no</defaultenabled>
55 <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
56 <depend>imap_tk</depend>
57 <conflict>ODBC_STORAGE</conflict>
59 <defaultenabled>no</defaultenabled>
66 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
76 #include <sys/types.h>
85 #ifdef USE_SYSTEM_IMAP
86 #include <imap/c-client.h>
87 #include <imap/imap4r1.h>
88 #include <imap/linkage.h>
96 #include "asterisk/lock.h"
97 #include "asterisk/file.h"
98 #include "asterisk/logger.h"
99 #include "asterisk/channel.h"
100 #include "asterisk/pbx.h"
101 #include "asterisk/options.h"
102 #include "asterisk/config.h"
103 #include "asterisk/say.h"
104 #include "asterisk/module.h"
105 #include "asterisk/adsi.h"
106 #include "asterisk/app.h"
107 #include "asterisk/manager.h"
108 #include "asterisk/dsp.h"
109 #include "asterisk/localtime.h"
110 #include "asterisk/cli.h"
111 #include "asterisk/utils.h"
112 #include "asterisk/stringfields.h"
113 #include "asterisk/smdi.h"
114 #include "asterisk/event.h"
117 #include "asterisk/res_odbc.h"
121 static char imapserver[48];
122 static char imapport[8];
123 static char imapflags[128];
124 static char imapfolder[64];
125 static char greetingfolder[64];
126 static char authuser[32];
127 static char authpassword[42];
129 static int expungeonhangup = 1;
130 static int imapgreetings = 0;
131 AST_MUTEX_DEFINE_STATIC(delimiter_lock);
132 static char delimiter = '\0';
137 /* Forward declarations for IMAP */
138 static int init_mailstream(struct vm_state *vms, int box);
139 static void write_file(char *filename, char *buffer, unsigned long len);
140 static void display_body(BODY *body, char *pfx, long i);
141 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
142 static void vm_imap_delete(int msgnum, struct vm_state *vms);
143 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
144 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
145 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
146 static void vmstate_insert(struct vm_state *vms);
147 static void vmstate_delete(struct vm_state *vms);
148 static void set_update(MAILSTREAM * stream);
149 static void init_vm_state(struct vm_state *vms);
150 static void check_msgArray(struct vm_state *vms);
151 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
152 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
153 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
154 static void get_mailbox_delimiter(MAILSTREAM *stream);
155 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
156 static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int target);
157 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);
158 static void update_messages_by_imapuser(const char *user, unsigned long number);
160 static int imap_remove_file (char *dir, int msgnum);
161 static int imap_retrieve_file (char *dir, int msgnum, char *mailbox, char *context);
162 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
164 struct vm_state *vms;
165 AST_LIST_ENTRY(vmstate) list;
168 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
172 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
174 #define COMMAND_TIMEOUT 5000
175 /* Don't modify these here; set your umask at runtime instead */
176 #define VOICEMAIL_DIR_MODE 0777
177 #define VOICEMAIL_FILE_MODE 0666
178 #define CHUNKSIZE 65536
180 #define VOICEMAIL_CONFIG "voicemail.conf"
181 #define ASTERISK_USERNAME "asterisk"
183 /* Define fast-forward, pause, restart, and reverse keys
184 while listening to a voicemail message - these are
185 strings, not characters */
186 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
187 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
188 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
189 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
190 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
191 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
193 /* Default mail command to mail voicemail. Change it with the
194 mailcmd= command in voicemail.conf */
195 #define SENDMAIL "/usr/sbin/sendmail -t"
197 #define INTRO "vm-intro"
200 #define MAXMSGLIMIT 9999
202 #define BASEMAXINLINE 256
203 #define BASELINELEN 72
204 #define BASEMAXINLINE 256
207 #define MAX_DATETIME_FORMAT 512
208 #define MAX_NUM_CID_CONTEXTS 10
210 #define VM_REVIEW (1 << 0)
211 #define VM_OPERATOR (1 << 1)
212 #define VM_SAYCID (1 << 2)
213 #define VM_SVMAIL (1 << 3)
214 #define VM_ENVELOPE (1 << 4)
215 #define VM_SAYDURATION (1 << 5)
216 #define VM_SKIPAFTERCMD (1 << 6)
217 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
218 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
219 #define VM_PBXSKIP (1 << 9)
220 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
221 #define VM_ATTACH (1 << 11)
222 #define VM_DELETE (1 << 12)
223 #define VM_ALLOCED (1 << 13)
224 #define VM_SEARCH (1 << 14)
225 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
226 #define ERROR_LOCK_PATH -100
239 OPT_SILENT = (1 << 0),
240 OPT_BUSY_GREETING = (1 << 1),
241 OPT_UNAVAIL_GREETING = (1 << 2),
242 OPT_RECORDGAIN = (1 << 3),
243 OPT_PREPEND_MAILBOX = (1 << 4),
244 OPT_AUTOPLAY = (1 << 6),
248 OPT_ARG_RECORDGAIN = 0,
249 OPT_ARG_PLAYFOLDER = 1,
250 /* This *must* be the last value in this enum! */
251 OPT_ARG_ARRAY_SIZE = 2,
254 AST_APP_OPTIONS(vm_app_options, {
255 AST_APP_OPTION('s', OPT_SILENT),
256 AST_APP_OPTION('b', OPT_BUSY_GREETING),
257 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
258 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
259 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
260 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
263 static int load_config(void);
265 /*! \page vmlang Voicemail Language Syntaxes Supported
267 \par Syntaxes supported, not really language codes.
274 \arg \b pt - Portuguese
275 \arg \b pt_BR - Portuguese (Brazil)
277 \arg \b no - Norwegian
279 \arg \b tw - Chinese (Taiwan)
281 German requires the following additional soundfile:
282 \arg \b 1F einE (feminine)
284 Spanish requires the following additional soundfile:
285 \arg \b 1M un (masculine)
287 Dutch, Portuguese & Spanish require the following additional soundfiles:
288 \arg \b vm-INBOXs singular of 'new'
289 \arg \b vm-Olds singular of 'old/heard/read'
292 \arg \b vm-INBOX nieuwe (nl)
293 \arg \b vm-Old oude (nl)
296 \arg \b vm-new-a 'new', feminine singular accusative
297 \arg \b vm-new-e 'new', feminine plural accusative
298 \arg \b vm-new-ych 'new', feminine plural genitive
299 \arg \b vm-old-a 'old', feminine singular accusative
300 \arg \b vm-old-e 'old', feminine plural accusative
301 \arg \b vm-old-ych 'old', feminine plural genitive
302 \arg \b digits/1-a 'one', not always same as 'digits/1'
303 \arg \b digits/2-ie 'two', not always same as 'digits/2'
306 \arg \b vm-nytt singular of 'new'
307 \arg \b vm-nya plural of 'new'
308 \arg \b vm-gammalt singular of 'old'
309 \arg \b vm-gamla plural of 'old'
310 \arg \b digits/ett 'one', not always same as 'digits/1'
313 \arg \b vm-ny singular of 'new'
314 \arg \b vm-nye plural of 'new'
315 \arg \b vm-gammel singular of 'old'
316 \arg \b vm-gamle plural of 'old'
324 Italian requires the following additional soundfile:
328 \arg \b vm-nuovi new plural
329 \arg \b vm-vecchio old
330 \arg \b vm-vecchi old plural
332 Chinese (Taiwan) requires the following additional soundfile:
333 \arg \b vm-tong A class-word for call (tong1)
334 \arg \b vm-ri A class-word for day (ri4)
335 \arg \b vm-you You (ni3)
336 \arg \b vm-haveno Have no (mei2 you3)
337 \arg \b vm-have Have (you3)
338 \arg \b vm-listen To listen (yao4 ting1)
341 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
342 spelled among others when you have to change folder. For the above reasons, vm-INBOX
343 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
352 unsigned char iobuf[BASEMAXINLINE];
355 /*! Structure for linked list of users
356 * Use ast_vm_user_destroy() to free one of these structures. */
358 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
359 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
360 char password[80]; /*!< Secret pin code, numbers only */
361 char fullname[80]; /*!< Full name, for directory app */
362 char email[80]; /*!< E-mail address */
363 char pager[80]; /*!< E-mail address to pager (no attachment) */
364 char serveremail[80]; /*!< From: Mail address */
365 char mailcmd[160]; /*!< Configurable mail command */
366 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
367 char zonetag[80]; /*!< Time zone */
370 char uniqueid[20]; /*!< Unique integer identifier */
372 char attachfmt[20]; /*!< Attachment format */
373 unsigned int flags; /*!< VM_ flags */
375 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
376 int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
378 char imapuser[80]; /*!< IMAP server login */
379 char imappassword[80]; /*!< IMAP server password if authpassword not defined */
381 double volgain; /*!< Volume gain for voicemails sent via email */
382 AST_LIST_ENTRY(ast_vm_user) list;
385 /*! Voicemail time zones */
387 AST_LIST_ENTRY(vm_zone) list;
390 char msg_format[512];
393 /*! Voicemail mailbox state */
397 char curdir[PATH_MAX];
398 char vmbox[PATH_MAX];
410 int updated; /*!< decremented on each mail check until 1 -allows delay */
412 MAILSTREAM *mailstream;
414 char imapuser[80]; /*!< IMAP server login */
416 unsigned int quota_limit;
417 unsigned int quota_usage;
418 struct vm_state *persist_vms;
424 static char odbc_database[80];
425 static char odbc_table[80];
426 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
427 #define DISPOSE(a,b) remove_file(a,b)
428 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
429 #define EXISTS(a,b,c,d) (message_exists(a,b))
430 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
431 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
432 #define DELETE(a,b,c) (delete_file(a,b))
435 #define RETRIEVE(a,b,c,d) (imap_retrieve_file(a,b,c,d ))
436 #define DISPOSE(a,b) (imap_remove_file(a,b))
437 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
438 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
439 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
440 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
441 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
442 #define DELETE(a,b,c) (vm_delete(c))
444 #define RETRIEVE(a,b,c,d)
446 #define STORE(a,b,c,d,e,f,g,h,i)
447 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
448 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
449 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
450 #define DELETE(a,b,c) (vm_delete(c))
454 static char VM_SPOOL_DIR[PATH_MAX];
456 static char ext_pass_cmd[128];
460 #define PWDCHANGE_INTERNAL (1 << 1)
461 #define PWDCHANGE_EXTERNAL (1 << 2)
462 static int pwdchange = PWDCHANGE_INTERNAL;
465 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
468 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
470 # define tdesc "Comedian Mail (Voicemail System)"
474 static char userscontext[AST_MAX_EXTENSION] = "default";
476 static char *addesc = "Comedian Mail";
478 static char *synopsis_vm = "Leave a Voicemail message";
480 static char *descrip_vm =
481 " VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
482 "application allows the calling party to leave a message for the specified\n"
483 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
484 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
485 "specified mailbox does not exist.\n"
486 " The Voicemail application will exit if any of the following DTMF digits are\n"
488 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
489 " * - Jump to the 'a' extension in the current dialplan context.\n"
490 " This application will set the following channel variable upon completion:\n"
491 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
492 " application. The possible values are:\n"
493 " SUCCESS | USEREXIT | FAILED\n\n"
495 " b - Play the 'busy' greeting to the calling party.\n"
496 " g(#) - Use the specified amount of gain when recording the voicemail\n"
497 " message. The units are whole-number decibels (dB).\n"
498 " s - Skip the playback of instructions for leaving a message to the\n"
500 " u - Play the 'unavailable' greeting.\n";
502 static char *synopsis_vmain = "Check Voicemail messages";
504 static char *descrip_vmain =
505 " VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
506 "calling party to check voicemail messages. A specific mailbox, and optional\n"
507 "corresponding context, may be specified. If a mailbox is not provided, the\n"
508 "calling party will be prompted to enter one. If a context is not specified,\n"
509 "the 'default' context will be used.\n\n"
511 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
512 " is entered by the caller.\n"
513 " g(#) - Use the specified amount of gain when recording a voicemail\n"
514 " message. The units are whole-number decibels (dB).\n"
515 " s - Skip checking the passcode for the mailbox.\n"
516 " a(#) - Skip folder prompt and go directly to folder specified.\n"
517 " Defaults to INBOX\n";
519 static char *synopsis_vm_box_exists =
520 "Check to see if Voicemail mailbox exists";
522 static char *descrip_vm_box_exists =
523 " MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
524 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
526 " This application will set the following channel variable upon completion:\n"
527 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
528 " MailboxExists application. Possible values include:\n"
529 " SUCCESS | FAILED\n\n"
530 " Options: (none)\n";
532 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
534 static char *descrip_vmauthenticate =
535 " VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
536 "same way as the Authenticate application, but the passwords are taken from\n"
538 " If the mailbox is specified, only that mailbox's password will be considered\n"
539 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
540 "be set with the authenticated mailbox.\n\n"
542 " s - Skip playing the initial prompts.\n";
544 /* Leave a message */
545 static char *app = "VoiceMail";
547 /* Check mail, control, etc */
548 static char *app2 = "VoiceMailMain";
550 static char *app3 = "MailboxExists";
551 static char *app4 = "VMAuthenticate";
553 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
554 static AST_LIST_HEAD_STATIC(zones, vm_zone);
555 static int maxsilence;
557 static int silencethreshold = 128;
558 static char serveremail[80];
559 static char mailcmd[160]; /* Configurable mail cmd */
560 static char externnotify[160];
561 static struct ast_smdi_interface *smdi_iface = NULL;
562 static char vmfmts[80];
563 static double volgain;
564 static int vmminsecs;
565 static int vmmaxsecs;
568 static int maxlogins;
570 /*! Poll mailboxes for changes since there is something external to
571 * app_voicemail that may change them. */
572 static unsigned int poll_mailboxes;
574 /*! Polling frequency */
575 static unsigned int poll_freq;
576 /*! By default, poll every 30 seconds */
577 #define DEFAULT_POLL_FREQ 30
579 AST_MUTEX_DEFINE_STATIC(poll_lock);
580 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
581 static pthread_t poll_thread = AST_PTHREADT_NULL;
582 static unsigned char poll_thread_run;
584 /*! Subscription to ... MWI event subscriptions */
585 static struct ast_event_sub *mwi_sub_sub;
586 /*! Subscription to ... MWI event un-subscriptions */
587 static struct ast_event_sub *mwi_unsub_sub;
590 * \brief An MWI subscription
592 * This is so we can keep track of which mailboxes are subscribed to.
593 * This way, we know which mailboxes to poll when the pollmailboxes
594 * option is being used.
597 AST_RWLIST_ENTRY(mwi_sub) entry;
604 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
606 /* custom audio control prompts for voicemail playback */
607 static char listen_control_forward_key[12];
608 static char listen_control_reverse_key[12];
609 static char listen_control_pause_key[12];
610 static char listen_control_restart_key[12];
611 static char listen_control_stop_key[12];
613 /* custom password sounds */
614 static char vm_password[80] = "vm-password";
615 static char vm_newpassword[80] = "vm-newpassword";
616 static char vm_passchanged[80] = "vm-passchanged";
617 static char vm_reenterpassword[80] = "vm-reenterpassword";
618 static char vm_mismatch[80] = "vm-mismatch";
620 static struct ast_flags globalflags = {0};
622 static int saydurationminfo;
624 static char dialcontext[AST_MAX_CONTEXT] = "";
625 static char callcontext[AST_MAX_CONTEXT] = "";
626 static char exitcontext[AST_MAX_CONTEXT] = "";
628 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
631 static char *emailbody = NULL;
632 static char *emailsubject = NULL;
633 static char *pagerbody = NULL;
634 static char *pagersubject = NULL;
635 static char fromstring[100];
636 static char pagerfromstring[100];
637 static char emailtitle[100];
638 static char charset[32] = "ISO-8859-1";
640 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
641 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
642 static int adsiver = 1;
643 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
645 /* Forward declarations - generic */
646 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
647 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);
648 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
649 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
650 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
651 signed char record_gain, struct vm_state *vms);
652 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
653 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
654 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
655 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);
656 static void apply_options(struct ast_vm_user *vmu, const char *options);
657 static int is_valid_dtmf(const char *key);
659 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
660 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
665 static void populate_defaults(struct ast_vm_user *vmu)
667 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
668 if (saydurationminfo)
669 vmu->saydurationm = saydurationminfo;
670 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
671 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
672 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
674 vmu->maxsecs = vmmaxsecs;
676 vmu->maxmsg = maxmsg;
677 vmu->volgain = volgain;
680 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
683 if (!strcasecmp(var, "attach")) {
684 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
685 } else if (!strcasecmp(var, "attachfmt")) {
686 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
687 } else if (!strcasecmp(var, "serveremail")) {
688 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
689 } else if (!strcasecmp(var, "language")) {
690 ast_copy_string(vmu->language, value, sizeof(vmu->language));
691 } else if (!strcasecmp(var, "tz")) {
692 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
694 } else if (!strcasecmp(var, "imapuser")) {
695 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
696 } else if (!strcasecmp(var, "imappassword")) {
697 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
699 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
700 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
701 } else if (!strcasecmp(var, "saycid")){
702 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
703 } else if (!strcasecmp(var,"sendvoicemail")){
704 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
705 } else if (!strcasecmp(var, "review")){
706 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
707 } else if (!strcasecmp(var, "tempgreetwarn")){
708 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
709 } else if (!strcasecmp(var, "operator")){
710 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
711 } else if (!strcasecmp(var, "envelope")){
712 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
713 } else if (!strcasecmp(var, "sayduration")){
714 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
715 } else if (!strcasecmp(var, "saydurationm")){
716 if (sscanf(value, "%d", &x) == 1) {
717 vmu->saydurationm = x;
719 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
721 } else if (!strcasecmp(var, "forcename")){
722 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
723 } else if (!strcasecmp(var, "forcegreetings")){
724 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
725 } else if (!strcasecmp(var, "callback")) {
726 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
727 } else if (!strcasecmp(var, "dialout")) {
728 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
729 } else if (!strcasecmp(var, "exitcontext")) {
730 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
731 } else if (!strcasecmp(var, "maxmessage")) {
732 if (vmu->maxsecs <= 0) {
733 ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %i\n", value, vmmaxsecs);
734 vmu->maxsecs = vmmaxsecs;
736 vmu->maxsecs = atoi(value);
738 } else if (!strcasecmp(var, "maxmsg")) {
739 vmu->maxmsg = atoi(value);
740 if (vmu->maxmsg <= 0) {
741 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
742 vmu->maxmsg = MAXMSG;
743 } else if (vmu->maxmsg > MAXMSGLIMIT) {
744 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
745 vmu->maxmsg = MAXMSGLIMIT;
747 } else if (!strcasecmp(var, "volgain")) {
748 sscanf(value, "%lf", &vmu->volgain);
749 } else if (!strcasecmp(var, "options")) {
750 apply_options(vmu, value);
754 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
757 if (!ast_strlen_zero(vmu->uniqueid)) {
758 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
760 ast_copy_string(vmu->password, password, sizeof(vmu->password));
770 static void apply_options(struct ast_vm_user *vmu, const char *options)
771 { /* Destructively Parse options and apply */
775 stringp = ast_strdupa(options);
776 while ((s = strsep(&stringp, "|"))) {
778 if ((var = strsep(&value, "=")) && value) {
779 apply_option(vmu, var, value);
784 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
786 struct ast_variable *tmp;
789 if (!strcasecmp(tmp->name, "vmsecret")) {
790 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
791 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
792 if (ast_strlen_zero(retval->password))
793 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
794 } else if (!strcasecmp(tmp->name, "uniqueid")) {
795 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
796 } else if (!strcasecmp(tmp->name, "pager")) {
797 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
798 } else if (!strcasecmp(tmp->name, "email")) {
799 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
800 } else if (!strcasecmp(tmp->name, "fullname")) {
801 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
802 } else if (!strcasecmp(tmp->name, "context")) {
803 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
805 } else if (!strcasecmp(tmp->name, "imapuser")) {
806 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
807 } else if (!strcasecmp(tmp->name, "imappassword")) {
808 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
811 apply_option(retval, tmp->name, tmp->value);
816 static int is_valid_dtmf(const char *key)
819 char *local_key = ast_strdupa(key);
821 for(i = 0; i < strlen(key); ++i) {
822 if(!strchr(VALID_DTMF, *local_key)) {
823 ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
831 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
833 struct ast_variable *var;
834 struct ast_vm_user *retval;
836 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
838 ast_set_flag(retval, VM_ALLOCED);
840 memset(retval, 0, sizeof(*retval));
842 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
843 populate_defaults(retval);
844 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
845 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
847 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
849 apply_options_full(retval, var);
850 ast_variables_destroy(var);
860 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
862 /* This function could be made to generate one from a database, too */
863 struct ast_vm_user *vmu=NULL, *cur;
864 AST_LIST_LOCK(&users);
866 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
869 AST_LIST_TRAVERSE(&users, cur, list) {
870 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
872 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
876 /* Make a copy, so that on a reload, we have no race */
877 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
878 memcpy(vmu, cur, sizeof(*vmu));
879 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
880 AST_LIST_NEXT(vmu, list) = NULL;
883 vmu = find_user_realtime(ivm, context, mailbox);
884 AST_LIST_UNLOCK(&users);
888 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
890 /* This function could be made to generate one from a database, too */
891 struct ast_vm_user *cur;
893 AST_LIST_LOCK(&users);
894 AST_LIST_TRAVERSE(&users, cur, list) {
895 if ((!context || !strcasecmp(context, cur->context)) &&
896 (!strcasecmp(mailbox, cur->mailbox)))
900 ast_copy_string(cur->password, newpass, sizeof(cur->password));
903 AST_LIST_UNLOCK(&users);
907 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
909 struct ast_config *cfg=NULL;
910 struct ast_variable *var=NULL;
911 struct ast_category *cat=NULL;
912 char *category=NULL, *value=NULL, *new=NULL;
913 const char *tmp=NULL;
915 if (!change_password_realtime(vmu, newpassword))
918 /* check voicemail.conf */
919 if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
920 while ((category = ast_category_browse(cfg, category))) {
921 if (!strcasecmp(category, vmu->context)) {
922 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
923 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
926 value = strstr(tmp,",");
928 ast_log(LOG_WARNING, "variable has bad format.\n");
931 new = alloca((strlen(value)+strlen(newpassword)+1));
932 sprintf(new,"%s%s", newpassword, value);
933 if (!(cat = ast_category_get(cfg, category))) {
934 ast_log(LOG_WARNING, "Failed to get category structure.\n");
937 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
940 /* save the results */
941 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
942 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
943 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
947 /* check users.conf and update the password stored for the mailbox*/
948 /* if no vmsecret entry exists create one. */
949 if ((cfg = ast_config_load_with_comments("users.conf"))) {
950 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
951 while ((category = ast_category_browse(cfg, category))) {
952 ast_debug(4, "users.conf: %s\n", category);
953 if (!strcasecmp(category, vmu->mailbox)) {
954 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
955 ast_debug(3, "looks like we need to make vmsecret!\n");
956 var = ast_variable_new("vmsecret", newpassword);
958 new = alloca(strlen(newpassword)+1);
959 sprintf(new, "%s", newpassword);
960 if (!(cat = ast_category_get(cfg, category))) {
961 ast_debug(4, "failed to get category!\n");
965 ast_variable_update(cat, "vmsecret", new, NULL, 0);
967 ast_variable_append(cat, var);
970 /* save the results and clean things up */
971 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
972 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
973 config_text_file_save("users.conf", cfg, "AppVoicemail");
977 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
980 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
981 if (!ast_safe_system(buf))
982 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
985 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
987 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
991 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
994 if ((res = ast_mkdir(dir, 01777))) {
995 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
996 return sprintf(dest, "%s/msg%04d", dir, num);
998 /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
999 return sprintf(dest, "%s/msg%04d", dir, num);
1002 static void vm_imap_delete(int msgnum, struct vm_state *vms)
1004 unsigned long messageNum = 0;
1007 /* find real message number based on msgnum */
1008 /* this may be an index into vms->msgArray based on the msgnum. */
1010 messageNum = vms->msgArray[msgnum];
1011 if (messageNum == 0) {
1012 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1015 ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1016 /* delete message */
1017 sprintf (arg,"%lu",messageNum);
1018 mail_setflag (vms->mailstream,arg,"\\DELETED");
1022 static int make_file(char *dest, int len, char *dir, int num)
1024 return snprintf(dest, len, "%s/msg%04d", dir, num);
1027 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1028 * \param dest String. base directory.
1029 * \param len Length of dest.
1030 * \param context String. Ignored if is null or empty string.
1031 * \param ext String. Ignored if is null or empty string.
1032 * \param folder String. Ignored if is null or empty string.
1033 * \return -1 on failure, 0 on success.
1035 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1037 mode_t mode = VOICEMAIL_DIR_MODE;
1040 make_dir(dest, len, context, ext, folder);
1041 if ((res = ast_mkdir(dest, mode))) {
1042 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1048 /*! \brief Lock file path
1049 only return failure if ast_lock_path returns 'timeout',
1050 not if the path does not exist or any other reason
1052 static int vm_lock_path(const char *path)
1054 switch (ast_lock_path(path)) {
1055 case AST_LOCK_TIMEOUT:
1064 static int retrieve_file(char *dir, int msgnum)
1070 void *fdm = MAP_FAILED;
1071 SQLSMALLINT colcount=0;
1078 SQLSMALLINT datatype;
1079 SQLSMALLINT decimaldigits;
1080 SQLSMALLINT nullable;
1086 char full_fn[PATH_MAX];
1089 struct odbc_obj *obj;
1090 obj = ast_odbc_request_obj(odbc_database, 0);
1092 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1093 c = strchr(fmt, '|');
1096 if (!strcasecmp(fmt, "wav49"))
1098 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1100 make_file(fn, sizeof(fn), dir, msgnum);
1102 ast_copy_string(fn, dir, sizeof(fn));
1103 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1105 if (!(f = fopen(full_fn, "w+"))) {
1106 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1110 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1111 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1112 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1113 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1114 ast_odbc_release_obj(obj);
1117 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1118 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1119 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1120 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1121 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1122 ast_odbc_release_obj(obj);
1125 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1126 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1127 res = ast_odbc_smart_execute(obj, stmt);
1128 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1129 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1130 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1131 ast_odbc_release_obj(obj);
1134 res = SQLFetch(stmt);
1135 if (res == SQL_NO_DATA) {
1136 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1137 ast_odbc_release_obj(obj);
1140 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1141 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1142 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1143 ast_odbc_release_obj(obj);
1146 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1148 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1149 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1150 ast_odbc_release_obj(obj);
1153 res = SQLNumResultCols(stmt, &colcount);
1154 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1155 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1156 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1157 ast_odbc_release_obj(obj);
1161 fprintf(f, "[message]\n");
1162 for (x=0;x<colcount;x++) {
1164 collen = sizeof(coltitle);
1165 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
1166 &datatype, &colsize, &decimaldigits, &nullable);
1167 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1168 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1169 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1170 ast_odbc_release_obj(obj);
1173 if (!strcasecmp(coltitle, "recording")) {
1175 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1179 lseek(fd, fdlen - 1, SEEK_SET);
1180 if (write(fd, tmp, 1) != 1) {
1185 /* Read out in small chunks */
1186 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1187 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1188 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1189 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1190 ast_odbc_release_obj(obj);
1193 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1194 munmap(fdm, CHUNKSIZE);
1195 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1196 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1198 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1199 ast_odbc_release_obj(obj);
1204 truncate(full_fn, fdlen);
1207 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1208 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1209 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1210 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1211 ast_odbc_release_obj(obj);
1214 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1215 fprintf(f, "%s=%s\n", coltitle, rowdata);
1218 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1219 ast_odbc_release_obj(obj);
1221 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1230 static int remove_file(char *dir, int msgnum)
1233 char full_fn[PATH_MAX];
1237 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1238 make_file(fn, sizeof(fn), dir, msgnum);
1240 ast_copy_string(fn, dir, sizeof(fn));
1241 ast_filedelete(fn, NULL);
1242 if (ast_check_realtime("voicemail_data")) {
1243 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1245 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1250 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1258 struct odbc_obj *obj;
1259 obj = ast_odbc_request_obj(odbc_database, 0);
1261 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1262 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1263 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1264 ast_odbc_release_obj(obj);
1267 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1268 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1269 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1270 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1271 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1272 ast_odbc_release_obj(obj);
1275 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1276 res = ast_odbc_smart_execute(obj, stmt);
1277 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1278 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1279 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1280 ast_odbc_release_obj(obj);
1283 res = SQLFetch(stmt);
1284 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1285 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1286 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1287 ast_odbc_release_obj(obj);
1290 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1291 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1292 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1293 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1294 ast_odbc_release_obj(obj);
1297 if (sscanf(rowdata, "%d", &x) != 1)
1298 ast_log(LOG_WARNING, "Failed to read message count!\n");
1299 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1300 ast_odbc_release_obj(obj);
1302 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1307 static int message_exists(char *dir, int msgnum)
1316 struct odbc_obj *obj;
1317 obj = ast_odbc_request_obj(odbc_database, 0);
1319 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1320 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1321 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1322 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1323 ast_odbc_release_obj(obj);
1326 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1327 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1328 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1329 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1330 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1331 ast_odbc_release_obj(obj);
1334 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1335 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1336 res = ast_odbc_smart_execute(obj, stmt);
1337 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1338 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1339 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1340 ast_odbc_release_obj(obj);
1343 res = SQLFetch(stmt);
1344 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1345 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1346 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1347 ast_odbc_release_obj(obj);
1350 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1351 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1352 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1353 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1354 ast_odbc_release_obj(obj);
1357 if (sscanf(rowdata, "%d", &x) != 1)
1358 ast_log(LOG_WARNING, "Failed to read message count!\n");
1359 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1360 ast_odbc_release_obj(obj);
1362 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1367 static int count_messages(struct ast_vm_user *vmu, char *dir)
1369 return last_message_index(vmu, dir) + 1;
1372 static void delete_file(char *sdir, int smsg)
1379 struct odbc_obj *obj;
1380 obj = ast_odbc_request_obj(odbc_database, 0);
1382 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1383 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1384 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1385 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1386 ast_odbc_release_obj(obj);
1389 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1390 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1391 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1392 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1393 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1394 ast_odbc_release_obj(obj);
1397 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1398 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1399 res = ast_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 ast_odbc_release_obj(obj);
1406 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1407 ast_odbc_release_obj(obj);
1409 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1414 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1421 struct odbc_obj *obj;
1423 delete_file(ddir, dmsg);
1424 obj = ast_odbc_request_obj(odbc_database, 0);
1426 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1427 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1428 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1429 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1430 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1431 ast_odbc_release_obj(obj);
1434 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);
1435 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1436 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1437 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1438 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1439 ast_odbc_release_obj(obj);
1442 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1443 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1444 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1445 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1446 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1447 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1448 res = ast_odbc_smart_execute(obj, stmt);
1449 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1450 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1451 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1452 ast_odbc_release_obj(obj);
1455 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1456 ast_odbc_release_obj(obj);
1458 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1463 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1468 void *fdm = MAP_FAILED;
1475 char full_fn[PATH_MAX];
1478 const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1479 const char *category = "";
1480 struct ast_config *cfg=NULL;
1481 struct odbc_obj *obj;
1483 delete_file(dir, msgnum);
1484 obj = ast_odbc_request_obj(odbc_database, 0);
1486 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1487 c = strchr(fmt, '|');
1490 if (!strcasecmp(fmt, "wav49"))
1492 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1494 make_file(fn, sizeof(fn), dir, msgnum);
1496 ast_copy_string(fn, dir, sizeof(fn));
1497 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1498 cfg = ast_config_load(full_fn);
1499 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1500 fd = open(full_fn, O_RDWR);
1502 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1503 ast_odbc_release_obj(obj);
1507 context = ast_variable_retrieve(cfg, "message", "context");
1508 if (!context) context = "";
1509 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1510 if (!macrocontext) macrocontext = "";
1511 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1512 if (!callerid) callerid = "";
1513 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1514 if (!origtime) origtime = "";
1515 duration = ast_variable_retrieve(cfg, "message", "duration");
1516 if (!duration) duration = "";
1517 category = ast_variable_retrieve(cfg, "message", "category");
1518 if (!category) category = "";
1520 fdlen = lseek(fd, 0, SEEK_END);
1521 lseek(fd, 0, SEEK_SET);
1522 printf("Length is %zd\n", fdlen);
1523 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1524 if (fdm == MAP_FAILED) {
1525 ast_log(LOG_WARNING, "Memory map failed!\n");
1526 ast_odbc_release_obj(obj);
1529 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1530 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1531 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1532 ast_odbc_release_obj(obj);
1535 if (!ast_strlen_zero(category))
1536 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1538 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1539 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1540 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1541 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1542 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1543 ast_odbc_release_obj(obj);
1546 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1547 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1548 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1549 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1550 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1551 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1552 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1553 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1554 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1555 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1556 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1557 if (!ast_strlen_zero(category))
1558 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1559 res = ast_odbc_smart_execute(obj, stmt);
1560 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1561 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1562 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1563 ast_odbc_release_obj(obj);
1566 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1567 ast_odbc_release_obj(obj);
1569 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1572 ast_config_destroy(cfg);
1573 if (fdm != MAP_FAILED)
1580 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1587 struct odbc_obj *obj;
1589 delete_file(ddir, dmsg);
1590 obj = ast_odbc_request_obj(odbc_database, 0);
1592 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1593 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1594 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1595 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1596 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1597 ast_odbc_release_obj(obj);
1600 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1601 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1602 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1603 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1604 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1605 ast_odbc_release_obj(obj);
1608 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1609 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1610 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1611 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1612 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1613 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1614 res = ast_odbc_smart_execute(obj, stmt);
1615 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1616 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1617 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1618 ast_odbc_release_obj(obj);
1621 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1622 ast_odbc_release_obj(obj);
1624 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1630 #ifndef IMAP_STORAGE
1631 static int count_messages(struct ast_vm_user *vmu, char *dir)
1633 /* Find all .txt files - even if they are not in sequence from 0000 */
1637 struct dirent *vment = NULL;
1639 if (vm_lock_path(dir))
1640 return ERROR_LOCK_PATH;
1642 if ((vmdir = opendir(dir))) {
1643 while ((vment = readdir(vmdir))) {
1644 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1649 ast_unlock_path(dir);
1654 static void rename_file(char *sfn, char *dfn)
1656 char stxt[PATH_MAX];
1657 char dtxt[PATH_MAX];
1658 ast_filerename(sfn,dfn,NULL);
1659 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1660 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1661 if (ast_check_realtime("voicemail_data")) {
1662 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1667 static int copy(char *infile, char *outfile)
1675 #ifdef HARDLINK_WHEN_POSSIBLE
1676 /* Hard link if possible; saves disk space & is faster */
1677 if (link(infile, outfile)) {
1679 if ((ifd = open(infile, O_RDONLY)) < 0) {
1680 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1683 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1684 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1689 len = read(ifd, buf, sizeof(buf));
1691 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1697 res = write(ofd, buf, len);
1698 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1699 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1709 #ifdef HARDLINK_WHEN_POSSIBLE
1711 /* Hard link succeeded */
1717 static void copy_file(char *frompath, char *topath)
1719 char frompath2[PATH_MAX], topath2[PATH_MAX];
1720 struct ast_variable *tmp,*var = NULL;
1721 char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
1722 ast_filecopy(frompath, topath, NULL);
1723 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1724 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1725 if (ast_check_realtime("voicemail_data")) {
1726 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
1727 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
1728 for (tmp = var; tmp; tmp = tmp->next) {
1729 if (!strcasecmp(tmp->name, "origmailbox")) {
1730 origmailbox = tmp->value;
1731 } else if (!strcasecmp(tmp->name, "context")) {
1732 context = tmp->value;
1733 } else if (!strcasecmp(tmp->name, "macrocontext")) {
1734 macrocontext = tmp->value;
1735 } else if (!strcasecmp(tmp->name, "exten")) {
1737 } else if (!strcasecmp(tmp->name, "priority")) {
1738 priority = tmp->value;
1739 } else if (!strcasecmp(tmp->name, "callerchan")) {
1740 callerchan = tmp->value;
1741 } else if (!strcasecmp(tmp->name, "callerid")) {
1742 callerid = tmp->value;
1743 } else if (!strcasecmp(tmp->name, "origdate")) {
1744 origdate = tmp->value;
1745 } else if (!strcasecmp(tmp->name, "origtime")) {
1746 origtime = tmp->value;
1747 } else if (!strcasecmp(tmp->name, "category")) {
1748 category = tmp->value;
1749 } else if (!strcasecmp(tmp->name, "duration")) {
1750 duration = tmp->value;
1753 ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, NULL);
1755 copy(frompath2, topath2);
1756 ast_variables_destroy(var);
1761 * A negative return value indicates an error.
1762 * \note Should always be called with a lock already set on dir.
1764 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1767 unsigned char map[MAXMSGLIMIT] = "";
1769 struct dirent *msgdirent;
1772 /* Reading the entire directory into a file map scales better than
1773 * doing a stat repeatedly on a predicted sequence. I suspect this
1774 * is partially due to stat(2) internally doing a readdir(2) itself to
1775 * find each file. */
1776 msgdir = opendir(dir);
1777 while ((msgdirent = readdir(msgdir))) {
1778 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1783 for (x = 0; x < vmu->maxmsg; x++) {
1793 #ifndef ODBC_STORAGE
1794 static int vm_delete(char *file)
1799 txtsize = (strlen(file) + 5)*sizeof(char);
1800 txt = alloca(txtsize);
1801 /* Sprintf here would safe because we alloca'd exactly the right length,
1802 * but trying to eliminate all sprintf's anyhow
1804 if (ast_check_realtime("voicemail_data")) {
1805 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
1807 snprintf(txt, txtsize, "%s.txt", file);
1809 return ast_filedelete(file, NULL);
1813 static int inbuf(struct baseio *bio, FILE *fi)
1820 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1834 static int inchar(struct baseio *bio, FILE *fi)
1836 if (bio->iocp>=bio->iolen) {
1837 if (!inbuf(bio, fi))
1841 return bio->iobuf[bio->iocp++];
1844 static int ochar(struct baseio *bio, int c, FILE *so)
1846 if (bio->linelength >= BASELINELEN) {
1847 if (fputs(eol,so) == EOF)
1853 if (putc(((unsigned char)c),so) == EOF)
1861 static int base_encode(char *filename, FILE *so)
1863 unsigned char dtable[BASEMAXINLINE];
1868 memset(&bio, 0, sizeof(bio));
1869 bio.iocp = BASEMAXINLINE;
1871 if (!(fi = fopen(filename, "rb"))) {
1872 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1876 for (i= 0; i<9; i++) {
1879 dtable[26+i]= 'a'+i;
1880 dtable[26+i+9]= 'j'+i;
1882 for (i= 0; i<8; i++) {
1883 dtable[i+18]= 'S'+i;
1884 dtable[26+i+18]= 's'+i;
1886 for (i= 0; i<10; i++) {
1887 dtable[52+i]= '0'+i;
1893 unsigned char igroup[3], ogroup[4];
1896 igroup[0]= igroup[1]= igroup[2]= 0;
1898 for (n= 0;n<3;n++) {
1899 if ((c = inchar(&bio, fi)) == EOF) {
1904 igroup[n]= (unsigned char)c;
1908 ogroup[0]= dtable[igroup[0]>>2];
1909 ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1910 ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1911 ogroup[3]= dtable[igroup[2]&0x3F];
1921 ochar(&bio, ogroup[i], so);
1925 if (fputs(eol,so) == EOF)
1933 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)
1936 /* Prepare variables for substitution in email body and subject */
1937 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1938 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1939 snprintf(passdata, passdatasize, "%d", msgnum);
1940 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1941 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1942 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1943 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1944 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1945 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1946 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1947 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1950 static char *quote(const char *from, char *to, size_t len)
1954 for (; ptr < to + len - 1; from++) {
1957 else if (*from == '\0')
1961 if (ptr < to + len - 1)
1968 * fill in *tm for current time according to the proper timezone, if any.
1969 * Return tm so it can be used as a function argument.
1971 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
1973 const struct vm_zone *z = NULL;
1974 struct timeval t = ast_tvnow();
1976 /* Does this user have a timezone specified? */
1977 if (!ast_strlen_zero(vmu->zonetag)) {
1978 /* Find the zone in the list */
1979 AST_LIST_LOCK(&zones);
1980 AST_LIST_TRAVERSE(&zones, z, list) {
1981 if (!strcmp(z->name, vmu->zonetag))
1984 AST_LIST_UNLOCK(&zones);
1986 ast_localtime(&t, tm, z ? z->timezone : NULL);
1990 /*! \brief same as mkstemp, but return a FILE * */
1991 static FILE *vm_mkftemp(char *template)
1994 int pfd = mkstemp(template);
1995 chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1997 p = fdopen(pfd, "w+");
2006 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)
2009 char host[MAXHOSTNAMELEN] = "";
2017 size_t len_passdata;
2018 char *greeting_attachment;
2026 gethostname(host, sizeof(host)-1);
2027 if (strchr(srcemail, '@'))
2028 ast_copy_string(who, srcemail, sizeof(who));
2030 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2032 greeting_attachment = strrchr(ast_strdupa(attach), '/');
2033 if (greeting_attachment)
2034 *greeting_attachment++ = '\0';
2036 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2037 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2038 fprintf(p, "Date: %s" ENDL, date);
2040 /* Set date format for voicemail mail */
2041 ast_strftime(date, sizeof(date), emaildateformat, &tm);
2043 if (!ast_strlen_zero(fromstring)) {
2044 struct ast_channel *ast;
2045 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2047 int vmlen = strlen(fromstring)*3 + 200;
2048 if ((passdata = alloca(vmlen))) {
2049 memset(passdata, 0, vmlen);
2050 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2051 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
2052 len_passdata = strlen(passdata) * 2 + 3;
2053 passdata2 = alloca(len_passdata);
2054 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
2056 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2057 ast_channel_free(ast);
2059 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2061 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
2062 len_passdata = strlen(vmu->fullname) * 2 + 3;
2063 passdata2 = alloca(len_passdata);
2064 fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
2065 if (!ast_strlen_zero(emailsubject)) {
2066 struct ast_channel *ast;
2067 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2069 int vmlen = strlen(emailsubject) * 3 + 200;
2070 if ((passdata = alloca(vmlen))) {
2071 memset(passdata, 0, vmlen);
2072 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2073 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
2074 fprintf(p, "Subject: %s" ENDL, passdata);
2076 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2077 ast_channel_free(ast);
2079 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2080 } else if (!ast_strlen_zero(emailtitle)) {
2081 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
2083 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
2084 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2086 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2087 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
2089 /* additional information needed for IMAP searching */
2090 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
2091 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
2092 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
2093 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
2094 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
2095 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
2096 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
2097 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
2098 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
2099 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
2100 if (!ast_strlen_zero(category))
2101 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
2102 fprintf(p, "X-Asterisk-VM-Message-Type: %s\n", msgnum > -1 ? "Message" : greeting_attachment);
2103 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
2104 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
2106 if (!ast_strlen_zero(cidnum))
2107 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
2108 if (!ast_strlen_zero(cidname))
2109 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
2110 fprintf(p, "MIME-Version: 1.0" ENDL);
2111 if (attach_user_voicemail) {
2112 /* Something unique. */
2113 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
2115 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
2116 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
2117 fprintf(p, "--%s" ENDL, bound);
2119 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
2121 struct ast_channel *ast;
2122 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2124 int vmlen = strlen(emailbody)*3 + 200;
2125 if ((passdata = alloca(vmlen))) {
2126 memset(passdata, 0, vmlen);
2127 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2128 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2129 fprintf(p, "%s" ENDL, passdata);
2131 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2132 ast_channel_free(ast);
2134 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2135 } else if (msgnum > -1){
2136 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
2138 "in mailbox %s from %s, on %s so you might" ENDL
2139 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
2140 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2142 fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
2143 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
2145 if (attach_user_voicemail) {
2146 /* Eww. We want formats to tell us their own MIME type */
2147 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2148 char tmpdir[256], newtmp[256];
2151 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2152 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2153 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2154 tmpfd = mkstemp(newtmp);
2155 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
2156 ast_debug(3, "newtmp: %s\n", newtmp);
2158 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2159 ast_safe_system(tmpcmd);
2161 ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2164 fprintf(p, "--%s" ENDL, bound);
2166 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2168 fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
2169 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2170 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2172 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2174 fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
2175 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2176 base_encode(fname, p);
2177 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
2187 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)
2190 char tmp[80] = "/tmp/astmail-XXXXXX";
2193 if (vmu && ast_strlen_zero(vmu->email)) {
2194 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
2197 if (!strcmp(format, "wav49"))
2199 ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
2200 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2202 if ((p = vm_mkftemp(tmp)) == NULL) {
2203 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2206 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2208 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2209 ast_safe_system(tmp2);
2210 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2215 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)
2218 char host[MAXHOSTNAMELEN] = "";
2221 char tmp[80] = "/tmp/astmail-XXXXXX";
2222 char tmp2[PATH_MAX];
2226 if ((p = vm_mkftemp(tmp)) == NULL) {
2227 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2230 gethostname(host, sizeof(host)-1);
2231 if (strchr(srcemail, '@'))
2232 ast_copy_string(who, srcemail, sizeof(who));
2234 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2235 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2236 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2237 fprintf(p, "Date: %s\n", date);
2239 if (*pagerfromstring) {
2240 struct ast_channel *ast;
2241 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2243 int vmlen = strlen(fromstring)*3 + 200;
2244 if ((passdata = alloca(vmlen))) {
2245 memset(passdata, 0, vmlen);
2246 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2247 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2248 fprintf(p, "From: %s <%s>\n", passdata, who);
2250 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2251 ast_channel_free(ast);
2253 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2255 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2256 fprintf(p, "To: %s\n", pager);
2258 struct ast_channel *ast;
2259 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2261 int vmlen = strlen(pagersubject) * 3 + 200;
2262 if ((passdata = alloca(vmlen))) {
2263 memset(passdata, 0, vmlen);
2264 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2265 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2266 fprintf(p, "Subject: %s\n\n", passdata);
2268 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2269 ast_channel_free(ast);
2271 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2273 fprintf(p, "Subject: New VM\n\n");
2275 ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2277 struct ast_channel *ast;
2278 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2280 int vmlen = strlen(pagerbody) * 3 + 200;
2281 passdata = alloca(vmlen);
2282 memset(passdata, 0, vmlen);
2283 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2284 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2285 fprintf(p, "%s\n", passdata);
2286 ast_channel_free(ast);
2288 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2290 fprintf(p, "New %s long msg in box %s\n"
2291 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2294 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2295 ast_safe_system(tmp2);
2296 ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2300 static int get_date(char *s, int len)
2303 struct timeval t = ast_tvnow();
2304 ast_localtime(&t, &tm, NULL);
2305 return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2308 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2312 char dest[PATH_MAX];
2314 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2316 if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
2317 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2321 RETRIEVE(fn, -1, ext, context);
2322 if (ast_fileexists(fn, NULL, NULL) > 0) {
2323 res = ast_stream_and_wait(chan, fn, ecodes);
2329 /* Dispose just in case */
2331 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2334 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2338 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2342 static void free_user(struct ast_vm_user *vmu)
2344 if (!ast_test_flag(vmu, VM_ALLOCED))
2350 static void free_zone(struct vm_zone *z)
2355 static const char *mbox(int id)
2357 static const char *msgs[] = {
2369 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2373 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2380 char tmp[PATH_MAX] = "";
2381 struct odbc_obj *obj;
2389 /* If no mailbox, return immediately */
2390 if (ast_strlen_zero(mailbox))
2393 ast_copy_string(tmp, mailbox, sizeof(tmp));
2395 context = strchr(tmp, '@');
2400 context = "default";
2402 obj = ast_odbc_request_obj(odbc_database, 0);
2404 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2405 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2406 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2407 ast_odbc_release_obj(obj);
2410 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2411 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2412 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2413 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2414 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2415 ast_odbc_release_obj(obj);
2418 res = ast_odbc_smart_execute(obj, stmt);
2419 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2420 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2421 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2422 ast_odbc_release_obj(obj);
2425 res = SQLFetch(stmt);
2426 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2427 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2428 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2429 ast_odbc_release_obj(obj);
2432 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2433 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2434 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2435 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2436 ast_odbc_release_obj(obj);
2439 *newmsgs = atoi(rowdata);
2440 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2442 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2443 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2444 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2445 ast_odbc_release_obj(obj);
2448 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2449 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2450 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2451 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2452 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2453 ast_odbc_release_obj(obj);
2456 res = ast_odbc_smart_execute(obj, stmt);
2457 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2458 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2459 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2460 ast_odbc_release_obj(obj);
2463 res = SQLFetch(stmt);
2464 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2465 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2466 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2467 ast_odbc_release_obj(obj);
2470 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2471 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2472 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2473 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2474 ast_odbc_release_obj(obj);
2477 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2478 ast_odbc_release_obj(obj);
2479 *oldmsgs = atoi(rowdata);
2482 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2488 static int messagecount(const char *context, const char *mailbox, const char *folder)
2490 struct odbc_obj *obj = NULL;
2493 SQLHSTMT stmt = NULL;
2498 /* If no mailbox, return immediately */
2499 if (ast_strlen_zero(mailbox))
2502 obj = ast_odbc_request_obj(odbc_database, 0);
2504 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2505 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2506 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2509 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2510 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2511 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2512 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2513 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2516 res = ast_odbc_smart_execute(obj, stmt);
2517 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2518 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2519 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2522 res = SQLFetch(stmt);
2523 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2524 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2525 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2528 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2529 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2530 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2531 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2534 nummsgs = atoi(rowdata);
2535 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2537 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2541 ast_odbc_release_obj(obj);
2545 static int has_voicemail(const char *mailbox, const char *folder)
2547 char *context, tmp[256];
2548 ast_copy_string(tmp, mailbox, sizeof(tmp));
2549 if ((context = strchr(tmp, '@')))
2552 context = "default";
2554 if (messagecount(context, tmp, folder))
2560 #elif defined(IMAP_STORAGE)
2562 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)
2564 char *myserveremail = serveremail;
2569 char tmp[80] = "/tmp/astmail-XXXXXX";
2574 /* Attach only the first format */
2575 fmt = ast_strdupa(fmt);
2577 strsep(&stringp, "|");
2579 if (!ast_strlen_zero(vmu->serveremail))
2580 myserveremail = vmu->serveremail;
2583 make_file(fn, sizeof(fn), dir, msgnum);
2585 ast_copy_string (fn, dir, sizeof(fn));
2587 if (ast_strlen_zero(vmu->email))
2588 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2590 if (!strcmp(fmt, "wav49"))
2592 ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2594 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2596 if (!(p = vm_mkftemp(tmp))) {
2597 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2602 if (msgnum < 0 && imapgreetings) {
2603 init_mailstream(vms, GREETINGS_FOLDER);
2604 imap_delete_old_greeting(fn, vms);
2607 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);
2608 /* read mail file to memory */
2611 if (!(buf = ast_malloc(len+1))) {
2612 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2615 fread(buf, len, 1, p);
2616 ((char *)buf)[len] = '\0';
2617 INIT(&str, mail_string, buf, len);
2618 init_mailstream(vms, NEW_FOLDER);
2619 imap_mailbox_name(mailbox, vms, NEW_FOLDER, 1);
2620 if(!mail_append(vms->mailstream, mailbox, &str))
2621 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2625 ast_debug(3, "%s stored\n", fn);
2630 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2635 struct ast_vm_user *vmu;
2636 struct vm_state *vms_p;
2649 ast_debug(3,"Mailbox is set to %s\n",mailbox);
2651 /* If no mailbox, return immediately */
2652 if (ast_strlen_zero(mailbox))
2655 if (strchr(mailbox, ',')) {
2657 ast_copy_string(tmp, mailbox, sizeof(tmp));
2660 while((cur = strsep(&mb, ", "))) {
2661 if (!ast_strlen_zero(cur)) {
2662 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2675 ast_copy_string(tmp, mailbox, sizeof(tmp));
2677 if ((context = strchr(tmp, '@'))) {
2682 context = "default";
2683 mailboxnc = (char *)mailbox;
2686 /* We have to get the user before we can open the stream! */
2687 /*ast_debug(1,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2688 if (!(vmu = find_user(NULL, context, mailboxnc))) {
2689 ast_log(LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2693 /* No IMAP account available */
2694 if (vmu->imapuser[0] == '\0') {
2695 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2700 /* check if someone is accessing this box right now... */
2701 if ((vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1)) || (vms_p = get_vm_state_by_mailbox(mailboxnc, 1))) {
2702 ast_debug(3,"Returning before search - user is logged in\n");
2703 *newmsgs = vms_p->newmessages;
2704 *oldmsgs = vms_p->oldmessages;
2709 /* add one if not there... */
2710 if (!(vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0)) && !(vms_p = get_vm_state_by_mailbox(mailboxnc, 0))) {
2711 ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
2712 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2716 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2717 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2718 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2719 ast_debug(3,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2721 /* set mailbox to INBOX! */
2722 ast_copy_string(vms_p->curbox, mbox(NEW_FOLDER), sizeof(vms_p->curbox));
2723 init_vm_state(vms_p);
2724 vmstate_insert(vms_p);
2727 /* If no mailstream exists yet and even after attempting to initialize it fails, bail out */
2728 ret = init_mailstream(vms_p, NEW_FOLDER);
2729 if (!vms_p->mailstream) {
2730 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2735 if (!ret && vms_p->updated > 0) {
2737 pgm = mail_newsearchpgm();
2738 hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (char *)mailboxnc);
2744 vms_p->vmArrayIndex = 0;
2745 mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2746 *newmsgs = vms_p->vmArrayIndex;
2747 vms_p->newmessages = vms_p->vmArrayIndex;
2748 mail_free_searchpgm(&pgm);
2751 pgm = mail_newsearchpgm ();
2752 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2758 vms_p->vmArrayIndex = 0;
2759 mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2760 *oldmsgs = vms_p->vmArrayIndex;
2761 vms_p->oldmessages = vms_p->vmArrayIndex;
2762 mail_free_searchpgm(&pgm);
2766 if (vms_p->updated > 1) { /* changes, so we did the searches above */
2768 } else { /* no changes, so don't search */
2769 mail_ping(vms_p->mailstream);
2770 /* Keep the old data */
2771 *newmsgs = vms_p->newmessages;
2772 *oldmsgs = vms_p->oldmessages;
2779 static int has_voicemail(const char *mailbox, const char *folder)
2781 int newmsgs, oldmsgs;
2783 if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2784 return folder? oldmsgs: newmsgs;
2789 static int messagecount(const char *context, const char *mailbox, const char *folder)
2791 int newmsgs, oldmsgs;
2794 if (ast_strlen_zero(mailbox))
2796 sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2798 if(inboxcount(tmp, &newmsgs, &oldmsgs))
2799 return folder? oldmsgs: newmsgs;
2805 #ifndef IMAP_STORAGE
2806 /* copy message only used by file storage */
2807 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)
2809 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2810 const char *frombox = mbox(imbox);
2813 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2815 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2818 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2820 ast_copy_string(fromdir, dir, sizeof(fromdir));
2822 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2823 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2825 if (vm_lock_path(todir))
2826 return ERROR_LOCK_PATH;
2828 recipmsgnum = last_message_index(recip, todir) + 1;
2829 if (recipmsgnum < recip->maxmsg) {
2830 make_file(topath, sizeof(topath), todir, recipmsgnum);
2831 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2833 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2835 ast_unlock_path(todir);
2836 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2841 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2842 static int messagecount(const char *context, const char *mailbox, const char *folder)
2844 return __has_voicemail(context, mailbox, folder, 0);
2848 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2855 /* If no mailbox, return immediately */
2856 if (ast_strlen_zero(mailbox))
2859 if (ast_strlen_zero(folder))
2861 if (ast_strlen_zero(context))
2862 context = "default";
2864 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2866 if (!(dir = opendir(fn)))
2869 while ((de = readdir(dir))) {
2870 if (!strncasecmp(de->d_name, "msg", 3)) {
2874 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2885 static int has_voicemail(const char *mailbox, const char *folder)
2887 char tmp[256], *tmp2 = tmp, *mbox, *context;
2888 ast_copy_string(tmp, mailbox, sizeof(tmp));
2889 while ((mbox = strsep(&tmp2, ","))) {
2890 if ((context = strchr(mbox, '@')))
2893 context = "default";
2894 if (__has_voicemail(context, mbox, folder, 1))
2901 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2906 /* If no mailbox, return immediately */
2907 if (ast_strlen_zero(mailbox))
2915 if (strchr(mailbox, ',')) {
2919 ast_copy_string(tmp, mailbox, sizeof(tmp));
2921 while ((cur = strsep(&mb, ", "))) {
2922 if (!ast_strlen_zero(cur)) {
2923 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2936 ast_copy_string(tmp, mailbox, sizeof(tmp));
2938 if ((context = strchr(tmp, '@')))
2941 context = "default";
2944 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2946 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2953 static void run_externnotify(char *context, char *extension)
2955 char arguments[255];
2956 char ext_context[256] = "";
2957 int newvoicemails = 0, oldvoicemails = 0;
2958 struct ast_smdi_mwi_message *mwi_msg;
2960 if (!ast_strlen_zero(context))
2961 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2963 ast_copy_string(ext_context, extension, sizeof(ext_context));
2966 if (ast_app_has_voicemail(ext_context, NULL))
2967 ast_smdi_mwi_set(smdi_iface, extension);
2969 ast_smdi_mwi_unset(smdi_iface, extension);
2971 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2972 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2973 if (!strncmp(mwi_msg->cause, "INV", 3))
2974 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2975 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2976 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2977 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2978 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2980 ast_debug(1, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2984 if (!ast_strlen_zero(externnotify)) {
2985 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2986 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2988 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2989 ast_debug(1, "Executing %s\n", arguments);
2990 ast_safe_system(arguments);
2995 struct leave_vm_options {
2997 signed char record_gain;
3000 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
3003 int newmsgs, oldmsgs;
3004 struct vm_state *vms = NULL;
3006 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
3022 char dir[PATH_MAX], tmpdir[PATH_MAX];
3024 char prefile[PATH_MAX] = "";
3025 char tempfile[PATH_MAX] = "";
3026 char ext_context[256] = "";
3029 char ecodes[16] = "#";
3030 char tmp[1024] = "", *tmpptr;
3031 struct ast_vm_user *vmu;
3032 struct ast_vm_user svm;
3033 const char *category = NULL;
3035 ast_copy_string(tmp, ext, sizeof(tmp));
3037 if ((context = strchr(tmp, '@'))) {
3039 tmpptr = strchr(context, '&');
3041 tmpptr = strchr(ext, '&');
3047 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3049 ast_debug(3, "Before find_user\n");
3050 if (!(vmu = find_user(&svm, context, ext))) {
3051 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3052 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3055 /* Setup pre-file if appropriate */
3056 if (strcmp(vmu->context, "default"))
3057 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3059 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
3060 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3061 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3062 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3063 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3065 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3066 if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
3067 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3070 RETRIEVE(tempfile, -1, ext, context);
3071 if (ast_fileexists(tempfile, NULL, NULL) > 0)
3072 ast_copy_string(prefile, tempfile, sizeof(prefile));