Option maxmessage should be maxsecs per-folder, too (closes issue #10729)
[asterisk/asterisk.git] / apps / app_voicemail.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief Comedian Mail - Voicemail System
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
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/
28  * 
29  * \par See also
30  * \arg \ref Config_vm
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
34  * during compilation.
35  *
36  *
37  *
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.
41  */
42
43 /*** MODULEINFO
44         <depend>res_smdi</depend>
45  ***/
46
47 /*** MAKEOPTS
48 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_directory.o">
49         <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
50                 <depend>unixodbc</depend>
51                 <depend>ltdl</depend>
52                 <conflict>IMAP_STORAGE</conflict>
53                 <defaultenabled>no</defaultenabled>
54         </member>
55         <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
56                 <depend>imap_tk</depend>
57                 <conflict>ODBC_STORAGE</conflict>
58                 <use>ssl</use>
59                 <defaultenabled>no</defaultenabled>
60         </member>
61 </category>
62  ***/
63
64 #include "asterisk.h"
65
66 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
67
68 #include <stdlib.h>
69 #include <errno.h>
70 #include <unistd.h>
71 #include <string.h>
72 #include <stdlib.h>
73 #include <stdio.h>
74 #include <sys/time.h>
75 #include <sys/stat.h>
76 #include <sys/types.h>
77 #include <sys/mman.h>
78 #include <time.h>
79 #include <dirent.h>
80
81 #ifdef IMAP_STORAGE
82 #include <ctype.h>
83 #include <signal.h>
84 #include <pwd.h>
85 #ifdef USE_SYSTEM_IMAP
86 #include <imap/c-client.h>
87 #include <imap/imap4r1.h>
88 #include <imap/linkage.h>
89 #else
90 #include "c-client.h"
91 #include "imap4r1.h"
92 #include "linkage.h"
93 #endif
94 #endif
95
96 #include "asterisk/lock.h"
97 #include "asterisk/file.h"
98 #include "asterisk/logger.h"
99 #include "asterisk/channel.h"
100 #include "asterisk/pbx.h"
101 #include "asterisk/options.h"
102 #include "asterisk/config.h"
103 #include "asterisk/say.h"
104 #include "asterisk/module.h"
105 #include "asterisk/adsi.h"
106 #include "asterisk/app.h"
107 #include "asterisk/manager.h"
108 #include "asterisk/dsp.h"
109 #include "asterisk/localtime.h"
110 #include "asterisk/cli.h"
111 #include "asterisk/utils.h"
112 #include "asterisk/stringfields.h"
113 #include "asterisk/smdi.h"
114 #include "asterisk/event.h"
115
116 #ifdef ODBC_STORAGE
117 #include "asterisk/res_odbc.h"
118 #endif
119
120 #ifdef IMAP_STORAGE
121 static char imapserver[48];
122 static char imapport[8];
123 static char imapflags[128];
124 static char imapfolder[64];
125 static char greetingfolder[64];
126 static char authuser[32];
127 static char authpassword[42];
128
129 static int expungeonhangup = 1;
130 static int imapgreetings = 0;
131 AST_MUTEX_DEFINE_STATIC(delimiter_lock);
132 static char delimiter = '\0';
133
134 struct vm_state;
135 struct ast_vm_user;
136
137 /* Forward declarations for IMAP */
138 static int init_mailstream(struct vm_state *vms, int box);
139 static void write_file(char *filename, char *buffer, unsigned long len);
140 static void display_body(BODY *body, char *pfx, long i);
141 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
142 static void vm_imap_delete(int msgnum, struct vm_state *vms);
143 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
144 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
145 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
146 static void vmstate_insert(struct vm_state *vms);
147 static void vmstate_delete(struct vm_state *vms);
148 static void set_update(MAILSTREAM * stream);
149 static void init_vm_state(struct vm_state *vms);
150 static void check_msgArray(struct vm_state *vms);
151 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
152 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
153 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
154 static void get_mailbox_delimiter(MAILSTREAM *stream);
155 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
156 static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int target);
157 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms);
158 static void update_messages_by_imapuser(const char *user, unsigned long number);
159
160 static int imap_remove_file (char *dir, int msgnum);
161 static int imap_retrieve_file (char *dir, int msgnum, char *mailbox, char *context);
162 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
163 struct vmstate {
164         struct vm_state *vms;
165         AST_LIST_ENTRY(vmstate) list;
166 };
167
168 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
169
170 #endif
171
172 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
173
174 #define COMMAND_TIMEOUT 5000
175 /* Don't modify these here; set your umask at runtime instead */
176 #define VOICEMAIL_DIR_MODE      0777
177 #define VOICEMAIL_FILE_MODE     0666
178 #define CHUNKSIZE       65536
179
180 #define VOICEMAIL_CONFIG "voicemail.conf"
181 #define ASTERISK_USERNAME "asterisk"
182
183 /* Define fast-forward, pause, restart, and reverse keys
184    while listening to a voicemail message - these are
185    strings, not characters */
186 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
187 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
188 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
189 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
190 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
191 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
192
193 /* Default mail command to mail voicemail. Change it with the
194     mailcmd= command in voicemail.conf */
195 #define SENDMAIL "/usr/sbin/sendmail -t"
196
197 #define INTRO "vm-intro"
198
199 #define MAXMSG 100
200 #define MAXMSGLIMIT 9999
201
202 #define BASEMAXINLINE 256
203 #define BASELINELEN 72
204 #define BASEMAXINLINE 256
205 #define eol "\r\n"
206
207 #define MAX_DATETIME_FORMAT     512
208 #define MAX_NUM_CID_CONTEXTS 10
209
210 #define VM_REVIEW        (1 << 0)
211 #define VM_OPERATOR      (1 << 1)
212 #define VM_SAYCID        (1 << 2)
213 #define VM_SVMAIL        (1 << 3)
214 #define VM_ENVELOPE      (1 << 4)
215 #define VM_SAYDURATION   (1 << 5)
216 #define VM_SKIPAFTERCMD  (1 << 6)
217 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
218 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
219 #define VM_PBXSKIP       (1 << 9)
220 #define VM_DIRECFORWARD  (1 << 10)  /*!< directory_forward */
221 #define VM_ATTACH        (1 << 11)
222 #define VM_DELETE        (1 << 12)
223 #define VM_ALLOCED       (1 << 13)
224 #define VM_SEARCH        (1 << 14)
225 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
226 #define ERROR_LOCK_PATH  -100
227
228
229 enum {
230         NEW_FOLDER,
231         OLD_FOLDER,
232         WORK_FOLDER,
233         FAMILY_FOLDER,
234         FRIENDS_FOLDER,
235         GREETINGS_FOLDER
236 } vm_box;
237
238 enum {
239         OPT_SILENT =           (1 << 0),
240         OPT_BUSY_GREETING =    (1 << 1),
241         OPT_UNAVAIL_GREETING = (1 << 2),
242         OPT_RECORDGAIN =       (1 << 3),
243         OPT_PREPEND_MAILBOX =  (1 << 4),
244         OPT_AUTOPLAY =         (1 << 6),
245 } vm_option_flags;
246
247 enum {
248         OPT_ARG_RECORDGAIN = 0,
249         OPT_ARG_PLAYFOLDER = 1,
250         /* This *must* be the last value in this enum! */
251         OPT_ARG_ARRAY_SIZE = 2,
252 } vm_option_args;
253
254 AST_APP_OPTIONS(vm_app_options, {
255         AST_APP_OPTION('s', OPT_SILENT),
256         AST_APP_OPTION('b', OPT_BUSY_GREETING),
257         AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
258         AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
259         AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
260         AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
261 });
262
263 static int load_config(int reload);
264
265 /*! \page vmlang Voicemail Language Syntaxes Supported
266
267         \par Syntaxes supported, not really language codes.
268         \arg \b en    - English
269         \arg \b de    - German
270         \arg \b es    - Spanish
271         \arg \b fr    - French
272         \arg \b it    - Italian
273         \arg \b nl    - Dutch
274         \arg \b pt    - Portuguese
275         \arg \b pt_BR - Portuguese (Brazil)
276         \arg \b gr    - Greek
277         \arg \b no    - Norwegian
278         \arg \b se    - Swedish
279         \arg \b tw    - Chinese (Taiwan)
280         \arg \b ua - Ukrainian
281
282 German requires the following additional soundfile:
283 \arg \b 1F      einE (feminine)
284
285 Spanish requires the following additional soundfile:
286 \arg \b 1M      un (masculine)
287
288 Dutch, Portuguese & Spanish require the following additional soundfiles:
289 \arg \b vm-INBOXs       singular of 'new'
290 \arg \b vm-Olds         singular of 'old/heard/read'
291
292 NB these are plural:
293 \arg \b vm-INBOX        nieuwe (nl)
294 \arg \b vm-Old          oude (nl)
295
296 Polish uses:
297 \arg \b vm-new-a        'new', feminine singular accusative
298 \arg \b vm-new-e        'new', feminine plural accusative
299 \arg \b vm-new-ych      'new', feminine plural genitive
300 \arg \b vm-old-a        'old', feminine singular accusative
301 \arg \b vm-old-e        'old', feminine plural accusative
302 \arg \b vm-old-ych      'old', feminine plural genitive
303 \arg \b digits/1-a      'one', not always same as 'digits/1'
304 \arg \b digits/2-ie     'two', not always same as 'digits/2'
305
306 Swedish uses:
307 \arg \b vm-nytt         singular of 'new'
308 \arg \b vm-nya          plural of 'new'
309 \arg \b vm-gammalt      singular of 'old'
310 \arg \b vm-gamla        plural of 'old'
311 \arg \b digits/ett      'one', not always same as 'digits/1'
312
313 Norwegian uses:
314 \arg \b vm-ny           singular of 'new'
315 \arg \b vm-nye          plural of 'new'
316 \arg \b vm-gammel       singular of 'old'
317 \arg \b vm-gamle        plural of 'old'
318
319 Dutch also uses:
320 \arg \b nl-om           'at'?
321
322 Spanish also uses:
323 \arg \b vm-youhaveno
324
325 Ukrainian requires the following additional soundfile:
326 \arg \b vm-nove         'nove'
327 \arg \b vm-stare        'stare'
328 \arg \b digits/ua/1e    'odne'
329
330 Italian requires the following additional soundfile:
331
332 For vm_intro_it:
333 \arg \b vm-nuovo        new
334 \arg \b vm-nuovi        new plural
335 \arg \b vm-vecchio      old
336 \arg \b vm-vecchi       old plural
337
338 Chinese (Taiwan) requires the following additional soundfile:
339 \arg \b vm-tong         A class-word for call (tong1)
340 \arg \b vm-ri           A class-word for day (ri4)
341 \arg \b vm-you          You (ni3)
342 \arg \b vm-haveno   Have no (mei2 you3)
343 \arg \b vm-have     Have (you3)
344 \arg \b vm-listen   To listen (yao4 ting1)
345
346
347 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
348 spelled among others when you have to change folder. For the above reasons, vm-INBOX
349 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
350
351 */
352
353 struct baseio {
354         int iocp;
355         int iolen;
356         int linelength;
357         int ateof;
358         unsigned char iobuf[BASEMAXINLINE];
359 };
360
361 /*! Structure for linked list of users 
362  * Use ast_vm_user_destroy() to free one of these structures. */
363 struct ast_vm_user {
364         char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
365         char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
366         char password[80];               /*!< Secret pin code, numbers only */
367         char fullname[80];               /*!< Full name, for directory app */
368         char email[80];                  /*!< E-mail address */
369         char pager[80];                  /*!< E-mail address to pager (no attachment) */
370         char serveremail[80];            /*!< From: Mail address */
371         char mailcmd[160];               /*!< Configurable mail command */
372         char language[MAX_LANGUAGE];     /*!< Config: Language setting */
373         char zonetag[80];                /*!< Time zone */
374         char callback[80];
375         char dialout[80];
376         char uniqueid[20];               /*!< Unique integer identifier */
377         char exit[80];
378         char attachfmt[20];              /*!< Attachment format */
379         unsigned int flags;              /*!< VM_ flags */      
380         int saydurationm;
381         int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
382         int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
383 #ifdef IMAP_STORAGE
384         char imapuser[80];               /*!< IMAP server login */
385         char imappassword[80];           /*!< IMAP server password if authpassword not defined */
386 #endif
387         double volgain;                  /*!< Volume gain for voicemails sent via email */
388         AST_LIST_ENTRY(ast_vm_user) list;
389 };
390
391 /*! Voicemail time zones */
392 struct vm_zone {
393         AST_LIST_ENTRY(vm_zone) list;
394         char name[80];
395         char timezone[80];
396         char msg_format[512];
397 };
398
399 /*! Voicemail mailbox state */
400 struct vm_state {
401         char curbox[80];
402         char username[80];
403         char curdir[PATH_MAX];
404         char vmbox[PATH_MAX];
405         char fn[PATH_MAX];
406         char fn2[PATH_MAX];
407         int *deleted;
408         int *heard;
409         int curmsg;
410         int lastmsg;
411         int newmessages;
412         int oldmessages;
413         int starting;
414         int repeats;
415 #ifdef IMAP_STORAGE
416         int updated;                         /*!< decremented on each mail check until 1 -allows delay */
417         long msgArray[256];
418         MAILSTREAM *mailstream;
419         int vmArrayIndex;
420         char imapuser[80];                   /*!< IMAP server login */
421         int interactive;
422         unsigned int quota_limit;
423         unsigned int quota_usage;
424         struct vm_state *persist_vms;
425 #endif
426 };
427
428
429 #ifdef ODBC_STORAGE
430 static char odbc_database[80];
431 static char odbc_table[80];
432 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
433 #define DISPOSE(a,b) remove_file(a,b)
434 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
435 #define EXISTS(a,b,c,d) (message_exists(a,b))
436 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
437 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
438 #define DELETE(a,b,c) (delete_file(a,b))
439 #else
440 #ifdef IMAP_STORAGE
441 #define RETRIEVE(a,b,c,d) (imap_retrieve_file(a,b,c,d ))
442 #define DISPOSE(a,b) (imap_remove_file(a,b))
443 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
444 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
445 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
446 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
447 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
448 #define DELETE(a,b,c) (vm_delete(c))
449 #else
450 #define RETRIEVE(a,b,c,d)
451 #define DISPOSE(a,b)
452 #define STORE(a,b,c,d,e,f,g,h,i)
453 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
454 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
455 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
456 #define DELETE(a,b,c) (vm_delete(c))
457 #endif
458 #endif
459
460 static char VM_SPOOL_DIR[PATH_MAX];
461
462 static char ext_pass_cmd[128];
463
464 int my_umask;
465
466 #define PWDCHANGE_INTERNAL (1 << 1)
467 #define PWDCHANGE_EXTERNAL (1 << 2)
468 static int pwdchange = PWDCHANGE_INTERNAL;
469
470 #ifdef ODBC_STORAGE
471 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
472 #else
473 # ifdef IMAP_STORAGE
474 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
475 # else
476 # define tdesc "Comedian Mail (Voicemail System)"
477 # endif
478 #endif
479
480 static char userscontext[AST_MAX_EXTENSION] = "default";
481
482 static char *addesc = "Comedian Mail";
483
484 static char *synopsis_vm = "Leave a Voicemail message";
485
486 static char *descrip_vm =
487         "  VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
488         "application allows the calling party to leave a message for the specified\n"
489         "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
490         "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
491         "specified mailbox does not exist.\n"
492         "  The Voicemail application will exit if any of the following DTMF digits are\n"
493         "received:\n"
494         "    0 - Jump to the 'o' extension in the current dialplan context.\n"
495         "    * - Jump to the 'a' extension in the current dialplan context.\n"
496         "  This application will set the following channel variable upon completion:\n"
497         "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
498         "               application. The possible values are:\n"
499         "               SUCCESS | USEREXIT | FAILED\n\n"
500         "  Options:\n"
501         "    b    - Play the 'busy' greeting to the calling party.\n"
502         "    g(#) - Use the specified amount of gain when recording the voicemail\n"
503         "           message. The units are whole-number decibels (dB).\n"
504         "    s    - Skip the playback of instructions for leaving a message to the\n"
505         "           calling party.\n"
506         "    u    - Play the 'unavailable' greeting.\n";
507
508 static char *synopsis_vmain = "Check Voicemail messages";
509
510 static char *descrip_vmain =
511         "  VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
512         "calling party to check voicemail messages. A specific mailbox, and optional\n"
513         "corresponding context, may be specified. If a mailbox is not provided, the\n"
514         "calling party will be prompted to enter one. If a context is not specified,\n"
515         "the 'default' context will be used.\n\n"
516         "  Options:\n"
517         "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
518         "           is entered by the caller.\n"
519         "    g(#) - Use the specified amount of gain when recording a voicemail\n"
520         "           message. The units are whole-number decibels (dB).\n"
521         "    s    - Skip checking the passcode for the mailbox.\n"
522         "    a(#) - Skip folder prompt and go directly to folder specified.\n"
523         "           Defaults to INBOX\n";
524
525 static char *synopsis_vm_box_exists =
526 "Check to see if Voicemail mailbox exists";
527
528 static char *descrip_vm_box_exists =
529         "  MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
530         "mailbox exists. If no voicemail context is specified, the 'default' context\n"
531         "will be used.\n"
532         "  This application will set the following channel variable upon completion:\n"
533         "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
534         "                        MailboxExists application. Possible values include:\n"
535         "                        SUCCESS | FAILED\n\n"
536         "  Options: (none)\n";
537
538 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
539
540 static char *descrip_vmauthenticate =
541         "  VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
542         "same way as the Authenticate application, but the passwords are taken from\n"
543         "voicemail.conf.\n"
544         "  If the mailbox is specified, only that mailbox's password will be considered\n"
545         "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
546         "be set with the authenticated mailbox.\n\n"
547         "  Options:\n"
548         "    s - Skip playing the initial prompts.\n";
549
550 /* Leave a message */
551 static char *app = "VoiceMail";
552
553 /* Check mail, control, etc */
554 static char *app2 = "VoiceMailMain";
555
556 static char *app3 = "MailboxExists";
557 static char *app4 = "VMAuthenticate";
558
559 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
560 static AST_LIST_HEAD_STATIC(zones, vm_zone);
561 static int maxsilence;
562 static int maxmsg;
563 static int silencethreshold = 128;
564 static char serveremail[80];
565 static char mailcmd[160];       /* Configurable mail cmd */
566 static char externnotify[160]; 
567 static struct ast_smdi_interface *smdi_iface = NULL;
568 static char vmfmts[80];
569 static double volgain;
570 static int vmminsecs;
571 static int vmmaxsecs;
572 static int maxgreet;
573 static int skipms;
574 static int maxlogins;
575
576 /*! Poll mailboxes for changes since there is something external to
577  *  app_voicemail that may change them. */
578 static unsigned int poll_mailboxes;
579
580 /*! Polling frequency */
581 static unsigned int poll_freq;
582 /*! By default, poll every 30 seconds */
583 #define DEFAULT_POLL_FREQ 30
584
585 AST_MUTEX_DEFINE_STATIC(poll_lock);
586 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
587 static pthread_t poll_thread = AST_PTHREADT_NULL;
588 static unsigned char poll_thread_run;
589
590 /*! Subscription to ... MWI event subscriptions */
591 static struct ast_event_sub *mwi_sub_sub;
592 /*! Subscription to ... MWI event un-subscriptions */
593 static struct ast_event_sub *mwi_unsub_sub;
594
595 /*!
596  * \brief An MWI subscription
597  *
598  * This is so we can keep track of which mailboxes are subscribed to.
599  * This way, we know which mailboxes to poll when the pollmailboxes
600  * option is being used.
601  */
602 struct mwi_sub {
603         AST_RWLIST_ENTRY(mwi_sub) entry;
604         int old_new;
605         int old_old;
606         uint32_t uniqueid;
607         char mailbox[1];
608 };
609
610 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
611
612 /* custom audio control prompts for voicemail playback */
613 static char listen_control_forward_key[12];
614 static char listen_control_reverse_key[12];
615 static char listen_control_pause_key[12];
616 static char listen_control_restart_key[12];
617 static char listen_control_stop_key[12];
618
619 /* custom password sounds */
620 static char vm_password[80] = "vm-password";
621 static char vm_newpassword[80] = "vm-newpassword";
622 static char vm_passchanged[80] = "vm-passchanged";
623 static char vm_reenterpassword[80] = "vm-reenterpassword";
624 static char vm_mismatch[80] = "vm-mismatch";
625
626 static struct ast_flags globalflags = {0};
627
628 static int saydurationminfo;
629
630 static char dialcontext[AST_MAX_CONTEXT] = "";
631 static char callcontext[AST_MAX_CONTEXT] = "";
632 static char exitcontext[AST_MAX_CONTEXT] = "";
633
634 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
635
636
637 static char *emailbody = NULL;
638 static char *emailsubject = NULL;
639 static char *pagerbody = NULL;
640 static char *pagersubject = NULL;
641 static char fromstring[100];
642 static char pagerfromstring[100];
643 static char emailtitle[100];
644 static char charset[32] = "ISO-8859-1";
645
646 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
647 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
648 static int adsiver = 1;
649 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
650
651 /* Forward declarations - generic */
652 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
653 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);
654 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
655 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
656                         char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
657                         signed char record_gain, struct vm_state *vms);
658 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
659 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
660 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
661 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);
662 static void apply_options(struct ast_vm_user *vmu, const char *options);
663 static int is_valid_dtmf(const char *key);
664
665 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
666 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
667 #endif
668
669
670
671 static void populate_defaults(struct ast_vm_user *vmu)
672 {
673         ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);     
674         if (saydurationminfo)
675                 vmu->saydurationm = saydurationminfo;
676         ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
677         ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
678         ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
679         if (vmmaxsecs)
680                 vmu->maxsecs = vmmaxsecs;
681         if (maxmsg)
682                 vmu->maxmsg = maxmsg;
683         vmu->volgain = volgain;
684 }
685
686 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
687 {
688         int x;
689         if (!strcasecmp(var, "attach")) {
690                 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
691         } else if (!strcasecmp(var, "attachfmt")) {
692                 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
693         } else if (!strcasecmp(var, "serveremail")) {
694                 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
695         } else if (!strcasecmp(var, "language")) {
696                 ast_copy_string(vmu->language, value, sizeof(vmu->language));
697         } else if (!strcasecmp(var, "tz")) {
698                 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
699 #ifdef IMAP_STORAGE
700         } else if (!strcasecmp(var, "imapuser")) {
701                 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
702         } else if (!strcasecmp(var, "imappassword")) {
703                 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
704 #endif
705         } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
706                 ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
707         } else if (!strcasecmp(var, "saycid")){
708                 ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
709         } else if (!strcasecmp(var,"sendvoicemail")){
710                 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
711         } else if (!strcasecmp(var, "review")){
712                 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
713         } else if (!strcasecmp(var, "tempgreetwarn")){
714                 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);  
715         } else if (!strcasecmp(var, "operator")){
716                 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
717         } else if (!strcasecmp(var, "envelope")){
718                 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
719         } else if (!strcasecmp(var, "sayduration")){
720                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
721         } else if (!strcasecmp(var, "saydurationm")){
722                 if (sscanf(value, "%d", &x) == 1) {
723                         vmu->saydurationm = x;
724                 } else {
725                         ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
726                 }
727         } else if (!strcasecmp(var, "forcename")){
728                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
729         } else if (!strcasecmp(var, "forcegreetings")){
730                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
731         } else if (!strcasecmp(var, "callback")) {
732                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
733         } else if (!strcasecmp(var, "dialout")) {
734                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
735         } else if (!strcasecmp(var, "exitcontext")) {
736                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
737         } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
738                 if (vmu->maxsecs <= 0) {
739                         ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
740                         vmu->maxsecs = vmmaxsecs;
741                 } else {
742                         vmu->maxsecs = atoi(value);
743                 }
744                 if (!strcasecmp(var, "maxmessage"))
745                         ast_log(LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
746         } else if (!strcasecmp(var, "maxmsg")) {
747                 vmu->maxmsg = atoi(value);
748                 if (vmu->maxmsg <= 0) {
749                         ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
750                         vmu->maxmsg = MAXMSG;
751                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
752                         ast_log(LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
753                         vmu->maxmsg = MAXMSGLIMIT;
754                 }
755         } else if (!strcasecmp(var, "volgain")) {
756                 sscanf(value, "%lf", &vmu->volgain);
757         } else if (!strcasecmp(var, "options")) {
758                 apply_options(vmu, value);
759         }
760 }
761
762 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
763 {
764         int res;
765         if (!ast_strlen_zero(vmu->uniqueid)) {
766                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
767                 if (res > 0) {
768                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
769                         res = 0;
770                 } else if (!res) {
771                         res = -1;
772                 }
773                 return res;
774         }
775         return -1;
776 }
777
778 static void apply_options(struct ast_vm_user *vmu, const char *options)
779 {       /* Destructively Parse options and apply */
780         char *stringp;
781         char *s;
782         char *var, *value;
783         stringp = ast_strdupa(options);
784         while ((s = strsep(&stringp, "|"))) {
785                 value = s;
786                 if ((var = strsep(&value, "=")) && value) {
787                         apply_option(vmu, var, value);
788                 }
789         }       
790 }
791
792 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
793 {
794         struct ast_variable *tmp;
795         tmp = var;
796         while (tmp) {
797                 if (!strcasecmp(tmp->name, "vmsecret")) {
798                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
799                 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
800                         if (ast_strlen_zero(retval->password))
801                                 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
802                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
803                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
804                 } else if (!strcasecmp(tmp->name, "pager")) {
805                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
806                 } else if (!strcasecmp(tmp->name, "email")) {
807                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
808                 } else if (!strcasecmp(tmp->name, "fullname")) {
809                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
810                 } else if (!strcasecmp(tmp->name, "context")) {
811                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
812 #ifdef IMAP_STORAGE
813                 } else if (!strcasecmp(tmp->name, "imapuser")) {
814                         ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
815                 } else if (!strcasecmp(tmp->name, "imappassword")) {
816                         ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
817 #endif
818                 } else
819                         apply_option(retval, tmp->name, tmp->value);
820                 tmp = tmp->next;
821         } 
822 }
823
824 static int is_valid_dtmf(const char *key)
825 {
826         int i;
827         char *local_key = ast_strdupa(key);
828
829         for(i = 0; i < strlen(key); ++i) {
830                 if(!strchr(VALID_DTMF, *local_key)) {
831                         ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
832                         return 0;
833                 }
834                 local_key++;
835         }
836         return 1;
837 }
838
839 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
840 {
841         struct ast_variable *var;
842         struct ast_vm_user *retval;
843
844         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
845                 if (!ivm)
846                         ast_set_flag(retval, VM_ALLOCED);       
847                 else
848                         memset(retval, 0, sizeof(*retval));
849                 if (mailbox) 
850                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
851                 populate_defaults(retval);
852                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
853                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
854                 else
855                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
856                 if (var) {
857                         apply_options_full(retval, var);
858                         ast_variables_destroy(var);
859                 } else { 
860                         if (!ivm) 
861                                 ast_free(retval);
862                         retval = NULL;
863                 }       
864         } 
865         return retval;
866 }
867
868 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
869 {
870         /* This function could be made to generate one from a database, too */
871         struct ast_vm_user *vmu=NULL, *cur;
872         AST_LIST_LOCK(&users);
873
874         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
875                 context = "default";
876
877         AST_LIST_TRAVERSE(&users, cur, list) {
878                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
879                         break;
880                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
881                         break;
882         }
883         if (cur) {
884                 /* Make a copy, so that on a reload, we have no race */
885                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
886                         memcpy(vmu, cur, sizeof(*vmu));
887                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
888                         AST_LIST_NEXT(vmu, list) = NULL;
889                 }
890         } else
891                 vmu = find_user_realtime(ivm, context, mailbox);
892         AST_LIST_UNLOCK(&users);
893         return vmu;
894 }
895
896 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
897 {
898         /* This function could be made to generate one from a database, too */
899         struct ast_vm_user *cur;
900         int res = -1;
901         AST_LIST_LOCK(&users);
902         AST_LIST_TRAVERSE(&users, cur, list) {
903                 if ((!context || !strcasecmp(context, cur->context)) &&
904                         (!strcasecmp(mailbox, cur->mailbox)))
905                                 break;
906         }
907         if (cur) {
908                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
909                 res = 0;
910         }
911         AST_LIST_UNLOCK(&users);
912         return res;
913 }
914
915 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
916 {
917         struct ast_config   *cfg=NULL;
918         struct ast_variable *var=NULL;
919         struct ast_category *cat=NULL;
920         char *category=NULL, *value=NULL, *new=NULL;
921         const char *tmp=NULL;
922         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
923                                         
924         if (!change_password_realtime(vmu, newpassword))
925                 return;
926
927         /* check voicemail.conf */
928         if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
929                 while ((category = ast_category_browse(cfg, category))) {
930                         if (!strcasecmp(category, vmu->context)) {
931                                 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
932                                         ast_log(LOG_WARNING, "We could not find the mailbox.\n");
933                                         break;
934                                 }
935                                 value = strstr(tmp,",");
936                                 if (!value) {
937                                         ast_log(LOG_WARNING, "variable has bad format.\n");
938                                         break;
939                                 }
940                                 new = alloca((strlen(value)+strlen(newpassword)+1));
941                                 sprintf(new,"%s%s", newpassword, value);
942                                 if (!(cat = ast_category_get(cfg, category))) {
943                                         ast_log(LOG_WARNING, "Failed to get category structure.\n");
944                                         break;
945                                 }
946                                 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
947                         }
948                 }
949                 /* save the results */
950                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
951                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
952                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
953         }
954         category = NULL;
955         var = NULL;
956         /* check users.conf and update the password stored for the mailbox*/
957         /* if no vmsecret entry exists create one. */
958         if ((cfg = ast_config_load("users.conf", config_flags))) {
959                 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
960                 while ((category = ast_category_browse(cfg, category))) {
961                         ast_debug(4, "users.conf: %s\n", category);
962                         if (!strcasecmp(category, vmu->mailbox)) {
963                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
964                                         ast_debug(3, "looks like we need to make vmsecret!\n");
965                                         var = ast_variable_new("vmsecret", newpassword, "");
966                                 } 
967                                 new = alloca(strlen(newpassword)+1);
968                                 sprintf(new, "%s", newpassword);
969                                 if (!(cat = ast_category_get(cfg, category))) {
970                                         ast_debug(4, "failed to get category!\n");
971                                         break;
972                                 }
973                                 if (!var)               
974                                         ast_variable_update(cat, "vmsecret", new, NULL, 0);
975                                 else
976                                         ast_variable_append(cat, var);
977                         }
978                 }
979                 /* save the results and clean things up */
980                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
981                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
982                 config_text_file_save("users.conf", cfg, "AppVoicemail");
983         }
984 }
985
986 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
987 {
988         char buf[255];
989         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
990         if (!ast_safe_system(buf))
991                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
992 }
993
994 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
995 {
996         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
997 }
998
999 #ifdef IMAP_STORAGE
1000 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
1001 {
1002         int res;
1003         if ((res = ast_mkdir(dir, 01777))) {
1004                 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
1005                 return sprintf(dest, "%s/msg%04d", dir, num);
1006         }
1007         /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
1008         return sprintf(dest, "%s/msg%04d", dir, num);
1009 }
1010
1011 static void vm_imap_delete(int msgnum, struct vm_state *vms)
1012 {
1013         unsigned long messageNum = 0;
1014         char arg[10];
1015
1016         /* find real message number based on msgnum */
1017         /* this may be an index into vms->msgArray based on the msgnum. */
1018
1019         messageNum = vms->msgArray[msgnum];
1020         if (messageNum == 0) {
1021                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1022                 return;
1023         }
1024         ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1025         /* delete message */
1026         sprintf (arg,"%lu",messageNum);
1027         mail_setflag (vms->mailstream,arg,"\\DELETED");
1028 }
1029
1030 #endif
1031 static int make_file(char *dest, int len, char *dir, int num)
1032 {
1033         return snprintf(dest, len, "%s/msg%04d", dir, num);
1034 }
1035
1036 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1037  * \param dest    String. base directory.
1038  * \param len     Length of dest.
1039  * \param context String. Ignored if is null or empty string.
1040  * \param ext     String. Ignored if is null or empty string.
1041  * \param folder  String. Ignored if is null or empty string. 
1042  * \return -1 on failure, 0 on success.
1043  */
1044 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1045 {
1046         mode_t  mode = VOICEMAIL_DIR_MODE;
1047         int res;
1048
1049         make_dir(dest, len, context, ext, folder);
1050         if ((res = ast_mkdir(dest, mode))) {
1051                 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1052                 return -1;
1053         }
1054         return 0;
1055 }
1056
1057 /*! \brief Lock file path
1058     only return failure if ast_lock_path returns 'timeout',
1059    not if the path does not exist or any other reason
1060 */
1061 static int vm_lock_path(const char *path)
1062 {
1063         switch (ast_lock_path(path)) {
1064         case AST_LOCK_TIMEOUT:
1065                 return -1;
1066         default:
1067                 return 0;
1068         }
1069 }
1070
1071
1072 #ifdef ODBC_STORAGE
1073 struct generic_prepare_struct {
1074         char *sql;
1075         int argc;
1076         char **argv;
1077 };
1078
1079 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
1080 {
1081         struct generic_prepare_struct *gps = data;
1082         int res, i;
1083         SQLHSTMT stmt;
1084
1085         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1086         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1087                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1088                 return NULL;
1089         }
1090         res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
1091         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1092                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
1093                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1094                 return NULL;
1095         }
1096         for (i = 0; i < gps->argc; i++)
1097                 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
1098
1099         return stmt;
1100 }
1101
1102 static int retrieve_file(char *dir, int msgnum)
1103 {
1104         int x = 0;
1105         int res;
1106         int fd=-1;
1107         size_t fdlen = 0;
1108         void *fdm = MAP_FAILED;
1109         SQLSMALLINT colcount=0;
1110         SQLHSTMT stmt;
1111         char sql[PATH_MAX];
1112         char fmt[80]="";
1113         char *c;
1114         char coltitle[256];
1115         SQLSMALLINT collen;
1116         SQLSMALLINT datatype;
1117         SQLSMALLINT decimaldigits;
1118         SQLSMALLINT nullable;
1119         SQLULEN colsize;
1120         SQLLEN colsize2;
1121         FILE *f=NULL;
1122         char rowdata[80];
1123         char fn[PATH_MAX];
1124         char full_fn[PATH_MAX];
1125         char msgnums[80];
1126         char *argv[] = { dir, msgnums };
1127         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1128
1129         struct odbc_obj *obj;
1130         obj = ast_odbc_request_obj(odbc_database, 0);
1131         if (obj) {
1132                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1133                 c = strchr(fmt, '|');
1134                 if (c)
1135                         *c = '\0';
1136                 if (!strcasecmp(fmt, "wav49"))
1137                         strcpy(fmt, "WAV");
1138                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1139                 if (msgnum > -1)
1140                         make_file(fn, sizeof(fn), dir, msgnum);
1141                 else
1142                         ast_copy_string(fn, dir, sizeof(fn));
1143                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1144                 
1145                 if (!(f = fopen(full_fn, "w+"))) {
1146                         ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1147                         goto yuck;
1148                 }
1149                 
1150                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1151                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1152                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1153                 if (!stmt) {
1154                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1155                         ast_odbc_release_obj(obj);
1156                         goto yuck;
1157                 }
1158                 res = SQLFetch(stmt);
1159                 if (res == SQL_NO_DATA) {
1160                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1161                         ast_odbc_release_obj(obj);
1162                         goto yuck;
1163                 }
1164                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1165                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1166                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1167                         ast_odbc_release_obj(obj);
1168                         goto yuck;
1169                 }
1170                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1171                 if (fd < 0) {
1172                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1173                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1174                         ast_odbc_release_obj(obj);
1175                         goto yuck;
1176                 }
1177                 res = SQLNumResultCols(stmt, &colcount);
1178                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1179                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1180                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1181                         ast_odbc_release_obj(obj);
1182                         goto yuck;
1183                 }
1184                 if (f) 
1185                         fprintf(f, "[message]\n");
1186                 for (x=0;x<colcount;x++) {
1187                         rowdata[0] = '\0';
1188                         collen = sizeof(coltitle);
1189                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
1190                                                 &datatype, &colsize, &decimaldigits, &nullable);
1191                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1192                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1193                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1194                                 ast_odbc_release_obj(obj);
1195                                 goto yuck;
1196                         }
1197                         if (!strcasecmp(coltitle, "recording")) {
1198                                 off_t offset;
1199                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1200                                 fdlen = colsize2;
1201                                 if (fd > -1) {
1202                                         char tmp[1]="";
1203                                         lseek(fd, fdlen - 1, SEEK_SET);
1204                                         if (write(fd, tmp, 1) != 1) {
1205                                                 close(fd);
1206                                                 fd = -1;
1207                                                 continue;
1208                                         }
1209                                         /* Read out in small chunks */
1210                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1211                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1212                                                         ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1213                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1214                                                         ast_odbc_release_obj(obj);
1215                                                         goto yuck;
1216                                                 } else {
1217                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1218                                                         munmap(fdm, CHUNKSIZE);
1219                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1220                                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1221                                                                 unlink(full_fn);
1222                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1223                                                                 ast_odbc_release_obj(obj);
1224                                                                 goto yuck;
1225                                                         }
1226                                                 }
1227                                         }
1228                                         truncate(full_fn, fdlen);
1229                                 }
1230                         } else {
1231                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1232                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1233                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1234                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1235                                         ast_odbc_release_obj(obj);
1236                                         goto yuck;
1237                                 }
1238                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1239                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1240                         }
1241                 }
1242                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1243                 ast_odbc_release_obj(obj);
1244         } else
1245                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1246 yuck:   
1247         if (f)
1248                 fclose(f);
1249         if (fd > -1)
1250                 close(fd);
1251         return x - 1;
1252 }
1253
1254 static int remove_file(char *dir, int msgnum)
1255 {
1256         char fn[PATH_MAX];
1257         char full_fn[PATH_MAX];
1258         char msgnums[80];
1259         
1260         if (msgnum > -1) {
1261                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1262                 make_file(fn, sizeof(fn), dir, msgnum);
1263         } else
1264                 ast_copy_string(fn, dir, sizeof(fn));
1265         ast_filedelete(fn, NULL);       
1266         if (ast_check_realtime("voicemail_data")) {
1267                 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1268         }
1269         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1270         unlink(full_fn);
1271         return 0;
1272 }
1273
1274 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1275 {
1276         int x = 0;
1277         int res;
1278         SQLHSTMT stmt;
1279         char sql[PATH_MAX];
1280         char rowdata[20];
1281         char *argv[] = { dir };
1282         struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1283
1284         struct odbc_obj *obj;
1285         obj = ast_odbc_request_obj(odbc_database, 0);
1286         if (obj) {
1287                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1288                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1289                 if (!stmt) {
1290                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1291                         ast_odbc_release_obj(obj);
1292                         goto yuck;
1293                 }
1294                 res = SQLFetch(stmt);
1295                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1296                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1297                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1298                         ast_odbc_release_obj(obj);
1299                         goto yuck;
1300                 }
1301                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1302                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1303                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1304                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1305                         ast_odbc_release_obj(obj);
1306                         goto yuck;
1307                 }
1308                 if (sscanf(rowdata, "%d", &x) != 1)
1309                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1310                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1311                 ast_odbc_release_obj(obj);
1312         } else
1313                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1314 yuck:   
1315         return x - 1;
1316 }
1317
1318 static int message_exists(char *dir, int msgnum)
1319 {
1320         int x = 0;
1321         int res;
1322         SQLHSTMT stmt;
1323         char sql[PATH_MAX];
1324         char rowdata[20];
1325         char msgnums[20];
1326         char *argv[] = { dir, msgnums };
1327         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1328
1329         struct odbc_obj *obj;
1330         obj = ast_odbc_request_obj(odbc_database, 0);
1331         if (obj) {
1332                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1333                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1334                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1335                 if (!stmt) {
1336                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1337                         ast_odbc_release_obj(obj);
1338                         goto yuck;
1339                 }
1340                 res = SQLFetch(stmt);
1341                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1342                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1343                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1344                         ast_odbc_release_obj(obj);
1345                         goto yuck;
1346                 }
1347                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1348                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1349                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1350                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1351                         ast_odbc_release_obj(obj);
1352                         goto yuck;
1353                 }
1354                 if (sscanf(rowdata, "%d", &x) != 1)
1355                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1356                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1357                 ast_odbc_release_obj(obj);
1358         } else
1359                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1360 yuck:   
1361         return x;
1362 }
1363
1364 static int count_messages(struct ast_vm_user *vmu, char *dir)
1365 {
1366         return last_message_index(vmu, dir) + 1;
1367 }
1368
1369 static void delete_file(char *sdir, int smsg)
1370 {
1371         SQLHSTMT stmt;
1372         char sql[PATH_MAX];
1373         char msgnums[20];
1374         char *argv[] = { sdir, msgnums };
1375         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1376
1377         struct odbc_obj *obj;
1378         obj = ast_odbc_request_obj(odbc_database, 0);
1379         if (obj) {
1380                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1381                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1382                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1383                 if (!stmt)
1384                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1385                 else
1386                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1387                 ast_odbc_release_obj(obj);
1388         } else
1389                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1390         return; 
1391 }
1392
1393 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1394 {
1395         SQLHSTMT stmt;
1396         char sql[512];
1397         char msgnums[20];
1398         char msgnumd[20];
1399         struct odbc_obj *obj;
1400         char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1401         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1402
1403         delete_file(ddir, dmsg);
1404         obj = ast_odbc_request_obj(odbc_database, 0);
1405         if (obj) {
1406                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1407                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1408                 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);
1409                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1410                 if (!stmt)
1411                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1412                 else
1413                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1414                 ast_odbc_release_obj(obj);
1415         } else
1416                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1417         return; 
1418 }
1419
1420 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1421 {
1422         int x = 0;
1423         int res;
1424         int fd = -1;
1425         void *fdm = MAP_FAILED;
1426         size_t fdlen = -1;
1427         SQLHSTMT stmt;
1428         SQLLEN len;
1429         char sql[PATH_MAX];
1430         char msgnums[20];
1431         char fn[PATH_MAX];
1432         char full_fn[PATH_MAX];
1433         char fmt[80]="";
1434         char *c;
1435         const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1436         const char *category = "";
1437         struct ast_config *cfg=NULL;
1438         struct odbc_obj *obj;
1439         struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
1440
1441         delete_file(dir, msgnum);
1442         obj = ast_odbc_request_obj(odbc_database, 0);
1443         if (obj) {
1444                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1445                 c = strchr(fmt, '|');
1446                 if (c)
1447                         *c = '\0';
1448                 if (!strcasecmp(fmt, "wav49"))
1449                         strcpy(fmt, "WAV");
1450                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1451                 if (msgnum > -1)
1452                         make_file(fn, sizeof(fn), dir, msgnum);
1453                 else
1454                         ast_copy_string(fn, dir, sizeof(fn));
1455                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1456                 cfg = ast_config_load(full_fn, config_flags);
1457                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1458                 fd = open(full_fn, O_RDWR);
1459                 if (fd < 0) {
1460                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1461                         ast_odbc_release_obj(obj);
1462                         goto yuck;
1463                 }
1464                 if (cfg) {
1465                         context = ast_variable_retrieve(cfg, "message", "context");
1466                         if (!context) context = "";
1467                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1468                         if (!macrocontext) macrocontext = "";
1469                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1470                         if (!callerid) callerid = "";
1471                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1472                         if (!origtime) origtime = "";
1473                         duration = ast_variable_retrieve(cfg, "message", "duration");
1474                         if (!duration) duration = "";
1475                         category = ast_variable_retrieve(cfg, "message", "category");
1476                         if (!category) category = "";
1477                 }
1478                 fdlen = lseek(fd, 0, SEEK_END);
1479                 lseek(fd, 0, SEEK_SET);
1480                 printf("Length is %zd\n", fdlen);
1481                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1482                 if (fdm == MAP_FAILED) {
1483                         ast_log(LOG_WARNING, "Memory map failed!\n");
1484                         ast_odbc_release_obj(obj);
1485                         goto yuck;
1486                 } 
1487                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1488                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1489                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1490                         ast_odbc_release_obj(obj);
1491                         goto yuck;
1492                 }
1493                 if (!ast_strlen_zero(category)) 
1494                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1495                 else
1496                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1497                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1498                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1499                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1500                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1501                         ast_odbc_release_obj(obj);
1502                         goto yuck;
1503                 }
1504                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1505                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1506                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1507                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1508                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1509                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1510                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1511                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1512                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1513                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1514                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1515                 if (!ast_strlen_zero(category))
1516                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1517                 res = ast_odbc_smart_execute(obj, stmt);
1518                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1519                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1520                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1521                         ast_odbc_release_obj(obj);
1522                         goto yuck;
1523                 }
1524                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1525                 ast_odbc_release_obj(obj);
1526         } else
1527                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1528 yuck:   
1529         if (cfg)
1530                 ast_config_destroy(cfg);
1531         if (fdm != MAP_FAILED)
1532                 munmap(fdm, fdlen);
1533         if (fd > -1)
1534                 close(fd);
1535         return x;
1536 }
1537
1538 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1539 {
1540         SQLHSTMT stmt;
1541         char sql[PATH_MAX];
1542         char msgnums[20];
1543         char msgnumd[20];
1544         struct odbc_obj *obj;
1545         char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1546         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1547
1548         delete_file(ddir, dmsg);
1549         obj = ast_odbc_request_obj(odbc_database, 0);
1550         if (obj) {
1551                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1552                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1553                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1554                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1555                 if (!stmt)
1556                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1557                 else
1558                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1559                 ast_odbc_release_obj(obj);
1560         } else
1561                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1562         return; 
1563 }
1564
1565 #else
1566 #ifndef IMAP_STORAGE
1567 static int count_messages(struct ast_vm_user *vmu, char *dir)
1568 {
1569         /* Find all .txt files - even if they are not in sequence from 0000 */
1570
1571         int vmcount = 0;
1572         DIR *vmdir = NULL;
1573         struct dirent *vment = NULL;
1574
1575         if (vm_lock_path(dir))
1576                 return ERROR_LOCK_PATH;
1577
1578         if ((vmdir = opendir(dir))) {
1579                 while ((vment = readdir(vmdir))) {
1580                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1581                                 vmcount++;
1582                 }
1583                 closedir(vmdir);
1584         }
1585         ast_unlock_path(dir);
1586         
1587         return vmcount;
1588 }
1589
1590 static void rename_file(char *sfn, char *dfn)
1591 {
1592         char stxt[PATH_MAX];
1593         char dtxt[PATH_MAX];
1594         ast_filerename(sfn,dfn,NULL);
1595         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1596         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1597         if (ast_check_realtime("voicemail_data")) {
1598                 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1599         }
1600         rename(stxt, dtxt);
1601 }
1602
1603 static int copy(char *infile, char *outfile)
1604 {
1605         int ifd;
1606         int ofd;
1607         int res;
1608         int len;
1609         char buf[4096];
1610
1611 #ifdef HARDLINK_WHEN_POSSIBLE
1612         /* Hard link if possible; saves disk space & is faster */
1613         if (link(infile, outfile)) {
1614 #endif
1615                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1616                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1617                         return -1;
1618                 }
1619                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1620                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1621                         close(ifd);
1622                         return -1;
1623                 }
1624                 do {
1625                         len = read(ifd, buf, sizeof(buf));
1626                         if (len < 0) {
1627                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1628                                 close(ifd);
1629                                 close(ofd);
1630                                 unlink(outfile);
1631                         }
1632                         if (len) {
1633                                 res = write(ofd, buf, len);
1634                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1635                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1636                                         close(ifd);
1637                                         close(ofd);
1638                                         unlink(outfile);
1639                                 }
1640                         }
1641                 } while (len);
1642                 close(ifd);
1643                 close(ofd);
1644                 return 0;
1645 #ifdef HARDLINK_WHEN_POSSIBLE
1646         } else {
1647                 /* Hard link succeeded */
1648                 return 0;
1649         }
1650 #endif
1651 }
1652
1653 static void copy_file(char *frompath, char *topath)
1654 {
1655         char frompath2[PATH_MAX], topath2[PATH_MAX];
1656         struct ast_variable *tmp,*var = NULL;
1657         char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
1658         ast_filecopy(frompath, topath, NULL);
1659         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1660         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1661         if (ast_check_realtime("voicemail_data")) {
1662                 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
1663                 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
1664                 for (tmp = var; tmp; tmp = tmp->next) {
1665                         if (!strcasecmp(tmp->name, "origmailbox")) {
1666                                 origmailbox = tmp->value;
1667                         } else if (!strcasecmp(tmp->name, "context")) {
1668                                 context = tmp->value;
1669                         } else if (!strcasecmp(tmp->name, "macrocontext")) {
1670                                 macrocontext = tmp->value;
1671                         } else if (!strcasecmp(tmp->name, "exten")) {
1672                                 exten = tmp->value;
1673                         } else if (!strcasecmp(tmp->name, "priority")) {
1674                                 priority = tmp->value;
1675                         } else if (!strcasecmp(tmp->name, "callerchan")) {
1676                                 callerchan = tmp->value;
1677                         } else if (!strcasecmp(tmp->name, "callerid")) {
1678                                 callerid = tmp->value;
1679                         } else if (!strcasecmp(tmp->name, "origdate")) {
1680                                 origdate = tmp->value;
1681                         } else if (!strcasecmp(tmp->name, "origtime")) {
1682                                 origtime = tmp->value;
1683                         } else if (!strcasecmp(tmp->name, "category")) {
1684                                 category = tmp->value;
1685                         } else if (!strcasecmp(tmp->name, "duration")) {
1686                                 duration = tmp->value;
1687                         }
1688                 }
1689                 ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, NULL);
1690         }
1691         copy(frompath2, topath2);
1692         ast_variables_destroy(var);
1693 }
1694 /*! \brief
1695  * A negative return value indicates an error.
1696  * \note Should always be called with a lock already set on dir.
1697  */
1698 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1699 {
1700         int x;
1701         unsigned char map[MAXMSGLIMIT] = "";
1702         DIR *msgdir;
1703         struct dirent *msgdirent;
1704         int msgdirint;
1705
1706         /* Reading the entire directory into a file map scales better than
1707          * doing a stat repeatedly on a predicted sequence.  I suspect this
1708          * is partially due to stat(2) internally doing a readdir(2) itself to
1709          * find each file. */
1710         msgdir = opendir(dir);
1711         while ((msgdirent = readdir(msgdir))) {
1712                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1713                         map[msgdirint] = 1;
1714         }
1715         closedir(msgdir);
1716
1717         for (x = 0; x < vmu->maxmsg; x++) {
1718                 if (map[x] == 0)
1719                         break;
1720         }
1721
1722         return x - 1;
1723 }
1724
1725 #endif /*#ifndef IMAP_STORAGE*/
1726 #endif /*#else of #ifdef ODBC_STORAGE*/
1727 #ifndef ODBC_STORAGE
1728 static int vm_delete(char *file)
1729 {
1730         char *txt;
1731         int txtsize = 0;
1732
1733         txtsize = (strlen(file) + 5)*sizeof(char);
1734         txt = alloca(txtsize);
1735         /* Sprintf here would safe because we alloca'd exactly the right length,
1736          * but trying to eliminate all sprintf's anyhow
1737          */
1738         if (ast_check_realtime("voicemail_data")) {
1739                 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
1740         }
1741         snprintf(txt, txtsize, "%s.txt", file);
1742         unlink(txt);
1743         return ast_filedelete(file, NULL);
1744 }
1745 #endif
1746
1747 static int inbuf(struct baseio *bio, FILE *fi)
1748 {
1749         int l;
1750
1751         if (bio->ateof)
1752                 return 0;
1753
1754         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1755                 if (ferror(fi))
1756                         return -1;
1757
1758                 bio->ateof = 1;
1759                 return 0;
1760         }
1761
1762         bio->iolen= l;
1763         bio->iocp= 0;
1764
1765         return 1;
1766 }
1767
1768 static int inchar(struct baseio *bio, FILE *fi)
1769 {
1770         if (bio->iocp>=bio->iolen) {
1771                 if (!inbuf(bio, fi))
1772                         return EOF;
1773         }
1774
1775         return bio->iobuf[bio->iocp++];
1776 }
1777
1778 static int ochar(struct baseio *bio, int c, FILE *so)
1779 {
1780         if (bio->linelength >= BASELINELEN) {
1781                 if (fputs(eol,so) == EOF)
1782                         return -1;
1783
1784                 bio->linelength= 0;
1785         }
1786
1787         if (putc(((unsigned char)c),so) == EOF)
1788                 return -1;
1789
1790         bio->linelength++;
1791
1792         return 1;
1793 }
1794
1795 static int base_encode(char *filename, FILE *so)
1796 {
1797         unsigned char dtable[BASEMAXINLINE];
1798         int i,hiteof= 0;
1799         FILE *fi;
1800         struct baseio bio;
1801
1802         memset(&bio, 0, sizeof(bio));
1803         bio.iocp = BASEMAXINLINE;
1804
1805         if (!(fi = fopen(filename, "rb"))) {
1806                 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
1807                 return -1;
1808         }
1809
1810         for (i= 0; i<9; i++) {
1811                 dtable[i]= 'A'+i;
1812                 dtable[i+9]= 'J'+i;
1813                 dtable[26+i]= 'a'+i;
1814                 dtable[26+i+9]= 'j'+i;
1815         }
1816         for (i= 0; i<8; i++) {
1817                 dtable[i+18]= 'S'+i;
1818                 dtable[26+i+18]= 's'+i;
1819         }
1820         for (i= 0; i<10; i++) {
1821                 dtable[52+i]= '0'+i;
1822         }
1823         dtable[62]= '+';
1824         dtable[63]= '/';
1825
1826         while (!hiteof){
1827                 unsigned char igroup[3], ogroup[4];
1828                 int c,n;
1829
1830                 igroup[0]= igroup[1]= igroup[2]= 0;
1831
1832                 for (n= 0;n<3;n++) {
1833                         if ((c = inchar(&bio, fi)) == EOF) {
1834                                 hiteof= 1;
1835                                 break;
1836                         }
1837
1838                         igroup[n]= (unsigned char)c;
1839                 }
1840
1841                 if (n> 0) {
1842                         ogroup[0]= dtable[igroup[0]>>2];
1843                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1844                         ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1845                         ogroup[3]= dtable[igroup[2]&0x3F];
1846
1847                         if (n<3) {
1848                                 ogroup[3]= '=';
1849
1850                                 if (n<2)
1851                                         ogroup[2]= '=';
1852                         }
1853
1854                         for (i= 0;i<4;i++)
1855                                 ochar(&bio, ogroup[i], so);
1856                 }
1857         }
1858
1859         if (fputs(eol,so) == EOF)
1860                 return 0;
1861
1862         fclose(fi);
1863
1864         return 1;
1865 }
1866
1867 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)
1868 {
1869         char callerid[256];
1870         /* Prepare variables for substitution in email body and subject */
1871         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1872         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1873         snprintf(passdata, passdatasize, "%d", msgnum);
1874         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1875         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1876         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1877         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1878         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1879         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1880         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1881         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1882 }
1883
1884 static char *quote(const char *from, char *to, size_t len)
1885 {
1886         char *ptr = to;
1887         *ptr++ = '"';
1888         for (; ptr < to + len - 1; from++) {
1889                 if (*from == '"')
1890                         *ptr++ = '\\';
1891                 else if (*from == '\0')
1892                         break;
1893                 *ptr++ = *from;
1894         }
1895         if (ptr < to + len - 1)
1896                 *ptr++ = '"';
1897         *ptr = '\0';
1898         return to;
1899 }
1900
1901 /*! \brief
1902  * fill in *tm for current time according to the proper timezone, if any.
1903  * Return tm so it can be used as a function argument.
1904  */
1905 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
1906 {
1907         const struct vm_zone *z = NULL;
1908         struct timeval t = ast_tvnow();
1909
1910         /* Does this user have a timezone specified? */
1911         if (!ast_strlen_zero(vmu->zonetag)) {
1912                 /* Find the zone in the list */
1913                 AST_LIST_LOCK(&zones);
1914                 AST_LIST_TRAVERSE(&zones, z, list) {
1915                         if (!strcmp(z->name, vmu->zonetag))
1916                                 break;
1917                 }
1918                 AST_LIST_UNLOCK(&zones);
1919         }
1920         ast_localtime(&t, tm, z ? z->timezone : NULL);
1921         return tm;
1922 }
1923
1924 /*! \brief same as mkstemp, but return a FILE * */
1925 static FILE *vm_mkftemp(char *template)
1926 {
1927         FILE *p = NULL;
1928         int pfd = mkstemp(template);
1929         chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1930         if (pfd > -1) {
1931                 p = fdopen(pfd, "w+");
1932                 if (!p) {
1933                         close(pfd);
1934                         pfd = -1;
1935                 }
1936         }
1937         return p;
1938 }
1939
1940 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)
1941 {
1942         char date[256];
1943         char host[MAXHOSTNAMELEN] = "";
1944         char who[256];
1945         char bound[256];
1946         char fname[256];
1947         char dur[256];
1948         char tmpcmd[256];
1949         struct ast_tm tm;
1950         char *passdata2;
1951         size_t len_passdata;
1952         char *greeting_attachment;
1953
1954 #ifdef IMAP_STORAGE
1955 #define ENDL "\r\n"
1956 #else
1957 #define ENDL "\n"
1958 #endif
1959
1960         gethostname(host, sizeof(host)-1);
1961         if (strchr(srcemail, '@'))
1962                 ast_copy_string(who, srcemail, sizeof(who));
1963         else 
1964                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1965         
1966         greeting_attachment = strrchr(ast_strdupa(attach), '/');
1967         if (greeting_attachment)
1968                 *greeting_attachment++ = '\0';
1969
1970         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1971         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1972         fprintf(p, "Date: %s" ENDL, date);
1973
1974         /* Set date format for voicemail mail */
1975         ast_strftime(date, sizeof(date), emaildateformat, &tm);
1976
1977         if (!ast_strlen_zero(fromstring)) {
1978                 struct ast_channel *ast;
1979                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1980                         char *passdata;
1981                         int vmlen = strlen(fromstring)*3 + 200;
1982                         if ((passdata = alloca(vmlen))) {
1983                                 memset(passdata, 0, vmlen);
1984                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1985                                 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1986                                 len_passdata = strlen(passdata) * 2 + 3;
1987                                 passdata2 = alloca(len_passdata);
1988                                 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1989                         } else
1990                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1991                         ast_channel_free(ast);
1992                 } else
1993                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1994         } else
1995                 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1996         len_passdata = strlen(vmu->fullname) * 2 + 3;
1997         passdata2 = alloca(len_passdata);
1998         fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1999         if (!ast_strlen_zero(emailsubject)) {
2000                 struct ast_channel *ast;
2001                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2002                         char *passdata;
2003                         int vmlen = strlen(emailsubject) * 3 + 200;
2004                         if ((passdata = alloca(vmlen))) {
2005                                 memset(passdata, 0, vmlen);
2006                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2007                                 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
2008                                 fprintf(p, "Subject: %s" ENDL, passdata);
2009                         } else
2010                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2011                         ast_channel_free(ast);
2012                 } else
2013                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2014         } else  if (!ast_strlen_zero(emailtitle)) {
2015                 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
2016                 fprintf(p, ENDL) ;
2017         } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
2018                 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2019         else
2020                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2021         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
2022         if(imap) {
2023                 /* additional information needed for IMAP searching */
2024                 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
2025                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
2026                 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
2027                 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
2028                 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
2029                 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
2030                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
2031                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
2032                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
2033                 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
2034                 if (!ast_strlen_zero(category)) 
2035                         fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
2036                 fprintf(p, "X-Asterisk-VM-Message-Type: %s\n", msgnum > -1 ? "Message" : greeting_attachment);
2037                 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
2038                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
2039         }
2040         if (!ast_strlen_zero(cidnum))
2041                 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
2042         if (!ast_strlen_zero(cidname))
2043                 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
2044         fprintf(p, "MIME-Version: 1.0" ENDL);
2045         if (attach_user_voicemail) {
2046                 /* Something unique. */
2047                 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
2048
2049                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
2050                 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
2051                 fprintf(p, "--%s" ENDL, bound);
2052         }
2053         fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
2054         if (emailbody) {
2055                 struct ast_channel *ast;
2056                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2057                         char *passdata;
2058                         int vmlen = strlen(emailbody)*3 + 200;
2059                         if ((passdata = alloca(vmlen))) {
2060                                 memset(passdata, 0, vmlen);
2061                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2062                                 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2063                                 fprintf(p, "%s" ENDL, passdata);
2064                         } else
2065                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2066                         ast_channel_free(ast);
2067                 } else
2068                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2069         } else if (msgnum > -1){
2070                 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
2071
2072                 "in mailbox %s from %s, on %s so you might" ENDL
2073                 "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
2074                 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2075         } else {
2076                 fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
2077                                 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
2078         }
2079         if (attach_user_voicemail) {
2080                 /* Eww. We want formats to tell us their own MIME type */
2081                 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2082                 char tmpdir[256], newtmp[256];
2083                 int tmpfd = -1;
2084         
2085                 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2086                         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2087                         snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2088                         tmpfd = mkstemp(newtmp);
2089                         chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
2090                         ast_debug(3, "newtmp: %s\n", newtmp);
2091                         if (tmpfd > -1) {
2092                                 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2093                                 ast_safe_system(tmpcmd);
2094                                 attach = newtmp;
2095                                 ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2096                         }
2097                 }
2098                 fprintf(p, "--%s" ENDL, bound);
2099                 if (msgnum > -1)
2100                         fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2101                 else
2102                         fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
2103                 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2104                 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2105                 if (msgnum > -1)
2106                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2107                 else
2108                         fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
2109                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2110                 base_encode(fname, p);
2111                 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
2112                 if (tmpfd > -1) {
2113                         unlink(fname);
2114                         close(tmpfd);
2115                         unlink(newtmp);
2116                 }
2117         }
2118 #undef ENDL
2119 }
2120
2121 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
2122 {
2123         FILE *p=NULL;
2124         char tmp[80] = "/tmp/astmail-XXXXXX";
2125         char tmp2[256];
2126
2127         if (vmu && ast_strlen_zero(vmu->email)) {
2128                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2129                 return(0);
2130         }
2131         if (!strcmp(format, "wav49"))
2132                 format = "WAV";
2133         ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
2134         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2135            command hangs */
2136         if ((p = vm_mkftemp(tmp)) == NULL) {
2137                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2138                 return -1;
2139         } else {
2140                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2141                 fclose(p);
2142                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2143                 ast_safe_system(tmp2);
2144                 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2145         }
2146         return 0;
2147 }
2148
2149 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category)
2150 {
2151         char date[256];
2152         char host[MAXHOSTNAMELEN] = "";
2153         char who[256];
2154         char dur[PATH_MAX];
2155         char tmp[80] = "/tmp/astmail-XXXXXX";
2156         char tmp2[PATH_MAX];
2157         struct ast_tm tm;
2158         FILE *p;
2159
2160         if ((p = vm_mkftemp(tmp)) == NULL) {
2161                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2162                 return -1;
2163         }
2164         gethostname(host, sizeof(host)-1);
2165         if (strchr(srcemail, '@'))
2166                 ast_copy_string(who, srcemail, sizeof(who));
2167         else 
2168                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2169         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2170         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2171         fprintf(p, "Date: %s\n", date);
2172
2173         if (*pagerfromstring) {
2174                 struct ast_channel *ast;
2175                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2176                         char *passdata;
2177                         int vmlen = strlen(fromstring)*3 + 200;
2178                         if ((passdata = alloca(vmlen))) {
2179                                 memset(passdata, 0, vmlen);
2180                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2181                                 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2182                                 fprintf(p, "From: %s <%s>\n", passdata, who);
2183                         } else 
2184                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2185                         ast_channel_free(ast);
2186                 } else 
2187                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2188         } else
2189                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2190         fprintf(p, "To: %s\n", pager);
2191         if (pagersubject) {
2192                 struct ast_channel *ast;
2193                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2194                         char *passdata;
2195                         int vmlen = strlen(pagersubject) * 3 + 200;
2196                         if ((passdata = alloca(vmlen))) {
2197                                 memset(passdata, 0, vmlen);
2198                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2199                                 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2200                                 fprintf(p, "Subject: %s\n\n", passdata);
2201                         } else
2202                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2203                         ast_channel_free(ast);
2204                 } else
2205                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2206         } else
2207                 fprintf(p, "Subject: New VM\n\n");
2208
2209         ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2210         if (pagerbody) {
2211                 struct ast_channel *ast;
2212                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2213                         char *passdata;
2214                         int vmlen = strlen(pagerbody) * 3 + 200;
2215                         passdata = alloca(vmlen);
2216                         memset(passdata, 0, vmlen);
2217                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2218                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2219                         fprintf(p, "%s\n", passdata);
2220                         ast_channel_free(ast);
2221                 } else
2222                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2223         } else {
2224                 fprintf(p, "New %s long msg in box %s\n"
2225                                 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2226         }
2227         fclose(p);
2228         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2229         ast_safe_system(tmp2);
2230         ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2231         return 0;
2232 }
2233
2234 static int get_date(char *s, int len)
2235 {
2236         struct ast_tm tm;
2237         struct timeval t = ast_tvnow();
2238         ast_localtime(&t, &tm, NULL);
2239         return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2240 }
2241
2242 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2243 {
2244         int res;
2245         char fn[PATH_MAX];
2246         char dest[PATH_MAX];
2247
2248         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2249
2250         if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
2251                 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2252                 return -1;
2253         }
2254
2255         RETRIEVE(fn, -1, ext, context);
2256         if (ast_fileexists(fn, NULL, NULL) > 0) {
2257                 res = ast_stream_and_wait(chan, fn, ecodes);
2258                 if (res) {
2259                         DISPOSE(fn, -1);
2260                         return res;
2261                 }
2262         } else {
2263                 /* Dispose just in case */
2264                 DISPOSE(fn, -1);
2265                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2266                 if (res)
2267                         return res;
2268                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2269                 if (res)
2270                         return res;
2271         }
2272         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2273         return res;
2274 }
2275
2276 static void free_user(struct ast_vm_user *vmu)
2277 {
2278         if (!ast_test_flag(vmu, VM_ALLOCED))
2279                 return;
2280
2281         ast_free(vmu);
2282 }
2283
2284 static void free_zone(struct vm_zone *z)
2285 {
2286         ast_free(z);
2287 }
2288
2289 static const char *mbox(int id)
2290 {
2291         static const char *msgs[] = {
2292                 "INBOX",
2293                 "Old",
2294                 "Work",
2295                 "Family",
2296                 "Friends",
2297                 "Cust1",
2298                 "Cust2",
2299                 "Cust3",
2300                 "Cust4",
2301                 "Cust5",
2302         };
2303         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2304 }
2305 #ifdef IMAP_STORAGE
2306 static int folder_int(const char *folder)
2307 {
2308         /*assume a NULL folder means INBOX*/
2309         if (!folder)
2310                 return 0;
2311         if(!strcasecmp(folder, "INBOX"))
2312                 return 0;
2313         else if (!strcasecmp(folder, "Old"))
2314                 return 1;
2315         else if (!strcasecmp(folder, "Work"))
2316                 return 2;
2317         else if (!strcasecmp(folder, "Family"))
2318                 return 3;
2319         else if (!strcasecmp(folder, "Friends"))
2320                 return 4;
2321         else if (!strcasecmp(folder, "Cust1"))
2322                 return 5;
2323         else if (!strcasecmp(folder, "Cust2"))
2324                 return 6;
2325         else if (!strcasecmp(folder, "Cust3"))
2326                 return 7;
2327         else if (!strcasecmp(folder, "Cust4"))
2328                 return 8;
2329         else if (!strcasecmp(folder, "Cust5"))
2330                 return 9;
2331         else /*assume they meant INBOX if folder is not found otherwise*/
2332                 return 0;
2333 }
2334 #endif
2335
2336 #ifdef ODBC_STORAGE
2337 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2338 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2339 {
2340         int x = -1;
2341         int res;
2342         SQLHSTMT stmt;
2343         char sql[PATH_MAX];
2344         char rowdata[20];
2345         char tmp[PATH_MAX] = "";
2346         struct odbc_obj *obj;
2347         char *context;
2348         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2349
2350         if (newmsgs)
2351                 *newmsgs = 0;
2352         if (oldmsgs)
2353                 *oldmsgs = 0;
2354
2355         /* If no mailbox, return immediately */
2356         if (ast_strlen_zero(mailbox))
2357                 return 0;
2358
2359         ast_copy_string(tmp, mailbox, sizeof(tmp));
2360         
2361         context = strchr(tmp, '@');
2362         if (context) {
2363                 *context = '\0';
2364                 context++;
2365         } else
2366                 context = "default";
2367         
2368         obj = ast_odbc_request_obj(odbc_database, 0);
2369         if (obj) {
2370                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2371                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2372                 if (!stmt) {
2373                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2374                         ast_odbc_release_obj(obj);
2375                         goto yuck;
2376                 }
2377                 res = SQLFetch(stmt);
2378                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2379                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2380                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2381                         ast_odbc_release_obj(obj);
2382                         goto yuck;
2383                 }
2384                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2385                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2386                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2387                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2388                         ast_odbc_release_obj(obj);
2389                         goto yuck;
2390                 }
2391                 *newmsgs = atoi(rowdata);
2392                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2393
2394                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2395                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2396                 if (!stmt) {
2397                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2398                         ast_odbc_release_obj(obj);
2399                         goto yuck;
2400                 }
2401                 res = SQLFetch(stmt);
2402                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2403                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2404                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2405                         ast_odbc_release_obj(obj);
2406                         goto yuck;
2407                 }
2408                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2409                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2410                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2411                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2412                         ast_odbc_release_obj(obj);
2413                         goto yuck;
2414                 }
2415                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2416                 ast_odbc_release_obj(obj);
2417                 *oldmsgs = atoi(rowdata);
2418                 x = 0;
2419         } else
2420                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2421                 
2422 yuck:   
2423         return x;
2424 }
2425
2426 static int messagecount(const char *context, const char *mailbox, const char *folder)
2427 {
2428         struct odbc_obj *obj = NULL;
2429         int nummsgs = 0;
2430         int res;
2431         SQLHSTMT stmt = NULL;
2432         char sql[PATH_MAX];
2433         char rowdata[20];
2434         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2435         if (!folder)
2436                 folder = "INBOX";
2437         /* If no mailbox, return immediately */
2438         if (ast_strlen_zero(mailbox))
2439                 return 0;
2440
2441         obj = ast_odbc_request_obj(odbc_database, 0);
2442         if (obj) {
2443                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2444                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2445                 if (!stmt) {
2446                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2447                         goto yuck;
2448                 }
2449                 res = SQLFetch(stmt);
2450                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2451                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2452                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2453                         goto yuck;
2454                 }
2455                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2456                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2457                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2458                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2459                         goto yuck;
2460                 }
2461                 nummsgs = atoi(rowdata);
2462                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2463         } else
2464                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2465
2466 yuck:
2467         if (obj)
2468                 ast_odbc_release_obj(obj);
2469         return nummsgs;
2470 }
2471
2472 static int has_voicemail(const char *mailbox, const char *folder)
2473 {
2474         char tmp[256], *tmp2 = tmp, *mbox, *context;
2475         ast_copy_string(tmp, mailbox, sizeof(tmp));
2476         while ((context = mbox = strsep(&tmp2, ","))) {
2477                 strsep(&context, "@");
2478                 if (ast_strlen_zero(context))
2479                         context = "default";
2480                 if (messagecount(context, mbox, folder))
2481                         return 1;
2482         }
2483         return 0;
2484 }
2485
2486 #elif defined(IMAP_STORAGE)
2487
2488 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)
2489 {
2490         char *myserveremail = serveremail;
2491         char fn[PATH_MAX];
2492         char mailbox[256];
2493         char *stringp;
2494         FILE *p=NULL;
2495         char tmp[80] = "/tmp/astmail-XXXXXX";
2496         long len;
2497         void *buf;
2498         STRING str;
2499         
2500         /* Attach only the first format */
2501         fmt = ast_strdupa(fmt);
2502         stringp = fmt;
2503         strsep(&stringp, "|");
2504
2505         if (!ast_strlen_zero(vmu->serveremail))
2506                 myserveremail = vmu->serveremail;
2507
2508         if (msgnum > -1)
2509                 make_file(fn, sizeof(fn), dir, msgnum);
2510         else
2511                 ast_copy_string (fn, dir, sizeof(fn));
2512         
2513         if (ast_strlen_zero(vmu->email))
2514                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2515
2516         if (!strcmp(fmt, "wav49"))
2517                 fmt = "WAV";
2518         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2519
2520         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2521            command hangs */
2522         if (!(p = vm_mkftemp(tmp))) {
2523                 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2524                 return -1;
2525
2526         }
2527
2528         if (msgnum < 0 && imapgreetings) {
2529                 init_mailstream(vms, GREETINGS_FOLDER);
2530                 imap_delete_old_greeting(fn, vms);
2531         }
2532         
2533         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);
2534         /* read mail file to memory */          
2535         len = ftell(p);
2536         rewind(p);
2537         if (!(buf = ast_malloc(len+1))) {
2538                 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2539                 return -1;
2540         }
2541         fread(buf, len, 1, p);
2542         ((char *)buf)[len] = '\0';
2543         INIT(&str, mail_string, buf, len);
2544         init_mailstream(vms, NEW_FOLDER);
2545         imap_mailbox_name(mailbox, vms, NEW_FOLDER, 1);
2546         if(!mail_append(vms->mailstream, mailbox, &str))
2547                 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2548         fclose(p);
2549         unlink(tmp);
2550         ast_free(buf);
2551         ast_debug(3, "%s stored\n", fn);
2552         return 0;
2553
2554 }
2555
2556 static int messagecount(const char *context, const char *mailbox, const char *folder)
2557 {
2558         SEARCHPGM *pgm;
2559         SEARCHHEADER *hdr;
2560  
2561         struct ast_vm_user *vmu, vmus;
2562         struct vm_state *vms_p;
2563         int ret = 0;
2564         int fold = folder_int(folder);
2565         
2566         if (ast_strlen_zero(mailbox))
2567                 return 0;
2568
2569         /* We have to get the user before we can open the stream! */
2570         /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2571         vmu = find_user(&vmus, context, mailbox);
2572         if (!vmu) {
2573                 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
2574                 return -1;
2575         } else {
2576                 /* No IMAP account available */
2577                 if (vmu->imapuser[0] == '\0') {
2578                         ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2579                         return -1;
2580                 }
2581         }
2582         
2583         /* No IMAP account available */
2584         if (vmu->imapuser[0] == '\0') {
2585                 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2586                 free_user(vmu);
2587                 return -1;
2588         }
2589
2590         /* check if someone is accessing this box right now... */
2591         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2592         if (!vms_p) {
2593                 vms_p = get_vm_state_by_mailbox(mailbox,1);
2594         }
2595         if (vms_p) {
2596                 ast_debug(3, "Returning before search - user is logged in\n");
2597                 if(fold == 0) {/*INBOX*/
2598                         return vms_p->newmessages;
2599                 }
2600                 if(fold == 1) {/*Old messages*/
2601                         return vms_p->oldmessages;
2602                 }
2603         }
2604
2605         /* add one if not there... */
2606         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2607         if (!vms_p) {
2608                 vms_p = get_vm_state_by_mailbox(mailbox,0);
2609         }
2610
2611         if (!vms_p) {
2612                 ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
2613                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2614                         return -1;
2615                 }
2616                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2617                 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2618                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2619                 ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2620                 vms_p->updated = 1;
2621                 /* set mailbox to INBOX! */
2622                 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
2623                 init_vm_state(vms_p);
2624                 vmstate_insert(vms_p);
2625         }
2626         ret = init_mailstream(vms_p, fold);
2627         if (!vms_p->mailstream) {
2628                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2629                 return -1;
2630         }
2631         if (ret == 0) {
2632                 pgm = mail_newsearchpgm ();
2633                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
2634                 pgm->header = hdr;
2635                 if (fold != 1) {
2636                         pgm->unseen = 1;
2637                         pgm->seen = 0;
2638                 }
2639                 /* In the special case where fold is 1 (old messages) we have to do things a bit
2640                  * differently. Old messages are stored in the INBOX but are marked as "seen"
2641                  */
2642                 else {
2643                         pgm->unseen = 0;
2644                         pgm->seen = 1;
2645                 }
2646                 pgm->undeleted = 1;
2647                 pgm->deleted = 0;
2648
2649                 vms_p->vmArrayIndex = 0;
2650                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2651                 if(fold == 0)
2652                         vms_p->newmessages = vms_p->vmArrayIndex;
2653                 if(fold == 1)
2654                         vms_p->oldmessages = vms_p->vmArrayIndex;
2655                 /*Freeing the searchpgm also frees the searchhdr*/
2656                 mail_free_searchpgm(&pgm);
2657                 vms_p->updated = 0;
2658                 return vms_p->vmArrayIndex;
2659         } else {  
2660                 mail_ping(vms_p->mailstream);
2661         }
2662         return 0;
2663 }
2664 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
2665 {
2666         char tmp[PATH_MAX] = "";
2667         char *mailboxnc;        
2668         char *context;
2669         char *mb;
2670         char *cur;
2671         if (newmsgs)
2672                 *newmsgs = 0;
2673         if (oldmsgs)
2674                 *oldmsgs = 0;
2675
2676         ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
2677         /* If no mailbox, return immediately */
2678         if (ast_strlen_zero(mailbox_context))
2679                 return 0;
2680         
2681         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2682         context = strchr(tmp, '@');
2683         if (strchr(mailbox_context, ',')) {
2684                 int tmpnew, tmpold;
2685                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2686                 mb = tmp;
2687                 while((cur = strsep(&mb, ", "))) {
2688                         if (!ast_strlen_zero(cur)) {
2689                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2690                                         return -1;
2691                                 else {
2692                                         if (newmsgs)
2693                                                 *newmsgs += tmpnew; 
2694                                         if (oldmsgs)
2695                                                 *oldmsgs += tmpold;
2696                                 }
2697                         }
2698                 }
2699                 return 0;
2700         }
2701         if (context) {
2702                 *context = '\0';
2703                 mailboxnc = tmp;
2704                 context++;
2705         } else {
2706                 context = "default";
2707                 mailboxnc = (char *)mailbox_context;
2708         }
2709         if (newmsgs) {
2710                 if((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
2711                         return -1;
2712         }
2713         if (oldmsgs) {
2714                 if((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
2715                         return -1;
2716         }
2717         return 0;
2718 }
2719         
2720
2721 static int has_voicemail(const char *mailbox, const char *folder)
2722 {
2723         char tmp[256], *tmp2, *mbox, *context;
2724         ast_copy_string(tmp, mailbox, sizeof(tmp));
2725         tmp2 = tmp;
2726         if(strchr(tmp2, ',')) {
2727                 while((mbox = strsep(&tmp2, ","))) {
2728                         if(!ast_strlen_zero(mbox)) {
2729                                 if (has_voicemail(mbox, folder))
2730                                         return 1;
2731                         }
2732                 }
2733         }
2734         if ((context= strchr(tmp, '@')))
2735                 *context++ = '\0';
2736         else
2737                 context = "default";
2738         return messagecount(context, tmp, folder) ? 1 : 0;
2739 }
2740
2741 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)
2742 {
2743         char dest[256];
2744         struct vm_state *sendvms = NULL, *destvms = NULL;
2745         char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2746         if(!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 2)))
2747         {
2748                 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2749                 return -1;
2750         }
2751         if(!(destvms = get_vm_state_by_imapuser(recip->imapuser, 2)))
2752         {
2753                 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2754                 return -1;
2755         }
2756         imap_mailbox_name(dest, destvms, imbox, 1);
2757         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2758         if((mail_copy(sendvms->mailstream, messagestring, dest) == T))
2759                 return 0;
2760         ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2761         return -1;
2762 }
2763
2764 #endif
2765 #ifndef IMAP_STORAGE
2766 /* copy message only used by file storage */
2767 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)
2768 {
2769         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2770         const char *frombox = mbox(imbox);
2771         int recipmsgnum;
2772
2773         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2774
2775         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2776         
2777         if (!dir)
2778                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2779         else
2780                 ast_copy_string(fromdir, dir, sizeof(fromdir));
2781
2782         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2783         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2784
2785         if (vm_lock_path(todir))
2786                 return ERROR_LOCK_PATH;
2787
2788         recipmsgnum = last_message_index(recip, todir) + 1;
2789         if (recipmsgnum < recip->maxmsg) {
2790                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2791                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2792         } else {
2793                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2794         }
2795         ast_unlock_path(todir);
2796         notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2797         
2798         return 0;
2799 }
2800 #endif
2801 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2802 static int messagecount(const char *context, const char *mailbox, const char *folder)
2803 {
2804         return __has_voicemail(context, mailbox, folder, 0);
2805 }
2806
2807
2808 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2809 {
2810         DIR *dir;
2811         struct dirent *de;
2812         char fn[256];
2813         int ret = 0;
2814
2815         /* If no mailbox, return immediately */
2816         if (ast_strlen_zero(mailbox))
2817                 return 0;
2818
2819         if (ast_strlen_zero(folder))
2820                 folder = "INBOX";
2821         if (ast_strlen_zero(context))
2822                 context = "default";
2823
2824         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2825
2826         if (!(dir = opendir(fn)))
2827                 return 0;
2828
2829         while ((de = readdir(dir))) {
2830                 if (!strncasecmp(de->d_name, "msg", 3)) {
2831                         if (shortcircuit) {
2832                                 ret = 1;
2833                                 break;
2834                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2835                                 ret++;
2836                 }
2837         }
2838
2839         closedir(dir);
2840
2841         return ret;
2842 }
2843
2844
2845 static int has_voicemail(const char *mailbox, const char *folder)
2846 {
2847         char tmp[256], *tmp2 = tmp, *mbox, *context;
2848         ast_copy_string(tmp, mailbox, sizeof(tmp));
2849         while ((mbox = strsep(&tmp2, ","))) {
2850                 if ((context = strchr(mbox, '@')))
2851                         *context++ = '\0';
2852                 else
2853                         context = "default";
2854                 if (__has_voicemail(context, mbox, folder, 1))
2855                         return 1;
2856         }
2857         return 0;
2858 }
2859
2860
2861 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2862 {
2863         char tmp[256];
2864         char *context;
2865
2866         /* If no mailbox, return immediately */
2867         if (ast_strlen_zero(mailbox))
2868                 return 0;
2869
2870         if (newmsgs)
2871                 *newmsgs = 0;
2872         if (oldmsgs)
2873                 *oldmsgs = 0;
2874
2875         if (strchr(mailbox, ',')) {
2876                 int tmpnew, tmpold;
2877                 char *mb, *cur;
2878
2879                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2880                 mb = tmp;
2881                 while ((cur = strsep(&mb, ", "))) {
2882                         if (!ast_strlen_zero(cur)) {
2883                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2884                                         return -1;
2885                                 else {
2886                                         if (newmsgs)
2887                                                 *newmsgs += tmpnew; 
2888                                         if (oldmsgs)
2889                                                 *oldmsgs += tmpold;
2890                                 }
2891                         }
2892                 }
2893                 return 0;
2894         }
2895
2896         ast_copy_string(tmp, mailbox, sizeof(tmp));
2897         
2898         if ((context = strchr(tmp, '@')))
2899                 *context++ = '\0';
2900         else
2901                 context = "default";
2902
2903         if (newmsgs)
2904                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2905         if (oldmsgs)
2906                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2907
2908         return 0;
2909 }
2910
2911 #endif
2912
2913 static void run_externnotify(char *context, char *extension)
2914 {
2915         char arguments[255];
2916         char ext_context[256] = "";
2917         int newvoicemails = 0, oldvoicemails = 0;
2918         struct ast_smdi_mwi_message *mwi_msg;
2919
2920         if (!ast_strlen_zero(context))
2921                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2922         else
2923                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2924
2925         if (smdi_iface) {
2926                 if (ast_app_has_voicemail(ext_context, NULL)) 
2927                         ast_smdi_mwi_set(smdi_iface, extension);
2928                 else
2929                         ast_smdi_mwi_unset(smdi_iface, extension);
2930
2931                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2932                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2933                         if (!strncmp(mwi_msg->cause, "INV", 3))
2934                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2935                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2936                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2937                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2938                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2939                 } else {
2940                         ast_debug(1, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2941                 }
2942         }
2943
2944         if (!ast_strlen_zero(externnotify)) {
2945                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2946                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2947                 } else {
2948                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2949                         ast_debug(1, "Executing %s\n", arguments);
2950                         ast_safe_system(arguments);
2951                 }
2952         }
2953 }
2954
2955 struct leave_vm_options {
2956         unsigned int flags;
2957         signed char record_gain;
2958 };
2959
2960 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2961 {
2962 #ifdef IMAP_STORAGE
2963         int newmsgs, oldmsgs;
2964         struct vm_state *vms = NULL;
2965 #endif
2966         char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2967         char callerid[256];
2968         FILE *txt;
2969         char date[256];
2970         int txtdes;
2971         int res = 0;
2972         int msgnum;
2973         int duration = 0;
2974         int ausemacro = 0;
2975         int ousemacro = 0;
2976         int ouseexten = 0;
2977         int rtmsgid = 0;
2978         char tmpid[16];
2979         char tmpdur[16];
2980         char priority[16];
2981         char origtime[16];
2982         char dir[PATH_MAX], tmpdir[PATH_MAX];
2983         char fn[PATH_MAX];
2984         char prefile[PATH_MAX] = "";
2985         char tempfile[PATH_MAX] = "";
2986         char ext_context[256] = "";
2987         char fmt[80];
2988         char *context;
2989         char ecodes[16] = "#";
2990         char tmp[1024] = "", *tmpptr;
2991         struct ast_vm_user *vmu;
2992         struct ast_vm_user svm;
2993         const char *category = NULL;
2994
2995         ast_copy_string(tmp, ext, sizeof(tmp));
2996         ext = tmp;
2997         if ((context = strchr(tmp, '@'))) {
2998                 *context++ = '\0';
2999                 tmpptr = strchr(context, '&');
3000         } else {
3001                 tmpptr = strchr(ext, '&');
3002         }
3003
3004         if (tmpptr)
3005                 *tmpptr++ = '\0';
3006
3007         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3008
3009         ast_debug(3, "Before find_user\n");
3010         if (!(vmu = find_user(&svm, context, ext))) {
3011                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3012                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3013                 return res;
3014         }
3015         /* Setup pre-file if appropriate */
3016         if (strcmp(vmu->context, "default"))
3017                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3018         else
3019                 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
3020         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3021                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3022         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3023                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3024         }
3025         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3026         if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
3027                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3028                 return -1;
3029         }
3030         RETRIEVE(tempfile, -1, ext, context);
3031         if (ast_fileexists(tempfile, NULL, NULL) > 0)
3032                 ast_copy_string(prefile, tempfile, sizeof(prefile));
3033         DISPOSE(tempfile, -1);
3034         /* It's easier just to try to make it than to check for its existence */
3035         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3036
3037         /* Check current or macro-calling context for special extensions */
3038         if (ast_test_flag(vmu, VM_OPERATOR)) {
3039                 if (!ast_strlen_zero(vmu->exit)) {
3040                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3041                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3042                                 ouseexten = 1;
3043                         }
3044                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3045                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3046                         ouseexten = 1;
3047                 }
3048                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3049                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3050                 ousemacro = 1;
3051                 }
3052         }
3053
3054         if (!ast_strlen_zero(vmu->exit)) {
3055                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3056                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3057         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3058                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3059         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3060                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3061                 ausemacro = 1;
3062         }
3063
3064         /* Play the beginning intro if desired */
3065         if (!ast_strlen_zero(prefile)) {
3066 #ifdef ODBC_STORAGE
3067                 int success = 
3068 #endif
3069                         RETRIEVE(prefile, -1, ext, context);
3070                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3071                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
3072                                 res = ast_waitstream(chan, ecodes);
3073 #ifdef ODBC_STORAGE
3074                         if (success == -1) {
3075                                 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
3076                                 ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
3077                                 store_file(prefile, vmu->mailbox, vmu->context, -1);
3078                         }
3079 #endif
3080                 } else {
3081                         ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
3082                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3083                 }
3084                 DISPOSE(prefile, -1);
3085                 if (res < 0) {
3086                         ast_debug(1, "Hang up during prefile playback\n");
3087                         free_user(vmu);
3088                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3089                         return -1;
3090                 }
3091         }
3092         if (res == '#') {
3093                 /* On a '#' we skip the instructions */
3094                 ast_set_flag(options, OPT_SILENT);
3095                 res = 0;