2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Comedian Mail - Voicemail System
23 * \author Mark Spencer <markster@digium.com>
25 * \extref Unixodbc - http://www.unixodbc.org
26 * \extref A source distribution of University of Washington's IMAP
27 c-client (http://www.washington.edu/imap/
31 * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
32 * \ingroup applications
33 * \note This module requires res_adsi to load. This needs to be optional
38 * \note This file is now almost impossible to work with, due to all #ifdefs.
39 * Feels like the database code before realtime. Someone - please come up
40 * with a plan to clean this up.
44 <depend>res_smdi</depend>
48 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o">
49 <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
50 <depend>unixodbc</depend>
51 <conflict>IMAP_STORAGE</conflict>
52 <defaultenabled>no</defaultenabled>
54 <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
55 <depend>imap_tk</depend>
56 <conflict>ODBC_STORAGE</conflict>
58 <defaultenabled>no</defaultenabled>
65 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
75 #include <sys/types.h>
89 #include "asterisk/lock.h"
90 #include "asterisk/file.h"
91 #include "asterisk/logger.h"
92 #include "asterisk/channel.h"
93 #include "asterisk/pbx.h"
94 #include "asterisk/options.h"
95 #include "asterisk/config.h"
96 #include "asterisk/say.h"
97 #include "asterisk/module.h"
98 #include "asterisk/adsi.h"
99 #include "asterisk/app.h"
100 #include "asterisk/manager.h"
101 #include "asterisk/dsp.h"
102 #include "asterisk/localtime.h"
103 #include "asterisk/cli.h"
104 #include "asterisk/utils.h"
105 #include "asterisk/stringfields.h"
106 #include "asterisk/smdi.h"
107 #include "asterisk/event.h"
110 #include "asterisk/res_odbc.h"
114 static char imapserver[48];
115 static char imapport[8];
116 static char imapflags[128];
117 static char imapfolder[64];
118 static char authuser[32];
119 static char authpassword[42];
121 static int expungeonhangup = 1;
122 AST_MUTEX_DEFINE_STATIC(delimiter_lock);
123 static char delimiter = '\0';
128 /* Forward declarations for IMAP */
129 static int init_mailstream(struct vm_state *vms, int box);
130 static void write_file(char *filename, char *buffer, unsigned long len);
131 static void display_body(BODY *body, char *pfx, long i);
132 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
133 static void vm_imap_delete(int msgnum, struct vm_state *vms);
134 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
135 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
136 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
137 static void vmstate_insert(struct vm_state *vms);
138 static void vmstate_delete(struct vm_state *vms);
139 static void set_update(MAILSTREAM * stream);
140 static void init_vm_state(struct vm_state *vms);
141 static void check_msgArray(struct vm_state *vms);
142 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
143 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
144 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
145 static void get_mailbox_delimiter(MAILSTREAM *stream);
146 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
147 static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int target);
148 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);
153 struct vm_state *vms;
154 AST_LIST_ENTRY(vmstate) list;
157 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
161 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
163 #define COMMAND_TIMEOUT 5000
164 /* Don't modify these here; set your umask at runtime instead */
165 #define VOICEMAIL_DIR_MODE 0777
166 #define VOICEMAIL_FILE_MODE 0666
167 #define CHUNKSIZE 65536
169 #define VOICEMAIL_CONFIG "voicemail.conf"
170 #define ASTERISK_USERNAME "asterisk"
172 /* Default mail command to mail voicemail. Change it with the
173 mailcmd= command in voicemail.conf */
174 #define SENDMAIL "/usr/sbin/sendmail -t"
176 #define INTRO "vm-intro"
179 #define MAXMSGLIMIT 9999
181 #define BASEMAXINLINE 256
182 #define BASELINELEN 72
183 #define BASEMAXINLINE 256
186 #define MAX_DATETIME_FORMAT 512
187 #define MAX_NUM_CID_CONTEXTS 10
189 #define VM_REVIEW (1 << 0)
190 #define VM_OPERATOR (1 << 1)
191 #define VM_SAYCID (1 << 2)
192 #define VM_SVMAIL (1 << 3)
193 #define VM_ENVELOPE (1 << 4)
194 #define VM_SAYDURATION (1 << 5)
195 #define VM_SKIPAFTERCMD (1 << 6)
196 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
197 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
198 #define VM_PBXSKIP (1 << 9)
199 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
200 #define VM_ATTACH (1 << 11)
201 #define VM_DELETE (1 << 12)
202 #define VM_ALLOCED (1 << 13)
203 #define VM_SEARCH (1 << 14)
204 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
205 #define ERROR_LOCK_PATH -100
209 OPT_SILENT = (1 << 0),
210 OPT_BUSY_GREETING = (1 << 1),
211 OPT_UNAVAIL_GREETING = (1 << 2),
212 OPT_RECORDGAIN = (1 << 3),
213 OPT_PREPEND_MAILBOX = (1 << 4),
214 OPT_PRIORITY_JUMP = (1 << 5),
215 OPT_AUTOPLAY = (1 << 6),
219 OPT_ARG_RECORDGAIN = 0,
220 OPT_ARG_PLAYFOLDER = 1,
221 /* This *must* be the last value in this enum! */
222 OPT_ARG_ARRAY_SIZE = 2,
225 AST_APP_OPTIONS(vm_app_options, {
226 AST_APP_OPTION('s', OPT_SILENT),
227 AST_APP_OPTION('b', OPT_BUSY_GREETING),
228 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
229 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
230 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
231 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
232 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
235 static int load_config(void);
237 /*! \page vmlang Voicemail Language Syntaxes Supported
239 \par Syntaxes supported, not really language codes.
246 \arg \b pt - Portuguese
247 \arg \b pt_BR - Portuguese (Brazil)
249 \arg \b no - Norwegian
252 German requires the following additional soundfile:
253 \arg \b 1F einE (feminine)
255 Spanish requires the following additional soundfile:
256 \arg \b 1M un (masculine)
258 Dutch, Portuguese & Spanish require the following additional soundfiles:
259 \arg \b vm-INBOXs singular of 'new'
260 \arg \b vm-Olds singular of 'old/heard/read'
263 \arg \b vm-INBOX nieuwe (nl)
264 \arg \b vm-Old oude (nl)
267 \arg \b vm-new-a 'new', feminine singular accusative
268 \arg \b vm-new-e 'new', feminine plural accusative
269 \arg \b vm-new-ych 'new', feminine plural genitive
270 \arg \b vm-old-a 'old', feminine singular accusative
271 \arg \b vm-old-e 'old', feminine plural accusative
272 \arg \b vm-old-ych 'old', feminine plural genitive
273 \arg \b digits/1-a 'one', not always same as 'digits/1'
274 \arg \b digits/2-ie 'two', not always same as 'digits/2'
277 \arg \b vm-nytt singular of 'new'
278 \arg \b vm-nya plural of 'new'
279 \arg \b vm-gammalt singular of 'old'
280 \arg \b vm-gamla plural of 'old'
281 \arg \b digits/ett 'one', not always same as 'digits/1'
284 \arg \b vm-ny singular of 'new'
285 \arg \b vm-nye plural of 'new'
286 \arg \b vm-gammel singular of 'old'
287 \arg \b vm-gamle plural of 'old'
295 Italian requires the following additional soundfile:
299 \arg \b vm-nuovi new plural
300 \arg \b vm-vecchio old
301 \arg \b vm-vecchi old plural
303 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
304 spelled among others when you have to change folder. For the above reasons, vm-INBOX
305 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
314 unsigned char iobuf[BASEMAXINLINE];
317 /*! Structure for linked list of users
318 * Use ast_vm_user_destroy() to free one of these structures. */
320 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
321 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
322 char password[80]; /*!< Secret pin code, numbers only */
323 char fullname[80]; /*!< Full name, for directory app */
324 char email[80]; /*!< E-mail address */
325 char pager[80]; /*!< E-mail address to pager (no attachment) */
326 char serveremail[80]; /*!< From: Mail address */
327 char mailcmd[160]; /*!< Configurable mail command */
328 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
329 char zonetag[80]; /*!< Time zone */
332 char uniqueid[20]; /*!< Unique integer identifier */
334 char attachfmt[20]; /*!< Attachment format */
335 unsigned int flags; /*!< VM_ flags */
337 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
338 int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
340 char imapuser[80]; /*!< IMAP server login */
341 char imappassword[80]; /*!< IMAP server password if authpassword not defined */
343 double volgain; /*!< Volume gain for voicemails sent via email */
344 AST_LIST_ENTRY(ast_vm_user) list;
347 /*! Voicemail time zones */
349 AST_LIST_ENTRY(vm_zone) list;
352 char msg_format[512];
355 /*! Voicemail mailbox state */
359 char curdir[PATH_MAX];
360 char vmbox[PATH_MAX];
372 int updated; /*!< decremented on each mail check until 1 -allows delay */
374 MAILSTREAM *mailstream;
376 char imapuser[80]; /*!< IMAP server login */
378 unsigned int quota_limit;
379 unsigned int quota_usage;
380 struct vm_state *persist_vms;
386 static char odbc_database[80];
387 static char odbc_table[80];
388 #define RETRIEVE(a,b) retrieve_file(a,b)
389 #define DISPOSE(a,b) remove_file(a,b)
390 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
391 #define EXISTS(a,b,c,d) (message_exists(a,b))
392 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
393 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
394 #define DELETE(a,b,c) (delete_file(a,b))
397 #define RETRIEVE(a,b)
399 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
400 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
401 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
402 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
403 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
404 #define DELETE(a,b,c) (vm_delete(c))
406 #define RETRIEVE(a,b)
408 #define STORE(a,b,c,d,e,f,g,h,i)
409 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
410 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
411 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
412 #define DELETE(a,b,c) (vm_delete(c))
416 static char VM_SPOOL_DIR[PATH_MAX];
418 static char ext_pass_cmd[128];
420 #define PWDCHANGE_INTERNAL (1 << 1)
421 #define PWDCHANGE_EXTERNAL (1 << 2)
422 static int pwdchange = PWDCHANGE_INTERNAL;
425 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
428 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
430 # define tdesc "Comedian Mail (Voicemail System)"
434 static char userscontext[AST_MAX_EXTENSION] = "default";
436 static char *addesc = "Comedian Mail";
438 static char *synopsis_vm = "Leave a Voicemail message";
440 static char *descrip_vm =
441 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
442 "application allows the calling party to leave a message for the specified\n"
443 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
444 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
445 "specified mailbox does not exist.\n"
446 " The Voicemail application will exit if any of the following DTMF digits are\n"
448 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
449 " * - Jump to the 'a' extension in the current dialplan context.\n"
450 " This application will set the following channel variable upon completion:\n"
451 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
452 " application. The possible values are:\n"
453 " SUCCESS | USEREXIT | FAILED\n\n"
455 " b - Play the 'busy' greeting to the calling party.\n"
456 " g(#) - Use the specified amount of gain when recording the voicemail\n"
457 " message. The units are whole-number decibels (dB).\n"
458 " s - Skip the playback of instructions for leaving a message to the\n"
460 " u - Play the 'unavailable greeting.\n"
461 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
464 static char *synopsis_vmain = "Check Voicemail messages";
466 static char *descrip_vmain =
467 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
468 "calling party to check voicemail messages. A specific mailbox, and optional\n"
469 "corresponding context, may be specified. If a mailbox is not provided, the\n"
470 "calling party will be prompted to enter one. If a context is not specified,\n"
471 "the 'default' context will be used.\n\n"
473 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
474 " is entered by the caller.\n"
475 " g(#) - Use the specified amount of gain when recording a voicemail\n"
476 " message. The units are whole-number decibels (dB).\n"
477 " s - Skip checking the passcode for the mailbox.\n"
478 " a(#) - Skip folder prompt and go directly to folder specified.\n"
479 " Defaults to INBOX\n";
481 static char *synopsis_vm_box_exists =
482 "Check to see if Voicemail mailbox exists";
484 static char *descrip_vm_box_exists =
485 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
486 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
488 " This application will set the following channel variable upon completion:\n"
489 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
490 " MailboxExists application. Possible values include:\n"
491 " SUCCESS | FAILED\n\n"
493 " j - Jump to priority n+101 if the mailbox is found.\n";
495 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
497 static char *descrip_vmauthenticate =
498 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
499 "same way as the Authenticate application, but the passwords are taken from\n"
501 " If the mailbox is specified, only that mailbox's password will be considered\n"
502 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
503 "be set with the authenticated mailbox.\n\n"
505 " s - Skip playing the initial prompts.\n";
507 /* Leave a message */
508 static char *app = "VoiceMail";
510 /* Check mail, control, etc */
511 static char *app2 = "VoiceMailMain";
513 static char *app3 = "MailboxExists";
514 static char *app4 = "VMAuthenticate";
516 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
517 static AST_LIST_HEAD_STATIC(zones, vm_zone);
518 static int maxsilence;
520 static int silencethreshold = 128;
521 static char serveremail[80];
522 static char mailcmd[160]; /* Configurable mail cmd */
523 static char externnotify[160];
524 static struct ast_smdi_interface *smdi_iface = NULL;
525 static char vmfmts[80];
526 static double volgain;
527 static int vmminsecs;
528 static int vmmaxsecs;
531 static int maxlogins;
533 /*! Poll mailboxes for changes since there is something external to
534 * app_voicemail that may change them. */
535 static unsigned int poll_mailboxes;
537 /*! Polling frequency */
538 static unsigned int poll_freq;
539 /*! By default, poll every 30 seconds */
540 #define DEFAULT_POLL_FREQ 30
542 AST_MUTEX_DEFINE_STATIC(poll_lock);
543 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
544 static pthread_t poll_thread = AST_PTHREADT_NULL;
545 static unsigned char poll_thread_run;
547 /*! Subscription to ... MWI event subscriptions */
548 static struct ast_event_sub *mwi_sub_sub;
549 /*! Subscription to ... MWI event un-subscriptions */
550 static struct ast_event_sub *mwi_unsub_sub;
553 * \brief An MWI subscription
555 * This is so we can keep track of which mailboxes are subscribed to.
556 * This way, we know which mailboxes to poll when the pollmailboxes
557 * option is being used.
560 AST_RWLIST_ENTRY(mwi_sub) entry;
567 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
569 /* custom password sounds */
570 static char vm_password[80] = "vm-password";
571 static char vm_newpassword[80] = "vm-newpassword";
572 static char vm_passchanged[80] = "vm-passchanged";
573 static char vm_reenterpassword[80] = "vm-reenterpassword";
574 static char vm_mismatch[80] = "vm-mismatch";
576 static struct ast_flags globalflags = {0};
578 static int saydurationminfo;
580 static char dialcontext[AST_MAX_CONTEXT];
581 static char callcontext[AST_MAX_CONTEXT];
582 static char exitcontext[AST_MAX_CONTEXT];
584 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
587 static char *emailbody = NULL;
588 static char *emailsubject = NULL;
589 static char *pagerbody = NULL;
590 static char *pagersubject = NULL;
591 static char fromstring[100];
592 static char pagerfromstring[100];
593 static char emailtitle[100];
594 static char charset[32] = "ISO-8859-1";
596 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
597 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
598 static int adsiver = 1;
599 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
601 /* Forward declarations - generic */
602 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
603 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);
604 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
605 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
606 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
607 signed char record_gain, struct vm_state *vms);
608 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
609 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
610 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
611 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);
612 static void apply_options(struct ast_vm_user *vmu, const char *options);
614 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
615 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
620 static void populate_defaults(struct ast_vm_user *vmu)
622 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
623 if (saydurationminfo)
624 vmu->saydurationm = saydurationminfo;
626 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
628 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
630 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
632 vmu->maxsecs = vmmaxsecs;
634 vmu->maxmsg = maxmsg;
635 vmu->volgain = volgain;
638 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
641 if (!strcasecmp(var, "attach")) {
642 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
643 } else if (!strcasecmp(var, "attachfmt")) {
644 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
645 } else if (!strcasecmp(var, "serveremail")) {
646 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
647 } else if (!strcasecmp(var, "language")) {
648 ast_copy_string(vmu->language, value, sizeof(vmu->language));
649 } else if (!strcasecmp(var, "tz")) {
650 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
652 } else if (!strcasecmp(var, "imapuser")) {
653 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
654 } else if (!strcasecmp(var, "imappassword")) {
655 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
657 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
658 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
659 } else if (!strcasecmp(var, "saycid")){
660 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
661 } else if (!strcasecmp(var,"sendvoicemail")){
662 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
663 } else if (!strcasecmp(var, "review")){
664 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
665 } else if (!strcasecmp(var, "tempgreetwarn")){
666 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
667 } else if (!strcasecmp(var, "operator")){
668 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
669 } else if (!strcasecmp(var, "envelope")){
670 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
671 } else if (!strcasecmp(var, "sayduration")){
672 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
673 } else if (!strcasecmp(var, "saydurationm")){
674 if (sscanf(value, "%d", &x) == 1) {
675 vmu->saydurationm = x;
677 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
679 } else if (!strcasecmp(var, "forcename")){
680 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
681 } else if (!strcasecmp(var, "forcegreetings")){
682 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
683 } else if (!strcasecmp(var, "callback")) {
684 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
685 } else if (!strcasecmp(var, "dialout")) {
686 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
687 } else if (!strcasecmp(var, "exitcontext")) {
688 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
689 } else if (!strcasecmp(var, "maxmessage")) {
690 if (vmu->maxsecs <= 0) {
691 ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %i\n", value, vmmaxsecs);
692 vmu->maxsecs = vmmaxsecs;
694 vmu->maxsecs = atoi(value);
696 } else if (!strcasecmp(var, "maxmsg")) {
697 vmu->maxmsg = atoi(value);
698 if (vmu->maxmsg <= 0) {
699 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
700 vmu->maxmsg = MAXMSG;
701 } else if (vmu->maxmsg > MAXMSGLIMIT) {
702 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
703 vmu->maxmsg = MAXMSGLIMIT;
705 } else if (!strcasecmp(var, "volgain")) {
706 sscanf(value, "%lf", &vmu->volgain);
707 } else if (!strcasecmp(var, "options")) {
708 apply_options(vmu, value);
712 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
715 if (!ast_strlen_zero(vmu->uniqueid)) {
716 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
718 ast_copy_string(vmu->password, password, sizeof(vmu->password));
728 static void apply_options(struct ast_vm_user *vmu, const char *options)
729 { /* Destructively Parse options and apply */
733 stringp = ast_strdupa(options);
734 while ((s = strsep(&stringp, "|"))) {
736 if ((var = strsep(&value, "=")) && value) {
737 apply_option(vmu, var, value);
742 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
744 struct ast_variable *tmp;
747 if (!strcasecmp(tmp->name, "vmsecret")) {
748 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
749 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
750 if (ast_strlen_zero(retval->password))
751 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
752 } else if (!strcasecmp(tmp->name, "uniqueid")) {
753 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
754 } else if (!strcasecmp(tmp->name, "pager")) {
755 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
756 } else if (!strcasecmp(tmp->name, "email")) {
757 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
758 } else if (!strcasecmp(tmp->name, "fullname")) {
759 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
760 } else if (!strcasecmp(tmp->name, "context")) {
761 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
763 } else if (!strcasecmp(tmp->name, "imapuser")) {
764 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
765 } else if (!strcasecmp(tmp->name, "imappassword")) {
766 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
769 apply_option(retval, tmp->name, tmp->value);
774 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
776 struct ast_variable *var;
777 struct ast_vm_user *retval;
779 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
781 ast_set_flag(retval, VM_ALLOCED);
783 memset(retval, 0, sizeof(*retval));
785 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
786 populate_defaults(retval);
787 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
788 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
790 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
792 apply_options_full(retval, var);
793 ast_variables_destroy(var);
803 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
805 /* This function could be made to generate one from a database, too */
806 struct ast_vm_user *vmu=NULL, *cur;
807 AST_LIST_LOCK(&users);
809 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
812 AST_LIST_TRAVERSE(&users, cur, list) {
813 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
815 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
819 /* Make a copy, so that on a reload, we have no race */
820 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
821 memcpy(vmu, cur, sizeof(*vmu));
822 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
823 AST_LIST_NEXT(vmu, list) = NULL;
826 vmu = find_user_realtime(ivm, context, mailbox);
827 AST_LIST_UNLOCK(&users);
831 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
833 /* This function could be made to generate one from a database, too */
834 struct ast_vm_user *cur;
836 AST_LIST_LOCK(&users);
837 AST_LIST_TRAVERSE(&users, cur, list) {
838 if ((!context || !strcasecmp(context, cur->context)) &&
839 (!strcasecmp(mailbox, cur->mailbox)))
843 ast_copy_string(cur->password, newpass, sizeof(cur->password));
846 AST_LIST_UNLOCK(&users);
850 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
852 struct ast_config *cfg=NULL;
853 struct ast_variable *var=NULL;
854 struct ast_category *cat=NULL;
855 char *category=NULL, *value=NULL, *new=NULL;
856 const char *tmp=NULL;
858 if (!change_password_realtime(vmu, newpassword))
861 /* check voicemail.conf */
862 if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
863 while ((category = ast_category_browse(cfg, category))) {
864 if (!strcasecmp(category, vmu->context)) {
865 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
866 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
869 value = strstr(tmp,",");
871 ast_log(LOG_WARNING, "variable has bad format.\n");
874 new = alloca((strlen(value)+strlen(newpassword)+1));
875 sprintf(new,"%s%s", newpassword, value);
876 if (!(cat = ast_category_get(cfg, category))) {
877 ast_log(LOG_WARNING, "Failed to get category structure.\n");
880 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
883 /* save the results */
884 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
885 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
886 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
890 /* check users.conf and update the password stored for the mailbox*/
891 /* if no vmsecret entry exists create one. */
892 if ((cfg = ast_config_load_with_comments("users.conf"))) {
893 if (option_debug > 3)
894 ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
895 while ((category = ast_category_browse(cfg, category))) {
896 if (option_debug > 3)
897 ast_log(LOG_DEBUG, "users.conf: %s\n", category);
898 if (!strcasecmp(category, vmu->mailbox)) {
899 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
900 if (option_debug > 3)
901 ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
902 var = ast_variable_new("vmsecret", newpassword);
904 new = alloca(strlen(newpassword)+1);
905 sprintf(new, "%s", newpassword);
906 if (!(cat = ast_category_get(cfg, category))) {
907 if (option_debug > 3)
908 ast_log(LOG_DEBUG, "failed to get category!\n");
912 ast_variable_update(cat, "vmsecret", new, NULL, 0);
914 ast_variable_append(cat, var);
917 /* save the results and clean things up */
918 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
919 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
920 config_text_file_save("users.conf", cfg, "AppVoicemail");
924 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
927 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
928 if (!ast_safe_system(buf))
929 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
932 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
934 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
938 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
940 if (mkdir(dir, 01777) && (errno != EEXIST)) {
941 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
942 return sprintf(dest, "%s/msg%04d", dir, num);
944 /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
945 return sprintf(dest, "%s/msg%04d", dir, num);
948 static void vm_imap_delete(int msgnum, struct vm_state *vms)
950 unsigned long messageNum = 0;
953 /* find real message number based on msgnum */
954 /* this may be an index into vms->msgArray based on the msgnum. */
956 messageNum = vms->msgArray[msgnum];
957 if (messageNum == 0) {
958 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
962 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
964 sprintf (arg,"%lu",messageNum);
965 mail_setflag (vms->mailstream,arg,"\\DELETED");
969 static int make_file(char *dest, int len, char *dir, int num)
971 return snprintf(dest, len, "%s/msg%04d", dir, num);
974 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
975 * \param dest String. base directory.
976 * \param len Length of dest.
977 * \param context String. Ignored if is null or empty string.
978 * \param ext String. Ignored if is null or empty string.
979 * \param folder String. Ignored if is null or empty string.
980 * \return -1 on failure, 0 on success.
982 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
984 mode_t mode = VOICEMAIL_DIR_MODE;
986 if (!ast_strlen_zero(context)) {
987 make_dir(dest, len, context, "", "");
988 if (mkdir(dest, mode) && errno != EEXIST) {
989 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
993 if (!ast_strlen_zero(ext)) {
994 make_dir(dest, len, context, ext, "");
995 if (mkdir(dest, mode) && errno != EEXIST) {
996 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
1000 if (!ast_strlen_zero(folder)) {
1001 make_dir(dest, len, context, ext, folder);
1002 if (mkdir(dest, mode) && errno != EEXIST) {
1003 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
1010 /*! \brief Lock file path
1011 only return failure if ast_lock_path returns 'timeout',
1012 not if the path does not exist or any other reason
1014 static int vm_lock_path(const char *path)
1016 switch (ast_lock_path(path)) {
1017 case AST_LOCK_TIMEOUT:
1026 static int retrieve_file(char *dir, int msgnum)
1032 void *fdm = MAP_FAILED;
1033 SQLSMALLINT colcount=0;
1040 SQLSMALLINT datatype;
1041 SQLSMALLINT decimaldigits;
1042 SQLSMALLINT nullable;
1048 char full_fn[PATH_MAX];
1051 struct odbc_obj *obj;
1052 obj = ast_odbc_request_obj(odbc_database, 0);
1054 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1055 c = strchr(fmt, '|');
1058 if (!strcasecmp(fmt, "wav49"))
1060 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1062 make_file(fn, sizeof(fn), dir, msgnum);
1064 ast_copy_string(fn, dir, sizeof(fn));
1065 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1067 if (!(f = fopen(full_fn, "w+"))) {
1068 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1072 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1073 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1074 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1075 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1076 ast_odbc_release_obj(obj);
1079 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1080 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1081 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1082 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1083 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1084 ast_odbc_release_obj(obj);
1087 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1088 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1089 res = ast_odbc_smart_execute(obj, stmt);
1090 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1091 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1092 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1093 ast_odbc_release_obj(obj);
1096 res = SQLFetch(stmt);
1097 if (res == SQL_NO_DATA) {
1098 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1099 ast_odbc_release_obj(obj);
1102 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1103 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1104 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1105 ast_odbc_release_obj(obj);
1108 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1110 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1111 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1112 ast_odbc_release_obj(obj);
1115 res = SQLNumResultCols(stmt, &colcount);
1116 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1117 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1118 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1119 ast_odbc_release_obj(obj);
1123 fprintf(f, "[message]\n");
1124 for (x=0;x<colcount;x++) {
1126 collen = sizeof(coltitle);
1127 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
1128 &datatype, &colsize, &decimaldigits, &nullable);
1129 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1130 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1131 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1132 ast_odbc_release_obj(obj);
1135 if (!strcasecmp(coltitle, "recording")) {
1137 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1141 lseek(fd, fdlen - 1, SEEK_SET);
1142 if (write(fd, tmp, 1) != 1) {
1147 /* Read out in small chunks */
1148 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1149 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1150 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1151 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1152 ast_odbc_release_obj(obj);
1155 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1156 munmap(fdm, CHUNKSIZE);
1157 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1158 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1160 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1161 ast_odbc_release_obj(obj);
1166 truncate(full_fn, fdlen);
1169 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1170 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1171 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1172 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1173 ast_odbc_release_obj(obj);
1176 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1177 fprintf(f, "%s=%s\n", coltitle, rowdata);
1180 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1181 ast_odbc_release_obj(obj);
1183 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1192 static int remove_file(char *dir, int msgnum)
1195 char full_fn[PATH_MAX];
1199 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1200 make_file(fn, sizeof(fn), dir, msgnum);
1202 ast_copy_string(fn, dir, sizeof(fn));
1203 ast_filedelete(fn, NULL);
1204 if (ast_check_realtime("voicemail_data")) {
1205 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1207 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1212 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1220 struct odbc_obj *obj;
1221 obj = ast_odbc_request_obj(odbc_database, 0);
1223 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1224 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1225 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1226 ast_odbc_release_obj(obj);
1229 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1230 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1231 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1232 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1233 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1234 ast_odbc_release_obj(obj);
1237 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1238 res = ast_odbc_smart_execute(obj, stmt);
1239 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1240 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1241 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1242 ast_odbc_release_obj(obj);
1245 res = SQLFetch(stmt);
1246 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1247 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1248 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1249 ast_odbc_release_obj(obj);
1252 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1253 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1254 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1255 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1256 ast_odbc_release_obj(obj);
1259 if (sscanf(rowdata, "%d", &x) != 1)
1260 ast_log(LOG_WARNING, "Failed to read message count!\n");
1261 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1262 ast_odbc_release_obj(obj);
1264 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1269 static int message_exists(char *dir, int msgnum)
1278 struct odbc_obj *obj;
1279 obj = ast_odbc_request_obj(odbc_database, 0);
1281 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1282 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1283 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1284 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1285 ast_odbc_release_obj(obj);
1288 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1289 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1290 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1291 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1292 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1293 ast_odbc_release_obj(obj);
1296 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1297 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1298 res = ast_odbc_smart_execute(obj, stmt);
1299 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1300 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1301 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1302 ast_odbc_release_obj(obj);
1305 res = SQLFetch(stmt);
1306 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1307 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1308 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1309 ast_odbc_release_obj(obj);
1312 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1313 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1314 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1315 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1316 ast_odbc_release_obj(obj);
1319 if (sscanf(rowdata, "%d", &x) != 1)
1320 ast_log(LOG_WARNING, "Failed to read message count!\n");
1321 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1322 ast_odbc_release_obj(obj);
1324 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1329 static int count_messages(struct ast_vm_user *vmu, char *dir)
1331 return last_message_index(vmu, dir) + 1;
1334 static void delete_file(char *sdir, int smsg)
1341 struct odbc_obj *obj;
1342 obj = ast_odbc_request_obj(odbc_database, 0);
1344 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1345 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1346 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1347 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1348 ast_odbc_release_obj(obj);
1351 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1352 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1353 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1354 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1355 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1356 ast_odbc_release_obj(obj);
1359 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1360 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1361 res = ast_odbc_smart_execute(obj, stmt);
1362 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1363 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1364 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1365 ast_odbc_release_obj(obj);
1368 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1369 ast_odbc_release_obj(obj);
1371 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1376 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1383 struct odbc_obj *obj;
1385 delete_file(ddir, dmsg);
1386 obj = ast_odbc_request_obj(odbc_database, 0);
1388 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1389 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1390 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1391 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1392 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1393 ast_odbc_release_obj(obj);
1396 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);
1397 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1398 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1399 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1400 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1401 ast_odbc_release_obj(obj);
1404 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1405 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1406 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1407 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1408 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1409 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1410 res = ast_odbc_smart_execute(obj, stmt);
1411 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1412 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1413 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1414 ast_odbc_release_obj(obj);
1417 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1418 ast_odbc_release_obj(obj);
1420 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1425 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1430 void *fdm = MAP_FAILED;
1437 char full_fn[PATH_MAX];
1440 const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1441 const char *category = "";
1442 struct ast_config *cfg=NULL;
1443 struct odbc_obj *obj;
1445 delete_file(dir, msgnum);
1446 obj = ast_odbc_request_obj(odbc_database, 0);
1448 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1449 c = strchr(fmt, '|');
1452 if (!strcasecmp(fmt, "wav49"))
1454 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1456 make_file(fn, sizeof(fn), dir, msgnum);
1458 ast_copy_string(fn, dir, sizeof(fn));
1459 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1460 cfg = ast_config_load(full_fn);
1461 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1462 fd = open(full_fn, O_RDWR);
1464 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1465 ast_odbc_release_obj(obj);
1469 context = ast_variable_retrieve(cfg, "message", "context");
1470 if (!context) context = "";
1471 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1472 if (!macrocontext) macrocontext = "";
1473 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1474 if (!callerid) callerid = "";
1475 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1476 if (!origtime) origtime = "";
1477 duration = ast_variable_retrieve(cfg, "message", "duration");
1478 if (!duration) duration = "";
1479 category = ast_variable_retrieve(cfg, "message", "category");
1480 if (!category) category = "";
1482 fdlen = lseek(fd, 0, SEEK_END);
1483 lseek(fd, 0, SEEK_SET);
1484 printf("Length is %zd\n", fdlen);
1485 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1486 if (fdm == MAP_FAILED) {
1487 ast_log(LOG_WARNING, "Memory map failed!\n");
1488 ast_odbc_release_obj(obj);
1491 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1492 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1493 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1494 ast_odbc_release_obj(obj);
1497 if (!ast_strlen_zero(category))
1498 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1500 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1501 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1502 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1503 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1504 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1505 ast_odbc_release_obj(obj);
1508 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1509 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1510 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1511 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1512 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1513 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1514 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1515 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1516 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1517 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1518 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1519 if (!ast_strlen_zero(category))
1520 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1521 res = ast_odbc_smart_execute(obj, stmt);
1522 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1523 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1524 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1525 ast_odbc_release_obj(obj);
1528 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1529 ast_odbc_release_obj(obj);
1531 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1534 ast_config_destroy(cfg);
1535 if (fdm != MAP_FAILED)
1542 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1549 struct odbc_obj *obj;
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 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1557 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1558 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1559 ast_odbc_release_obj(obj);
1562 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1563 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1564 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1565 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1566 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1567 ast_odbc_release_obj(obj);
1570 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1571 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1572 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1573 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1574 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1575 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1576 res = ast_odbc_smart_execute(obj, stmt);
1577 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1578 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1579 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1580 ast_odbc_release_obj(obj);
1583 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1584 ast_odbc_release_obj(obj);
1586 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1592 #ifndef IMAP_STORAGE
1593 static int count_messages(struct ast_vm_user *vmu, char *dir)
1595 /* Find all .txt files - even if they are not in sequence from 0000 */
1599 struct dirent *vment = NULL;
1601 if (vm_lock_path(dir))
1602 return ERROR_LOCK_PATH;
1604 if ((vmdir = opendir(dir))) {
1605 while ((vment = readdir(vmdir))) {
1606 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1611 ast_unlock_path(dir);
1616 static void rename_file(char *sfn, char *dfn)
1618 char stxt[PATH_MAX];
1619 char dtxt[PATH_MAX];
1620 ast_filerename(sfn,dfn,NULL);
1621 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1622 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1623 if (ast_check_realtime("voicemail_data")) {
1624 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1629 static int copy(char *infile, char *outfile)
1637 #ifdef HARDLINK_WHEN_POSSIBLE
1638 /* Hard link if possible; saves disk space & is faster */
1639 if (link(infile, outfile)) {
1641 if ((ifd = open(infile, O_RDONLY)) < 0) {
1642 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1645 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1646 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1651 len = read(ifd, buf, sizeof(buf));
1653 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1659 res = write(ofd, buf, len);
1660 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1661 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1671 #ifdef HARDLINK_WHEN_POSSIBLE
1673 /* Hard link succeeded */
1679 static void copy_file(char *frompath, char *topath)
1681 char frompath2[PATH_MAX], topath2[PATH_MAX];
1682 struct ast_variable *tmp,*var = NULL;
1683 char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
1684 ast_filecopy(frompath, topath, NULL);
1685 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1686 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1687 if (ast_check_realtime("voicemail_data")) {
1688 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
1689 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
1690 for (tmp = var; tmp; tmp = tmp->next) {
1691 if (!strcasecmp(tmp->name, "origmailbox")) {
1692 origmailbox = tmp->value;
1693 } else if (!strcasecmp(tmp->name, "context")) {
1694 context = tmp->value;
1695 } else if (!strcasecmp(tmp->name, "macrocontext")) {
1696 macrocontext = tmp->value;
1697 } else if (!strcasecmp(tmp->name, "exten")) {
1699 } else if (!strcasecmp(tmp->name, "priority")) {
1700 priority = tmp->value;
1701 } else if (!strcasecmp(tmp->name, "callerchan")) {
1702 callerchan = tmp->value;
1703 } else if (!strcasecmp(tmp->name, "callerid")) {
1704 callerid = tmp->value;
1705 } else if (!strcasecmp(tmp->name, "origdate")) {
1706 origdate = tmp->value;
1707 } else if (!strcasecmp(tmp->name, "origtime")) {
1708 origtime = tmp->value;
1709 } else if (!strcasecmp(tmp->name, "category")) {
1710 category = tmp->value;
1711 } else if (!strcasecmp(tmp->name, "duration")) {
1712 duration = tmp->value;
1715 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);
1717 copy(frompath2, topath2);
1718 ast_variables_destroy(var);
1723 * A negative return value indicates an error.
1724 * \note Should always be called with a lock already set on dir.
1726 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1729 unsigned char map[MAXMSGLIMIT] = "";
1731 struct dirent *msgdirent;
1734 /* Reading the entire directory into a file map scales better than
1735 * doing a stat repeatedly on a predicted sequence. I suspect this
1736 * is partially due to stat(2) internally doing a readdir(2) itself to
1737 * find each file. */
1738 msgdir = opendir(dir);
1739 while ((msgdirent = readdir(msgdir))) {
1740 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1745 for (x = 0; x < vmu->maxmsg; x++) {
1753 static int vm_delete(char *file)
1758 txtsize = (strlen(file) + 5)*sizeof(char);
1759 txt = alloca(txtsize);
1760 /* Sprintf here would safe because we alloca'd exactly the right length,
1761 * but trying to eliminate all sprintf's anyhow
1763 if (ast_check_realtime("voicemail_data")) {
1764 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
1766 snprintf(txt, txtsize, "%s.txt", file);
1768 return ast_filedelete(file, NULL);
1773 static int inbuf(struct baseio *bio, FILE *fi)
1780 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1794 static int inchar(struct baseio *bio, FILE *fi)
1796 if (bio->iocp>=bio->iolen) {
1797 if (!inbuf(bio, fi))
1801 return bio->iobuf[bio->iocp++];
1804 static int ochar(struct baseio *bio, int c, FILE *so)
1806 if (bio->linelength >= BASELINELEN) {
1807 if (fputs(eol,so) == EOF)
1813 if (putc(((unsigned char)c),so) == EOF)
1821 static int base_encode(char *filename, FILE *so)
1823 unsigned char dtable[BASEMAXINLINE];
1828 memset(&bio, 0, sizeof(bio));
1829 bio.iocp = BASEMAXINLINE;
1831 if (!(fi = fopen(filename, "rb"))) {
1832 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1836 for (i= 0; i<9; i++) {
1839 dtable[26+i]= 'a'+i;
1840 dtable[26+i+9]= 'j'+i;
1842 for (i= 0; i<8; i++) {
1843 dtable[i+18]= 'S'+i;
1844 dtable[26+i+18]= 's'+i;
1846 for (i= 0; i<10; i++) {
1847 dtable[52+i]= '0'+i;
1853 unsigned char igroup[3], ogroup[4];
1856 igroup[0]= igroup[1]= igroup[2]= 0;
1858 for (n= 0;n<3;n++) {
1859 if ((c = inchar(&bio, fi)) == EOF) {
1864 igroup[n]= (unsigned char)c;
1868 ogroup[0]= dtable[igroup[0]>>2];
1869 ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1870 ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1871 ogroup[3]= dtable[igroup[2]&0x3F];
1881 ochar(&bio, ogroup[i], so);
1885 if (fputs(eol,so) == EOF)
1893 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)
1896 /* Prepare variables for substitution in email body and subject */
1897 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1898 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1899 snprintf(passdata, passdatasize, "%d", msgnum);
1900 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1901 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1902 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1903 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1904 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1905 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1906 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1907 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1910 static char *quote(const char *from, char *to, size_t len)
1914 for (; ptr < to + len - 1; from++) {
1917 else if (*from == '\0')
1921 if (ptr < to + len - 1)
1928 * fill in *tm for current time according to the proper timezone, if any.
1929 * Return tm so it can be used as a function argument.
1931 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1933 const struct vm_zone *z = NULL;
1934 time_t t = time(NULL);
1936 /* Does this user have a timezone specified? */
1937 if (!ast_strlen_zero(vmu->zonetag)) {
1938 /* Find the zone in the list */
1939 AST_LIST_LOCK(&zones);
1940 AST_LIST_TRAVERSE(&zones, z, list) {
1941 if (!strcmp(z->name, vmu->zonetag))
1944 AST_LIST_UNLOCK(&zones);
1946 ast_localtime(&t, tm, z ? z->timezone : NULL);
1950 /*! \brief same as mkstemp, but return a FILE * */
1951 static FILE *vm_mkftemp(char *template)
1954 int pfd = mkstemp(template);
1956 p = fdopen(pfd, "w+");
1965 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)
1968 char host[MAXHOSTNAMELEN] = "";
1976 size_t len_passdata;
1983 gethostname(host, sizeof(host)-1);
1984 if (strchr(srcemail, '@'))
1985 ast_copy_string(who, srcemail, sizeof(who));
1987 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1988 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1989 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1990 fprintf(p, "Date: %s" ENDL, date);
1992 /* Set date format for voicemail mail */
1993 strftime(date, sizeof(date), emaildateformat, &tm);
1995 if (!ast_strlen_zero(fromstring)) {
1996 struct ast_channel *ast;
1997 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1999 int vmlen = strlen(fromstring)*3 + 200;
2000 if ((passdata = alloca(vmlen))) {
2001 memset(passdata, 0, vmlen);
2002 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2003 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
2004 len_passdata = strlen(passdata) * 2 + 3;
2005 passdata2 = alloca(len_passdata);
2006 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
2008 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2009 ast_channel_free(ast);
2011 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2013 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
2014 len_passdata = strlen(vmu->fullname) * 2 + 3;
2015 passdata2 = alloca(len_passdata);
2016 fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
2017 if (!ast_strlen_zero(emailsubject)) {
2018 struct ast_channel *ast;
2019 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2021 int vmlen = strlen(emailsubject) * 3 + 200;
2022 if ((passdata = alloca(vmlen))) {
2023 memset(passdata, 0, vmlen);
2024 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2025 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
2026 fprintf(p, "Subject: %s" ENDL, passdata);
2028 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2029 ast_channel_free(ast);
2031 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2032 } else if (!ast_strlen_zero(emailtitle)) {
2033 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
2035 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
2036 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2038 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2039 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, getpid(), host);
2041 /* additional information needed for IMAP searching */
2042 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
2043 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
2044 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
2045 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
2046 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
2047 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
2048 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
2049 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
2050 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
2051 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
2052 if (!ast_strlen_zero(category))
2053 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
2054 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
2055 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
2057 if (!ast_strlen_zero(cidnum))
2058 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
2059 if (!ast_strlen_zero(cidname))
2060 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
2061 fprintf(p, "MIME-Version: 1.0" ENDL);
2062 if (attach_user_voicemail) {
2063 /* Something unique. */
2064 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum + 1, mailbox, getpid(), (unsigned int)ast_random());
2066 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL ENDL ENDL, bound);
2068 fprintf(p, "--%s" ENDL, bound);
2070 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
2072 struct ast_channel *ast;
2073 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2075 int vmlen = strlen(emailbody)*3 + 200;
2076 if ((passdata = alloca(vmlen))) {
2077 memset(passdata, 0, vmlen);
2078 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2079 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2080 fprintf(p, "%s" ENDL, passdata);
2082 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2083 ast_channel_free(ast);
2085 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2087 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
2089 "in mailbox %s from %s, on %s so you might" ENDL
2090 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
2091 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2093 if (attach_user_voicemail) {
2094 /* Eww. We want formats to tell us their own MIME type */
2095 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2096 char tmpdir[256], newtmp[256];
2099 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2100 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2101 tmpfd = mkstemp(newtmp);
2102 if (option_debug > 2)
2103 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
2104 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2105 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2106 ast_safe_system(tmpcmd);
2108 if (option_debug > 2)
2109 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2111 fprintf(p, "--%s" ENDL, bound);
2112 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2113 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2114 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2115 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2116 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2117 base_encode(fname, p);
2118 /* only attach if necessary */
2119 if (imap && !strcmp(format, "gsm")) {
2120 fprintf(p, "--%s" ENDL, bound);
2121 fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"" ENDL, msgnum + 1, format);
2122 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2123 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2124 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"" ENDL ENDL, msgnum + 1);
2125 snprintf(fname, sizeof(fname), "%s.gsm", attach);
2126 base_encode(fname, p);
2128 fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
2136 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)
2139 char tmp[80] = "/tmp/astmail-XXXXXX";
2142 if (vmu && ast_strlen_zero(vmu->email)) {
2143 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
2146 if (!strcmp(format, "wav49"))
2148 if (option_debug > 2)
2149 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
2150 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2152 if ((p = vm_mkftemp(tmp)) == NULL) {
2153 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2156 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2158 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2159 ast_safe_system(tmp2);
2161 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2166 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)
2169 char host[MAXHOSTNAMELEN] = "";
2172 char tmp[80] = "/tmp/astmail-XXXXXX";
2173 char tmp2[PATH_MAX];
2177 if ((p = vm_mkftemp(tmp)) == NULL) {
2178 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2181 gethostname(host, sizeof(host)-1);
2182 if (strchr(srcemail, '@'))
2183 ast_copy_string(who, srcemail, sizeof(who));
2185 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2186 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2187 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2188 fprintf(p, "Date: %s\n", date);
2190 if (*pagerfromstring) {
2191 struct ast_channel *ast;
2192 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2194 int vmlen = strlen(fromstring)*3 + 200;
2195 if ((passdata = alloca(vmlen))) {
2196 memset(passdata, 0, vmlen);
2197 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2198 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2199 fprintf(p, "From: %s <%s>\n", passdata, who);
2201 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2202 ast_channel_free(ast);
2204 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2206 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2207 fprintf(p, "To: %s\n", pager);
2209 struct ast_channel *ast;
2210 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2212 int vmlen = strlen(pagersubject) * 3 + 200;
2213 if ((passdata = alloca(vmlen))) {
2214 memset(passdata, 0, vmlen);
2215 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2216 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2217 fprintf(p, "Subject: %s\n\n", passdata);
2219 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2220 ast_channel_free(ast);
2222 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2224 fprintf(p, "Subject: New VM\n\n");
2226 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2228 struct ast_channel *ast;
2229 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2231 int vmlen = strlen(pagerbody)*3 + 200;
2232 if ((passdata = alloca(vmlen))) {
2233 memset(passdata, 0, vmlen);
2234 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2235 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2236 fprintf(p, "%s\n", passdata);
2238 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2239 ast_channel_free(ast);
2241 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2243 fprintf(p, "New %s long msg in box %s\n"
2244 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2247 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2248 ast_safe_system(tmp2);
2250 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2254 static int get_date(char *s, int len)
2259 localtime_r(&t,&tm);
2260 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2263 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2267 char dest[PATH_MAX];
2269 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2271 if ((res = create_dirpath(dest, sizeof(dest), context, ext, "greet"))) {
2272 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2277 if (ast_fileexists(fn, NULL, NULL) > 0) {
2278 res = ast_stream_and_wait(chan, fn, ecodes);
2284 /* Dispose just in case */
2286 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2289 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2293 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2297 static void free_user(struct ast_vm_user *vmu)
2299 if (!ast_test_flag(vmu, VM_ALLOCED))
2305 static void free_zone(struct vm_zone *z)
2310 static const char *mbox(int id)
2312 static const char *msgs[] = {
2324 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2328 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2335 char tmp[PATH_MAX] = "";
2336 struct odbc_obj *obj;
2344 /* If no mailbox, return immediately */
2345 if (ast_strlen_zero(mailbox))
2348 ast_copy_string(tmp, mailbox, sizeof(tmp));
2350 context = strchr(tmp, '@');
2355 context = "default";
2357 obj = ast_odbc_request_obj(odbc_database, 0);
2359 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2360 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2361 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2362 ast_odbc_release_obj(obj);
2365 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2366 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2367 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2368 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2369 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2370 ast_odbc_release_obj(obj);
2373 res = ast_odbc_smart_execute(obj, stmt);
2374 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2375 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2376 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2377 ast_odbc_release_obj(obj);
2380 res = SQLFetch(stmt);
2381 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2382 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2383 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2384 ast_odbc_release_obj(obj);
2387 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2388 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2389 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2390 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2391 ast_odbc_release_obj(obj);
2394 *newmsgs = atoi(rowdata);
2395 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2397 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2398 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2399 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2400 ast_odbc_release_obj(obj);
2403 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2404 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2405 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2406 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2407 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2408 ast_odbc_release_obj(obj);
2411 res = ast_odbc_smart_execute(obj, stmt);
2412 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2413 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2414 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2415 ast_odbc_release_obj(obj);
2418 res = SQLFetch(stmt);
2419 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2420 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2421 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2422 ast_odbc_release_obj(obj);
2425 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2426 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2427 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2428 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2429 ast_odbc_release_obj(obj);
2432 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2433 ast_odbc_release_obj(obj);
2434 *oldmsgs = atoi(rowdata);
2437 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2443 static int messagecount(const char *context, const char *mailbox, const char *folder)
2445 struct odbc_obj *obj = NULL;
2448 SQLHSTMT stmt = NULL;
2453 /* If no mailbox, return immediately */
2454 if (ast_strlen_zero(mailbox))
2457 obj = ast_odbc_request_obj(odbc_database, 0);
2459 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2460 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2461 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2464 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2465 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2466 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2467 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2468 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2471 res = ast_odbc_smart_execute(obj, stmt);
2472 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2473 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2474 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2477 res = SQLFetch(stmt);
2478 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2479 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2480 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2483 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2484 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2485 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2486 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2489 nummsgs = atoi(rowdata);
2490 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2492 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2496 ast_odbc_release_obj(obj);
2500 static int has_voicemail(const char *mailbox, const char *folder)
2502 char *context, tmp[256];
2503 ast_copy_string(tmp, mailbox, sizeof(tmp));
2504 if ((context = strchr(tmp, '@')))
2507 context = "default";
2509 if (messagecount(context, tmp, folder))
2515 #elif defined(IMAP_STORAGE)
2517 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)
2519 char *myserveremail = serveremail;
2524 char tmp[80] = "/tmp/astmail-XXXXXX";
2529 /* Attach only the first format */
2530 fmt = ast_strdupa(fmt);
2532 strsep(&stringp, "|");
2534 if (!ast_strlen_zero(vmu->serveremail))
2535 myserveremail = vmu->serveremail;
2537 make_file(fn, sizeof(fn), dir, msgnum);
2539 if (ast_strlen_zero(vmu->email))
2540 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2542 if (!strcmp(fmt, "wav49"))
2544 if(option_debug > 2)
2545 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2547 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2549 if (!(p = vm_mkftemp(tmp))) {
2550 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2554 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);
2555 /* read mail file to memory */
2558 if (!(buf = ast_malloc(len+1))) {
2559 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2562 fread(buf, len, 1, p);
2563 ((char *)buf)[len] = '\0';
2564 INIT(&str, mail_string, buf, len);
2565 init_mailstream(vms, 0);
2566 imap_mailbox_name(mailbox, vms, 0, 1);
2567 if(!mail_append(vms->mailstream, mailbox, &str))
2568 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2572 if(option_debug > 2)
2573 ast_log(LOG_DEBUG, "%s stored\n", fn);
2578 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2583 struct ast_vm_user *vmu;
2584 struct vm_state *vms_p;
2597 if(option_debug > 2)
2598 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2600 /* If no mailbox, return immediately */
2601 if (ast_strlen_zero(mailbox))
2604 if (strchr(mailbox, ',')) {
2606 ast_copy_string(tmp, mailbox, sizeof(tmp));
2609 while((cur = strsep(&mb, ", "))) {
2610 if (!ast_strlen_zero(cur)) {
2611 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2624 ast_copy_string(tmp, mailbox, sizeof(tmp));
2626 if ((context = strchr(tmp, '@'))) {
2631 context = "default";
2632 mailboxnc = (char *)mailbox;
2635 /* We have to get the user before we can open the stream! */
2636 /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2637 if (!(vmu = find_user(NULL, context, mailboxnc))) {
2638 ast_log(LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2642 /* No IMAP account available */
2643 if (vmu->imapuser[0] == '\0') {
2644 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2649 /* check if someone is accessing this box right now... */
2650 if ((vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1)) || (vms_p = get_vm_state_by_mailbox(mailboxnc, 1))) {
2651 if(option_debug > 2)
2652 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2653 *newmsgs = vms_p->newmessages;
2654 *oldmsgs = vms_p->oldmessages;
2659 /* add one if not there... */
2660 if (!(vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0)) && !(vms_p = get_vm_state_by_mailbox(mailboxnc, 0))) {
2661 if(option_debug > 2)
2662 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2663 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2667 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2668 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2669 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2670 if(option_debug > 2)
2671 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2673 /* set mailbox to INBOX! */
2674 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2675 init_vm_state(vms_p);
2676 vmstate_insert(vms_p);
2679 /* If no mailstream exists yet and even after attempting to initialize it fails, bail out */
2680 ret = init_mailstream(vms_p, 0);
2681 if (!vms_p->mailstream) {
2682 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2687 if (!ret && vms_p->updated == 1) {
2689 pgm = mail_newsearchpgm();
2690 hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (char *)mailboxnc);
2696 vms_p->vmArrayIndex = 0;
2697 mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2698 *newmsgs = vms_p->vmArrayIndex;
2699 vms_p->newmessages = vms_p->vmArrayIndex;
2700 mail_free_searchpgm(&pgm);
2703 pgm = mail_newsearchpgm ();
2704 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2710 vms_p->vmArrayIndex = 0;
2711 mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2712 *oldmsgs = vms_p->vmArrayIndex;
2713 vms_p->oldmessages = vms_p->vmArrayIndex;
2714 mail_free_searchpgm(&pgm);
2718 if (vms_p->updated == 1) { /* changes, so we did the searches above */
2720 } else if (vms_p->updated > 1) { /* decrement delay count */
2722 } else { /* no changes, so don't search */
2723 mail_ping(vms_p->mailstream);
2724 /* Keep the old data */
2725 *newmsgs = vms_p->newmessages;
2726 *oldmsgs = vms_p->oldmessages;
2733 static int has_voicemail(const char *mailbox, const char *folder)
2735 int newmsgs, oldmsgs;
2737 if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2738 return folder? oldmsgs: newmsgs;
2743 static int messagecount(const char *context, const char *mailbox, const char *folder)
2745 int newmsgs, oldmsgs;
2748 if (ast_strlen_zero(mailbox))
2750 sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2752 if(inboxcount(tmp, &newmsgs, &oldmsgs))
2753 return folder? oldmsgs: newmsgs;
2759 #ifndef IMAP_STORAGE
2760 /* copy message only used by file storage */
2761 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)
2763 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2764 const char *frombox = mbox(imbox);
2767 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2769 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2772 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2774 ast_copy_string(fromdir, dir, sizeof(fromdir));
2776 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2777 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2779 if (vm_lock_path(todir))
2780 return ERROR_LOCK_PATH;
2782 recipmsgnum = last_message_index(recip, todir) + 1;
2783 if (recipmsgnum < recip->maxmsg) {
2784 make_file(topath, sizeof(topath), todir, recipmsgnum);
2785 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2787 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2789 ast_unlock_path(todir);
2790 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2795 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2796 static int messagecount(const char *context, const char *mailbox, const char *folder)
2798 return __has_voicemail(context, mailbox, folder, 0);
2802 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2809 /* If no mailbox, return immediately */
2810 if (ast_strlen_zero(mailbox))
2813 if (ast_strlen_zero(folder))
2815 if (ast_strlen_zero(context))
2816 context = "default";
2818 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2820 if (!(dir = opendir(fn)))
2823 while ((de = readdir(dir))) {
2824 if (!strncasecmp(de->d_name, "msg", 3)) {
2828 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2839 static int has_voicemail(const char *mailbox, const char *folder)
2841 char tmp[256], *tmp2 = tmp, *mbox, *context;
2842 ast_copy_string(tmp, mailbox, sizeof(tmp));
2843 while ((mbox = strsep(&tmp2, ","))) {
2844 if ((context = strchr(mbox, '@')))
2847 context = "default";
2848 if (__has_voicemail(context, mbox, folder, 1))
2855 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2860 /* If no mailbox, return immediately */
2861 if (ast_strlen_zero(mailbox))
2869 if (strchr(mailbox, ',')) {
2873 ast_copy_string(tmp, mailbox, sizeof(tmp));
2875 while ((cur = strsep(&mb, ", "))) {
2876 if (!ast_strlen_zero(cur)) {
2877 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2890 ast_copy_string(tmp, mailbox, sizeof(tmp));
2892 if ((context = strchr(tmp, '@')))
2895 context = "default";
2898 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2900 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2907 static void run_externnotify(char *context, char *extension)
2909 char arguments[255];
2910 char ext_context[256] = "";
2911 int newvoicemails = 0, oldvoicemails = 0;
2912 struct ast_smdi_mwi_message *mwi_msg;
2914 if (!ast_strlen_zero(context))
2915 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2917 ast_copy_string(ext_context, extension, sizeof(ext_context));
2920 if (ast_app_has_voicemail(ext_context, NULL))
2921 ast_smdi_mwi_set(smdi_iface, extension);
2923 ast_smdi_mwi_unset(smdi_iface, extension);
2925 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2926 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2927 if (!strncmp(mwi_msg->cause, "INV", 3))
2928 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2929 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2930 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2931 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2932 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2935 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2939 if (!ast_strlen_zero(externnotify)) {
2940 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2941 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2943 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2945 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2946 ast_safe_system(arguments);
2951 struct leave_vm_options {
2953 signed char record_gain;
2956 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2959 int newmsgs, oldmsgs;
2960 struct vm_state *vms = NULL;
2962 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2978 char dir[PATH_MAX], tmpdir[PATH_MAX];
2979 char dest[PATH_MAX];
2981 char prefile[PATH_MAX] = "";
2982 char tempfile[PATH_MAX] = "";
2983 char ext_context[256] = "";
2986 char ecodes[16] = "#";
2987 char tmp[1024] = "", *tmpptr;
2988 struct ast_vm_user *vmu;
2989 struct ast_vm_user svm;
2990 const char *category = NULL;
2992 ast_copy_string(tmp, ext, sizeof(tmp));
2994 if ((context = strchr(tmp, '@'))) {
2996 tmpptr = strchr(context, '&');
2998 tmpptr = strchr(ext, '&');
3004 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3006 if(option_debug > 2)
3007 ast_log(LOG_DEBUG, "Before find_user\n");
3008 if (!(vmu = find_user(&svm, context, ext))) {
3009 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3010 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
3011 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3012 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3015 /* Setup pre-file if appropriate */
3016 if (strcmp(vmu->context, "default"))
3017 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3019 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
3020 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3021 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
3022 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3023 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3024 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
3025 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3027 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3028 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
3029 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3032 RETRIEVE(tempfile, -1);
3033 if (ast_fileexists(tempfile, NULL, NULL) > 0)
3034 ast_copy_string(prefile, tempfile, sizeof(prefile));
3035 DISPOSE(tempfile, -1);
3036 /* It's easier just to try to make it than to check for its existence */
3037 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3038 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
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 /* Play the beginning intro if desired */
3068 if (!ast_strlen_zero(prefile)) {
3069 RETRIEVE(prefile, -1);
3070 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3071 if (ast_streamfile(chan, prefile, chan->language) > -1)
3072 res = ast_waitstream(chan, ecodes);
3075 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
3076 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3078 DISPOSE(prefile, -1);
3081 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
3083 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3088 /* On a '#' we skip the instructions */
3089 ast_set_flag(options, OPT_SILENT);
3092 if (!res && !ast_test_flag(options, OPT_SILENT)) {
3093 res = ast_stream_and_wait(chan, INTRO, ecodes);
3095 ast_set_flag(options, OPT_SILENT);
3100 ast_stopstream(chan);
3101 /* Check for a '*' here in case the caller wants to escape from voicemail to something
3102 other than the operator -- an automated attendant or mailbox login for example */
3104 chan->exten[0] = 'a';
3105 chan->exten[1] = '\0';
3106 if (!ast_strlen_zero(vmu->exit)) {
3107 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3108 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3109 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3113 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3117 /* Check for a '0' here */
3120 if (ouseexten || ousemacro) {
3121 chan->exten[0] = 'o';
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 (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3126 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3128 ast_play_and_wait(chan, "transfer");
3131 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3137 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3140 /* The meat of recording the message... All the announcements and beeps have been played*/
3141 ast_copy_string(fmt, vmfmts, sizeof(fmt));
3142 if (!ast_strlen_zero(fmt)) {
3146 /* Is ext a mailbox? */
3147 /* must open stream for this user to get info! */
3148 vms = get_vm_state_by_mailbox(ext,0);
3150 if(option_debug > 2)
3151 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
3152 newmsgs = vms->newmessages++;
3153 oldmsgs = vms->oldmessages;
3155 res = inboxcount(ext, &newmsgs, &oldmsgs);
3157 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3160 vms = get_vm_state_by_mailbox(ext,0);
3162 /* here is a big difference! We add one to it later */
3163 msgnum = newmsgs + oldmsgs;
3164 if(option_debug > 2)
3165 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3166 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3167 /* set variable for compatibility */
3168 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3170 /* Check if mailbox is full */
3171 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3173 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3174 ast_play_and_wait(chan, "vm-mailboxfull");
3177 /* here is a big difference! We add one to it later */
3178 msgnum = newmsgs + oldmsgs;
3179 if(option_debug > 2)
3180 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3183 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3184 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3186 res = ast_waitstream(chan, "");
3187 ast_log(LOG_WARNING, "No more messages possible\n");
3188 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3193 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3194 txtdes = mkstemp(tmptxtfile);
3196 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3198 res = ast_waitstream(chan, "");
3199 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3200 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3204 /* Now play the beep once we have the message number for our next message. */
3206 /* Unless we're *really* silent, try to send the beep */
3207 res = ast_stream_and_wait(chan, "beep", "");
3210 /* Store information in real-time storage */
3211 if (ast_check_realtime("voicemail_data")) {
3212 snprintf(priority, sizeof(priority), "%d", chan->priority);
3213 snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
3214 get_date(date, sizeof(date));
3215 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);
3218 /* Store information */
3219 txt = fdopen(txtdes, "w+");
3221 get_date(date, sizeof(date));
3224 "; Message Information file\n"
3243 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3244 date, (long)time(NULL),
3245 category ? category : "");
3247 ast_log(LOG_WARNING, "Error opening text file for output\n");
3249 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3251 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3255 if (duration < vmminsecs) {
3256 if (option_verbose > 2)
3257 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
3258 ast_filedelete(tmptxtfile, NULL);
3260 if (ast_check_realtime("voicemail_data")) {
3261 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3262 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3265 fprintf(txt, "duration=%d\n", duration);
3267 if (vm_lock_path(dir)) {
3268 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
3270 ast_filedelete(tmptxtfile, NULL);
3272 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3274 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3276 ast_unlock_path(dir);
3277 if (ast_check_realtime("voicemail_data")) {
3278 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3279 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3282 msgnum = last_message_index(vmu, dir) + 1;
3283 make_file(fn, sizeof(fn), dir, msgnum);
3285 /* assign a variable with the name of the voicemail file */
3286 #ifndef IMAP_STORAGE
3287 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3289 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3292 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3293 ast_filerename(tmptxtfile, fn, NULL);
3294 rename(tmptxtfile, txtfile);
3296 ast_unlock_path(dir);
3297 if (ast_check_realtime("voicemail_data")) {
3298 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3299 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
3300 ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
3302 #ifndef IMAP_STORAGE
3303 /* Are there to be more recipients of this message? */
3305 struct ast_vm_user recipu, *recip;
3306 char *exten, *context;
3308 exten = strsep(&tmpptr, "&");
3309 context = strchr(exten, '@');
3314 if ((recip = find_user(&recipu, context, exten))) {
3315 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3320 if (ast_fileexists(fn, NULL, NULL)) {
3321 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3322 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3323 DISPOSE(dir, msgnum);
3333 if (duration < vmminsecs)
3334 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3335 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3337 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3339 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3346 #ifndef IMAP_STORAGE
3347 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3349 /* we know max messages, so stop process when number is hit */
3355 if (vm_lock_path(dir))
3356 return ERROR_LOCK_PATH;
3358 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3359 make_file(sfn, sizeof(sfn), dir, x);
3360 if (EXISTS(dir, x, sfn, NULL)) {
3363 make_file(dfn, sizeof(dfn), dir, dest);
3364 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3370 ast_unlock_path(dir);
3376 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3379 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3383 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3386 /* we must use mbox(x) folder names, and copy the message there */
3392 /* if save to Old folder, just leave in INBOX */
3393 if (box == 1) return 10;
3394 /* get the real IMAP message number for this message */
3395 sprintf(sequence,"%ld",vms->msgArray[msg]);
3396 imap_mailbox_name(dbox, vms, box, 1);
3397 if (option_debug > 2)
3398 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3399 res = mail_copy(vms->mailstream, sequence, dbox);
3400 if (res == 1) return 0;
3403 char *dir = vms->curdir;
3404 char *username = vms->username;
3405 char *context = vmu->context;
3408 char ddir[PATH_MAX];
3409 const char *dbox = mbox(box);
3411 make_file(sfn, sizeof(sfn), dir, msg);
3412 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3414 if (vm_lock_path(ddir))
3415 return ERROR_LOCK_PATH;
3417 x = last_message_index(vmu, ddir) + 1;
3418 make_file(dfn, sizeof(dfn), ddir, x);
3420 if (x >= vmu->maxmsg) {
3421 ast_unlock_path(ddir);
3424 if (strcmp(sfn, dfn)) {
3425 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3427 ast_unlock_path(ddir);
3432 static int adsi_logo(unsigned char *buf)
3435 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3436 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT,&