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>
64 /*It is important to include the IMAP_STORAGE related headers
65 * before asterisk.h since asterisk.h includes logger.h. logger.h
66 * and c-client.h have conflicting definitions for LOG_WARNING and
67 * LOG_DEBUG, so it's important that we use Asterisk's definitions
68 * here instead of the c-client's
74 #ifdef USE_SYSTEM_IMAP
75 #include <imap/c-client.h>
76 #include <imap/imap4r1.h>
77 #include <imap/linkage.h>
87 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
89 #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
96 #include "asterisk/lock.h"
97 #include "asterisk/file.h"
98 #include "asterisk/channel.h"
99 #include "asterisk/pbx.h"
100 #include "asterisk/config.h"
101 #include "asterisk/say.h"
102 #include "asterisk/module.h"
103 #include "asterisk/adsi.h"
104 #include "asterisk/app.h"
105 #include "asterisk/manager.h"
106 #include "asterisk/dsp.h"
107 #include "asterisk/localtime.h"
108 #include "asterisk/cli.h"
109 #include "asterisk/utils.h"
110 #include "asterisk/stringfields.h"
111 #include "asterisk/smdi.h"
112 #include "asterisk/event.h"
115 #include "asterisk/res_odbc.h"
119 static char imapserver[48];
120 static char imapport[8];
121 static char imapflags[128];
122 static char imapfolder[64];
123 static char greetingfolder[64];
124 static char authuser[32];
125 static char authpassword[42];
127 static int expungeonhangup = 1;
128 static int imapgreetings = 0;
129 static char delimiter = '\0';
134 /* Forward declarations for IMAP */
135 static int init_mailstream(struct vm_state *vms, int box);
136 static void write_file(char *filename, char *buffer, unsigned long len);
137 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
138 static void vm_imap_delete(int msgnum, struct vm_state *vms);
139 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
140 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
141 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
142 static void vmstate_insert(struct vm_state *vms);
143 static void vmstate_delete(struct vm_state *vms);
144 static void set_update(MAILSTREAM * stream);
145 static void init_vm_state(struct vm_state *vms);
146 static void check_msgArray(struct vm_state *vms);
147 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
148 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
149 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num);
150 static void get_mailbox_delimiter(MAILSTREAM *stream);
151 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
152 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
153 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);
154 static void update_messages_by_imapuser(const char *user, unsigned long number);
156 static int imap_remove_file (char *dir, int msgnum);
157 static int imap_retrieve_file (char *dir, int msgnum, const char *mailbox, char *context);
158 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
159 static void check_quota(struct vm_state *vms, char *mailbox);
160 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
162 struct vm_state *vms;
163 AST_LIST_ENTRY(vmstate) list;
166 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
170 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
172 #define COMMAND_TIMEOUT 5000
173 /* Don't modify these here; set your umask at runtime instead */
174 #define VOICEMAIL_DIR_MODE 0777
175 #define VOICEMAIL_FILE_MODE 0666
176 #define CHUNKSIZE 65536
178 #define VOICEMAIL_CONFIG "voicemail.conf"
179 #define ASTERISK_USERNAME "asterisk"
181 /* Define fast-forward, pause, restart, and reverse keys
182 while listening to a voicemail message - these are
183 strings, not characters */
184 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
185 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
186 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
187 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
188 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
189 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
191 /* Default mail command to mail voicemail. Change it with the
192 mailcmd= command in voicemail.conf */
193 #define SENDMAIL "/usr/sbin/sendmail -t"
195 #define INTRO "vm-intro"
198 #define MAXMSGLIMIT 9999
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),
243 OPT_DTMFEXIT = (1 << 7),
247 OPT_ARG_RECORDGAIN = 0,
248 OPT_ARG_PLAYFOLDER = 1,
249 OPT_ARG_DTMFEXIT = 2,
250 /* This *must* be the last value in this enum! */
251 OPT_ARG_ARRAY_SIZE = 3,
254 AST_APP_OPTIONS(vm_app_options, {
255 AST_APP_OPTION('s', OPT_SILENT),
256 AST_APP_OPTION('b', OPT_BUSY_GREETING),
257 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
258 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
259 AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
260 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
261 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
264 static int load_config(int reload);
266 /*! \page vmlang Voicemail Language Syntaxes Supported
268 \par Syntaxes supported, not really language codes.
275 \arg \b pt - Portuguese
276 \arg \b pt_BR - Portuguese (Brazil)
278 \arg \b no - Norwegian
280 \arg \b tw - Chinese (Taiwan)
281 \arg \b ua - Ukrainian
283 German requires the following additional soundfile:
284 \arg \b 1F einE (feminine)
286 Spanish requires the following additional soundfile:
287 \arg \b 1M un (masculine)
289 Dutch, Portuguese & Spanish require the following additional soundfiles:
290 \arg \b vm-INBOXs singular of 'new'
291 \arg \b vm-Olds singular of 'old/heard/read'
294 \arg \b vm-INBOX nieuwe (nl)
295 \arg \b vm-Old oude (nl)
298 \arg \b vm-new-a 'new', feminine singular accusative
299 \arg \b vm-new-e 'new', feminine plural accusative
300 \arg \b vm-new-ych 'new', feminine plural genitive
301 \arg \b vm-old-a 'old', feminine singular accusative
302 \arg \b vm-old-e 'old', feminine plural accusative
303 \arg \b vm-old-ych 'old', feminine plural genitive
304 \arg \b digits/1-a 'one', not always same as 'digits/1'
305 \arg \b digits/2-ie 'two', not always same as 'digits/2'
308 \arg \b vm-nytt singular of 'new'
309 \arg \b vm-nya plural of 'new'
310 \arg \b vm-gammalt singular of 'old'
311 \arg \b vm-gamla plural of 'old'
312 \arg \b digits/ett 'one', not always same as 'digits/1'
315 \arg \b vm-ny singular of 'new'
316 \arg \b vm-nye plural of 'new'
317 \arg \b vm-gammel singular of 'old'
318 \arg \b vm-gamle plural of 'old'
326 Ukrainian requires the following additional soundfile:
327 \arg \b vm-nove 'nove'
328 \arg \b vm-stare 'stare'
329 \arg \b digits/ua/1e 'odne'
331 Italian requires the following additional soundfile:
335 \arg \b vm-nuovi new plural
336 \arg \b vm-vecchio old
337 \arg \b vm-vecchi old plural
339 Chinese (Taiwan) requires the following additional soundfile:
340 \arg \b vm-tong A class-word for call (tong1)
341 \arg \b vm-ri A class-word for day (ri4)
342 \arg \b vm-you You (ni3)
343 \arg \b vm-haveno Have no (mei2 you3)
344 \arg \b vm-have Have (you3)
345 \arg \b vm-listen To listen (yao4 ting1)
348 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
349 spelled among others when you have to change folder. For the above reasons, vm-INBOX
350 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
359 unsigned char iobuf[BASEMAXINLINE];
362 /*! Structure for linked list of users
363 * Use ast_vm_user_destroy() to free one of these structures. */
365 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
366 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
367 char password[80]; /*!< Secret pin code, numbers only */
368 char fullname[80]; /*!< Full name, for directory app */
369 char email[80]; /*!< E-mail address */
370 char pager[80]; /*!< E-mail address to pager (no attachment) */
371 char serveremail[80]; /*!< From: Mail address */
372 char mailcmd[160]; /*!< Configurable mail command */
373 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
374 char zonetag[80]; /*!< Time zone */
377 char uniqueid[20]; /*!< Unique integer identifier */
379 char attachfmt[20]; /*!< Attachment format */
380 unsigned int flags; /*!< VM_ flags */
382 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
383 int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
385 char imapuser[80]; /*!< IMAP server login */
386 char imappassword[80]; /*!< IMAP server password if authpassword not defined */
388 double volgain; /*!< Volume gain for voicemails sent via email */
389 AST_LIST_ENTRY(ast_vm_user) list;
392 /*! Voicemail time zones */
394 AST_LIST_ENTRY(vm_zone) list;
397 char msg_format[512];
400 /*! Voicemail mailbox state */
404 char curdir[PATH_MAX];
405 char vmbox[PATH_MAX];
417 int updated; /*!< decremented on each mail check until 1 -allows delay */
419 MAILSTREAM *mailstream;
421 char imapuser[80]; /*!< IMAP server login */
423 unsigned int quota_limit;
424 unsigned int quota_usage;
425 struct vm_state *persist_vms;
431 static char odbc_database[80];
432 static char odbc_table[80];
433 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
434 #define DISPOSE(a,b) remove_file(a,b)
435 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
436 #define EXISTS(a,b,c,d) (message_exists(a,b))
437 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
438 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
439 #define DELETE(a,b,c) (delete_file(a,b))
442 #define RETRIEVE(a,b,c,d) (imap_retrieve_file(a,b,c,d ))
443 #define DISPOSE(a,b) (imap_remove_file(a,b))
444 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
445 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
446 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
447 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
448 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
449 #define DELETE(a,b,c) (vm_delete(c))
451 #define RETRIEVE(a,b,c,d)
453 #define STORE(a,b,c,d,e,f,g,h,i)
454 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
455 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
456 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
457 #define DELETE(a,b,c) (vm_delete(c))
461 static char VM_SPOOL_DIR[PATH_MAX];
463 static char ext_pass_cmd[128];
467 #define PWDCHANGE_INTERNAL (1 << 1)
468 #define PWDCHANGE_EXTERNAL (1 << 2)
469 static int pwdchange = PWDCHANGE_INTERNAL;
472 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
475 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
477 # define tdesc "Comedian Mail (Voicemail System)"
481 static char userscontext[AST_MAX_EXTENSION] = "default";
483 static char *addesc = "Comedian Mail";
485 static char *synopsis_vm = "Leave a Voicemail message";
487 static char *descrip_vm =
488 " VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
489 "application allows the calling party to leave a message for the specified\n"
490 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
491 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
492 "specified mailbox does not exist.\n"
493 " The Voicemail application will exit if any of the following DTMF digits are\n"
495 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
496 " * - Jump to the 'a' extension in the current dialplan context.\n"
497 " This application will set the following channel variable upon completion:\n"
498 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
499 " application. The possible values are:\n"
500 " SUCCESS | USEREXIT | FAILED\n\n"
502 " b - Play the 'busy' greeting to the calling party.\n"
503 " d([c]) - Accept digits for a new extension in context c, if played during\n"
504 " the greeting. Context defaults to the current context.\n"
505 " g(#) - Use the specified amount of gain when recording the voicemail\n"
506 " message. The units are whole-number decibels (dB).\n"
507 " s - Skip the playback of instructions for leaving a message to the\n"
509 " u - Play the 'unavailable' greeting.\n";
511 static char *synopsis_vmain = "Check Voicemail messages";
513 static char *descrip_vmain =
514 " VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
515 "calling party to check voicemail messages. A specific mailbox, and optional\n"
516 "corresponding context, may be specified. If a mailbox is not provided, the\n"
517 "calling party will be prompted to enter one. If a context is not specified,\n"
518 "the 'default' context will be used.\n\n"
520 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
521 " is entered by the caller.\n"
522 " g(#) - Use the specified amount of gain when recording a voicemail\n"
523 " message. The units are whole-number decibels (dB).\n"
524 " s - Skip checking the passcode for the mailbox.\n"
525 " a(#) - Skip folder prompt and go directly to folder specified.\n"
526 " Defaults to INBOX\n";
528 static char *synopsis_vm_box_exists =
529 "Check to see if Voicemail mailbox exists";
531 static char *descrip_vm_box_exists =
532 " MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
533 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
535 " This application will set the following channel variable upon completion:\n"
536 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
537 " MailboxExists application. Possible values include:\n"
538 " SUCCESS | FAILED\n\n"
539 " Options: (none)\n";
541 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
543 static char *descrip_vmauthenticate =
544 " VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
545 "same way as the Authenticate application, but the passwords are taken from\n"
547 " If the mailbox is specified, only that mailbox's password will be considered\n"
548 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
549 "be set with the authenticated mailbox.\n\n"
551 " s - Skip playing the initial prompts.\n";
553 /* Leave a message */
554 static char *app = "VoiceMail";
556 /* Check mail, control, etc */
557 static char *app2 = "VoiceMailMain";
559 static char *app3 = "MailboxExists";
560 static char *app4 = "VMAuthenticate";
562 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
563 static AST_LIST_HEAD_STATIC(zones, vm_zone);
564 static int maxsilence;
566 static int silencethreshold = 128;
567 static char serveremail[80];
568 static char mailcmd[160]; /* Configurable mail cmd */
569 static char externnotify[160];
570 static struct ast_smdi_interface *smdi_iface = NULL;
571 static char vmfmts[80];
572 static double volgain;
573 static int vmminsecs;
574 static int vmmaxsecs;
577 static int maxlogins;
579 /*! Poll mailboxes for changes since there is something external to
580 * app_voicemail that may change them. */
581 static unsigned int poll_mailboxes;
583 /*! Polling frequency */
584 static unsigned int poll_freq;
585 /*! By default, poll every 30 seconds */
586 #define DEFAULT_POLL_FREQ 30
588 AST_MUTEX_DEFINE_STATIC(poll_lock);
589 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
590 static pthread_t poll_thread = AST_PTHREADT_NULL;
591 static unsigned char poll_thread_run;
593 /*! Subscription to ... MWI event subscriptions */
594 static struct ast_event_sub *mwi_sub_sub;
595 /*! Subscription to ... MWI event un-subscriptions */
596 static struct ast_event_sub *mwi_unsub_sub;
599 * \brief An MWI subscription
601 * This is so we can keep track of which mailboxes are subscribed to.
602 * This way, we know which mailboxes to poll when the pollmailboxes
603 * option is being used.
606 AST_RWLIST_ENTRY(mwi_sub) entry;
613 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
615 /* custom audio control prompts for voicemail playback */
616 static char listen_control_forward_key[12];
617 static char listen_control_reverse_key[12];
618 static char listen_control_pause_key[12];
619 static char listen_control_restart_key[12];
620 static char listen_control_stop_key[12];
622 /* custom password sounds */
623 static char vm_password[80] = "vm-password";
624 static char vm_newpassword[80] = "vm-newpassword";
625 static char vm_passchanged[80] = "vm-passchanged";
626 static char vm_reenterpassword[80] = "vm-reenterpassword";
627 static char vm_mismatch[80] = "vm-mismatch";
629 static struct ast_flags globalflags = {0};
631 static int saydurationminfo;
633 static char dialcontext[AST_MAX_CONTEXT] = "";
634 static char callcontext[AST_MAX_CONTEXT] = "";
635 static char exitcontext[AST_MAX_CONTEXT] = "";
637 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
640 static char *emailbody = NULL;
641 static char *emailsubject = NULL;
642 static char *pagerbody = NULL;
643 static char *pagersubject = NULL;
644 static char fromstring[100];
645 static char pagerfromstring[100];
646 static char emailtitle[100];
647 static char charset[32] = "ISO-8859-1";
649 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
650 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
651 static int adsiver = 1;
652 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
654 /* Forward declarations - generic */
655 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
656 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);
657 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
658 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
659 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
660 signed char record_gain, struct vm_state *vms);
661 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
662 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
663 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
664 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);
665 static void apply_options(struct ast_vm_user *vmu, const char *options);
666 static int is_valid_dtmf(const char *key);
668 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
669 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
674 static void populate_defaults(struct ast_vm_user *vmu)
676 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
677 if (saydurationminfo)
678 vmu->saydurationm = saydurationminfo;
679 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
680 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
681 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
683 vmu->maxsecs = vmmaxsecs;
685 vmu->maxmsg = maxmsg;
686 vmu->volgain = volgain;
689 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
692 if (!strcasecmp(var, "attach")) {
693 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
694 } else if (!strcasecmp(var, "attachfmt")) {
695 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
696 } else if (!strcasecmp(var, "serveremail")) {
697 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
698 } else if (!strcasecmp(var, "language")) {
699 ast_copy_string(vmu->language, value, sizeof(vmu->language));
700 } else if (!strcasecmp(var, "tz")) {
701 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
703 } else if (!strcasecmp(var, "imapuser")) {
704 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
705 } else if (!strcasecmp(var, "imappassword")) {
706 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
708 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
709 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
710 } else if (!strcasecmp(var, "saycid")){
711 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
712 } else if (!strcasecmp(var,"sendvoicemail")){
713 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
714 } else if (!strcasecmp(var, "review")){
715 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
716 } else if (!strcasecmp(var, "tempgreetwarn")){
717 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
718 } else if (!strcasecmp(var, "operator")){
719 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
720 } else if (!strcasecmp(var, "envelope")){
721 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
722 } else if (!strcasecmp(var, "moveheard")){
723 ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
724 } else if (!strcasecmp(var, "sayduration")){
725 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
726 } else if (!strcasecmp(var, "saydurationm")){
727 if (sscanf(value, "%d", &x) == 1) {
728 vmu->saydurationm = x;
730 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
732 } else if (!strcasecmp(var, "forcename")){
733 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
734 } else if (!strcasecmp(var, "forcegreetings")){
735 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
736 } else if (!strcasecmp(var, "callback")) {
737 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
738 } else if (!strcasecmp(var, "dialout")) {
739 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
740 } else if (!strcasecmp(var, "exitcontext")) {
741 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
742 } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
743 if (vmu->maxsecs <= 0) {
744 ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
745 vmu->maxsecs = vmmaxsecs;
747 vmu->maxsecs = atoi(value);
749 if (!strcasecmp(var, "maxmessage"))
750 ast_log(LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
751 } else if (!strcasecmp(var, "maxmsg")) {
752 vmu->maxmsg = atoi(value);
753 if (vmu->maxmsg <= 0) {
754 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
755 vmu->maxmsg = MAXMSG;
756 } else if (vmu->maxmsg > MAXMSGLIMIT) {
757 ast_log(LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
758 vmu->maxmsg = MAXMSGLIMIT;
760 } else if (!strcasecmp(var, "volgain")) {
761 sscanf(value, "%lf", &vmu->volgain);
762 } else if (!strcasecmp(var, "options")) {
763 apply_options(vmu, value);
767 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
770 if (!ast_strlen_zero(vmu->uniqueid)) {
771 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
773 ast_copy_string(vmu->password, password, sizeof(vmu->password));
783 static void apply_options(struct ast_vm_user *vmu, const char *options)
784 { /* Destructively Parse options and apply */
788 stringp = ast_strdupa(options);
789 while ((s = strsep(&stringp, "|"))) {
791 if ((var = strsep(&value, "=")) && value) {
792 apply_option(vmu, var, value);
797 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
799 struct ast_variable *tmp;
802 if (!strcasecmp(tmp->name, "vmsecret")) {
803 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
804 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
805 if (ast_strlen_zero(retval->password))
806 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
807 } else if (!strcasecmp(tmp->name, "uniqueid")) {
808 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
809 } else if (!strcasecmp(tmp->name, "pager")) {
810 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
811 } else if (!strcasecmp(tmp->name, "email")) {
812 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
813 } else if (!strcasecmp(tmp->name, "fullname")) {
814 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
815 } else if (!strcasecmp(tmp->name, "context")) {
816 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
818 } else if (!strcasecmp(tmp->name, "imapuser")) {
819 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
820 } else if (!strcasecmp(tmp->name, "imappassword")) {
821 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
824 apply_option(retval, tmp->name, tmp->value);
829 static int is_valid_dtmf(const char *key)
832 char *local_key = ast_strdupa(key);
834 for (i = 0; i < strlen(key); ++i) {
835 if (!strchr(VALID_DTMF, *local_key)) {
836 ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
844 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
846 struct ast_variable *var;
847 struct ast_vm_user *retval;
849 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
851 ast_set_flag(retval, VM_ALLOCED);
853 memset(retval, 0, sizeof(*retval));
855 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
856 populate_defaults(retval);
857 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
858 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
860 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
862 apply_options_full(retval, var);
863 ast_variables_destroy(var);
873 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
875 /* This function could be made to generate one from a database, too */
876 struct ast_vm_user *vmu=NULL, *cur;
877 AST_LIST_LOCK(&users);
879 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
882 AST_LIST_TRAVERSE(&users, cur, list) {
883 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
885 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
889 /* Make a copy, so that on a reload, we have no race */
890 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
891 memcpy(vmu, cur, sizeof(*vmu));
892 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
893 AST_LIST_NEXT(vmu, list) = NULL;
896 vmu = find_user_realtime(ivm, context, mailbox);
897 AST_LIST_UNLOCK(&users);
901 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
903 /* This function could be made to generate one from a database, too */
904 struct ast_vm_user *cur;
906 AST_LIST_LOCK(&users);
907 AST_LIST_TRAVERSE(&users, cur, list) {
908 if ((!context || !strcasecmp(context, cur->context)) &&
909 (!strcasecmp(mailbox, cur->mailbox)))
913 ast_copy_string(cur->password, newpass, sizeof(cur->password));
916 AST_LIST_UNLOCK(&users);
920 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
922 struct ast_config *cfg=NULL;
923 struct ast_variable *var=NULL;
924 struct ast_category *cat=NULL;
925 char *category=NULL, *value=NULL, *new=NULL;
926 const char *tmp=NULL;
927 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
928 if (!change_password_realtime(vmu, newpassword))
931 /* check voicemail.conf */
932 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
933 while ((category = ast_category_browse(cfg, category))) {
934 if (!strcasecmp(category, vmu->context)) {
935 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
936 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
939 value = strstr(tmp,",");
941 ast_log(LOG_WARNING, "variable has bad format.\n");
944 new = alloca((strlen(value)+strlen(newpassword)+1));
945 sprintf(new,"%s%s", newpassword, value);
946 if (!(cat = ast_category_get(cfg, category))) {
947 ast_log(LOG_WARNING, "Failed to get category structure.\n");
950 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
953 /* save the results */
954 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
955 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
956 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
960 /* check users.conf and update the password stored for the mailbox*/
961 /* if no vmsecret entry exists create one. */
962 if ((cfg = ast_config_load("users.conf", config_flags))) {
963 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
964 while ((category = ast_category_browse(cfg, category))) {
965 ast_debug(4, "users.conf: %s\n", category);
966 if (!strcasecmp(category, vmu->mailbox)) {
967 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
968 ast_debug(3, "looks like we need to make vmsecret!\n");
969 var = ast_variable_new("vmsecret", newpassword, "");
971 new = alloca(strlen(newpassword)+1);
972 sprintf(new, "%s", newpassword);
973 if (!(cat = ast_category_get(cfg, category))) {
974 ast_debug(4, "failed to get category!\n");
978 ast_variable_update(cat, "vmsecret", new, NULL, 0);
980 ast_variable_append(cat, var);
983 /* save the results and clean things up */
984 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
985 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
986 config_text_file_save("users.conf", cfg, "AppVoicemail");
990 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
993 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
994 if (!ast_safe_system(buf))
995 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
998 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1000 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1004 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num)
1007 if ((res = ast_mkdir(dir, 01777))) {
1008 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
1009 return snprintf(dest, len, "%s/msg%04d", dir, num);
1011 return snprintf(dest, len, "%s/msg%04d", dir, num);
1014 static void vm_imap_delete(int msgnum, struct vm_state *vms)
1016 unsigned long messageNum = 0;
1019 /* find real message number based on msgnum */
1020 /* this may be an index into vms->msgArray based on the msgnum. */
1022 messageNum = vms->msgArray[msgnum];
1023 if (messageNum == 0) {
1024 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1027 ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1028 /* delete message */
1029 snprintf (arg, sizeof(arg), "%lu",messageNum);
1030 mail_setflag (vms->mailstream,arg,"\\DELETED");
1034 static int make_file(char *dest, int len, char *dir, int num)
1036 return snprintf(dest, len, "%s/msg%04d", dir, num);
1039 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1040 * \param dest String. base directory.
1041 * \param len Length of dest.
1042 * \param context String. Ignored if is null or empty string.
1043 * \param ext String. Ignored if is null or empty string.
1044 * \param folder String. Ignored if is null or empty string.
1045 * \return -1 on failure, 0 on success.
1047 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1049 mode_t mode = VOICEMAIL_DIR_MODE;
1052 make_dir(dest, len, context, ext, folder);
1053 if ((res = ast_mkdir(dest, mode))) {
1054 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1060 /*! \brief Lock file path
1061 only return failure if ast_lock_path returns 'timeout',
1062 not if the path does not exist or any other reason
1064 static int vm_lock_path(const char *path)
1066 switch (ast_lock_path(path)) {
1067 case AST_LOCK_TIMEOUT:
1076 struct generic_prepare_struct {
1082 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
1084 struct generic_prepare_struct *gps = data;
1088 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1089 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1090 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1093 res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
1094 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1095 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
1096 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1099 for (i = 0; i < gps->argc; i++)
1100 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
1105 static int retrieve_file(char *dir, int msgnum)
1111 void *fdm = MAP_FAILED;
1112 SQLSMALLINT colcount=0;
1119 SQLSMALLINT datatype;
1120 SQLSMALLINT decimaldigits;
1121 SQLSMALLINT nullable;
1127 char full_fn[PATH_MAX];
1129 char *argv[] = { dir, msgnums };
1130 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1132 struct odbc_obj *obj;
1133 obj = ast_odbc_request_obj(odbc_database, 0);
1135 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1136 c = strchr(fmt, '|');
1139 if (!strcasecmp(fmt, "wav49"))
1141 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1143 make_file(fn, sizeof(fn), dir, msgnum);
1145 ast_copy_string(fn, dir, sizeof(fn));
1146 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1148 if (!(f = fopen(full_fn, "w+"))) {
1149 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1153 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1154 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1155 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1157 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1158 ast_odbc_release_obj(obj);
1161 res = SQLFetch(stmt);
1162 if (res == SQL_NO_DATA) {
1163 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1164 ast_odbc_release_obj(obj);
1167 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1168 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1169 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1170 ast_odbc_release_obj(obj);
1173 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1175 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1176 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1177 ast_odbc_release_obj(obj);
1180 res = SQLNumResultCols(stmt, &colcount);
1181 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1182 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1183 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1184 ast_odbc_release_obj(obj);
1188 fprintf(f, "[message]\n");
1189 for (x=0;x<colcount;x++) {
1191 collen = sizeof(coltitle);
1192 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
1193 &datatype, &colsize, &decimaldigits, &nullable);
1194 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1195 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1196 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1197 ast_odbc_release_obj(obj);
1200 if (!strcasecmp(coltitle, "recording")) {
1202 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1206 lseek(fd, fdlen - 1, SEEK_SET);
1207 if (write(fd, tmp, 1) != 1) {
1212 /* Read out in small chunks */
1213 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1214 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1215 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1216 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1217 ast_odbc_release_obj(obj);
1220 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1221 munmap(fdm, CHUNKSIZE);
1222 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1223 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1225 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1226 ast_odbc_release_obj(obj);
1231 truncate(full_fn, fdlen);
1234 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1235 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1236 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1237 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1238 ast_odbc_release_obj(obj);
1241 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1242 fprintf(f, "%s=%s\n", coltitle, rowdata);
1245 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1246 ast_odbc_release_obj(obj);
1248 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1257 static int remove_file(char *dir, int msgnum)
1260 char full_fn[PATH_MAX];
1264 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1265 make_file(fn, sizeof(fn), dir, msgnum);
1267 ast_copy_string(fn, dir, sizeof(fn));
1268 ast_filedelete(fn, NULL);
1269 if (ast_check_realtime("voicemail_data")) {
1270 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1272 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1277 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1284 char *argv[] = { dir };
1285 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1287 struct odbc_obj *obj;
1288 obj = ast_odbc_request_obj(odbc_database, 0);
1290 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1291 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1293 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1294 ast_odbc_release_obj(obj);
1297 res = SQLFetch(stmt);
1298 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1299 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1300 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1301 ast_odbc_release_obj(obj);
1304 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1305 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1306 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1307 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1308 ast_odbc_release_obj(obj);
1311 if (sscanf(rowdata, "%d", &x) != 1)
1312 ast_log(LOG_WARNING, "Failed to read message count!\n");
1313 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1314 ast_odbc_release_obj(obj);
1316 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1321 static int message_exists(char *dir, int msgnum)
1329 char *argv[] = { dir, msgnums };
1330 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1332 struct odbc_obj *obj;
1333 obj = ast_odbc_request_obj(odbc_database, 0);
1335 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1336 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1337 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1339 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1340 ast_odbc_release_obj(obj);
1343 res = SQLFetch(stmt);
1344 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1345 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1346 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1347 ast_odbc_release_obj(obj);
1350 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1351 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1352 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1353 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1354 ast_odbc_release_obj(obj);
1357 if (sscanf(rowdata, "%d", &x) != 1)
1358 ast_log(LOG_WARNING, "Failed to read message count!\n");
1359 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1360 ast_odbc_release_obj(obj);
1362 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1367 static int count_messages(struct ast_vm_user *vmu, char *dir)
1369 return last_message_index(vmu, dir) + 1;
1372 static void delete_file(char *sdir, int smsg)
1377 char *argv[] = { sdir, msgnums };
1378 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1380 struct odbc_obj *obj;
1381 obj = ast_odbc_request_obj(odbc_database, 0);
1383 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1384 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1385 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1387 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1389 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1390 ast_odbc_release_obj(obj);
1392 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1396 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1402 struct odbc_obj *obj;
1403 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1404 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1406 delete_file(ddir, dmsg);
1407 obj = ast_odbc_request_obj(odbc_database, 0);
1409 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1410 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1411 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);
1412 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1414 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1416 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1417 ast_odbc_release_obj(obj);
1419 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1423 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1428 void *fdm = MAP_FAILED;
1435 char full_fn[PATH_MAX];
1438 const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1439 const char *category = "";
1440 struct ast_config *cfg=NULL;
1441 struct odbc_obj *obj;
1442 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
1444 delete_file(dir, msgnum);
1445 obj = ast_odbc_request_obj(odbc_database, 0);
1447 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1448 c = strchr(fmt, '|');
1451 if (!strcasecmp(fmt, "wav49"))
1453 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1455 make_file(fn, sizeof(fn), dir, msgnum);
1457 ast_copy_string(fn, dir, sizeof(fn));
1458 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1459 cfg = ast_config_load(full_fn, config_flags);
1460 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1461 fd = open(full_fn, O_RDWR);
1463 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1464 ast_odbc_release_obj(obj);
1468 context = ast_variable_retrieve(cfg, "message", "context");
1469 if (!context) context = "";
1470 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1471 if (!macrocontext) macrocontext = "";
1472 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1473 if (!callerid) callerid = "";
1474 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1475 if (!origtime) origtime = "";
1476 duration = ast_variable_retrieve(cfg, "message", "duration");
1477 if (!duration) duration = "";
1478 category = ast_variable_retrieve(cfg, "message", "category");
1479 if (!category) category = "";
1481 fdlen = lseek(fd, 0, SEEK_END);
1482 lseek(fd, 0, SEEK_SET);
1483 printf("Length is %zd\n", fdlen);
1484 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1485 if (fdm == MAP_FAILED) {
1486 ast_log(LOG_WARNING, "Memory map failed!\n");
1487 ast_odbc_release_obj(obj);
1490 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1491 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1492 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1493 ast_odbc_release_obj(obj);
1496 if (!ast_strlen_zero(category))
1497 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1499 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1500 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1501 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1502 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1503 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1504 ast_odbc_release_obj(obj);
1507 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1508 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1509 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1510 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1511 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1512 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1513 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1514 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1515 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1516 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1517 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1518 if (!ast_strlen_zero(category))
1519 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1520 res = ast_odbc_smart_execute(obj, stmt);
1521 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1522 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1523 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1524 ast_odbc_release_obj(obj);
1527 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1528 ast_odbc_release_obj(obj);
1530 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1533 ast_config_destroy(cfg);
1534 if (fdm != MAP_FAILED)
1541 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1547 struct odbc_obj *obj;
1548 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1549 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1551 delete_file(ddir, dmsg);
1552 obj = ast_odbc_request_obj(odbc_database, 0);
1554 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1555 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1556 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1557 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1559 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1561 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1562 ast_odbc_release_obj(obj);
1564 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1569 #ifndef IMAP_STORAGE
1570 static int count_messages(struct ast_vm_user *vmu, char *dir)
1572 /* Find all .txt files - even if they are not in sequence from 0000 */
1576 struct dirent *vment = NULL;
1578 if (vm_lock_path(dir))
1579 return ERROR_LOCK_PATH;
1581 if ((vmdir = opendir(dir))) {
1582 while ((vment = readdir(vmdir))) {
1583 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1588 ast_unlock_path(dir);
1593 static void rename_file(char *sfn, char *dfn)
1595 char stxt[PATH_MAX];
1596 char dtxt[PATH_MAX];
1597 ast_filerename(sfn,dfn,NULL);
1598 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1599 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1600 if (ast_check_realtime("voicemail_data")) {
1601 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1606 static int copy(char *infile, char *outfile)
1614 #ifdef HARDLINK_WHEN_POSSIBLE
1615 /* Hard link if possible; saves disk space & is faster */
1616 if (link(infile, outfile)) {
1618 if ((ifd = open(infile, O_RDONLY)) < 0) {
1619 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1622 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1623 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1628 len = read(ifd, buf, sizeof(buf));
1630 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1636 res = write(ofd, buf, len);
1637 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1638 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1648 #ifdef HARDLINK_WHEN_POSSIBLE
1650 /* Hard link succeeded */
1656 static void copy_file(char *frompath, char *topath)
1658 char frompath2[PATH_MAX], topath2[PATH_MAX];
1659 struct ast_variable *tmp,*var = NULL;
1660 const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
1661 ast_filecopy(frompath, topath, NULL);
1662 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1663 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1664 if (ast_check_realtime("voicemail_data")) {
1665 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
1666 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
1667 for (tmp = var; tmp; tmp = tmp->next) {
1668 if (!strcasecmp(tmp->name, "origmailbox")) {
1669 origmailbox = tmp->value;
1670 } else if (!strcasecmp(tmp->name, "context")) {
1671 context = tmp->value;
1672 } else if (!strcasecmp(tmp->name, "macrocontext")) {
1673 macrocontext = tmp->value;
1674 } else if (!strcasecmp(tmp->name, "exten")) {
1676 } else if (!strcasecmp(tmp->name, "priority")) {
1677 priority = tmp->value;
1678 } else if (!strcasecmp(tmp->name, "callerchan")) {
1679 callerchan = tmp->value;
1680 } else if (!strcasecmp(tmp->name, "callerid")) {
1681 callerid = tmp->value;
1682 } else if (!strcasecmp(tmp->name, "origdate")) {
1683 origdate = tmp->value;
1684 } else if (!strcasecmp(tmp->name, "origtime")) {
1685 origtime = tmp->value;
1686 } else if (!strcasecmp(tmp->name, "category")) {
1687 category = tmp->value;
1688 } else if (!strcasecmp(tmp->name, "duration")) {
1689 duration = tmp->value;
1692 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);
1694 copy(frompath2, topath2);
1695 ast_variables_destroy(var);
1698 * A negative return value indicates an error.
1699 * \note Should always be called with a lock already set on dir.
1701 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1704 unsigned char map[MAXMSGLIMIT] = "";
1706 struct dirent *msgdirent;
1709 /* Reading the entire directory into a file map scales better than
1710 * doing a stat repeatedly on a predicted sequence. I suspect this
1711 * is partially due to stat(2) internally doing a readdir(2) itself to
1712 * find each file. */
1713 msgdir = opendir(dir);
1714 while ((msgdirent = readdir(msgdir))) {
1715 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1720 for (x = 0; x < vmu->maxmsg; x++) {
1728 #endif /*#ifndef IMAP_STORAGE*/
1729 #endif /*#else of #ifdef ODBC_STORAGE*/
1730 #ifndef ODBC_STORAGE
1731 static int vm_delete(char *file)
1736 txtsize = (strlen(file) + 5)*sizeof(char);
1737 txt = alloca(txtsize);
1738 /* Sprintf here would safe because we alloca'd exactly the right length,
1739 * but trying to eliminate all sprintf's anyhow
1741 if (ast_check_realtime("voicemail_data")) {
1742 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
1744 snprintf(txt, txtsize, "%s.txt", file);
1746 return ast_filedelete(file, NULL);
1750 static int inbuf(struct baseio *bio, FILE *fi)
1757 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1771 static int inchar(struct baseio *bio, FILE *fi)
1773 if (bio->iocp>=bio->iolen) {
1774 if (!inbuf(bio, fi))
1778 return bio->iobuf[bio->iocp++];
1781 static int ochar(struct baseio *bio, int c, FILE *so)
1783 if (bio->linelength >= BASELINELEN) {
1784 if (fputs(eol,so) == EOF)
1790 if (putc(((unsigned char)c),so) == EOF)
1798 static int base_encode(char *filename, FILE *so)
1800 static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
1801 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
1802 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
1803 '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
1808 memset(&bio, 0, sizeof(bio));
1809 bio.iocp = BASEMAXINLINE;
1811 if (!(fi = fopen(filename, "rb"))) {
1812 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
1817 unsigned char igroup[3], ogroup[4];
1820 igroup[0]= igroup[1]= igroup[2]= 0;
1822 for (n= 0;n<3;n++) {
1823 if ((c = inchar(&bio, fi)) == EOF) {
1828 igroup[n]= (unsigned char)c;
1832 ogroup[0]= dtable[igroup[0]>>2];
1833 ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1834 ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1835 ogroup[3]= dtable[igroup[2]&0x3F];
1845 ochar(&bio, ogroup[i], so);
1851 if (fputs(eol,so)==EOF)
1857 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)
1860 /* Prepare variables for substitution in email body and subject */
1861 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1862 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1863 snprintf(passdata, passdatasize, "%d", msgnum);
1864 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1865 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1866 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1867 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1868 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1869 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1870 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1871 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1874 static char *quote(const char *from, char *to, size_t len)
1878 for (; ptr < to + len - 1; from++) {
1881 else if (*from == '\0')
1885 if (ptr < to + len - 1)
1892 * fill in *tm for current time according to the proper timezone, if any.
1893 * Return tm so it can be used as a function argument.
1895 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
1897 const struct vm_zone *z = NULL;
1898 struct timeval t = ast_tvnow();
1900 /* Does this user have a timezone specified? */
1901 if (!ast_strlen_zero(vmu->zonetag)) {
1902 /* Find the zone in the list */
1903 AST_LIST_LOCK(&zones);
1904 AST_LIST_TRAVERSE(&zones, z, list) {
1905 if (!strcmp(z->name, vmu->zonetag))
1908 AST_LIST_UNLOCK(&zones);
1910 ast_localtime(&t, tm, z ? z->timezone : NULL);
1914 /*! \brief same as mkstemp, but return a FILE * */
1915 static FILE *vm_mkftemp(char *template)
1918 int pfd = mkstemp(template);
1919 chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1921 p = fdopen(pfd, "w+");
1930 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)
1933 char host[MAXHOSTNAMELEN] = "";
1941 size_t len_passdata;
1942 char *greeting_attachment;
1950 gethostname(host, sizeof(host)-1);
1952 if (strchr(srcemail, '@'))
1953 ast_copy_string(who, srcemail, sizeof(who));
1955 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1957 greeting_attachment = strrchr(ast_strdupa(attach), '/');
1958 if (greeting_attachment)
1959 *greeting_attachment++ = '\0';
1961 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1962 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1963 fprintf(p, "Date: %s" ENDL, date);
1965 /* Set date format for voicemail mail */
1966 ast_strftime(date, sizeof(date), emaildateformat, &tm);
1968 if (!ast_strlen_zero(fromstring)) {
1969 struct ast_channel *ast;
1970 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1972 int vmlen = strlen(fromstring)*3 + 200;
1973 passdata = alloca(vmlen);
1974 memset(passdata, 0, vmlen);
1975 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1976 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1977 len_passdata = strlen(passdata) * 2 + 3;
1978 passdata2 = alloca(len_passdata);
1979 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1980 ast_channel_free(ast);
1982 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1984 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1985 len_passdata = strlen(vmu->fullname) * 2 + 3;
1986 passdata2 = alloca(len_passdata);
1987 fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1988 if (!ast_strlen_zero(emailsubject)) {
1989 struct ast_channel *ast;
1990 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1992 int vmlen = strlen(emailsubject) * 3 + 200;
1993 passdata = alloca(vmlen);
1994 memset(passdata, 0, vmlen);
1995 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1996 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1997 fprintf(p, "Subject: %s" ENDL, passdata);
1998 ast_channel_free(ast);
2000 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2001 } else if (!ast_strlen_zero(emailtitle)) {
2002 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
2004 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
2005 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2007 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2008 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
2010 /* additional information needed for IMAP searching */
2011 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
2012 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
2013 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
2014 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
2015 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
2016 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
2017 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
2018 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
2019 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
2020 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
2021 if (!ast_strlen_zero(category))
2022 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
2023 fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
2024 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
2025 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
2027 if (!ast_strlen_zero(cidnum))
2028 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
2029 if (!ast_strlen_zero(cidname))
2030 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
2031 fprintf(p, "MIME-Version: 1.0" ENDL);
2032 if (attach_user_voicemail) {
2033 /* Something unique. */
2034 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
2036 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
2037 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
2038 fprintf(p, "--%s" ENDL, bound);
2040 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
2042 struct ast_channel *ast;
2043 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2045 int vmlen = strlen(emailbody)*3 + 200;
2046 passdata = alloca(vmlen);
2047 memset(passdata, 0, vmlen);
2048 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2049 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2050 fprintf(p, "%s" ENDL, passdata);
2051 ast_channel_free(ast);
2053 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2054 } else if (msgnum > -1){
2055 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
2057 "in mailbox %s from %s, on %s so you might" ENDL
2058 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
2059 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2061 fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
2062 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
2064 if (attach_user_voicemail) {
2065 /* Eww. We want formats to tell us their own MIME type */
2066 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2067 char tmpdir[256], newtmp[256];
2070 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2071 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2072 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2073 tmpfd = mkstemp(newtmp);
2074 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
2075 ast_debug(3, "newtmp: %s\n", newtmp);
2077 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2078 ast_safe_system(tmpcmd);
2080 ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2083 fprintf(p, "--%s" ENDL, bound);
2085 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2087 fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
2088 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2089 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2091 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2093 fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
2094 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2095 base_encode(fname, p);
2096 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
2106 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)
2109 char tmp[80] = "/tmp/astmail-XXXXXX";
2112 if (vmu && ast_strlen_zero(vmu->email)) {
2113 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
2116 if (!strcmp(format, "wav49"))
2118 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));
2119 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2121 if ((p = vm_mkftemp(tmp)) == NULL) {
2122 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2125 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2127 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2128 ast_safe_system(tmp2);
2129 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2134 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)
2137 char host[MAXHOSTNAMELEN] = "";
2140 char tmp[80] = "/tmp/astmail-XXXXXX";
2141 char tmp2[PATH_MAX];
2145 if ((p = vm_mkftemp(tmp)) == NULL) {
2146 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2149 gethostname(host, sizeof(host)-1);
2150 if (strchr(srcemail, '@'))
2151 ast_copy_string(who, srcemail, sizeof(who));
2153 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2154 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2155 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2156 fprintf(p, "Date: %s\n", date);
2158 if (*pagerfromstring) {
2159 struct ast_channel *ast;
2160 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2162 int vmlen = strlen(fromstring)*3 + 200;
2163 passdata = alloca(vmlen);
2164 memset(passdata, 0, vmlen);
2165 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2166 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2167 fprintf(p, "From: %s <%s>\n", passdata, who);
2168 ast_channel_free(ast);
2170 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2172 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2173 fprintf(p, "To: %s\n", pager);
2175 struct ast_channel *ast;
2176 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2178 int vmlen = strlen(pagersubject) * 3 + 200;
2179 passdata = alloca(vmlen);
2180 memset(passdata, 0, vmlen);
2181 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2182 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2183 fprintf(p, "Subject: %s\n\n", passdata);
2184 ast_channel_free(ast);
2186 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2188 fprintf(p, "Subject: New VM\n\n");
2190 ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2192 struct ast_channel *ast;
2193 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2195 int vmlen = strlen(pagerbody) * 3 + 200;
2196 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, pagerbody, passdata, vmlen);
2200 fprintf(p, "%s\n", passdata);
2201 ast_channel_free(ast);
2203 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2205 fprintf(p, "New %s long msg in box %s\n"
2206 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2209 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2210 ast_safe_system(tmp2);
2211 ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2215 static int get_date(char *s, int len)
2218 struct timeval t = ast_tvnow();
2220 ast_localtime(&t, &tm, "UTC");
2222 return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
2225 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2229 char dest[PATH_MAX];
2231 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2233 if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
2234 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2238 RETRIEVE(fn, -1, ext, context);
2239 if (ast_fileexists(fn, NULL, NULL) > 0) {
2240 res = ast_stream_and_wait(chan, fn, ecodes);
2246 /* Dispose just in case */
2248 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2251 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2255 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2259 static void free_user(struct ast_vm_user *vmu)
2261 if (!ast_test_flag(vmu, VM_ALLOCED))
2267 static void free_zone(struct vm_zone *z)
2272 static const char *mbox(int id)
2274 static const char *msgs[] = {
2290 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2293 static int folder_int(const char *folder)
2295 /*assume a NULL folder means INBOX*/
2299 if (!strcasecmp(folder, imapfolder))
2301 if (!strcasecmp(folder, "INBOX"))
2304 else if (!strcasecmp(folder, "Old"))
2306 else if (!strcasecmp(folder, "Work"))
2308 else if (!strcasecmp(folder, "Family"))
2310 else if (!strcasecmp(folder, "Friends"))
2312 else if (!strcasecmp(folder, "Cust1"))
2314 else if (!strcasecmp(folder, "Cust2"))
2316 else if (!strcasecmp(folder, "Cust3"))
2318 else if (!strcasecmp(folder, "Cust4"))
2320 else if (!strcasecmp(folder, "Cust5"))
2322 else /*assume they meant INBOX if folder is not found otherwise*/
2328 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2329 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2336 char tmp[PATH_MAX] = "";
2337 struct odbc_obj *obj;
2339 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2346 /* If no mailbox, return immediately */
2347 if (ast_strlen_zero(mailbox))
2350 ast_copy_string(tmp, mailbox, sizeof(tmp));
2352 context = strchr(tmp, '@');
2357 context = "default";
2359 obj = ast_odbc_request_obj(odbc_database, 0);
2361 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2362 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2364 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2365 ast_odbc_release_obj(obj);
2368 res = SQLFetch(stmt);
2369 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2370 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2371 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2372 ast_odbc_release_obj(obj);
2375 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2376 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2377 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2378 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2379 ast_odbc_release_obj(obj);
2382 *newmsgs = atoi(rowdata);
2383 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2385 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2386 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2388 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2389 ast_odbc_release_obj(obj);
2392 res = SQLFetch(stmt);
2393 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2394 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2395 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2396 ast_odbc_release_obj(obj);
2399 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2400 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2401 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2402 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2403 ast_odbc_release_obj(obj);
2406 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2407 ast_odbc_release_obj(obj);
2408 *oldmsgs = atoi(rowdata);
2411 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2417 static int messagecount(const char *context, const char *mailbox, const char *folder)
2419 struct odbc_obj *obj = NULL;
2422 SQLHSTMT stmt = NULL;
2425 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2428 /* If no mailbox, return immediately */
2429 if (ast_strlen_zero(mailbox))
2432 obj = ast_odbc_request_obj(odbc_database, 0);
2434 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2435 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2437 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2440 res = SQLFetch(stmt);
2441 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2442 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2443 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2446 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2447 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2448 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2449 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2452 nummsgs = atoi(rowdata);
2453 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2455 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2459 ast_odbc_release_obj(obj);
2463 static int has_voicemail(const char *mailbox, const char *folder)
2465 char tmp[256], *tmp2 = tmp, *mbox, *context;
2466 ast_copy_string(tmp, mailbox, sizeof(tmp));
2467 while ((context = mbox = strsep(&tmp2, ","))) {
2468 strsep(&context, "@");
2469 if (ast_strlen_zero(context))
2470 context = "default";
2471 if (messagecount(context, mbox, folder))
2477 #elif defined(IMAP_STORAGE)
2479 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)
2481 char *myserveremail = serveremail;
2486 char tmp[80] = "/tmp/astmail-XXXXXX";
2492 /* Attach only the first format */
2493 fmt = ast_strdupa(fmt);
2495 strsep(&stringp, "|");
2497 if (!ast_strlen_zero(vmu->serveremail))
2498 myserveremail = vmu->serveremail;
2501 make_file(fn, sizeof(fn), dir, msgnum);
2503 ast_copy_string (fn, dir, sizeof(fn));
2505 if (ast_strlen_zero(vmu->email)) {
2506 /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
2507 * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
2508 * string if tempcopy is 1
2510 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2514 if (!strcmp(fmt, "wav49"))
2516 ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2518 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2520 if (!(p = vm_mkftemp(tmp))) {
2521 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2523 *(vmu->email) = '\0';
2527 if (msgnum < 0 && imapgreetings) {
2528 init_mailstream(vms, GREETINGS_FOLDER);
2529 imap_delete_old_greeting(fn, vms);
2532 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);
2533 /* read mail file to memory */
2536 if (!(buf = ast_malloc(len+1))) {
2537 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2540 *(vmu->email) = '\0';
2543 fread(buf, len, 1, p);
2544 ((char *)buf)[len] = '\0';
2545 INIT(&str, mail_string, buf, len);
2546 init_mailstream(vms, NEW_FOLDER);
2547 imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
2548 if (!mail_append(vms->mailstream, mailbox, &str))
2549 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2553 ast_debug(3, "%s stored\n", fn);
2556 *(vmu->email) = '\0';
2562 static int messagecount(const char *context, const char *mailbox, const char *folder)
2567 struct ast_vm_user *vmu, vmus;
2568 struct vm_state *vms_p;
2570 int fold = folder_int(folder);
2572 if (ast_strlen_zero(mailbox))
2575 /* We have to get the user before we can open the stream! */
2576 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2577 vmu = find_user(&vmus, context, mailbox);
2579 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
2582 /* No IMAP account available */
2583 if (vmu->imapuser[0] == '\0') {
2584 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2589 /* No IMAP account available */
2590 if (vmu->imapuser[0] == '\0') {
2591 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2596 /* check if someone is accessing this box right now... */
2597 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2599 vms_p = get_vm_state_by_mailbox(mailbox,1);
2602 ast_debug(3, "Returning before search - user is logged in\n");
2603 if (fold == 0) {/*INBOX*/
2604 return vms_p->newmessages;
2606 if (fold == 1) {/*Old messages*/
2607 return vms_p->oldmessages;
2611 /* add one if not there... */
2612 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2614 vms_p = get_vm_state_by_mailbox(mailbox,0);
2618 ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
2619 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2622 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2623 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2624 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2625 ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2627 /* set mailbox to INBOX! */
2628 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
2629 init_vm_state(vms_p);
2630 vmstate_insert(vms_p);
2632 ret = init_mailstream(vms_p, fold);
2633 if (!vms_p->mailstream) {
2634 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2638 pgm = mail_newsearchpgm ();
2639 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
2645 /* In the special case where fold is 1 (old messages) we have to do things a bit
2646 * differently. Old messages are stored in the INBOX but are marked as "seen"
2655 vms_p->vmArrayIndex = 0;
2656 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2658 vms_p->newmessages = vms_p->vmArrayIndex;
2660 vms_p->oldmessages = vms_p->vmArrayIndex;
2661 /*Freeing the searchpgm also frees the searchhdr*/
2662 mail_free_searchpgm(&pgm);
2664 return vms_p->vmArrayIndex;
2666 mail_ping(vms_p->mailstream);
2670 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
2672 char tmp[PATH_MAX] = "";
2682 ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
2683 /* If no mailbox, return immediately */
2684 if (ast_strlen_zero(mailbox_context))
2687 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2688 context = strchr(tmp, '@');
2689 if (strchr(mailbox_context, ',')) {
2691 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2693 while ((cur = strsep(&mb, ", "))) {
2694 if (!ast_strlen_zero(cur)) {
2695 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2712 context = "default";
2713 mailboxnc = (char *)mailbox_context;
2716 if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
2720 if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
2727 static int has_voicemail(const char *mailbox, const char *folder)
2729 char tmp[256], *tmp2, *mbox, *context;
2730 ast_copy_string(tmp, mailbox, sizeof(tmp));
2732 if (strchr(tmp2, ',')) {
2733 while ((mbox = strsep(&tmp2, ","))) {
2734 if (!ast_strlen_zero(mbox)) {
2735 if (has_voicemail(mbox, folder))
2740 if ((context= strchr(tmp, '@')))
2743 context = "default";
2744 return messagecount(context, tmp, folder) ? 1 : 0;
2747 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)
2749 struct vm_state *sendvms = NULL, *destvms = NULL;
2750 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2751 if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2752 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2755 if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
2756 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2759 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2760 if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
2762 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2767 #ifndef IMAP_STORAGE
2768 /* copy message only used by file storage */
2769 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)
2771 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2772 const char *frombox = mbox(imbox);
2775 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2777 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2780 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2782 ast_copy_string(fromdir, dir, sizeof(fromdir));
2784 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2785 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2787 if (vm_lock_path(todir))
2788 return ERROR_LOCK_PATH;
2790 recipmsgnum = last_message_index(recip, todir) + 1;
2791 if (recipmsgnum < recip->maxmsg) {
2792 make_file(topath, sizeof(topath), todir, recipmsgnum);
2793 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2795 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2797 ast_unlock_path(todir);
2798 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2803 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2804 static int messagecount(const char *context, const char *mailbox, const char *folder)
2806 return __has_voicemail(context, mailbox, folder, 0);
2810 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2817 /* If no mailbox, return immediately */
2818 if (ast_strlen_zero(mailbox))
2821 if (ast_strlen_zero(folder))
2823 if (ast_strlen_zero(context))
2824 context = "default";
2826 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2828 if (!(dir = opendir(fn)))
2831 while ((de = readdir(dir))) {
2832 if (!strncasecmp(de->d_name, "msg", 3)) {
2836 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2847 static int has_voicemail(const char *mailbox, const char *folder)
2849 char tmp[256], *tmp2 = tmp, *mbox, *context;
2850 ast_copy_string(tmp, mailbox, sizeof(tmp));
2851 while ((mbox = strsep(&tmp2, ","))) {
2852 if ((context = strchr(mbox, '@')))
2855 context = "default";
2856 if (__has_voicemail(context, mbox, folder, 1))
2863 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2868 /* If no mailbox, return immediately */
2869 if (ast_strlen_zero(mailbox))
2877 if (strchr(mailbox, ',')) {
2881 ast_copy_string(tmp, mailbox, sizeof(tmp));
2883 while ((cur = strsep(&mb, ", "))) {
2884 if (!ast_strlen_zero(cur)) {
2885 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2898 ast_copy_string(tmp, mailbox, sizeof(tmp));
2900 if ((context = strchr(tmp, '@')))
2903 context = "default";
2906 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2908 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2915 static void run_externnotify(char *context, char *extension)
2917 char arguments[255];
2918 char ext_context[256] = "";
2919 int newvoicemails = 0, oldvoicemails = 0;
2920 struct ast_smdi_mwi_message *mwi_msg;
2922 if (!ast_strlen_zero(context))
2923 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2925 ast_copy_string(ext_context, extension, sizeof(ext_context));
2928 if (ast_app_has_voicemail(ext_context, NULL))
2929 ast_smdi_mwi_set(smdi_iface, extension);
2931 ast_smdi_mwi_unset(smdi_iface, extension);
2933 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2934 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2935 if (!strncmp(mwi_msg->cause, "INV", 3))
2936 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2937 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2938 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2939 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2940 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2942 ast_debug(1, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2946 if (!ast_strlen_zero(externnotify)) {
2947 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2948 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2950 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2951 ast_debug(1, "Executing %s\n", arguments);
2952 ast_safe_system(arguments);
2957 struct leave_vm_options {
2959 signed char record_gain;
2963 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2966 int newmsgs, oldmsgs;
2967 struct vm_state *vms = NULL;
2969 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2985 char dir[PATH_MAX], tmpdir[PATH_MAX];
2987 char prefile[PATH_MAX] = "";
2988 char tempfile[PATH_MAX] = "";
2989 char ext_context[256] = "";
2992 char ecodes[17] = "#";
2993 char tmp[1024] = "", *tmpptr;
2994 struct ast_vm_user *vmu;
2995 struct ast_vm_user svm;
2996 const char *category = NULL, *code, *alldtmf = "0123456789ABCD*#";
2998 ast_copy_string(tmp, ext, sizeof(tmp));
3000 if ((context = strchr(tmp, '@'))) {
3002 tmpptr = strchr(context, '&');
3004 tmpptr = strchr(ext, '&');
3010 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3012 ast_debug(3, "Before find_user\n");
3013 if (!(vmu = find_user(&svm, context, ext))) {
3014 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3015 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3018 /* Setup pre-file if appropriate */
3019 if (strcmp(vmu->context, "default"))
3020 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3022 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
3023 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3024 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3025 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3026 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3028 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3029 if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
3030 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3033 RETRIEVE(tempfile, -1, ext, context);
3034 if (ast_fileexists(tempfile, NULL, NULL) > 0)
3035 ast_copy_string(prefile, tempfile, sizeof(prefile));
3036 DISPOSE(tempfile, -1);
3037 /* It's easier just to try to make it than to check for its existence */
3038 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3040 /* Check current or macro-calling context for special extensions */
3041 if (ast_test_flag(vmu, VM_OPERATOR)) {
3042 if (!ast_strlen_zero(vmu->exit)) {
3043 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3044 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3047 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3048 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3051 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3052 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3057 if (!ast_strlen_zero(vmu->exit)) {
3058 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3059 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3060 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3061 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3062 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3063 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3067 if (ast_test_flag(options, OPT_DTMFEXIT)) {
3068 for (code = alldtmf; *code; code++) {
3071 if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
3072 strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
3076 /* Play the beginning intro if desired */
3077 if (!ast_strlen_zero(prefile)) {
3081 RETRIEVE(prefile, -1, ext, context);
3082 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3083 if (ast_streamfile(chan, prefile, chan->language) > -1)
3084 res = ast_waitstream(chan, ecodes);
3086 if (success == -1) {
3087 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
3088 ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
3089 store_file(prefile, vmu->mailbox, vmu->context, -1);
3093 ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
3094 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3096 DISPOSE(prefile, -1);
3098 ast_debug(1, "Hang up during prefile playback\n");
3100 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3105 /* On a '#' we skip the instructions */
3106 ast_set_flag(options, OPT_SILENT);
3109 if (!res && !ast_test_flag(options, OPT_SILENT)) {
3110 res = ast_stream_and_wait(chan, INTRO, ecodes);
3112 ast_set_flag(options, OPT_SILENT);
3117 ast_stopstream(chan);
3118 /* Check for a '*' here in case the caller wants to escape from voicemail to something
3119 other than the operator -- an automated attendant or mailbox login for example */
3120 if (!ast_strlen_zero(vmu->exit) && (res == '*')) {
3121 chan->exten[0] = 'a';
3122 chan->exten[1] = '\0';
3123 if (!ast_strlen_zero(vmu->exit)) {
3124 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3125 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3126 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3130 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3134 /* Check for a '0' here */
3135 if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
3137 if (ouseexten || ousemacro) {
3138 chan->exten[0] = 'o';
3139 chan->exten[1] = '\0';
3140 if (!ast_strlen_zero(vmu->exit)) {
3141 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3142 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3143 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3145 ast_play_and_wait(chan, "transfer");
3148 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3153 /* Allow all other digits to exit Voicemail and return to the dialplan */
3154 if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
3155 if (!ast_strlen_zero(options->exitcontext))
3156 ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
3158 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3164 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3167 /* The meat of recording the message... All the announcements and beeps have been played*/
3168 ast_copy_string(fmt, vmfmts, sizeof(fmt));
3169 if (!ast_strlen_zero(fmt)) {
3173 /* Is ext a mailbox? */
3174 /* must open stream for this user to get info! */
3175 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
3177 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3180 if (!(vms = get_vm_state_by_mailbox(ext,0))) {
3181 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3183 if (!(vms = ast_calloc(1, sizeof(*vms)))) {
3184 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
3187 ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3188 ast_copy_string(vms->username, ext, sizeof(vms->username));
3189 vms->mailstream = NIL;
3190 ast_debug(3, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
3192 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
3194 vmstate_insert(vms);
3195 vms = get_vm_state_by_mailbox(ext,0);
3199 /* here is a big difference! We add one to it later */
3200 msgnum = newmsgs + oldmsgs;
3201 ast_debug(3, "Messagecount set to %d\n",msgnum);
3202 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3203 /* set variable for compatibility */
3204 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3206 /* Check if mailbox is full */
3207 check_quota(vms, imapfolder);
3208 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3209 ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3210 ast_play_and_wait(chan, "vm-mailboxfull");
3214 /* Check if we have exceeded maxmsg */
3215 if (msgnum >= vmu->maxmsg) {
3216 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
3217 ast_play_and_wait(chan, "vm-mailboxfull");
3222 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3223 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3225 res = ast_waitstream(chan, "");
3226 ast_log(LOG_WARNING, "No more messages possible\n");
3227 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3232 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3233 txtdes = mkstemp(tmptxtfile);
3234 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
3236 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3238 res = ast_waitstream(chan, "");
3239 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3240 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3244 /* Now play the beep once we have the message number for our next message. */
3246 /* Unless we're *really* silent, try to send the beep */
3247 res = ast_stream_and_wait(chan, "beep", "");
3250 /* Store information in real-time storage */
3251 if (ast_check_realtime("voicemail_data")) {
3252 snprintf(priority, sizeof(priority), "%d", chan->priority);
3253 snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
3254 get_date(date, sizeof(date));
3255 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);
3258 /* Store information */
3259 txt = fdopen(txtdes, "w+");
3261 get_date(date, sizeof(date));
3264 "; Message Information file\n"
3283 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3284 date, (long)time(NULL),
3285 category ? category : "");
3287 ast_log(LOG_WARNING, "Error opening text file for output\n");
3289 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3291 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3295 if (duration < vmminsecs) {
3297 if (option_verbose > 2)
3298 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
3299 ast_filedelete(tmptxtfile, NULL);
3301 if (ast_check_realtime("voicemail_data")) {
3302 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3303 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3306 fprintf(txt, "duration=%d\n", duration);
3308 if (vm_lock_path(dir)) {
3309 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
3311 ast_filedelete(tmptxtfile, NULL);
3313 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3314 ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
3316 ast_unlock_path(dir);
3317 if (ast_check_realtime("voicemail_data")) {
3318 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3319 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3322 #ifndef IMAP_STORAGE
3323 msgnum = last_message_index(vmu, dir) + 1;
3325 make_file(fn, sizeof(fn), dir, msgnum);
3327 /* assign a variable with the name of the voicemail file */
3328 #ifndef IMAP_STORAGE
3329 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3331 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3334 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3335 ast_filerename(tmptxtfile, fn, NULL);
3336 rename(tmptxtfile, txtfile);
3338 /* Properly set permissions on voicemail text descriptor file.
3339 Unfortunately mkstemp() makes this file 0600 on most unix systems. */
3340 if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
3341 ast_log(LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
3343 ast_unlock_path(dir);
3344 if (ast_check_realtime("voicemail_data")) {
3345 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3346 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
3347 ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
3349 /* We must store the file first, before copying the message, because
3350 * ODBC storage does the entire copy with SQL.
3352 if (ast_fileexists(fn, NULL, NULL) > 0) {
3353 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3356 /* Are there to be more recipients of this message? */
3358 struct ast_vm_user recipu, *recip;
3359 char *exten, *context;
3361 exten = strsep(&tmpptr, "&");
3362 context = strchr(exten, '@');
3367 if ((recip = find_user(&recipu, context, exten))) {
3368 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3372 /* Notification and disposal needs to happen after the copy, though. */
3373 if (ast_fileexists(fn, NULL, NULL)) {
3374 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3375 DISPOSE(dir, msgnum);
3385 if (duration < vmminsecs)
3386 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3387 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3389 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3391 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3398 #ifndef IMAP_STORAGE
3399 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3401 /* we know max messages, so stop process when number is hit */
3407 if (vm_lock_path(dir))
3408 return ERROR_LOCK_PATH;
3410 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3411 make_file(sfn, sizeof(sfn), dir, x);
3412 if (EXISTS(dir, x, sfn, NULL)) {
3415 make_file(dfn, sizeof(dfn), dir, dest);
3416 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3422 ast_unlock_path(dir);
3428 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3431 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
3435 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3438 /* we must use mbox(x) folder names, and copy the message there */
3443 /* if save to Old folder, just leave in INBOX */
3444 if (box == 1) return 10;
3445 /* get the real IMAP message number for this message */
3446 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
3447 ast_debug(3, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3448 res = mail_copy(vms->mailstream,sequence,(char *) mbox(box));
3449 if (res == 1) return 0;
3452 char *dir = vms->curdir;
3453 char *username = vms->username;
3454 char *context = vmu->context;
3457 char ddir[PATH_MAX];
3458 const char *dbox = mbox(box);
3460 make_file(sfn, sizeof(sfn), dir, msg);
3461 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3463 if (vm_lock_path(ddir))
3464 return ERROR_LOCK_PATH;
3466 x = last_message_index(vmu, ddir) + 1;
3467 make_file(dfn, sizeof(dfn), ddir, x);
3469 if (x >= vmu->maxmsg) {
3470 ast_unlock_path(ddir);
3473 if (strcmp(sfn, dfn)) {
3474 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3476 ast_unlock_path(ddir);
3481 static int adsi_logo(unsigned char *buf)
3484 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3485 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3489 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3491 unsigned char buf[256];
3497 bytes += ast_adsi_data_mode(buf + bytes);
3498 ast_adsi_transmit_message(chan, buf,&nb