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 apps/app_directory.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$")
75 #include <sys/types.h>
84 #ifdef USE_SYSTEM_IMAP
85 #include <imap/c-client.h>
86 #include <imap/imap4r1.h>
87 #include <imap/linkage.h>
95 #include "asterisk/lock.h"
96 #include "asterisk/file.h"
97 #include "asterisk/logger.h"
98 #include "asterisk/channel.h"
99 #include "asterisk/pbx.h"
100 #include "asterisk/options.h"
101 #include "asterisk/config.h"
102 #include "asterisk/say.h"
103 #include "asterisk/module.h"
104 #include "asterisk/adsi.h"
105 #include "asterisk/app.h"
106 #include "asterisk/manager.h"
107 #include "asterisk/dsp.h"
108 #include "asterisk/localtime.h"
109 #include "asterisk/cli.h"
110 #include "asterisk/utils.h"
111 #include "asterisk/stringfields.h"
112 #include "asterisk/smdi.h"
113 #include "asterisk/event.h"
116 #include "asterisk/res_odbc.h"
120 static char imapserver[48];
121 static char imapport[8];
122 static char imapflags[128];
123 static char imapfolder[64];
124 static char greetingfolder[64];
125 static char authuser[32];
126 static char authpassword[42];
128 static int expungeonhangup = 1;
129 static int imapgreetings = 0;
130 static char delimiter = '\0';
135 /* Forward declarations for IMAP */
136 static int init_mailstream(struct vm_state *vms, int box);
137 static void write_file(char *filename, char *buffer, unsigned long len);
138 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
139 static void vm_imap_delete(int msgnum, struct vm_state *vms);
140 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
141 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
142 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
143 static void vmstate_insert(struct vm_state *vms);
144 static void vmstate_delete(struct vm_state *vms);
145 static void set_update(MAILSTREAM * stream);
146 static void init_vm_state(struct vm_state *vms);
147 static void check_msgArray(struct vm_state *vms);
148 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
149 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
150 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num);
151 static void get_mailbox_delimiter(MAILSTREAM *stream);
152 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
153 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
154 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);
155 static void update_messages_by_imapuser(const char *user, unsigned long number);
157 static int imap_remove_file (char *dir, int msgnum);
158 static int imap_retrieve_file (char *dir, int msgnum, char *mailbox, char *context);
159 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
161 struct vm_state *vms;
162 AST_LIST_ENTRY(vmstate) list;
165 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
169 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
171 #define COMMAND_TIMEOUT 5000
172 /* Don't modify these here; set your umask at runtime instead */
173 #define VOICEMAIL_DIR_MODE 0777
174 #define VOICEMAIL_FILE_MODE 0666
175 #define CHUNKSIZE 65536
177 #define VOICEMAIL_CONFIG "voicemail.conf"
178 #define ASTERISK_USERNAME "asterisk"
180 /* Define fast-forward, pause, restart, and reverse keys
181 while listening to a voicemail message - these are
182 strings, not characters */
183 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
184 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
185 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
186 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
187 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
188 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
190 /* Default mail command to mail voicemail. Change it with the
191 mailcmd= command in voicemail.conf */
192 #define SENDMAIL "/usr/sbin/sendmail -t"
194 #define INTRO "vm-intro"
197 #define MAXMSGLIMIT 9999
199 #define BASEMAXINLINE 256
200 #define BASELINELEN 72
201 #define BASEMAXINLINE 256
204 #define MAX_DATETIME_FORMAT 512
205 #define MAX_NUM_CID_CONTEXTS 10
207 #define VM_REVIEW (1 << 0)
208 #define VM_OPERATOR (1 << 1)
209 #define VM_SAYCID (1 << 2)
210 #define VM_SVMAIL (1 << 3)
211 #define VM_ENVELOPE (1 << 4)
212 #define VM_SAYDURATION (1 << 5)
213 #define VM_SKIPAFTERCMD (1 << 6)
214 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
215 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
216 #define VM_PBXSKIP (1 << 9)
217 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
218 #define VM_ATTACH (1 << 11)
219 #define VM_DELETE (1 << 12)
220 #define VM_ALLOCED (1 << 13)
221 #define VM_SEARCH (1 << 14)
222 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
223 #define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
224 #define ERROR_LOCK_PATH -100
237 OPT_SILENT = (1 << 0),
238 OPT_BUSY_GREETING = (1 << 1),
239 OPT_UNAVAIL_GREETING = (1 << 2),
240 OPT_RECORDGAIN = (1 << 3),
241 OPT_PREPEND_MAILBOX = (1 << 4),
242 OPT_AUTOPLAY = (1 << 6),
246 OPT_ARG_RECORDGAIN = 0,
247 OPT_ARG_PLAYFOLDER = 1,
248 /* This *must* be the last value in this enum! */
249 OPT_ARG_ARRAY_SIZE = 2,
252 AST_APP_OPTIONS(vm_app_options, {
253 AST_APP_OPTION('s', OPT_SILENT),
254 AST_APP_OPTION('b', OPT_BUSY_GREETING),
255 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
256 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
257 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
258 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
261 static int load_config(int reload);
263 /*! \page vmlang Voicemail Language Syntaxes Supported
265 \par Syntaxes supported, not really language codes.
272 \arg \b pt - Portuguese
273 \arg \b pt_BR - Portuguese (Brazil)
275 \arg \b no - Norwegian
277 \arg \b tw - Chinese (Taiwan)
278 \arg \b ua - Ukrainian
280 German requires the following additional soundfile:
281 \arg \b 1F einE (feminine)
283 Spanish requires the following additional soundfile:
284 \arg \b 1M un (masculine)
286 Dutch, Portuguese & Spanish require the following additional soundfiles:
287 \arg \b vm-INBOXs singular of 'new'
288 \arg \b vm-Olds singular of 'old/heard/read'
291 \arg \b vm-INBOX nieuwe (nl)
292 \arg \b vm-Old oude (nl)
295 \arg \b vm-new-a 'new', feminine singular accusative
296 \arg \b vm-new-e 'new', feminine plural accusative
297 \arg \b vm-new-ych 'new', feminine plural genitive
298 \arg \b vm-old-a 'old', feminine singular accusative
299 \arg \b vm-old-e 'old', feminine plural accusative
300 \arg \b vm-old-ych 'old', feminine plural genitive
301 \arg \b digits/1-a 'one', not always same as 'digits/1'
302 \arg \b digits/2-ie 'two', not always same as 'digits/2'
305 \arg \b vm-nytt singular of 'new'
306 \arg \b vm-nya plural of 'new'
307 \arg \b vm-gammalt singular of 'old'
308 \arg \b vm-gamla plural of 'old'
309 \arg \b digits/ett 'one', not always same as 'digits/1'
312 \arg \b vm-ny singular of 'new'
313 \arg \b vm-nye plural of 'new'
314 \arg \b vm-gammel singular of 'old'
315 \arg \b vm-gamle plural of 'old'
323 Ukrainian requires the following additional soundfile:
324 \arg \b vm-nove 'nove'
325 \arg \b vm-stare 'stare'
326 \arg \b digits/ua/1e 'odne'
328 Italian requires the following additional soundfile:
332 \arg \b vm-nuovi new plural
333 \arg \b vm-vecchio old
334 \arg \b vm-vecchi old plural
336 Chinese (Taiwan) requires the following additional soundfile:
337 \arg \b vm-tong A class-word for call (tong1)
338 \arg \b vm-ri A class-word for day (ri4)
339 \arg \b vm-you You (ni3)
340 \arg \b vm-haveno Have no (mei2 you3)
341 \arg \b vm-have Have (you3)
342 \arg \b vm-listen To listen (yao4 ting1)
345 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
346 spelled among others when you have to change folder. For the above reasons, vm-INBOX
347 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
356 unsigned char iobuf[BASEMAXINLINE];
359 /*! Structure for linked list of users
360 * Use ast_vm_user_destroy() to free one of these structures. */
362 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
363 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
364 char password[80]; /*!< Secret pin code, numbers only */
365 char fullname[80]; /*!< Full name, for directory app */
366 char email[80]; /*!< E-mail address */
367 char pager[80]; /*!< E-mail address to pager (no attachment) */
368 char serveremail[80]; /*!< From: Mail address */
369 char mailcmd[160]; /*!< Configurable mail command */
370 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
371 char zonetag[80]; /*!< Time zone */
374 char uniqueid[20]; /*!< Unique integer identifier */
376 char attachfmt[20]; /*!< Attachment format */
377 unsigned int flags; /*!< VM_ flags */
379 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
380 int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
382 char imapuser[80]; /*!< IMAP server login */
383 char imappassword[80]; /*!< IMAP server password if authpassword not defined */
385 double volgain; /*!< Volume gain for voicemails sent via email */
386 AST_LIST_ENTRY(ast_vm_user) list;
389 /*! Voicemail time zones */
391 AST_LIST_ENTRY(vm_zone) list;
394 char msg_format[512];
397 /*! Voicemail mailbox state */
401 char curdir[PATH_MAX];
402 char vmbox[PATH_MAX];
414 int updated; /*!< decremented on each mail check until 1 -allows delay */
416 MAILSTREAM *mailstream;
418 char imapuser[80]; /*!< IMAP server login */
420 unsigned int quota_limit;
421 unsigned int quota_usage;
422 struct vm_state *persist_vms;
428 static char odbc_database[80];
429 static char odbc_table[80];
430 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
431 #define DISPOSE(a,b) remove_file(a,b)
432 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
433 #define EXISTS(a,b,c,d) (message_exists(a,b))
434 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
435 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
436 #define DELETE(a,b,c) (delete_file(a,b))
439 #define RETRIEVE(a,b,c,d) (imap_retrieve_file(a,b,c,d ))
440 #define DISPOSE(a,b) (imap_remove_file(a,b))
441 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
442 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
443 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
444 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
445 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
446 #define DELETE(a,b,c) (vm_delete(c))
448 #define RETRIEVE(a,b,c,d)
450 #define STORE(a,b,c,d,e,f,g,h,i)
451 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
452 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
453 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
454 #define DELETE(a,b,c) (vm_delete(c))
458 static char VM_SPOOL_DIR[PATH_MAX];
460 static char ext_pass_cmd[128];
464 #define PWDCHANGE_INTERNAL (1 << 1)
465 #define PWDCHANGE_EXTERNAL (1 << 2)
466 static int pwdchange = PWDCHANGE_INTERNAL;
469 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
472 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
474 # define tdesc "Comedian Mail (Voicemail System)"
478 static char userscontext[AST_MAX_EXTENSION] = "default";
480 static char *addesc = "Comedian Mail";
482 static char *synopsis_vm = "Leave a Voicemail message";
484 static char *descrip_vm =
485 " VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
486 "application allows the calling party to leave a message for the specified\n"
487 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
488 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
489 "specified mailbox does not exist.\n"
490 " The Voicemail application will exit if any of the following DTMF digits are\n"
492 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
493 " * - Jump to the 'a' extension in the current dialplan context.\n"
494 " This application will set the following channel variable upon completion:\n"
495 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
496 " application. The possible values are:\n"
497 " SUCCESS | USEREXIT | FAILED\n\n"
499 " b - Play the 'busy' greeting to the calling party.\n"
500 " g(#) - Use the specified amount of gain when recording the voicemail\n"
501 " message. The units are whole-number decibels (dB).\n"
502 " s - Skip the playback of instructions for leaving a message to the\n"
504 " u - Play the 'unavailable' greeting.\n";
506 static char *synopsis_vmain = "Check Voicemail messages";
508 static char *descrip_vmain =
509 " VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
510 "calling party to check voicemail messages. A specific mailbox, and optional\n"
511 "corresponding context, may be specified. If a mailbox is not provided, the\n"
512 "calling party will be prompted to enter one. If a context is not specified,\n"
513 "the 'default' context will be used.\n\n"
515 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
516 " is entered by the caller.\n"
517 " g(#) - Use the specified amount of gain when recording a voicemail\n"
518 " message. The units are whole-number decibels (dB).\n"
519 " s - Skip checking the passcode for the mailbox.\n"
520 " a(#) - Skip folder prompt and go directly to folder specified.\n"
521 " Defaults to INBOX\n";
523 static char *synopsis_vm_box_exists =
524 "Check to see if Voicemail mailbox exists";
526 static char *descrip_vm_box_exists =
527 " MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
528 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
530 " This application will set the following channel variable upon completion:\n"
531 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
532 " MailboxExists application. Possible values include:\n"
533 " SUCCESS | FAILED\n\n"
534 " Options: (none)\n";
536 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
538 static char *descrip_vmauthenticate =
539 " VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
540 "same way as the Authenticate application, but the passwords are taken from\n"
542 " If the mailbox is specified, only that mailbox's password will be considered\n"
543 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
544 "be set with the authenticated mailbox.\n\n"
546 " s - Skip playing the initial prompts.\n";
548 /* Leave a message */
549 static char *app = "VoiceMail";
551 /* Check mail, control, etc */
552 static char *app2 = "VoiceMailMain";
554 static char *app3 = "MailboxExists";
555 static char *app4 = "VMAuthenticate";
557 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
558 static AST_LIST_HEAD_STATIC(zones, vm_zone);
559 static int maxsilence;
561 static int silencethreshold = 128;
562 static char serveremail[80];
563 static char mailcmd[160]; /* Configurable mail cmd */
564 static char externnotify[160];
565 static struct ast_smdi_interface *smdi_iface = NULL;
566 static char vmfmts[80];
567 static double volgain;
568 static int vmminsecs;
569 static int vmmaxsecs;
572 static int maxlogins;
574 /*! Poll mailboxes for changes since there is something external to
575 * app_voicemail that may change them. */
576 static unsigned int poll_mailboxes;
578 /*! Polling frequency */
579 static unsigned int poll_freq;
580 /*! By default, poll every 30 seconds */
581 #define DEFAULT_POLL_FREQ 30
583 AST_MUTEX_DEFINE_STATIC(poll_lock);
584 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
585 static pthread_t poll_thread = AST_PTHREADT_NULL;
586 static unsigned char poll_thread_run;
588 /*! Subscription to ... MWI event subscriptions */
589 static struct ast_event_sub *mwi_sub_sub;
590 /*! Subscription to ... MWI event un-subscriptions */
591 static struct ast_event_sub *mwi_unsub_sub;
594 * \brief An MWI subscription
596 * This is so we can keep track of which mailboxes are subscribed to.
597 * This way, we know which mailboxes to poll when the pollmailboxes
598 * option is being used.
601 AST_RWLIST_ENTRY(mwi_sub) entry;
608 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
610 /* custom audio control prompts for voicemail playback */
611 static char listen_control_forward_key[12];
612 static char listen_control_reverse_key[12];
613 static char listen_control_pause_key[12];
614 static char listen_control_restart_key[12];
615 static char listen_control_stop_key[12];
617 /* custom password sounds */
618 static char vm_password[80] = "vm-password";
619 static char vm_newpassword[80] = "vm-newpassword";
620 static char vm_passchanged[80] = "vm-passchanged";
621 static char vm_reenterpassword[80] = "vm-reenterpassword";
622 static char vm_mismatch[80] = "vm-mismatch";
624 static struct ast_flags globalflags = {0};
626 static int saydurationminfo;
628 static char dialcontext[AST_MAX_CONTEXT] = "";
629 static char callcontext[AST_MAX_CONTEXT] = "";
630 static char exitcontext[AST_MAX_CONTEXT] = "";
632 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
635 static char *emailbody = NULL;
636 static char *emailsubject = NULL;
637 static char *pagerbody = NULL;
638 static char *pagersubject = NULL;
639 static char fromstring[100];
640 static char pagerfromstring[100];
641 static char emailtitle[100];
642 static char charset[32] = "ISO-8859-1";
644 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
645 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
646 static int adsiver = 1;
647 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
649 /* Forward declarations - generic */
650 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
651 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);
652 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
653 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
654 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
655 signed char record_gain, struct vm_state *vms);
656 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
657 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
658 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
659 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);
660 static void apply_options(struct ast_vm_user *vmu, const char *options);
661 static int is_valid_dtmf(const char *key);
663 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
664 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
669 static void populate_defaults(struct ast_vm_user *vmu)
671 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
672 if (saydurationminfo)
673 vmu->saydurationm = saydurationminfo;
674 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
675 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
676 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
678 vmu->maxsecs = vmmaxsecs;
680 vmu->maxmsg = maxmsg;
681 vmu->volgain = volgain;
684 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
687 if (!strcasecmp(var, "attach")) {
688 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
689 } else if (!strcasecmp(var, "attachfmt")) {
690 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
691 } else if (!strcasecmp(var, "serveremail")) {
692 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
693 } else if (!strcasecmp(var, "language")) {
694 ast_copy_string(vmu->language, value, sizeof(vmu->language));
695 } else if (!strcasecmp(var, "tz")) {
696 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
698 } else if (!strcasecmp(var, "imapuser")) {
699 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
700 } else if (!strcasecmp(var, "imappassword")) {
701 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
703 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
704 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
705 } else if (!strcasecmp(var, "saycid")){
706 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
707 } else if (!strcasecmp(var,"sendvoicemail")){
708 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
709 } else if (!strcasecmp(var, "review")){
710 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
711 } else if (!strcasecmp(var, "tempgreetwarn")){
712 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
713 } else if (!strcasecmp(var, "operator")){
714 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
715 } else if (!strcasecmp(var, "envelope")){
716 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
717 } else if (!strcasecmp(var, "moveheard")){
718 ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
719 } else if (!strcasecmp(var, "sayduration")){
720 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
721 } else if (!strcasecmp(var, "saydurationm")){
722 if (sscanf(value, "%d", &x) == 1) {
723 vmu->saydurationm = x;
725 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
727 } else if (!strcasecmp(var, "forcename")){
728 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
729 } else if (!strcasecmp(var, "forcegreetings")){
730 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
731 } else if (!strcasecmp(var, "callback")) {
732 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
733 } else if (!strcasecmp(var, "dialout")) {
734 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
735 } else if (!strcasecmp(var, "exitcontext")) {
736 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
737 } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
738 if (vmu->maxsecs <= 0) {
739 ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
740 vmu->maxsecs = vmmaxsecs;
742 vmu->maxsecs = atoi(value);
744 if (!strcasecmp(var, "maxmessage"))
745 ast_log(LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
746 } else if (!strcasecmp(var, "maxmsg")) {
747 vmu->maxmsg = atoi(value);
748 if (vmu->maxmsg <= 0) {
749 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
750 vmu->maxmsg = MAXMSG;
751 } else if (vmu->maxmsg > MAXMSGLIMIT) {
752 ast_log(LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
753 vmu->maxmsg = MAXMSGLIMIT;
755 } else if (!strcasecmp(var, "volgain")) {
756 sscanf(value, "%lf", &vmu->volgain);
757 } else if (!strcasecmp(var, "options")) {
758 apply_options(vmu, value);
762 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
765 if (!ast_strlen_zero(vmu->uniqueid)) {
766 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
768 ast_copy_string(vmu->password, password, sizeof(vmu->password));
778 static void apply_options(struct ast_vm_user *vmu, const char *options)
779 { /* Destructively Parse options and apply */
783 stringp = ast_strdupa(options);
784 while ((s = strsep(&stringp, "|"))) {
786 if ((var = strsep(&value, "=")) && value) {
787 apply_option(vmu, var, value);
792 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
794 struct ast_variable *tmp;
797 if (!strcasecmp(tmp->name, "vmsecret")) {
798 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
799 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
800 if (ast_strlen_zero(retval->password))
801 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
802 } else if (!strcasecmp(tmp->name, "uniqueid")) {
803 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
804 } else if (!strcasecmp(tmp->name, "pager")) {
805 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
806 } else if (!strcasecmp(tmp->name, "email")) {
807 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
808 } else if (!strcasecmp(tmp->name, "fullname")) {
809 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
810 } else if (!strcasecmp(tmp->name, "context")) {
811 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
813 } else if (!strcasecmp(tmp->name, "imapuser")) {
814 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
815 } else if (!strcasecmp(tmp->name, "imappassword")) {
816 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
819 apply_option(retval, tmp->name, tmp->value);
824 static int is_valid_dtmf(const char *key)
827 char *local_key = ast_strdupa(key);
829 for(i = 0; i < strlen(key); ++i) {
830 if(!strchr(VALID_DTMF, *local_key)) {
831 ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
839 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
841 struct ast_variable *var;
842 struct ast_vm_user *retval;
844 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
846 ast_set_flag(retval, VM_ALLOCED);
848 memset(retval, 0, sizeof(*retval));
850 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
851 populate_defaults(retval);
852 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
853 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
855 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
857 apply_options_full(retval, var);
858 ast_variables_destroy(var);
868 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
870 /* This function could be made to generate one from a database, too */
871 struct ast_vm_user *vmu=NULL, *cur;
872 AST_LIST_LOCK(&users);
874 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
877 AST_LIST_TRAVERSE(&users, cur, list) {
878 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
880 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
884 /* Make a copy, so that on a reload, we have no race */
885 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
886 memcpy(vmu, cur, sizeof(*vmu));
887 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
888 AST_LIST_NEXT(vmu, list) = NULL;
891 vmu = find_user_realtime(ivm, context, mailbox);
892 AST_LIST_UNLOCK(&users);
896 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
898 /* This function could be made to generate one from a database, too */
899 struct ast_vm_user *cur;
901 AST_LIST_LOCK(&users);
902 AST_LIST_TRAVERSE(&users, cur, list) {
903 if ((!context || !strcasecmp(context, cur->context)) &&
904 (!strcasecmp(mailbox, cur->mailbox)))
908 ast_copy_string(cur->password, newpass, sizeof(cur->password));
911 AST_LIST_UNLOCK(&users);
915 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
917 struct ast_config *cfg=NULL;
918 struct ast_variable *var=NULL;
919 struct ast_category *cat=NULL;
920 char *category=NULL, *value=NULL, *new=NULL;
921 const char *tmp=NULL;
922 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
924 if (!change_password_realtime(vmu, newpassword))
927 /* check voicemail.conf */
928 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
929 while ((category = ast_category_browse(cfg, category))) {
930 if (!strcasecmp(category, vmu->context)) {
931 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
932 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
935 value = strstr(tmp,",");
937 ast_log(LOG_WARNING, "variable has bad format.\n");
940 new = alloca((strlen(value)+strlen(newpassword)+1));
941 sprintf(new,"%s%s", newpassword, value);
942 if (!(cat = ast_category_get(cfg, category))) {
943 ast_log(LOG_WARNING, "Failed to get category structure.\n");
946 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
949 /* save the results */
950 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
951 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
952 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
956 /* check users.conf and update the password stored for the mailbox*/
957 /* if no vmsecret entry exists create one. */
958 if ((cfg = ast_config_load("users.conf", config_flags))) {
959 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
960 while ((category = ast_category_browse(cfg, category))) {
961 ast_debug(4, "users.conf: %s\n", category);
962 if (!strcasecmp(category, vmu->mailbox)) {
963 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
964 ast_debug(3, "looks like we need to make vmsecret!\n");
965 var = ast_variable_new("vmsecret", newpassword, "");
967 new = alloca(strlen(newpassword)+1);
968 sprintf(new, "%s", newpassword);
969 if (!(cat = ast_category_get(cfg, category))) {
970 ast_debug(4, "failed to get category!\n");
974 ast_variable_update(cat, "vmsecret", new, NULL, 0);
976 ast_variable_append(cat, var);
979 /* save the results and clean things up */
980 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
981 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
982 config_text_file_save("users.conf", cfg, "AppVoicemail");
986 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
989 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
990 if (!ast_safe_system(buf))
991 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
994 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
996 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1000 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num)
1003 if ((res = ast_mkdir(dir, 01777))) {
1004 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
1005 return snprintf(dest, len, "%s/msg%04d", dir, num);
1007 return snprintf(dest, len, "%s/msg%04d", dir, num);
1010 static void vm_imap_delete(int msgnum, struct vm_state *vms)
1012 unsigned long messageNum = 0;
1015 /* find real message number based on msgnum */
1016 /* this may be an index into vms->msgArray based on the msgnum. */
1018 messageNum = vms->msgArray[msgnum];
1019 if (messageNum == 0) {
1020 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1023 ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1024 /* delete message */
1025 snprintf (arg, sizeof(arg), "%lu",messageNum);
1026 mail_setflag (vms->mailstream,arg,"\\DELETED");
1030 static int make_file(char *dest, int len, char *dir, int num)
1032 return snprintf(dest, len, "%s/msg%04d", dir, num);
1035 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1036 * \param dest String. base directory.
1037 * \param len Length of dest.
1038 * \param context String. Ignored if is null or empty string.
1039 * \param ext String. Ignored if is null or empty string.
1040 * \param folder String. Ignored if is null or empty string.
1041 * \return -1 on failure, 0 on success.
1043 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1045 mode_t mode = VOICEMAIL_DIR_MODE;
1048 make_dir(dest, len, context, ext, folder);
1049 if ((res = ast_mkdir(dest, mode))) {
1050 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1056 /*! \brief Lock file path
1057 only return failure if ast_lock_path returns 'timeout',
1058 not if the path does not exist or any other reason
1060 static int vm_lock_path(const char *path)
1062 switch (ast_lock_path(path)) {
1063 case AST_LOCK_TIMEOUT:
1072 struct generic_prepare_struct {
1078 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
1080 struct generic_prepare_struct *gps = data;
1084 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1085 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1086 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1089 res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
1090 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1091 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
1092 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1095 for (i = 0; i < gps->argc; i++)
1096 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
1101 static int retrieve_file(char *dir, int msgnum)
1107 void *fdm = MAP_FAILED;
1108 SQLSMALLINT colcount=0;
1115 SQLSMALLINT datatype;
1116 SQLSMALLINT decimaldigits;
1117 SQLSMALLINT nullable;
1123 char full_fn[PATH_MAX];
1125 char *argv[] = { dir, msgnums };
1126 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1128 struct odbc_obj *obj;
1129 obj = ast_odbc_request_obj(odbc_database, 0);
1131 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1132 c = strchr(fmt, '|');
1135 if (!strcasecmp(fmt, "wav49"))
1137 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1139 make_file(fn, sizeof(fn), dir, msgnum);
1141 ast_copy_string(fn, dir, sizeof(fn));
1142 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1144 if (!(f = fopen(full_fn, "w+"))) {
1145 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1149 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1150 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1151 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1153 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1154 ast_odbc_release_obj(obj);
1157 res = SQLFetch(stmt);
1158 if (res == SQL_NO_DATA) {
1159 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1160 ast_odbc_release_obj(obj);
1163 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1164 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1165 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1166 ast_odbc_release_obj(obj);
1169 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1171 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1172 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1173 ast_odbc_release_obj(obj);
1176 res = SQLNumResultCols(stmt, &colcount);
1177 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1178 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1179 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1180 ast_odbc_release_obj(obj);
1184 fprintf(f, "[message]\n");
1185 for (x=0;x<colcount;x++) {
1187 collen = sizeof(coltitle);
1188 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
1189 &datatype, &colsize, &decimaldigits, &nullable);
1190 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1191 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1192 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1193 ast_odbc_release_obj(obj);
1196 if (!strcasecmp(coltitle, "recording")) {
1198 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1202 lseek(fd, fdlen - 1, SEEK_SET);
1203 if (write(fd, tmp, 1) != 1) {
1208 /* Read out in small chunks */
1209 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1210 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1211 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1212 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1213 ast_odbc_release_obj(obj);
1216 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1217 munmap(fdm, CHUNKSIZE);
1218 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1219 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1221 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1222 ast_odbc_release_obj(obj);
1227 truncate(full_fn, fdlen);
1230 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1231 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1232 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1233 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1234 ast_odbc_release_obj(obj);
1237 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1238 fprintf(f, "%s=%s\n", coltitle, rowdata);
1241 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1242 ast_odbc_release_obj(obj);
1244 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1253 static int remove_file(char *dir, int msgnum)
1256 char full_fn[PATH_MAX];
1260 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1261 make_file(fn, sizeof(fn), dir, msgnum);
1263 ast_copy_string(fn, dir, sizeof(fn));
1264 ast_filedelete(fn, NULL);
1265 if (ast_check_realtime("voicemail_data")) {
1266 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1268 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1273 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1280 char *argv[] = { dir };
1281 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1283 struct odbc_obj *obj;
1284 obj = ast_odbc_request_obj(odbc_database, 0);
1286 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1287 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1289 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1290 ast_odbc_release_obj(obj);
1293 res = SQLFetch(stmt);
1294 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1295 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1296 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1297 ast_odbc_release_obj(obj);
1300 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1301 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1302 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1303 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1304 ast_odbc_release_obj(obj);
1307 if (sscanf(rowdata, "%d", &x) != 1)
1308 ast_log(LOG_WARNING, "Failed to read message count!\n");
1309 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1310 ast_odbc_release_obj(obj);
1312 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1317 static int message_exists(char *dir, int msgnum)
1325 char *argv[] = { dir, msgnums };
1326 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1328 struct odbc_obj *obj;
1329 obj = ast_odbc_request_obj(odbc_database, 0);
1331 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1332 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1333 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1335 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1336 ast_odbc_release_obj(obj);
1339 res = SQLFetch(stmt);
1340 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1341 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1342 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1343 ast_odbc_release_obj(obj);
1346 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1347 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1348 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1349 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1350 ast_odbc_release_obj(obj);
1353 if (sscanf(rowdata, "%d", &x) != 1)
1354 ast_log(LOG_WARNING, "Failed to read message count!\n");
1355 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1356 ast_odbc_release_obj(obj);
1358 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1363 static int count_messages(struct ast_vm_user *vmu, char *dir)
1365 return last_message_index(vmu, dir) + 1;
1368 static void delete_file(char *sdir, int smsg)
1373 char *argv[] = { sdir, msgnums };
1374 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1376 struct odbc_obj *obj;
1377 obj = ast_odbc_request_obj(odbc_database, 0);
1379 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1380 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1381 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1383 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1385 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1386 ast_odbc_release_obj(obj);
1388 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1392 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1398 struct odbc_obj *obj;
1399 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1400 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1402 delete_file(ddir, dmsg);
1403 obj = ast_odbc_request_obj(odbc_database, 0);
1405 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1406 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1407 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);
1408 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1410 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1412 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1413 ast_odbc_release_obj(obj);
1415 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1419 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1424 void *fdm = MAP_FAILED;
1431 char full_fn[PATH_MAX];
1434 const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1435 const char *category = "";
1436 struct ast_config *cfg=NULL;
1437 struct odbc_obj *obj;
1438 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
1440 delete_file(dir, msgnum);
1441 obj = ast_odbc_request_obj(odbc_database, 0);
1443 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1444 c = strchr(fmt, '|');
1447 if (!strcasecmp(fmt, "wav49"))
1449 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1451 make_file(fn, sizeof(fn), dir, msgnum);
1453 ast_copy_string(fn, dir, sizeof(fn));
1454 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1455 cfg = ast_config_load(full_fn, config_flags);
1456 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1457 fd = open(full_fn, O_RDWR);
1459 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1460 ast_odbc_release_obj(obj);
1464 context = ast_variable_retrieve(cfg, "message", "context");
1465 if (!context) context = "";
1466 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1467 if (!macrocontext) macrocontext = "";
1468 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1469 if (!callerid) callerid = "";
1470 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1471 if (!origtime) origtime = "";
1472 duration = ast_variable_retrieve(cfg, "message", "duration");
1473 if (!duration) duration = "";
1474 category = ast_variable_retrieve(cfg, "message", "category");
1475 if (!category) category = "";
1477 fdlen = lseek(fd, 0, SEEK_END);
1478 lseek(fd, 0, SEEK_SET);
1479 printf("Length is %zd\n", fdlen);
1480 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1481 if (fdm == MAP_FAILED) {
1482 ast_log(LOG_WARNING, "Memory map failed!\n");
1483 ast_odbc_release_obj(obj);
1486 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1487 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1488 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1489 ast_odbc_release_obj(obj);
1492 if (!ast_strlen_zero(category))
1493 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1495 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1496 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1497 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1498 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1499 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1500 ast_odbc_release_obj(obj);
1503 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1504 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1505 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1506 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1507 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1508 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1509 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1510 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1511 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1512 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1513 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1514 if (!ast_strlen_zero(category))
1515 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1516 res = ast_odbc_smart_execute(obj, stmt);
1517 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1518 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1519 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1520 ast_odbc_release_obj(obj);
1523 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1524 ast_odbc_release_obj(obj);
1526 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1529 ast_config_destroy(cfg);
1530 if (fdm != MAP_FAILED)
1537 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1543 struct odbc_obj *obj;
1544 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1545 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1547 delete_file(ddir, dmsg);
1548 obj = ast_odbc_request_obj(odbc_database, 0);
1550 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1551 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1552 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1553 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1555 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1557 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1558 ast_odbc_release_obj(obj);
1560 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1565 #ifndef IMAP_STORAGE
1566 static int count_messages(struct ast_vm_user *vmu, char *dir)
1568 /* Find all .txt files - even if they are not in sequence from 0000 */
1572 struct dirent *vment = NULL;
1574 if (vm_lock_path(dir))
1575 return ERROR_LOCK_PATH;
1577 if ((vmdir = opendir(dir))) {
1578 while ((vment = readdir(vmdir))) {
1579 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1584 ast_unlock_path(dir);
1589 static void rename_file(char *sfn, char *dfn)
1591 char stxt[PATH_MAX];
1592 char dtxt[PATH_MAX];
1593 ast_filerename(sfn,dfn,NULL);
1594 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1595 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1596 if (ast_check_realtime("voicemail_data")) {
1597 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1602 static int copy(char *infile, char *outfile)
1610 #ifdef HARDLINK_WHEN_POSSIBLE
1611 /* Hard link if possible; saves disk space & is faster */
1612 if (link(infile, outfile)) {
1614 if ((ifd = open(infile, O_RDONLY)) < 0) {
1615 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1618 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1619 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1624 len = read(ifd, buf, sizeof(buf));
1626 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1632 res = write(ofd, buf, len);
1633 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1634 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1644 #ifdef HARDLINK_WHEN_POSSIBLE
1646 /* Hard link succeeded */
1652 static void copy_file(char *frompath, char *topath)
1654 char frompath2[PATH_MAX], topath2[PATH_MAX];
1655 struct ast_variable *tmp,*var = NULL;
1656 char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
1657 ast_filecopy(frompath, topath, NULL);
1658 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1659 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1660 if (ast_check_realtime("voicemail_data")) {
1661 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
1662 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
1663 for (tmp = var; tmp; tmp = tmp->next) {
1664 if (!strcasecmp(tmp->name, "origmailbox")) {
1665 origmailbox = tmp->value;
1666 } else if (!strcasecmp(tmp->name, "context")) {
1667 context = tmp->value;
1668 } else if (!strcasecmp(tmp->name, "macrocontext")) {
1669 macrocontext = tmp->value;
1670 } else if (!strcasecmp(tmp->name, "exten")) {
1672 } else if (!strcasecmp(tmp->name, "priority")) {
1673 priority = tmp->value;
1674 } else if (!strcasecmp(tmp->name, "callerchan")) {
1675 callerchan = tmp->value;
1676 } else if (!strcasecmp(tmp->name, "callerid")) {
1677 callerid = tmp->value;
1678 } else if (!strcasecmp(tmp->name, "origdate")) {
1679 origdate = tmp->value;
1680 } else if (!strcasecmp(tmp->name, "origtime")) {
1681 origtime = tmp->value;
1682 } else if (!strcasecmp(tmp->name, "category")) {
1683 category = tmp->value;
1684 } else if (!strcasecmp(tmp->name, "duration")) {
1685 duration = tmp->value;
1688 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);
1690 copy(frompath2, topath2);
1691 ast_variables_destroy(var);
1694 * A negative return value indicates an error.
1695 * \note Should always be called with a lock already set on dir.
1697 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1700 unsigned char map[MAXMSGLIMIT] = "";
1702 struct dirent *msgdirent;
1705 /* Reading the entire directory into a file map scales better than
1706 * doing a stat repeatedly on a predicted sequence. I suspect this
1707 * is partially due to stat(2) internally doing a readdir(2) itself to
1708 * find each file. */
1709 msgdir = opendir(dir);
1710 while ((msgdirent = readdir(msgdir))) {
1711 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1716 for (x = 0; x < vmu->maxmsg; x++) {
1724 #endif /*#ifndef IMAP_STORAGE*/
1725 #endif /*#else of #ifdef ODBC_STORAGE*/
1726 #ifndef ODBC_STORAGE
1727 static int vm_delete(char *file)
1732 txtsize = (strlen(file) + 5)*sizeof(char);
1733 txt = alloca(txtsize);
1734 /* Sprintf here would safe because we alloca'd exactly the right length,
1735 * but trying to eliminate all sprintf's anyhow
1737 if (ast_check_realtime("voicemail_data")) {
1738 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
1740 snprintf(txt, txtsize, "%s.txt", file);
1742 return ast_filedelete(file, NULL);
1746 static int inbuf(struct baseio *bio, FILE *fi)
1753 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1767 static int inchar(struct baseio *bio, FILE *fi)
1769 if (bio->iocp>=bio->iolen) {
1770 if (!inbuf(bio, fi))
1774 return bio->iobuf[bio->iocp++];
1777 static int ochar(struct baseio *bio, int c, FILE *so)
1779 if (bio->linelength >= BASELINELEN) {
1780 if (fputs(eol,so) == EOF)
1786 if (putc(((unsigned char)c),so) == EOF)
1794 static int base_encode(char *filename, FILE *so)
1796 unsigned char dtable[BASEMAXINLINE];
1801 memset(&bio, 0, sizeof(bio));
1802 bio.iocp = BASEMAXINLINE;
1804 if (!(fi = fopen(filename, "rb"))) {
1805 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
1809 for (i= 0; i<9; i++) {
1812 dtable[26+i]= 'a'+i;
1813 dtable[26+i+9]= 'j'+i;
1815 for (i= 0; i<8; i++) {
1816 dtable[i+18]= 'S'+i;
1817 dtable[26+i+18]= 's'+i;
1819 for (i= 0; i<10; i++) {
1820 dtable[52+i]= '0'+i;
1826 unsigned char igroup[3], ogroup[4];
1829 igroup[0]= igroup[1]= igroup[2]= 0;
1831 for (n= 0;n<3;n++) {
1832 if ((c = inchar(&bio, fi)) == EOF) {
1837 igroup[n]= (unsigned char)c;
1841 ogroup[0]= dtable[igroup[0]>>2];
1842 ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1843 ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1844 ogroup[3]= dtable[igroup[2]&0x3F];
1854 ochar(&bio, ogroup[i], so);
1858 if (fputs(eol,so) == EOF)
1866 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)
1869 /* Prepare variables for substitution in email body and subject */
1870 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1871 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1872 snprintf(passdata, passdatasize, "%d", msgnum);
1873 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1874 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1875 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1876 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1877 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1878 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1879 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1880 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1883 static char *quote(const char *from, char *to, size_t len)
1887 for (; ptr < to + len - 1; from++) {
1890 else if (*from == '\0')
1894 if (ptr < to + len - 1)
1901 * fill in *tm for current time according to the proper timezone, if any.
1902 * Return tm so it can be used as a function argument.
1904 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
1906 const struct vm_zone *z = NULL;
1907 struct timeval t = ast_tvnow();
1909 /* Does this user have a timezone specified? */
1910 if (!ast_strlen_zero(vmu->zonetag)) {
1911 /* Find the zone in the list */
1912 AST_LIST_LOCK(&zones);
1913 AST_LIST_TRAVERSE(&zones, z, list) {
1914 if (!strcmp(z->name, vmu->zonetag))
1917 AST_LIST_UNLOCK(&zones);
1919 ast_localtime(&t, tm, z ? z->timezone : NULL);
1923 /*! \brief same as mkstemp, but return a FILE * */
1924 static FILE *vm_mkftemp(char *template)
1927 int pfd = mkstemp(template);
1928 chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1930 p = fdopen(pfd, "w+");
1939 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)
1942 char host[MAXHOSTNAMELEN] = "";
1950 size_t len_passdata;
1951 char *greeting_attachment;
1959 gethostname(host, sizeof(host)-1);
1961 if (strchr(srcemail, '@'))
1962 ast_copy_string(who, srcemail, sizeof(who));
1964 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1966 greeting_attachment = strrchr(ast_strdupa(attach), '/');
1967 if (greeting_attachment)
1968 *greeting_attachment++ = '\0';
1970 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1971 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1972 fprintf(p, "Date: %s" ENDL, date);
1974 /* Set date format for voicemail mail */
1975 ast_strftime(date, sizeof(date), emaildateformat, &tm);
1977 if (!ast_strlen_zero(fromstring)) {
1978 struct ast_channel *ast;
1979 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1981 int vmlen = strlen(fromstring)*3 + 200;
1982 if ((passdata = alloca(vmlen))) {
1983 memset(passdata, 0, vmlen);
1984 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1985 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1986 len_passdata = strlen(passdata) * 2 + 3;
1987 passdata2 = alloca(len_passdata);
1988 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1990 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1991 ast_channel_free(ast);
1993 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1995 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1996 len_passdata = strlen(vmu->fullname) * 2 + 3;
1997 passdata2 = alloca(len_passdata);
1998 fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1999 if (!ast_strlen_zero(emailsubject)) {
2000 struct ast_channel *ast;
2001 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2003 int vmlen = strlen(emailsubject) * 3 + 200;
2004 if ((passdata = alloca(vmlen))) {
2005 memset(passdata, 0, vmlen);
2006 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2007 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
2008 fprintf(p, "Subject: %s" ENDL, passdata);
2010 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2011 ast_channel_free(ast);
2013 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2014 } else if (!ast_strlen_zero(emailtitle)) {
2015 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
2017 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
2018 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2020 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2021 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
2023 /* additional information needed for IMAP searching */
2024 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
2025 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
2026 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
2027 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
2028 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
2029 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
2030 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
2031 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
2032 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
2033 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
2034 if (!ast_strlen_zero(category))
2035 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
2036 fprintf(p, "X-Asterisk-VM-Message-Type: %s\n", msgnum > -1 ? "Message" : greeting_attachment);
2037 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
2038 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
2040 if (!ast_strlen_zero(cidnum))
2041 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
2042 if (!ast_strlen_zero(cidname))
2043 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
2044 fprintf(p, "MIME-Version: 1.0" ENDL);
2045 if (attach_user_voicemail) {
2046 /* Something unique. */
2047 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
2049 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
2050 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
2051 fprintf(p, "--%s" ENDL, bound);
2053 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
2055 struct ast_channel *ast;
2056 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2058 int vmlen = strlen(emailbody)*3 + 200;
2059 if ((passdata = alloca(vmlen))) {
2060 memset(passdata, 0, vmlen);
2061 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2062 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2063 fprintf(p, "%s" ENDL, passdata);
2065 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2066 ast_channel_free(ast);
2068 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2069 } else if (msgnum > -1){
2070 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
2072 "in mailbox %s from %s, on %s so you might" ENDL
2073 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
2074 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2076 fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
2077 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
2079 if (attach_user_voicemail) {
2080 /* Eww. We want formats to tell us their own MIME type */
2081 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2082 char tmpdir[256], newtmp[256];
2085 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2086 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2087 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2088 tmpfd = mkstemp(newtmp);
2089 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
2090 ast_debug(3, "newtmp: %s\n", newtmp);
2092 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2093 ast_safe_system(tmpcmd);
2095 ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2098 fprintf(p, "--%s" ENDL, bound);
2100 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2102 fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
2103 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2104 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2106 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2108 fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
2109 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2110 base_encode(fname, p);
2111 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
2121 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)
2124 char tmp[80] = "/tmp/astmail-XXXXXX";
2127 if (vmu && ast_strlen_zero(vmu->email)) {
2128 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
2131 if (!strcmp(format, "wav49"))
2133 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));
2134 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2136 if ((p = vm_mkftemp(tmp)) == NULL) {
2137 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2140 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2142 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2143 ast_safe_system(tmp2);
2144 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2149 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)
2152 char host[MAXHOSTNAMELEN] = "";
2155 char tmp[80] = "/tmp/astmail-XXXXXX";
2156 char tmp2[PATH_MAX];
2160 if ((p = vm_mkftemp(tmp)) == NULL) {
2161 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2164 gethostname(host, sizeof(host)-1);
2165 if (strchr(srcemail, '@'))
2166 ast_copy_string(who, srcemail, sizeof(who));
2168 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2169 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2170 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2171 fprintf(p, "Date: %s\n", date);
2173 if (*pagerfromstring) {
2174 struct ast_channel *ast;
2175 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2177 int vmlen = strlen(fromstring)*3 + 200;
2178 if ((passdata = alloca(vmlen))) {
2179 memset(passdata, 0, vmlen);
2180 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2181 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2182 fprintf(p, "From: %s <%s>\n", passdata, who);
2184 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2185 ast_channel_free(ast);
2187 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2189 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2190 fprintf(p, "To: %s\n", pager);
2192 struct ast_channel *ast;
2193 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2195 int vmlen = strlen(pagersubject) * 3 + 200;
2196 if ((passdata = alloca(vmlen))) {
2197 memset(passdata, 0, vmlen);
2198 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2199 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2200 fprintf(p, "Subject: %s\n\n", passdata);
2202 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2203 ast_channel_free(ast);
2205 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2207 fprintf(p, "Subject: New VM\n\n");
2209 ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2211 struct ast_channel *ast;
2212 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2214 int vmlen = strlen(pagerbody) * 3 + 200;
2215 passdata = alloca(vmlen);
2216 memset(passdata, 0, vmlen);
2217 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2218 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2219 fprintf(p, "%s\n", passdata);
2220 ast_channel_free(ast);
2222 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2224 fprintf(p, "New %s long msg in box %s\n"
2225 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2228 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2229 ast_safe_system(tmp2);
2230 ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2234 static int get_date(char *s, int len)
2237 struct timeval t = ast_tvnow();
2239 ast_localtime(&t, &tm, NULL);
2241 return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2244 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2248 char dest[PATH_MAX];
2250 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2252 if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
2253 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2257 RETRIEVE(fn, -1, ext, context);
2258 if (ast_fileexists(fn, NULL, NULL) > 0) {
2259 res = ast_stream_and_wait(chan, fn, ecodes);
2265 /* Dispose just in case */
2267 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2270 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2274 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2278 static void free_user(struct ast_vm_user *vmu)
2280 if (!ast_test_flag(vmu, VM_ALLOCED))
2286 static void free_zone(struct vm_zone *z)
2291 static const char *mbox(int id)
2293 static const char *msgs[] = {
2305 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2308 static int folder_int(const char *folder)
2310 /*assume a NULL folder means INBOX*/
2313 if(!strcasecmp(folder, "INBOX"))
2315 else if (!strcasecmp(folder, "Old"))
2317 else if (!strcasecmp(folder, "Work"))
2319 else if (!strcasecmp(folder, "Family"))
2321 else if (!strcasecmp(folder, "Friends"))
2323 else if (!strcasecmp(folder, "Cust1"))
2325 else if (!strcasecmp(folder, "Cust2"))
2327 else if (!strcasecmp(folder, "Cust3"))
2329 else if (!strcasecmp(folder, "Cust4"))
2331 else if (!strcasecmp(folder, "Cust5"))
2333 else /*assume they meant INBOX if folder is not found otherwise*/
2339 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2340 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2347 char tmp[PATH_MAX] = "";
2348 struct odbc_obj *obj;
2350 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2357 /* If no mailbox, return immediately */
2358 if (ast_strlen_zero(mailbox))
2361 ast_copy_string(tmp, mailbox, sizeof(tmp));
2363 context = strchr(tmp, '@');
2368 context = "default";
2370 obj = ast_odbc_request_obj(odbc_database, 0);
2372 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2373 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2375 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2376 ast_odbc_release_obj(obj);
2379 res = SQLFetch(stmt);
2380 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2381 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2382 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2383 ast_odbc_release_obj(obj);
2386 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2387 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2388 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2389 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2390 ast_odbc_release_obj(obj);
2393 *newmsgs = atoi(rowdata);
2394 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2396 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2397 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2399 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2400 ast_odbc_release_obj(obj);
2403 res = SQLFetch(stmt);
2404 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2405 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2406 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2407 ast_odbc_release_obj(obj);
2410 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2411 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2412 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2413 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2414 ast_odbc_release_obj(obj);
2417 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2418 ast_odbc_release_obj(obj);
2419 *oldmsgs = atoi(rowdata);
2422 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2428 static int messagecount(const char *context, const char *mailbox, const char *folder)
2430 struct odbc_obj *obj = NULL;
2433 SQLHSTMT stmt = NULL;
2436 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2439 /* If no mailbox, return immediately */
2440 if (ast_strlen_zero(mailbox))
2443 obj = ast_odbc_request_obj(odbc_database, 0);
2445 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2446 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2448 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2451 res = SQLFetch(stmt);
2452 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2453 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2454 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2457 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2458 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2459 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2460 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2463 nummsgs = atoi(rowdata);
2464 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2466 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2470 ast_odbc_release_obj(obj);
2474 static int has_voicemail(const char *mailbox, const char *folder)
2476 char tmp[256], *tmp2 = tmp, *mbox, *context;
2477 ast_copy_string(tmp, mailbox, sizeof(tmp));
2478 while ((context = mbox = strsep(&tmp2, ","))) {
2479 strsep(&context, "@");
2480 if (ast_strlen_zero(context))
2481 context = "default";
2482 if (messagecount(context, mbox, folder))
2488 #elif defined(IMAP_STORAGE)
2490 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)
2492 char *myserveremail = serveremail;
2497 char tmp[80] = "/tmp/astmail-XXXXXX";
2502 /* Attach only the first format */
2503 fmt = ast_strdupa(fmt);
2505 strsep(&stringp, "|");
2507 if (!ast_strlen_zero(vmu->serveremail))
2508 myserveremail = vmu->serveremail;
2511 make_file(fn, sizeof(fn), dir, msgnum);
2513 ast_copy_string (fn, dir, sizeof(fn));
2515 if (ast_strlen_zero(vmu->email))
2516 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2518 if (!strcmp(fmt, "wav49"))
2520 ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2522 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2524 if (!(p = vm_mkftemp(tmp))) {
2525 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2529 if (msgnum < 0 && imapgreetings) {
2530 init_mailstream(vms, GREETINGS_FOLDER);
2531 imap_delete_old_greeting(fn, vms);
2534 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);
2535 /* read mail file to memory */
2538 if (!(buf = ast_malloc(len+1))) {
2539 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2542 fread(buf, len, 1, p);
2543 ((char *)buf)[len] = '\0';
2544 INIT(&str, mail_string, buf, len);
2545 init_mailstream(vms, NEW_FOLDER);
2546 imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
2547 if(!mail_append(vms->mailstream, mailbox, &str))
2548 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2552 ast_debug(3, "%s stored\n", fn);
2557 static int messagecount(const char *context, const char *mailbox, const char *folder)
2562 struct ast_vm_user *vmu, vmus;
2563 struct vm_state *vms_p;
2565 int fold = folder_int(folder);
2567 if (ast_strlen_zero(mailbox))
2570 /* We have to get the user before we can open the stream! */
2571 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2572 vmu = find_user(&vmus, context, mailbox);
2574 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
2577 /* No IMAP account available */
2578 if (vmu->imapuser[0] == '\0') {
2579 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2584 /* No IMAP account available */
2585 if (vmu->imapuser[0] == '\0') {
2586 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2591 /* check if someone is accessing this box right now... */
2592 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2594 vms_p = get_vm_state_by_mailbox(mailbox,1);
2597 ast_debug(3, "Returning before search - user is logged in\n");
2598 if(fold == 0) {/*INBOX*/
2599 return vms_p->newmessages;
2601 if(fold == 1) {/*Old messages*/
2602 return vms_p->oldmessages;
2606 /* add one if not there... */
2607 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2609 vms_p = get_vm_state_by_mailbox(mailbox,0);
2613 ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
2614 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2617 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2618 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2619 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2620 ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2622 /* set mailbox to INBOX! */
2623 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
2624 init_vm_state(vms_p);
2625 vmstate_insert(vms_p);
2627 ret = init_mailstream(vms_p, fold);
2628 if (!vms_p->mailstream) {
2629 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2633 pgm = mail_newsearchpgm ();
2634 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
2640 /* In the special case where fold is 1 (old messages) we have to do things a bit
2641 * differently. Old messages are stored in the INBOX but are marked as "seen"
2650 vms_p->vmArrayIndex = 0;
2651 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2653 vms_p->newmessages = vms_p->vmArrayIndex;
2655 vms_p->oldmessages = vms_p->vmArrayIndex;
2656 /*Freeing the searchpgm also frees the searchhdr*/
2657 mail_free_searchpgm(&pgm);
2659 return vms_p->vmArrayIndex;
2661 mail_ping(vms_p->mailstream);
2665 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
2667 char tmp[PATH_MAX] = "";
2677 ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
2678 /* If no mailbox, return immediately */
2679 if (ast_strlen_zero(mailbox_context))
2682 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2683 context = strchr(tmp, '@');
2684 if (strchr(mailbox_context, ',')) {
2686 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2688 while((cur = strsep(&mb, ", "))) {
2689 if (!ast_strlen_zero(cur)) {
2690 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2707 context = "default";
2708 mailboxnc = (char *)mailbox_context;
2711 if((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
2715 if((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
2722 static int has_voicemail(const char *mailbox, const char *folder)
2724 char tmp[256], *tmp2, *mbox, *context;
2725 ast_copy_string(tmp, mailbox, sizeof(tmp));
2727 if(strchr(tmp2, ',')) {
2728 while((mbox = strsep(&tmp2, ","))) {
2729 if(!ast_strlen_zero(mbox)) {
2730 if (has_voicemail(mbox, folder))
2735 if ((context= strchr(tmp, '@')))
2738 context = "default";
2739 return messagecount(context, tmp, folder) ? 1 : 0;
2742 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)
2745 struct vm_state *sendvms = NULL, *destvms = NULL;
2746 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2747 if(!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 2)))
2749 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2752 if(!(destvms = get_vm_state_by_imapuser(recip->imapuser, 2)))
2754 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2757 imap_mailbox_name(dest, sizeof(dest), destvms, imbox, 1);
2758 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2759 if((mail_copy(sendvms->mailstream, messagestring, dest) == T))
2761 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2766 #ifndef IMAP_STORAGE
2767 /* copy message only used by file storage */
2768 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)
2770 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2771 const char *frombox = mbox(imbox);
2774 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2776 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2779 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2781 ast_copy_string(fromdir, dir, sizeof(fromdir));
2783 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2784 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2786 if (vm_lock_path(todir))
2787 return ERROR_LOCK_PATH;
2789 recipmsgnum = last_message_index(recip, todir) + 1;
2790 if (recipmsgnum < recip->maxmsg) {
2791 make_file(topath, sizeof(topath), todir, recipmsgnum);
2792 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2794 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2796 ast_unlock_path(todir);
2797 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2802 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2803 static int messagecount(const char *context, const char *mailbox, const char *folder)
2805 return __has_voicemail(context, mailbox, folder, 0);
2809 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2816 /* If no mailbox, return immediately */
2817 if (ast_strlen_zero(mailbox))
2820 if (ast_strlen_zero(folder))
2822 if (ast_strlen_zero(context))
2823 context = "default";
2825 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2827 if (!(dir = opendir(fn)))
2830 while ((de = readdir(dir))) {
2831 if (!strncasecmp(de->d_name, "msg", 3)) {
2835 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2846 static int has_voicemail(const char *mailbox, const char *folder)
2848 char tmp[256], *tmp2 = tmp, *mbox, *context;
2849 ast_copy_string(tmp, mailbox, sizeof(tmp));
2850 while ((mbox = strsep(&tmp2, ","))) {
2851 if ((context = strchr(mbox, '@')))
2854 context = "default";
2855 if (__has_voicemail(context, mbox, folder, 1))
2862 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2867 /* If no mailbox, return immediately */
2868 if (ast_strlen_zero(mailbox))
2876 if (strchr(mailbox, ',')) {
2880 ast_copy_string(tmp, mailbox, sizeof(tmp));
2882 while ((cur = strsep(&mb, ", "))) {
2883 if (!ast_strlen_zero(cur)) {
2884 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2897 ast_copy_string(tmp, mailbox, sizeof(tmp));
2899 if ((context = strchr(tmp, '@')))
2902 context = "default";
2905 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2907 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2914 static void run_externnotify(char *context, char *extension)
2916 char arguments[255];
2917 char ext_context[256] = "";
2918 int newvoicemails = 0, oldvoicemails = 0;
2919 struct ast_smdi_mwi_message *mwi_msg;
2921 if (!ast_strlen_zero(context))
2922 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2924 ast_copy_string(ext_context, extension, sizeof(ext_context));
2927 if (ast_app_has_voicemail(ext_context, NULL))
2928 ast_smdi_mwi_set(smdi_iface, extension);
2930 ast_smdi_mwi_unset(smdi_iface, extension);
2932 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2933 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2934 if (!strncmp(mwi_msg->cause, "INV", 3))
2935 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2936 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2937 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2938 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2939 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2941 ast_debug(1, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2945 if (!ast_strlen_zero(externnotify)) {
2946 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2947 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2949 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2950 ast_debug(1, "Executing %s\n", arguments);
2951 ast_safe_system(arguments);
2956 struct leave_vm_options {
2958 signed char record_gain;
2961 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2964 int newmsgs, oldmsgs;
2965 struct vm_state *vms = NULL;
2967 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2983 char dir[PATH_MAX], tmpdir[PATH_MAX];
2985 char prefile[PATH_MAX] = "";
2986 char tempfile[PATH_MAX] = "";
2987 char ext_context[256] = "";
2990 char ecodes[16] = "#";
2991 char tmp[1024] = "", *tmpptr;
2992 struct ast_vm_user *vmu;
2993 struct ast_vm_user svm;
2994 const char *category = NULL;
2996 ast_copy_string(tmp, ext, sizeof(tmp));
2998 if ((context = strchr(tmp, '@'))) {
3000 tmpptr = strchr(context, '&');
3002 tmpptr = strchr(ext, '&');
3008 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3010 ast_debug(3, "Before find_user\n");
3011 if (!(vmu = find_user(&svm, context, ext))) {
3012 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3013 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3016 /* Setup pre-file if appropriate */
3017 if (strcmp(vmu->context, "default"))
3018 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3020 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
3021 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3022 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3023 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3024 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3026 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3027 if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
3028 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3031 RETRIEVE(tempfile, -1, ext, context);
3032 if (ast_fileexists(tempfile, NULL, NULL) > 0)
3033 ast_copy_string(prefile, tempfile, sizeof(prefile));
3034 DISPOSE(tempfile, -1);
3035 /* It's easier just to try to make it than to check for its existence */
3036 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3038 /* Check current or macro-calling context for special extensions */
3039 if (ast_test_flag(vmu, VM_OPERATOR)) {
3040 if (!ast_strlen_zero(vmu->exit)) {
3041 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3042 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3045 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3046 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3049 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3050 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3055 if (!ast_strlen_zero(vmu->exit)) {
3056 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3057 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3058 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3059 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3060 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3061 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3065 /* Play the beginning intro if desired */
3066 if (!ast_strlen_zero(prefile)) {
3070 RETRIEVE(prefile, -1, ext, context);
3071 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3072 if (ast_streamfile(chan, prefile, chan->language) > -1)
3073 res = ast_waitstream(chan, ecodes);
3075 if (success == -1) {
3076 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
3077 ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
3078 store_file(prefile, vmu->mailbox, vmu->context, -1);
3082 ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
3083 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3085 DISPOSE(prefile, -1);
3087 ast_debug(1, "Hang up during prefile playback\n");
3089 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3094 /* On a '#' we skip the instructions */
3095 ast_set_flag(options, OPT_SILENT);
3098 if (!res && !ast_test_flag(options, OPT_SILENT)) {
3099 res = ast_stream_and_wait(chan, INTRO, ecodes);
3101 ast_set_flag(options, OPT_SILENT);
3106 ast_stopstream(chan);
3107 /* Check for a '*' here in case the caller wants to escape from voicemail to something
3108 other than the operator -- an automated attendant or mailbox login for example */
3110 chan->exten[0] = 'a';
3111 chan->exten[1] = '\0';
3112 if (!ast_strlen_zero(vmu->exit)) {
3113 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3114 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3115 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3119 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3123 /* Check for a '0' here */
3126 if (ouseexten || ousemacro) {
3127 chan->exten[0] = 'o';
3128 chan->exten[1] = '\0';
3129 if (!ast_strlen_zero(vmu->exit)) {
3130 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3131 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3132 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3134 ast_play_and_wait(chan, "transfer");
3137 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3143 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3146 /* The meat of recording the message... All the announcements and beeps have been played*/
3147 ast_copy_string(fmt, vmfmts, sizeof(fmt));
3148 if (!ast_strlen_zero(fmt)) {
3152 /* Is ext a mailbox? */
3153 /* must open stream for this user to get info! */
3154 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
3156 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3159 if(!(vms = get_vm_state_by_mailbox(ext,0))) {
3160 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3162 if (!(vms = ast_calloc(1, sizeof(*vms)))) {
3163 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
3166 ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3167 ast_copy_string(vms->username, ext, sizeof(vms->username));
3168 vms->mailstream = NIL;
3169 ast_debug(3, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
3171 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
3173 vmstate_insert(vms);
3174 vms = get_vm_state_by_mailbox(ext,0);
3178 /* here is a big difference! We add one to it later */
3179 msgnum = newmsgs + oldmsgs;
3180 ast_debug(3, "Messagecount set to %d\n",msgnum);
3181 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3182 /* set variable for compatibility */
3183 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3185 /* Check if mailbox is full */
3186 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3187 ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3188 ast_play_and_wait(chan, "vm-mailboxfull");
3191 /* here is a big difference! We add one to it later */
3192 ast_debug(3, "Messagecount set to %d\n",msgnum);
3195 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3196 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3198 res = ast_waitstream(chan, "");
3199 ast_log(LOG_WARNING, "No more messages possible\n");
3200 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3205 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3206 txtdes = mkstemp(tmptxtfile);
3207 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
3209 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3211 res = ast_waitstream(chan, "");
3212 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3213 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3217 /* Now play the beep once we have the message number for our next message. */
3219 /* Unless we're *really* silent, try to send the beep */
3220 res = ast_stream_and_wait(chan, "beep", "");
3223 /* Store information in real-time storage */
3224 if (ast_check_realtime("voicemail_data")) {
3225 snprintf(priority, sizeof(priority), "%d", chan->priority);
3226 snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
3227 get_date(date, sizeof(date));
3228 rtmsgid = ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", category ? category : "", NULL);
3231 /* Store information */
3232 txt = fdopen(txtdes, "w+");
3234 get_date(date, sizeof(date));
3237 "; Message Information file\n"
3256 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3257 date, (long)time(NULL),
3258 category ? category : "");
3260 ast_log(LOG_WARNING, "Error opening text file for output\n");
3262 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3264 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3268 if (duration < vmminsecs) {
3270 if (option_verbose > 2)
3271 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
3272 ast_filedelete(tmptxtfile, NULL);
3274 if (ast_check_realtime("voicemail_data")) {
3275 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3276 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3279 fprintf(txt, "duration=%d\n", duration);
3281 if (vm_lock_path(dir)) {
3282 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
3284 ast_filedelete(tmptxtfile, NULL);
3286 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3287 ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
3289 ast_unlock_path(dir);
3290 if (ast_check_realtime("voicemail_data")) {
3291 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3292 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3295 #ifndef IMAP_STORAGE
3296 msgnum = last_message_index(vmu, dir) + 1;
3298 make_file(fn, sizeof(fn), dir, msgnum);
3300 /* assign a variable with the name of the voicemail file */
3301 #ifndef IMAP_STORAGE
3302 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3304 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3307 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3308 ast_filerename(tmptxtfile, fn, NULL);
3309 rename(tmptxtfile, txtfile);
3311 /* Properly set permissions on voicemail text descriptor file.
3312 Unfortunately mkstemp() makes this file 0600 on most unix systems. */
3313 if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
3314 ast_log(LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
3316 ast_unlock_path(dir);
3317 if (ast_check_realtime("voicemail_data")) {
3318 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3319 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
3320 ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
3322 /* We must store the file first, before copying the message, because
3323 * ODBC storage does the entire copy with SQL.
3325 if (ast_fileexists(fn, NULL, NULL) > 0) {
3326 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3329 /* Are there to be more recipients of this message? */
3331 struct ast_vm_user recipu, *recip;
3332 char *exten, *context;
3334 exten = strsep(&tmpptr, "&");
3335 context = strchr(exten, '@');
3340 if ((recip = find_user(&recipu, context, exten))) {
3341 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3345 /* Notification and disposal needs to happen after the copy, though. */
3346 if (ast_fileexists(fn, NULL, NULL)) {
3347 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3348 DISPOSE(dir, msgnum);
3358 if (duration < vmminsecs)
3359 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3360 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3362 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3364 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3371 #ifndef IMAP_STORAGE
3372 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3374 /* we know max messages, so stop process when number is hit */
3380 if (vm_lock_path(dir))
3381 return ERROR_LOCK_PATH;
3383 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3384 make_file(sfn, sizeof(sfn), dir, x);
3385 if (EXISTS(dir, x, sfn, NULL)) {
3388 make_file(dfn, sizeof(dfn), dir, dest);
3389 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3395 ast_unlock_path(dir);
3401 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3404 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3408 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3411 /* we must use mbox(x) folder names, and copy the message there */
3417 /* if save to Old folder, just leave in INBOX */
3418 if (box == 1) return 10;
3419 /* get the real IMAP message number for this message */
3420 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
3421 imap_mailbox_name(dbox, sizeof(dbox), vms, box, 1);
3422 ast_debug(3, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3423 res = mail_copy(vms->mailstream, sequence, dbox);
3424 if (res == 1) return 0;
3427 char *dir = vms->curdir;
3428 char *username = vms->username;
3429 char *context = vmu->context;
3432 char ddir[PATH_MAX];
3433 const char *dbox = mbox(box);
3435 make_file(sfn, sizeof(sfn), dir, msg);
3436 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3438 if (vm_lock_path(ddir))
3439 return ERROR_LOCK_PATH;
3441 x = last_message_index(vmu, ddir) + 1;
3442 make_file(dfn, sizeof(dfn), ddir, x);
3444 if (x >= vmu->maxmsg) {
3445 ast_unlock_path(ddir);
3448 if (strcmp(sfn, dfn)) {
3449 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3451 ast_unlock_path(ddir);
3456 static int adsi_logo(unsigned char *buf)
3459 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3460 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3464 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3466 unsigned char buf[256];
3472 bytes += ast_adsi_data_mode(buf + bytes);
3473 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3476 bytes += adsi_logo(buf);
3477 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3479 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
3481 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3482 bytes += ast_adsi_data_mode(buf + bytes);
3483 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3485 if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3487 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3488 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3489 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3490 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3491 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3498 bytes += ast_adsi_logo(buf);
3499 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE,&