Fix IMAP compilation error.
[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 char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
141 static void vm_imap_delete(int msgnum, struct vm_state *vms);
142 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
143 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
144 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
145 static void vmstate_insert(struct vm_state *vms);
146 static void vmstate_delete(struct vm_state *vms);
147 static void set_update(MAILSTREAM * stream);
148 static void init_vm_state(struct vm_state *vms);
149 static void check_msgArray(struct vm_state *vms);
150 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
151 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
152 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num);
153 static void get_mailbox_delimiter(MAILSTREAM *stream);
154 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
155 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
156 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);
157 static void update_messages_by_imapuser(const char *user, unsigned long number);
158
159 static int imap_remove_file (char *dir, int msgnum);
160 static int imap_retrieve_file (char *dir, int msgnum, char *mailbox, char *context);
161 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
162 struct vmstate {
163         struct vm_state *vms;
164         AST_LIST_ENTRY(vmstate) list;
165 };
166
167 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
168
169 #endif
170
171 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
172
173 #define COMMAND_TIMEOUT 5000
174 /* Don't modify these here; set your umask at runtime instead */
175 #define VOICEMAIL_DIR_MODE      0777
176 #define VOICEMAIL_FILE_MODE     0666
177 #define CHUNKSIZE       65536
178
179 #define VOICEMAIL_CONFIG "voicemail.conf"
180 #define ASTERISK_USERNAME "asterisk"
181
182 /* Define fast-forward, pause, restart, and reverse keys
183    while listening to a voicemail message - these are
184    strings, not characters */
185 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
186 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
187 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
188 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
189 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
190 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
191
192 /* Default mail command to mail voicemail. Change it with the
193     mailcmd= command in voicemail.conf */
194 #define SENDMAIL "/usr/sbin/sendmail -t"
195
196 #define INTRO "vm-intro"
197
198 #define MAXMSG 100
199 #define MAXMSGLIMIT 9999
200
201 #define BASEMAXINLINE 256
202 #define BASELINELEN 72
203 #define BASEMAXINLINE 256
204 #define eol "\r\n"
205
206 #define MAX_DATETIME_FORMAT     512
207 #define MAX_NUM_CID_CONTEXTS 10
208
209 #define VM_REVIEW        (1 << 0)
210 #define VM_OPERATOR      (1 << 1)
211 #define VM_SAYCID        (1 << 2)
212 #define VM_SVMAIL        (1 << 3)
213 #define VM_ENVELOPE      (1 << 4)
214 #define VM_SAYDURATION   (1 << 5)
215 #define VM_SKIPAFTERCMD  (1 << 6)
216 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
217 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
218 #define VM_PBXSKIP       (1 << 9)
219 #define VM_DIRECFORWARD  (1 << 10)  /*!< directory_forward */
220 #define VM_ATTACH        (1 << 11)
221 #define VM_DELETE        (1 << 12)
222 #define VM_ALLOCED       (1 << 13)
223 #define VM_SEARCH        (1 << 14)
224 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
225 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
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, "moveheard")){
720                 ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
721         } else if (!strcasecmp(var, "sayduration")){
722                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
723         } else if (!strcasecmp(var, "saydurationm")){
724                 if (sscanf(value, "%d", &x) == 1) {
725                         vmu->saydurationm = x;
726                 } else {
727                         ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
728                 }
729         } else if (!strcasecmp(var, "forcename")){
730                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
731         } else if (!strcasecmp(var, "forcegreetings")){
732                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
733         } else if (!strcasecmp(var, "callback")) {
734                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
735         } else if (!strcasecmp(var, "dialout")) {
736                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
737         } else if (!strcasecmp(var, "exitcontext")) {
738                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
739         } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
740                 if (vmu->maxsecs <= 0) {
741                         ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
742                         vmu->maxsecs = vmmaxsecs;
743                 } else {
744                         vmu->maxsecs = atoi(value);
745                 }
746                 if (!strcasecmp(var, "maxmessage"))
747                         ast_log(LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
748         } else if (!strcasecmp(var, "maxmsg")) {
749                 vmu->maxmsg = atoi(value);
750                 if (vmu->maxmsg <= 0) {
751                         ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
752                         vmu->maxmsg = MAXMSG;
753                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
754                         ast_log(LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
755                         vmu->maxmsg = MAXMSGLIMIT;
756                 }
757         } else if (!strcasecmp(var, "volgain")) {
758                 sscanf(value, "%lf", &vmu->volgain);
759         } else if (!strcasecmp(var, "options")) {
760                 apply_options(vmu, value);
761         }
762 }
763
764 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
765 {
766         int res;
767         if (!ast_strlen_zero(vmu->uniqueid)) {
768                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
769                 if (res > 0) {
770                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
771                         res = 0;
772                 } else if (!res) {
773                         res = -1;
774                 }
775                 return res;
776         }
777         return -1;
778 }
779
780 static void apply_options(struct ast_vm_user *vmu, const char *options)
781 {       /* Destructively Parse options and apply */
782         char *stringp;
783         char *s;
784         char *var, *value;
785         stringp = ast_strdupa(options);
786         while ((s = strsep(&stringp, "|"))) {
787                 value = s;
788                 if ((var = strsep(&value, "=")) && value) {
789                         apply_option(vmu, var, value);
790                 }
791         }       
792 }
793
794 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
795 {
796         struct ast_variable *tmp;
797         tmp = var;
798         while (tmp) {
799                 if (!strcasecmp(tmp->name, "vmsecret")) {
800                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
801                 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
802                         if (ast_strlen_zero(retval->password))
803                                 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
804                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
805                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
806                 } else if (!strcasecmp(tmp->name, "pager")) {
807                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
808                 } else if (!strcasecmp(tmp->name, "email")) {
809                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
810                 } else if (!strcasecmp(tmp->name, "fullname")) {
811                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
812                 } else if (!strcasecmp(tmp->name, "context")) {
813                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
814 #ifdef IMAP_STORAGE
815                 } else if (!strcasecmp(tmp->name, "imapuser")) {
816                         ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
817                 } else if (!strcasecmp(tmp->name, "imappassword")) {
818                         ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
819 #endif
820                 } else
821                         apply_option(retval, tmp->name, tmp->value);
822                 tmp = tmp->next;
823         } 
824 }
825
826 static int is_valid_dtmf(const char *key)
827 {
828         int i;
829         char *local_key = ast_strdupa(key);
830
831         for(i = 0; i < strlen(key); ++i) {
832                 if(!strchr(VALID_DTMF, *local_key)) {
833                         ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
834                         return 0;
835                 }
836                 local_key++;
837         }
838         return 1;
839 }
840
841 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
842 {
843         struct ast_variable *var;
844         struct ast_vm_user *retval;
845
846         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
847                 if (!ivm)
848                         ast_set_flag(retval, VM_ALLOCED);       
849                 else
850                         memset(retval, 0, sizeof(*retval));
851                 if (mailbox) 
852                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
853                 populate_defaults(retval);
854                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
855                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
856                 else
857                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
858                 if (var) {
859                         apply_options_full(retval, var);
860                         ast_variables_destroy(var);
861                 } else { 
862                         if (!ivm) 
863                                 ast_free(retval);
864                         retval = NULL;
865                 }       
866         } 
867         return retval;
868 }
869
870 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
871 {
872         /* This function could be made to generate one from a database, too */
873         struct ast_vm_user *vmu=NULL, *cur;
874         AST_LIST_LOCK(&users);
875
876         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
877                 context = "default";
878
879         AST_LIST_TRAVERSE(&users, cur, list) {
880                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
881                         break;
882                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
883                         break;
884         }
885         if (cur) {
886                 /* Make a copy, so that on a reload, we have no race */
887                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
888                         memcpy(vmu, cur, sizeof(*vmu));
889                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
890                         AST_LIST_NEXT(vmu, list) = NULL;
891                 }
892         } else
893                 vmu = find_user_realtime(ivm, context, mailbox);
894         AST_LIST_UNLOCK(&users);
895         return vmu;
896 }
897
898 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
899 {
900         /* This function could be made to generate one from a database, too */
901         struct ast_vm_user *cur;
902         int res = -1;
903         AST_LIST_LOCK(&users);
904         AST_LIST_TRAVERSE(&users, cur, list) {
905                 if ((!context || !strcasecmp(context, cur->context)) &&
906                         (!strcasecmp(mailbox, cur->mailbox)))
907                                 break;
908         }
909         if (cur) {
910                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
911                 res = 0;
912         }
913         AST_LIST_UNLOCK(&users);
914         return res;
915 }
916
917 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
918 {
919         struct ast_config   *cfg=NULL;
920         struct ast_variable *var=NULL;
921         struct ast_category *cat=NULL;
922         char *category=NULL, *value=NULL, *new=NULL;
923         const char *tmp=NULL;
924         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
925                                         
926         if (!change_password_realtime(vmu, newpassword))
927                 return;
928
929         /* check voicemail.conf */
930         if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
931                 while ((category = ast_category_browse(cfg, category))) {
932                         if (!strcasecmp(category, vmu->context)) {
933                                 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
934                                         ast_log(LOG_WARNING, "We could not find the mailbox.\n");
935                                         break;
936                                 }
937                                 value = strstr(tmp,",");
938                                 if (!value) {
939                                         ast_log(LOG_WARNING, "variable has bad format.\n");
940                                         break;
941                                 }
942                                 new = alloca((strlen(value)+strlen(newpassword)+1));
943                                 sprintf(new,"%s%s", newpassword, value);
944                                 if (!(cat = ast_category_get(cfg, category))) {
945                                         ast_log(LOG_WARNING, "Failed to get category structure.\n");
946                                         break;
947                                 }
948                                 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
949                         }
950                 }
951                 /* save the results */
952                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
953                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
954                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
955         }
956         category = NULL;
957         var = NULL;
958         /* check users.conf and update the password stored for the mailbox*/
959         /* if no vmsecret entry exists create one. */
960         if ((cfg = ast_config_load("users.conf", config_flags))) {
961                 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
962                 while ((category = ast_category_browse(cfg, category))) {
963                         ast_debug(4, "users.conf: %s\n", category);
964                         if (!strcasecmp(category, vmu->mailbox)) {
965                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
966                                         ast_debug(3, "looks like we need to make vmsecret!\n");
967                                         var = ast_variable_new("vmsecret", newpassword, "");
968                                 } 
969                                 new = alloca(strlen(newpassword)+1);
970                                 sprintf(new, "%s", newpassword);
971                                 if (!(cat = ast_category_get(cfg, category))) {
972                                         ast_debug(4, "failed to get category!\n");
973                                         break;
974                                 }
975                                 if (!var)               
976                                         ast_variable_update(cat, "vmsecret", new, NULL, 0);
977                                 else
978                                         ast_variable_append(cat, var);
979                         }
980                 }
981                 /* save the results and clean things up */
982                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
983                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
984                 config_text_file_save("users.conf", cfg, "AppVoicemail");
985         }
986 }
987
988 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
989 {
990         char buf[255];
991         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
992         if (!ast_safe_system(buf))
993                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
994 }
995
996 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
997 {
998         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
999 }
1000
1001 #ifdef IMAP_STORAGE
1002 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num)
1003 {
1004         int res;
1005         if ((res = ast_mkdir(dir, 01777))) {
1006                 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
1007                 return snprintf(dest, len, "%s/msg%04d", dir, num);
1008         }
1009         return snprintf(dest, len, "%s/msg%04d", dir, num);
1010 }
1011
1012 static void vm_imap_delete(int msgnum, struct vm_state *vms)
1013 {
1014         unsigned long messageNum = 0;
1015         char arg[10];
1016
1017         /* find real message number based on msgnum */
1018         /* this may be an index into vms->msgArray based on the msgnum. */
1019
1020         messageNum = vms->msgArray[msgnum];
1021         if (messageNum == 0) {
1022                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1023                 return;
1024         }
1025         ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1026         /* delete message */
1027         snprintf (arg, sizeof(arg), "%lu",messageNum);
1028         mail_setflag (vms->mailstream,arg,"\\DELETED");
1029 }
1030
1031 #endif
1032 static int make_file(char *dest, int len, char *dir, int num)
1033 {
1034         return snprintf(dest, len, "%s/msg%04d", dir, num);
1035 }
1036
1037 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1038  * \param dest    String. base directory.
1039  * \param len     Length of dest.
1040  * \param context String. Ignored if is null or empty string.
1041  * \param ext     String. Ignored if is null or empty string.
1042  * \param folder  String. Ignored if is null or empty string. 
1043  * \return -1 on failure, 0 on success.
1044  */
1045 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1046 {
1047         mode_t  mode = VOICEMAIL_DIR_MODE;
1048         int res;
1049
1050         make_dir(dest, len, context, ext, folder);
1051         if ((res = ast_mkdir(dest, mode))) {
1052                 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1053                 return -1;
1054         }
1055         return 0;
1056 }
1057
1058 /*! \brief Lock file path
1059     only return failure if ast_lock_path returns 'timeout',
1060    not if the path does not exist or any other reason
1061 */
1062 static int vm_lock_path(const char *path)
1063 {
1064         switch (ast_lock_path(path)) {
1065         case AST_LOCK_TIMEOUT:
1066                 return -1;
1067         default:
1068                 return 0;
1069         }
1070 }
1071
1072
1073 #ifdef ODBC_STORAGE
1074 struct generic_prepare_struct {
1075         char *sql;
1076         int argc;
1077         char **argv;
1078 };
1079
1080 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
1081 {
1082         struct generic_prepare_struct *gps = data;
1083         int res, i;
1084         SQLHSTMT stmt;
1085
1086         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1087         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1088                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1089                 return NULL;
1090         }
1091         res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
1092         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1093                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
1094                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1095                 return NULL;
1096         }
1097         for (i = 0; i < gps->argc; i++)
1098                 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
1099
1100         return stmt;
1101 }
1102
1103 static int retrieve_file(char *dir, int msgnum)
1104 {
1105         int x = 0;
1106         int res;
1107         int fd=-1;
1108         size_t fdlen = 0;
1109         void *fdm = MAP_FAILED;
1110         SQLSMALLINT colcount=0;
1111         SQLHSTMT stmt;
1112         char sql[PATH_MAX];
1113         char fmt[80]="";
1114         char *c;
1115         char coltitle[256];
1116         SQLSMALLINT collen;
1117         SQLSMALLINT datatype;
1118         SQLSMALLINT decimaldigits;
1119         SQLSMALLINT nullable;
1120         SQLULEN colsize;
1121         SQLLEN colsize2;
1122         FILE *f=NULL;
1123         char rowdata[80];
1124         char fn[PATH_MAX];
1125         char full_fn[PATH_MAX];
1126         char msgnums[80];
1127         char *argv[] = { dir, msgnums };
1128         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1129
1130         struct odbc_obj *obj;
1131         obj = ast_odbc_request_obj(odbc_database, 0);
1132         if (obj) {
1133                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1134                 c = strchr(fmt, '|');
1135                 if (c)
1136                         *c = '\0';
1137                 if (!strcasecmp(fmt, "wav49"))
1138                         strcpy(fmt, "WAV");
1139                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1140                 if (msgnum > -1)
1141                         make_file(fn, sizeof(fn), dir, msgnum);
1142                 else
1143                         ast_copy_string(fn, dir, sizeof(fn));
1144                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1145                 
1146                 if (!(f = fopen(full_fn, "w+"))) {
1147                         ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1148                         goto yuck;
1149                 }
1150                 
1151                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1152                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1153                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1154                 if (!stmt) {
1155                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1156                         ast_odbc_release_obj(obj);
1157                         goto yuck;
1158                 }
1159                 res = SQLFetch(stmt);
1160                 if (res == SQL_NO_DATA) {
1161                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1162                         ast_odbc_release_obj(obj);
1163                         goto yuck;
1164                 }
1165                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1166                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1167                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1168                         ast_odbc_release_obj(obj);
1169                         goto yuck;
1170                 }
1171                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1172                 if (fd < 0) {
1173                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1174                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1175                         ast_odbc_release_obj(obj);
1176                         goto yuck;
1177                 }
1178                 res = SQLNumResultCols(stmt, &colcount);
1179                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1180                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1181                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1182                         ast_odbc_release_obj(obj);
1183                         goto yuck;
1184                 }
1185                 if (f) 
1186                         fprintf(f, "[message]\n");
1187                 for (x=0;x<colcount;x++) {
1188                         rowdata[0] = '\0';
1189                         collen = sizeof(coltitle);
1190                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
1191                                                 &datatype, &colsize, &decimaldigits, &nullable);
1192                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1193                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1194                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1195                                 ast_odbc_release_obj(obj);
1196                                 goto yuck;
1197                         }
1198                         if (!strcasecmp(coltitle, "recording")) {
1199                                 off_t offset;
1200                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1201                                 fdlen = colsize2;
1202                                 if (fd > -1) {
1203                                         char tmp[1]="";
1204                                         lseek(fd, fdlen - 1, SEEK_SET);
1205                                         if (write(fd, tmp, 1) != 1) {
1206                                                 close(fd);
1207                                                 fd = -1;
1208                                                 continue;
1209                                         }
1210                                         /* Read out in small chunks */
1211                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1212                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1213                                                         ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1214                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1215                                                         ast_odbc_release_obj(obj);
1216                                                         goto yuck;
1217                                                 } else {
1218                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1219                                                         munmap(fdm, CHUNKSIZE);
1220                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1221                                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1222                                                                 unlink(full_fn);
1223                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1224                                                                 ast_odbc_release_obj(obj);
1225                                                                 goto yuck;
1226                                                         }
1227                                                 }
1228                                         }
1229                                         truncate(full_fn, fdlen);
1230                                 }
1231                         } else {
1232                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1233                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1234                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1235                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1236                                         ast_odbc_release_obj(obj);
1237                                         goto yuck;
1238                                 }
1239                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1240                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1241                         }
1242                 }
1243                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1244                 ast_odbc_release_obj(obj);
1245         } else
1246                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1247 yuck:   
1248         if (f)
1249                 fclose(f);
1250         if (fd > -1)
1251                 close(fd);
1252         return x - 1;
1253 }
1254
1255 static int remove_file(char *dir, int msgnum)
1256 {
1257         char fn[PATH_MAX];
1258         char full_fn[PATH_MAX];
1259         char msgnums[80];
1260         
1261         if (msgnum > -1) {
1262                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1263                 make_file(fn, sizeof(fn), dir, msgnum);
1264         } else
1265                 ast_copy_string(fn, dir, sizeof(fn));
1266         ast_filedelete(fn, NULL);       
1267         if (ast_check_realtime("voicemail_data")) {
1268                 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1269         }
1270         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1271         unlink(full_fn);
1272         return 0;
1273 }
1274
1275 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1276 {
1277         int x = 0;
1278         int res;
1279         SQLHSTMT stmt;
1280         char sql[PATH_MAX];
1281         char rowdata[20];
1282         char *argv[] = { dir };
1283         struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1284
1285         struct odbc_obj *obj;
1286         obj = ast_odbc_request_obj(odbc_database, 0);
1287         if (obj) {
1288                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1289                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1290                 if (!stmt) {
1291                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1292                         ast_odbc_release_obj(obj);
1293                         goto yuck;
1294                 }
1295                 res = SQLFetch(stmt);
1296                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1297                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1298                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1299                         ast_odbc_release_obj(obj);
1300                         goto yuck;
1301                 }
1302                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1303                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1304                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1305                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1306                         ast_odbc_release_obj(obj);
1307                         goto yuck;
1308                 }
1309                 if (sscanf(rowdata, "%d", &x) != 1)
1310                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1311                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1312                 ast_odbc_release_obj(obj);
1313         } else
1314                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1315 yuck:   
1316         return x - 1;
1317 }
1318
1319 static int message_exists(char *dir, int msgnum)
1320 {
1321         int x = 0;
1322         int res;
1323         SQLHSTMT stmt;
1324         char sql[PATH_MAX];
1325         char rowdata[20];
1326         char msgnums[20];
1327         char *argv[] = { dir, msgnums };
1328         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1329
1330         struct odbc_obj *obj;
1331         obj = ast_odbc_request_obj(odbc_database, 0);
1332         if (obj) {
1333                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1334                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1335                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1336                 if (!stmt) {
1337                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1338                         ast_odbc_release_obj(obj);
1339                         goto yuck;
1340                 }
1341                 res = SQLFetch(stmt);
1342                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1343                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1344                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1345                         ast_odbc_release_obj(obj);
1346                         goto yuck;
1347                 }
1348                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1349                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1350                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1351                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1352                         ast_odbc_release_obj(obj);
1353                         goto yuck;
1354                 }
1355                 if (sscanf(rowdata, "%d", &x) != 1)
1356                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1357                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1358                 ast_odbc_release_obj(obj);
1359         } else
1360                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1361 yuck:   
1362         return x;
1363 }
1364
1365 static int count_messages(struct ast_vm_user *vmu, char *dir)
1366 {
1367         return last_message_index(vmu, dir) + 1;
1368 }
1369
1370 static void delete_file(char *sdir, int smsg)
1371 {
1372         SQLHSTMT stmt;
1373         char sql[PATH_MAX];
1374         char msgnums[20];
1375         char *argv[] = { sdir, msgnums };
1376         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1377
1378         struct odbc_obj *obj;
1379         obj = ast_odbc_request_obj(odbc_database, 0);
1380         if (obj) {
1381                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1382                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1383                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1384                 if (!stmt)
1385                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1386                 else
1387                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1388                 ast_odbc_release_obj(obj);
1389         } else
1390                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1391         return; 
1392 }
1393
1394 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1395 {
1396         SQLHSTMT stmt;
1397         char sql[512];
1398         char msgnums[20];
1399         char msgnumd[20];
1400         struct odbc_obj *obj;
1401         char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1402         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1403
1404         delete_file(ddir, dmsg);
1405         obj = ast_odbc_request_obj(odbc_database, 0);
1406         if (obj) {
1407                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1408                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1409                 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);
1410                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1411                 if (!stmt)
1412                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1413                 else
1414                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1415                 ast_odbc_release_obj(obj);
1416         } else
1417                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1418         return; 
1419 }
1420
1421 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1422 {
1423         int x = 0;
1424         int res;
1425         int fd = -1;
1426         void *fdm = MAP_FAILED;
1427         size_t fdlen = -1;
1428         SQLHSTMT stmt;
1429         SQLLEN len;
1430         char sql[PATH_MAX];
1431         char msgnums[20];
1432         char fn[PATH_MAX];
1433         char full_fn[PATH_MAX];
1434         char fmt[80]="";
1435         char *c;
1436         const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1437         const char *category = "";
1438         struct ast_config *cfg=NULL;
1439         struct odbc_obj *obj;
1440         struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
1441
1442         delete_file(dir, msgnum);
1443         obj = ast_odbc_request_obj(odbc_database, 0);
1444         if (obj) {
1445                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1446                 c = strchr(fmt, '|');
1447                 if (c)
1448                         *c = '\0';
1449                 if (!strcasecmp(fmt, "wav49"))
1450                         strcpy(fmt, "WAV");
1451                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1452                 if (msgnum > -1)
1453                         make_file(fn, sizeof(fn), dir, msgnum);
1454                 else
1455                         ast_copy_string(fn, dir, sizeof(fn));
1456                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1457                 cfg = ast_config_load(full_fn, config_flags);
1458                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1459                 fd = open(full_fn, O_RDWR);
1460                 if (fd < 0) {
1461                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1462                         ast_odbc_release_obj(obj);
1463                         goto yuck;
1464                 }
1465                 if (cfg) {
1466                         context = ast_variable_retrieve(cfg, "message", "context");
1467                         if (!context) context = "";
1468                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1469                         if (!macrocontext) macrocontext = "";
1470                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1471                         if (!callerid) callerid = "";
1472                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1473                         if (!origtime) origtime = "";
1474                         duration = ast_variable_retrieve(cfg, "message", "duration");
1475                         if (!duration) duration = "";
1476                         category = ast_variable_retrieve(cfg, "message", "category");
1477                         if (!category) category = "";
1478                 }
1479                 fdlen = lseek(fd, 0, SEEK_END);
1480                 lseek(fd, 0, SEEK_SET);
1481                 printf("Length is %zd\n", fdlen);
1482                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1483                 if (fdm == MAP_FAILED) {
1484                         ast_log(LOG_WARNING, "Memory map failed!\n");
1485                         ast_odbc_release_obj(obj);
1486                         goto yuck;
1487                 } 
1488                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1489                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1490                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1491                         ast_odbc_release_obj(obj);
1492                         goto yuck;
1493                 }
1494                 if (!ast_strlen_zero(category)) 
1495                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1496                 else
1497                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1498                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1499                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1500                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1501                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1502                         ast_odbc_release_obj(obj);
1503                         goto yuck;
1504                 }
1505                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1506                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1507                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1508                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1509                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1510                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1511                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1512                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1513                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1514                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1515                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1516                 if (!ast_strlen_zero(category))
1517                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1518                 res = ast_odbc_smart_execute(obj, stmt);
1519                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1520                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1521                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1522                         ast_odbc_release_obj(obj);
1523                         goto yuck;
1524                 }
1525                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1526                 ast_odbc_release_obj(obj);
1527         } else
1528                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1529 yuck:   
1530         if (cfg)
1531                 ast_config_destroy(cfg);
1532         if (fdm != MAP_FAILED)
1533                 munmap(fdm, fdlen);
1534         if (fd > -1)
1535                 close(fd);
1536         return x;
1537 }
1538
1539 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1540 {
1541         SQLHSTMT stmt;
1542         char sql[PATH_MAX];
1543         char msgnums[20];
1544         char msgnumd[20];
1545         struct odbc_obj *obj;
1546         char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1547         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1548
1549         delete_file(ddir, dmsg);
1550         obj = ast_odbc_request_obj(odbc_database, 0);
1551         if (obj) {
1552                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1553                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1554                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1555                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1556                 if (!stmt)
1557                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1558                 else
1559                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1560                 ast_odbc_release_obj(obj);
1561         } else
1562                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1563         return; 
1564 }
1565
1566 #else
1567 #ifndef IMAP_STORAGE
1568 static int count_messages(struct ast_vm_user *vmu, char *dir)
1569 {
1570         /* Find all .txt files - even if they are not in sequence from 0000 */
1571
1572         int vmcount = 0;
1573         DIR *vmdir = NULL;
1574         struct dirent *vment = NULL;
1575
1576         if (vm_lock_path(dir))
1577                 return ERROR_LOCK_PATH;
1578
1579         if ((vmdir = opendir(dir))) {
1580                 while ((vment = readdir(vmdir))) {
1581                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1582                                 vmcount++;
1583                 }
1584                 closedir(vmdir);
1585         }
1586         ast_unlock_path(dir);
1587         
1588         return vmcount;
1589 }
1590
1591 static void rename_file(char *sfn, char *dfn)
1592 {
1593         char stxt[PATH_MAX];
1594         char dtxt[PATH_MAX];
1595         ast_filerename(sfn,dfn,NULL);
1596         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1597         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1598         if (ast_check_realtime("voicemail_data")) {
1599                 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1600         }
1601         rename(stxt, dtxt);
1602 }
1603
1604 static int copy(char *infile, char *outfile)
1605 {
1606         int ifd;
1607         int ofd;
1608         int res;
1609         int len;
1610         char buf[4096];
1611
1612 #ifdef HARDLINK_WHEN_POSSIBLE
1613         /* Hard link if possible; saves disk space & is faster */
1614         if (link(infile, outfile)) {
1615 #endif
1616                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1617                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1618                         return -1;
1619                 }
1620                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1621                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1622                         close(ifd);
1623                         return -1;
1624                 }
1625                 do {
1626                         len = read(ifd, buf, sizeof(buf));
1627                         if (len < 0) {
1628                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1629                                 close(ifd);
1630                                 close(ofd);
1631                                 unlink(outfile);
1632                         }
1633                         if (len) {
1634                                 res = write(ofd, buf, len);
1635                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1636                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1637                                         close(ifd);
1638                                         close(ofd);
1639                                         unlink(outfile);
1640                                 }
1641                         }
1642                 } while (len);
1643                 close(ifd);
1644                 close(ofd);
1645                 return 0;
1646 #ifdef HARDLINK_WHEN_POSSIBLE
1647         } else {
1648                 /* Hard link succeeded */
1649                 return 0;
1650         }
1651 #endif
1652 }
1653
1654 static void copy_file(char *frompath, char *topath)
1655 {
1656         char frompath2[PATH_MAX], topath2[PATH_MAX];
1657         struct ast_variable *tmp,*var = NULL;
1658         char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
1659         ast_filecopy(frompath, topath, NULL);
1660         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1661         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1662         if (ast_check_realtime("voicemail_data")) {
1663                 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
1664                 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
1665                 for (tmp = var; tmp; tmp = tmp->next) {
1666                         if (!strcasecmp(tmp->name, "origmailbox")) {
1667                                 origmailbox = tmp->value;
1668                         } else if (!strcasecmp(tmp->name, "context")) {
1669                                 context = tmp->value;
1670                         } else if (!strcasecmp(tmp->name, "macrocontext")) {
1671                                 macrocontext = tmp->value;
1672                         } else if (!strcasecmp(tmp->name, "exten")) {
1673                                 exten = tmp->value;
1674                         } else if (!strcasecmp(tmp->name, "priority")) {
1675                                 priority = tmp->value;
1676                         } else if (!strcasecmp(tmp->name, "callerchan")) {
1677                                 callerchan = tmp->value;
1678                         } else if (!strcasecmp(tmp->name, "callerid")) {
1679                                 callerid = tmp->value;
1680                         } else if (!strcasecmp(tmp->name, "origdate")) {
1681                                 origdate = tmp->value;
1682                         } else if (!strcasecmp(tmp->name, "origtime")) {
1683                                 origtime = tmp->value;
1684                         } else if (!strcasecmp(tmp->name, "category")) {
1685                                 category = tmp->value;
1686                         } else if (!strcasecmp(tmp->name, "duration")) {
1687                                 duration = tmp->value;
1688                         }
1689                 }
1690                 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);
1691         }
1692         copy(frompath2, topath2);
1693         ast_variables_destroy(var);
1694 }
1695 /*! \brief
1696  * A negative return value indicates an error.
1697  * \note Should always be called with a lock already set on dir.
1698  */
1699 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1700 {
1701         int x;
1702         unsigned char map[MAXMSGLIMIT] = "";
1703         DIR *msgdir;
1704         struct dirent *msgdirent;
1705         int msgdirint;
1706
1707         /* Reading the entire directory into a file map scales better than
1708          * doing a stat repeatedly on a predicted sequence.  I suspect this
1709          * is partially due to stat(2) internally doing a readdir(2) itself to
1710          * find each file. */
1711         msgdir = opendir(dir);
1712         while ((msgdirent = readdir(msgdir))) {
1713                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1714                         map[msgdirint] = 1;
1715         }
1716         closedir(msgdir);
1717
1718         for (x = 0; x < vmu->maxmsg; x++) {
1719                 if (map[x] == 0)
1720                         break;
1721         }
1722
1723         return x - 1;
1724 }
1725
1726 #endif /*#ifndef IMAP_STORAGE*/
1727 #endif /*#else of #ifdef ODBC_STORAGE*/
1728 #ifndef ODBC_STORAGE
1729 static int vm_delete(char *file)
1730 {
1731         char *txt;
1732         int txtsize = 0;
1733
1734         txtsize = (strlen(file) + 5)*sizeof(char);
1735         txt = alloca(txtsize);
1736         /* Sprintf here would safe because we alloca'd exactly the right length,
1737          * but trying to eliminate all sprintf's anyhow
1738          */
1739         if (ast_check_realtime("voicemail_data")) {
1740                 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
1741         }
1742         snprintf(txt, txtsize, "%s.txt", file);
1743         unlink(txt);
1744         return ast_filedelete(file, NULL);
1745 }
1746 #endif
1747
1748 static int inbuf(struct baseio *bio, FILE *fi)
1749 {
1750         int l;
1751
1752         if (bio->ateof)
1753                 return 0;
1754
1755         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1756                 if (ferror(fi))
1757                         return -1;
1758
1759                 bio->ateof = 1;
1760                 return 0;
1761         }
1762
1763         bio->iolen= l;
1764         bio->iocp= 0;
1765
1766         return 1;
1767 }
1768
1769 static int inchar(struct baseio *bio, FILE *fi)
1770 {
1771         if (bio->iocp>=bio->iolen) {
1772                 if (!inbuf(bio, fi))
1773                         return EOF;
1774         }
1775
1776         return bio->iobuf[bio->iocp++];
1777 }
1778
1779 static int ochar(struct baseio *bio, int c, FILE *so)
1780 {
1781         if (bio->linelength >= BASELINELEN) {
1782                 if (fputs(eol,so) == EOF)
1783                         return -1;
1784
1785                 bio->linelength= 0;
1786         }
1787
1788         if (putc(((unsigned char)c),so) == EOF)
1789                 return -1;
1790
1791         bio->linelength++;
1792
1793         return 1;
1794 }
1795
1796 static int base_encode(char *filename, FILE *so)
1797 {
1798         unsigned char dtable[BASEMAXINLINE];
1799         int i,hiteof= 0;
1800         FILE *fi;
1801         struct baseio bio;
1802
1803         memset(&bio, 0, sizeof(bio));
1804         bio.iocp = BASEMAXINLINE;
1805
1806         if (!(fi = fopen(filename, "rb"))) {
1807                 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
1808                 return -1;
1809         }
1810
1811         for (i= 0; i<9; i++) {
1812                 dtable[i]= 'A'+i;
1813                 dtable[i+9]= 'J'+i;
1814                 dtable[26+i]= 'a'+i;
1815                 dtable[26+i+9]= 'j'+i;
1816         }
1817         for (i= 0; i<8; i++) {
1818                 dtable[i+18]= 'S'+i;
1819                 dtable[26+i+18]= 's'+i;
1820         }
1821         for (i= 0; i<10; i++) {
1822                 dtable[52+i]= '0'+i;
1823         }
1824         dtable[62]= '+';
1825         dtable[63]= '/';
1826
1827         while (!hiteof){
1828                 unsigned char igroup[3], ogroup[4];
1829                 int c,n;
1830
1831                 igroup[0]= igroup[1]= igroup[2]= 0;
1832
1833                 for (n= 0;n<3;n++) {
1834                         if ((c = inchar(&bio, fi)) == EOF) {
1835                                 hiteof= 1;
1836                                 break;
1837                         }
1838
1839                         igroup[n]= (unsigned char)c;
1840                 }
1841
1842                 if (n> 0) {
1843                         ogroup[0]= dtable[igroup[0]>>2];
1844                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1845                         ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1846                         ogroup[3]= dtable[igroup[2]&0x3F];
1847
1848                         if (n<3) {
1849                                 ogroup[3]= '=';
1850
1851                                 if (n<2)
1852                                         ogroup[2]= '=';
1853                         }
1854
1855                         for (i= 0;i<4;i++)
1856                                 ochar(&bio, ogroup[i], so);
1857                 }
1858         }
1859
1860         if (fputs(eol,so) == EOF)
1861                 return 0;
1862
1863         fclose(fi);
1864
1865         return 1;
1866 }
1867
1868 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category)
1869 {
1870         char callerid[256];
1871         /* Prepare variables for substitution in email body and subject */
1872         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1873         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1874         snprintf(passdata, passdatasize, "%d", msgnum);
1875         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1876         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1877         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1878         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1879         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1880         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1881         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1882         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1883 }
1884
1885 static char *quote(const char *from, char *to, size_t len)
1886 {
1887         char *ptr = to;
1888         *ptr++ = '"';
1889         for (; ptr < to + len - 1; from++) {
1890                 if (*from == '"')
1891                         *ptr++ = '\\';
1892                 else if (*from == '\0')
1893                         break;
1894                 *ptr++ = *from;
1895         }
1896         if (ptr < to + len - 1)
1897                 *ptr++ = '"';
1898         *ptr = '\0';
1899         return to;
1900 }
1901
1902 /*! \brief
1903  * fill in *tm for current time according to the proper timezone, if any.
1904  * Return tm so it can be used as a function argument.
1905  */
1906 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
1907 {
1908         const struct vm_zone *z = NULL;
1909         struct timeval t = ast_tvnow();
1910
1911         /* Does this user have a timezone specified? */
1912         if (!ast_strlen_zero(vmu->zonetag)) {
1913                 /* Find the zone in the list */
1914                 AST_LIST_LOCK(&zones);
1915                 AST_LIST_TRAVERSE(&zones, z, list) {
1916                         if (!strcmp(z->name, vmu->zonetag))
1917                                 break;
1918                 }
1919                 AST_LIST_UNLOCK(&zones);
1920         }
1921         ast_localtime(&t, tm, z ? z->timezone : NULL);
1922         return tm;
1923 }
1924
1925 /*! \brief same as mkstemp, but return a FILE * */
1926 static FILE *vm_mkftemp(char *template)
1927 {
1928         FILE *p = NULL;
1929         int pfd = mkstemp(template);
1930         chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1931         if (pfd > -1) {
1932                 p = fdopen(pfd, "w+");
1933                 if (!p) {
1934                         close(pfd);
1935                         pfd = -1;
1936                 }
1937         }
1938         return p;
1939 }
1940
1941 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
1942 {
1943         char date[256];
1944         char host[MAXHOSTNAMELEN] = "";
1945         char who[256];
1946         char bound[256];
1947         char fname[256];
1948         char dur[256];
1949         char tmpcmd[256];
1950         struct ast_tm tm;
1951         char *passdata2;
1952         size_t len_passdata;
1953         char *greeting_attachment;
1954
1955 #ifdef IMAP_STORAGE
1956 #define ENDL "\r\n"
1957 #else
1958 #define ENDL "\n"
1959 #endif
1960
1961         gethostname(host, sizeof(host)-1);
1962
1963         if (strchr(srcemail, '@'))
1964                 ast_copy_string(who, srcemail, sizeof(who));
1965         else 
1966                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1967         
1968         greeting_attachment = strrchr(ast_strdupa(attach), '/');
1969         if (greeting_attachment)
1970                 *greeting_attachment++ = '\0';
1971
1972         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1973         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1974         fprintf(p, "Date: %s" ENDL, date);
1975
1976         /* Set date format for voicemail mail */
1977         ast_strftime(date, sizeof(date), emaildateformat, &tm);
1978
1979         if (!ast_strlen_zero(fromstring)) {
1980                 struct ast_channel *ast;
1981                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1982                         char *passdata;
1983                         int vmlen = strlen(fromstring)*3 + 200;
1984                         if ((passdata = alloca(vmlen))) {
1985                                 memset(passdata, 0, vmlen);
1986                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1987                                 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1988                                 len_passdata = strlen(passdata) * 2 + 3;
1989                                 passdata2 = alloca(len_passdata);
1990                                 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1991                         } else
1992                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1993                         ast_channel_free(ast);
1994                 } else
1995                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1996         } else
1997                 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1998         len_passdata = strlen(vmu->fullname) * 2 + 3;
1999         passdata2 = alloca(len_passdata);
2000         fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
2001         if (!ast_strlen_zero(emailsubject)) {
2002                 struct ast_channel *ast;
2003                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2004                         char *passdata;
2005                         int vmlen = strlen(emailsubject) * 3 + 200;
2006                         if ((passdata = alloca(vmlen))) {
2007                                 memset(passdata, 0, vmlen);
2008                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2009                                 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
2010                                 fprintf(p, "Subject: %s" ENDL, passdata);
2011                         } else
2012                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2013                         ast_channel_free(ast);
2014                 } else
2015                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2016         } else  if (!ast_strlen_zero(emailtitle)) {
2017                 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
2018                 fprintf(p, ENDL) ;
2019         } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
2020                 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2021         else
2022                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2023         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
2024         if(imap) {
2025                 /* additional information needed for IMAP searching */
2026                 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
2027                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
2028                 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
2029                 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
2030                 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
2031                 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
2032                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
2033                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
2034                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
2035                 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
2036                 if (!ast_strlen_zero(category)) 
2037                         fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
2038                 fprintf(p, "X-Asterisk-VM-Message-Type: %s\n", msgnum > -1 ? "Message" : greeting_attachment);
2039                 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
2040                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
2041         }
2042         if (!ast_strlen_zero(cidnum))
2043                 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
2044         if (!ast_strlen_zero(cidname))
2045                 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
2046         fprintf(p, "MIME-Version: 1.0" ENDL);
2047         if (attach_user_voicemail) {
2048                 /* Something unique. */
2049                 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
2050
2051                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
2052                 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
2053                 fprintf(p, "--%s" ENDL, bound);
2054         }
2055         fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
2056         if (emailbody) {
2057                 struct ast_channel *ast;
2058                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2059                         char *passdata;
2060                         int vmlen = strlen(emailbody)*3 + 200;
2061                         if ((passdata = alloca(vmlen))) {
2062                                 memset(passdata, 0, vmlen);
2063                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2064                                 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2065                                 fprintf(p, "%s" ENDL, passdata);
2066                         } else
2067                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2068                         ast_channel_free(ast);
2069                 } else
2070                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2071         } else if (msgnum > -1){
2072                 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
2073
2074                 "in mailbox %s from %s, on %s so you might" ENDL
2075                 "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
2076                 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2077         } else {
2078                 fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
2079                                 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
2080         }
2081         if (attach_user_voicemail) {
2082                 /* Eww. We want formats to tell us their own MIME type */
2083                 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2084                 char tmpdir[256], newtmp[256];
2085                 int tmpfd = -1;
2086         
2087                 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2088                         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2089                         snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2090                         tmpfd = mkstemp(newtmp);
2091                         chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
2092                         ast_debug(3, "newtmp: %s\n", newtmp);
2093                         if (tmpfd > -1) {
2094                                 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2095                                 ast_safe_system(tmpcmd);
2096                                 attach = newtmp;
2097                                 ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2098                         }
2099                 }
2100                 fprintf(p, "--%s" ENDL, bound);
2101                 if (msgnum > -1)
2102                         fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2103                 else
2104                         fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
2105                 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2106                 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2107                 if (msgnum > -1)
2108                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2109                 else
2110                         fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
2111                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2112                 base_encode(fname, p);
2113                 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
2114                 if (tmpfd > -1) {
2115                         unlink(fname);
2116                         close(tmpfd);
2117                         unlink(newtmp);
2118                 }
2119         }
2120 #undef ENDL
2121 }
2122
2123 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
2124 {
2125         FILE *p=NULL;
2126         char tmp[80] = "/tmp/astmail-XXXXXX";
2127         char tmp2[256];
2128
2129         if (vmu && ast_strlen_zero(vmu->email)) {
2130                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2131                 return(0);
2132         }
2133         if (!strcmp(format, "wav49"))
2134                 format = "WAV";
2135         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));
2136         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2137            command hangs */
2138         if ((p = vm_mkftemp(tmp)) == NULL) {
2139                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2140                 return -1;
2141         } else {
2142                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2143                 fclose(p);
2144                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2145                 ast_safe_system(tmp2);
2146                 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2147         }
2148         return 0;
2149 }
2150
2151 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category)
2152 {
2153         char date[256];
2154         char host[MAXHOSTNAMELEN] = "";
2155         char who[256];
2156         char dur[PATH_MAX];
2157         char tmp[80] = "/tmp/astmail-XXXXXX";
2158         char tmp2[PATH_MAX];
2159         struct ast_tm tm;
2160         FILE *p;
2161
2162         if ((p = vm_mkftemp(tmp)) == NULL) {
2163                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2164                 return -1;
2165         }
2166         gethostname(host, sizeof(host)-1);
2167         if (strchr(srcemail, '@'))
2168                 ast_copy_string(who, srcemail, sizeof(who));
2169         else 
2170                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2171         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2172         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2173         fprintf(p, "Date: %s\n", date);
2174
2175         if (*pagerfromstring) {
2176                 struct ast_channel *ast;
2177                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2178                         char *passdata;
2179                         int vmlen = strlen(fromstring)*3 + 200;
2180                         if ((passdata = alloca(vmlen))) {
2181                                 memset(passdata, 0, vmlen);
2182                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2183                                 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2184                                 fprintf(p, "From: %s <%s>\n", passdata, who);
2185                         } else 
2186                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2187                         ast_channel_free(ast);
2188                 } else 
2189                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2190         } else
2191                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2192         fprintf(p, "To: %s\n", pager);
2193         if (pagersubject) {
2194                 struct ast_channel *ast;
2195                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2196                         char *passdata;
2197                         int vmlen = strlen(pagersubject) * 3 + 200;
2198                         if ((passdata = alloca(vmlen))) {
2199                                 memset(passdata, 0, vmlen);
2200                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2201                                 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2202                                 fprintf(p, "Subject: %s\n\n", passdata);
2203                         } else
2204                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2205                         ast_channel_free(ast);
2206                 } else
2207                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2208         } else
2209                 fprintf(p, "Subject: New VM\n\n");
2210
2211         ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2212         if (pagerbody) {
2213                 struct ast_channel *ast;
2214                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2215                         char *passdata;
2216                         int vmlen = strlen(pagerbody) * 3 + 200;
2217                         passdata = alloca(vmlen);
2218                         memset(passdata, 0, vmlen);
2219                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2220                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2221                         fprintf(p, "%s\n", passdata);
2222                         ast_channel_free(ast);
2223                 } else
2224                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2225         } else {
2226                 fprintf(p, "New %s long msg in box %s\n"
2227                                 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2228         }
2229         fclose(p);
2230         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2231         ast_safe_system(tmp2);
2232         ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2233         return 0;
2234 }
2235
2236 static int get_date(char *s, int len)
2237 {
2238         struct ast_tm tm;
2239         struct timeval t = ast_tvnow();
2240         
2241         ast_localtime(&t, &tm, NULL);
2242
2243         return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2244 }
2245
2246 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2247 {
2248         int res;
2249         char fn[PATH_MAX];
2250         char dest[PATH_MAX];
2251
2252         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2253
2254         if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
2255                 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2256                 return -1;
2257         }
2258
2259         RETRIEVE(fn, -1, ext, context);
2260         if (ast_fileexists(fn, NULL, NULL) > 0) {
2261                 res = ast_stream_and_wait(chan, fn, ecodes);
2262                 if (res) {
2263                         DISPOSE(fn, -1);
2264                         return res;
2265                 }
2266         } else {
2267                 /* Dispose just in case */
2268                 DISPOSE(fn, -1);
2269                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2270                 if (res)
2271                         return res;
2272                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2273                 if (res)
2274                         return res;
2275         }
2276         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2277         return res;
2278 }
2279
2280 static void free_user(struct ast_vm_user *vmu)
2281 {
2282         if (!ast_test_flag(vmu, VM_ALLOCED))
2283                 return;
2284
2285         ast_free(vmu);
2286 }
2287
2288 static void free_zone(struct vm_zone *z)
2289 {
2290         ast_free(z);
2291 }
2292
2293 static const char *mbox(int id)
2294 {
2295         static const char *msgs[] = {
2296                 "INBOX",
2297                 "Old",
2298                 "Work",
2299                 "Family",
2300                 "Friends",
2301                 "Cust1",
2302                 "Cust2",
2303                 "Cust3",
2304                 "Cust4",
2305                 "Cust5",
2306         };
2307         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2308 }
2309 #ifdef IMAP_STORAGE
2310 static int folder_int(const char *folder)
2311 {
2312         /*assume a NULL folder means INBOX*/
2313         if (!folder)
2314                 return 0;
2315         if(!strcasecmp(folder, "INBOX"))
2316                 return 0;
2317         else if (!strcasecmp(folder, "Old"))
2318                 return 1;
2319         else if (!strcasecmp(folder, "Work"))
2320                 return 2;
2321         else if (!strcasecmp(folder, "Family"))
2322                 return 3;
2323         else if (!strcasecmp(folder, "Friends"))
2324                 return 4;
2325         else if (!strcasecmp(folder, "Cust1"))
2326                 return 5;
2327         else if (!strcasecmp(folder, "Cust2"))
2328                 return 6;
2329         else if (!strcasecmp(folder, "Cust3"))
2330                 return 7;
2331         else if (!strcasecmp(folder, "Cust4"))
2332                 return 8;
2333         else if (!strcasecmp(folder, "Cust5"))
2334                 return 9;
2335         else /*assume they meant INBOX if folder is not found otherwise*/
2336                 return 0;
2337 }
2338 #endif
2339
2340 #ifdef ODBC_STORAGE
2341 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2342 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2343 {
2344         int x = -1;
2345         int res;
2346         SQLHSTMT stmt;
2347         char sql[PATH_MAX];
2348         char rowdata[20];
2349         char tmp[PATH_MAX] = "";
2350         struct odbc_obj *obj;
2351         char *context;
2352         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2353
2354         if (newmsgs)
2355                 *newmsgs = 0;
2356         if (oldmsgs)
2357                 *oldmsgs = 0;
2358
2359         /* If no mailbox, return immediately */
2360         if (ast_strlen_zero(mailbox))
2361                 return 0;
2362
2363         ast_copy_string(tmp, mailbox, sizeof(tmp));
2364         
2365         context = strchr(tmp, '@');
2366         if (context) {
2367                 *context = '\0';
2368                 context++;
2369         } else
2370                 context = "default";
2371         
2372         obj = ast_odbc_request_obj(odbc_database, 0);
2373         if (obj) {
2374                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2375                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2376                 if (!stmt) {
2377                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2378                         ast_odbc_release_obj(obj);
2379                         goto yuck;
2380                 }
2381                 res = SQLFetch(stmt);
2382                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2383                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2384                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2385                         ast_odbc_release_obj(obj);
2386                         goto yuck;
2387                 }
2388                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2389                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2390                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2391                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2392                         ast_odbc_release_obj(obj);
2393                         goto yuck;
2394                 }
2395                 *newmsgs = atoi(rowdata);
2396                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2397
2398                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2399                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2400                 if (!stmt) {
2401                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2402                         ast_odbc_release_obj(obj);
2403                         goto yuck;
2404                 }
2405                 res = SQLFetch(stmt);
2406                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2407                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2408                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2409                         ast_odbc_release_obj(obj);
2410                         goto yuck;
2411                 }
2412                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2413                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2414                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2415                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2416                         ast_odbc_release_obj(obj);
2417                         goto yuck;
2418                 }
2419                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2420                 ast_odbc_release_obj(obj);
2421                 *oldmsgs = atoi(rowdata);
2422                 x = 0;
2423         } else
2424                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2425                 
2426 yuck:   
2427         return x;
2428 }
2429
2430 static int messagecount(const char *context, const char *mailbox, const char *folder)
2431 {
2432         struct odbc_obj *obj = NULL;
2433         int nummsgs = 0;
2434         int res;
2435         SQLHSTMT stmt = NULL;
2436         char sql[PATH_MAX];
2437         char rowdata[20];
2438         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2439         if (!folder)
2440                 folder = "INBOX";
2441         /* If no mailbox, return immediately */
2442         if (ast_strlen_zero(mailbox))
2443                 return 0;
2444
2445         obj = ast_odbc_request_obj(odbc_database, 0);
2446         if (obj) {
2447                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2448                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2449                 if (!stmt) {
2450                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2451                         goto yuck;
2452                 }
2453                 res = SQLFetch(stmt);
2454                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2455                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2456                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2457                         goto yuck;
2458                 }
2459                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2460                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2461                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2462                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2463                         goto yuck;
2464                 }
2465                 nummsgs = atoi(rowdata);
2466                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2467         } else
2468                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2469
2470 yuck:
2471         if (obj)
2472                 ast_odbc_release_obj(obj);
2473         return nummsgs;
2474 }
2475
2476 static int has_voicemail(const char *mailbox, const char *folder)
2477 {
2478         char tmp[256], *tmp2 = tmp, *mbox, *context;
2479         ast_copy_string(tmp, mailbox, sizeof(tmp));
2480         while ((context = mbox = strsep(&tmp2, ","))) {
2481                 strsep(&context, "@");
2482                 if (ast_strlen_zero(context))
2483                         context = "default";
2484                 if (messagecount(context, mbox, folder))
2485                         return 1;
2486         }
2487         return 0;
2488 }
2489
2490 #elif defined(IMAP_STORAGE)
2491
2492 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)
2493 {
2494         char *myserveremail = serveremail;
2495         char fn[PATH_MAX];
2496         char mailbox[256];
2497         char *stringp;
2498         FILE *p=NULL;
2499         char tmp[80] = "/tmp/astmail-XXXXXX";
2500         long len;
2501         void *buf;
2502         STRING str;
2503         
2504         /* Attach only the first format */
2505         fmt = ast_strdupa(fmt);
2506         stringp = fmt;
2507         strsep(&stringp, "|");
2508
2509         if (!ast_strlen_zero(vmu->serveremail))
2510                 myserveremail = vmu->serveremail;
2511
2512         if (msgnum > -1)
2513                 make_file(fn, sizeof(fn), dir, msgnum);
2514         else
2515                 ast_copy_string (fn, dir, sizeof(fn));
2516         
2517         if (ast_strlen_zero(vmu->email))
2518                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2519
2520         if (!strcmp(fmt, "wav49"))
2521                 fmt = "WAV";
2522         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2523
2524         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2525            command hangs */
2526         if (!(p = vm_mkftemp(tmp))) {
2527                 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2528                 return -1;
2529         }
2530
2531         if (msgnum < 0 && imapgreetings) {
2532                 init_mailstream(vms, GREETINGS_FOLDER);
2533                 imap_delete_old_greeting(fn, vms);
2534         }
2535         
2536         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);
2537         /* read mail file to memory */          
2538         len = ftell(p);
2539         rewind(p);
2540         if (!(buf = ast_malloc(len+1))) {
2541                 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2542                 return -1;
2543         }
2544         fread(buf, len, 1, p);
2545         ((char *)buf)[len] = '\0';
2546         INIT(&str, mail_string, buf, len);
2547         init_mailstream(vms, NEW_FOLDER);
2548         imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
2549         if(!mail_append(vms->mailstream, mailbox, &str))
2550                 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2551         fclose(p);
2552         unlink(tmp);
2553         ast_free(buf);
2554         ast_debug(3, "%s stored\n", fn);
2555         return 0;
2556
2557 }
2558
2559 static int messagecount(const char *context, const char *mailbox, const char *folder)
2560 {
2561         SEARCHPGM *pgm;
2562         SEARCHHEADER *hdr;
2563  
2564         struct ast_vm_user *vmu, vmus;
2565         struct vm_state *vms_p;
2566         int ret = 0;
2567         int fold = folder_int(folder);
2568         
2569         if (ast_strlen_zero(mailbox))
2570                 return 0;
2571
2572         /* We have to get the user before we can open the stream! */
2573         /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2574         vmu = find_user(&vmus, context, mailbox);
2575         if (!vmu) {
2576                 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
2577                 return -1;
2578         } else {
2579                 /* No IMAP account available */
2580                 if (vmu->imapuser[0] == '\0') {
2581                         ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2582                         return -1;
2583                 }
2584         }
2585         
2586         /* No IMAP account available */
2587         if (vmu->imapuser[0] == '\0') {
2588                 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2589                 free_user(vmu);
2590                 return -1;
2591         }
2592
2593         /* check if someone is accessing this box right now... */
2594         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2595         if (!vms_p) {
2596                 vms_p = get_vm_state_by_mailbox(mailbox,1);
2597         }
2598         if (vms_p) {
2599                 ast_debug(3, "Returning before search - user is logged in\n");
2600                 if(fold == 0) {/*INBOX*/
2601                         return vms_p->newmessages;
2602                 }
2603                 if(fold == 1) {/*Old messages*/
2604                         return vms_p->oldmessages;
2605                 }
2606         }
2607
2608         /* add one if not there... */
2609         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2610         if (!vms_p) {
2611                 vms_p = get_vm_state_by_mailbox(mailbox,0);
2612         }
2613
2614         if (!vms_p) {
2615                 ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
2616                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2617                         return -1;
2618                 }
2619                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2620                 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2621                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2622                 ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2623                 vms_p->updated = 1;
2624                 /* set mailbox to INBOX! */
2625                 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
2626                 init_vm_state(vms_p);
2627                 vmstate_insert(vms_p);
2628         }
2629         ret = init_mailstream(vms_p, fold);
2630         if (!vms_p->mailstream) {
2631                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2632                 return -1;
2633         }
2634         if (ret == 0) {
2635                 pgm = mail_newsearchpgm ();
2636                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
2637                 pgm->header = hdr;
2638                 if (fold != 1) {
2639                         pgm->unseen = 1;
2640                         pgm->seen = 0;
2641                 }
2642                 /* In the special case where fold is 1 (old messages) we have to do things a bit
2643                  * differently. Old messages are stored in the INBOX but are marked as "seen"
2644                  */
2645                 else {
2646                         pgm->unseen = 0;
2647                         pgm->seen = 1;
2648                 }
2649                 pgm->undeleted = 1;
2650                 pgm->deleted = 0;
2651
2652                 vms_p->vmArrayIndex = 0;
2653                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2654                 if(fold == 0)
2655                         vms_p->newmessages = vms_p->vmArrayIndex;
2656                 if(fold == 1)
2657                         vms_p->oldmessages = vms_p->vmArrayIndex;
2658                 /*Freeing the searchpgm also frees the searchhdr*/
2659                 mail_free_searchpgm(&pgm);
2660                 vms_p->updated = 0;
2661                 return vms_p->vmArrayIndex;
2662         } else {  
2663                 mail_ping(vms_p->mailstream);
2664         }
2665         return 0;
2666 }
2667 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
2668 {
2669         char tmp[PATH_MAX] = "";
2670         char *mailboxnc;        
2671         char *context;
2672         char *mb;
2673         char *cur;
2674         if (newmsgs)
2675                 *newmsgs = 0;
2676         if (oldmsgs)
2677                 *oldmsgs = 0;
2678
2679         ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
2680         /* If no mailbox, return immediately */
2681         if (ast_strlen_zero(mailbox_context))
2682                 return 0;
2683         
2684         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2685         context = strchr(tmp, '@');
2686         if (strchr(mailbox_context, ',')) {
2687                 int tmpnew, tmpold;
2688                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2689                 mb = tmp;
2690                 while((cur = strsep(&mb, ", "))) {
2691                         if (!ast_strlen_zero(cur)) {
2692                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2693                                         return -1;
2694                                 else {
2695                                         if (newmsgs)
2696                                                 *newmsgs += tmpnew; 
2697                                         if (oldmsgs)
2698                                                 *oldmsgs += tmpold;
2699                                 }
2700                         }
2701                 }
2702                 return 0;
2703         }
2704         if (context) {
2705                 *context = '\0';
2706                 mailboxnc = tmp;
2707                 context++;
2708         } else {
2709                 context = "default";
2710                 mailboxnc = (char *)mailbox_context;
2711         }
2712         if (newmsgs) {
2713                 if((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
2714                         return -1;
2715         }
2716         if (oldmsgs) {
2717                 if((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
2718                         return -1;
2719         }
2720         return 0;
2721 }
2722         
2723
2724 static int has_voicemail(const char *mailbox, const char *folder)
2725 {
2726         char tmp[256], *tmp2, *mbox, *context;
2727         ast_copy_string(tmp, mailbox, sizeof(tmp));
2728         tmp2 = tmp;
2729         if(strchr(tmp2, ',')) {
2730                 while((mbox = strsep(&tmp2, ","))) {
2731                         if(!ast_strlen_zero(mbox)) {
2732                                 if (has_voicemail(mbox, folder))
2733                                         return 1;
2734                         }
2735                 }
2736         }
2737         if ((context= strchr(tmp, '@')))
2738                 *context++ = '\0';
2739         else
2740                 context = "default";
2741         return messagecount(context, tmp, folder) ? 1 : 0;
2742 }
2743
2744 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
2745 {
2746         char dest[256];
2747         struct vm_state *sendvms = NULL, *destvms = NULL;
2748         char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2749         if(!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 2)))
2750         {
2751                 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2752                 return -1;
2753         }
2754         if(!(destvms = get_vm_state_by_imapuser(recip->imapuser, 2)))
2755         {
2756                 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2757                 return -1;
2758         }
2759         imap_mailbox_name(dest, sizeof(dest), destvms, imbox, 1);
2760         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2761         if((mail_copy(sendvms->mailstream, messagestring, dest) == T))
2762                 return 0;
2763         ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2764         return -1;
2765 }
2766
2767 #endif
2768 #ifndef IMAP_STORAGE
2769 /* copy message only used by file storage */
2770 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
2771 {
2772         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2773         const char *frombox = mbox(imbox);
2774         int recipmsgnum;
2775
2776         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2777
2778         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2779         
2780         if (!dir)
2781                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2782         else
2783                 ast_copy_string(fromdir, dir, sizeof(fromdir));
2784
2785         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2786         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2787
2788         if (vm_lock_path(todir))
2789                 return ERROR_LOCK_PATH;
2790
2791         recipmsgnum = last_message_index(recip, todir) + 1;
2792         if (recipmsgnum < recip->maxmsg) {
2793                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2794                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2795         } else {
2796                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2797         }
2798         ast_unlock_path(todir);
2799         notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2800         
2801         return 0;
2802 }
2803 #endif
2804 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2805 static int messagecount(const char *context, const char *mailbox, const char *folder)
2806 {
2807         return __has_voicemail(context, mailbox, folder, 0);
2808 }
2809
2810
2811 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2812 {
2813         DIR *dir;
2814         struct dirent *de;
2815         char fn[256];
2816         int ret = 0;
2817
2818         /* If no mailbox, return immediately */
2819         if (ast_strlen_zero(mailbox))
2820                 return 0;
2821
2822         if (ast_strlen_zero(folder))
2823                 folder = "INBOX";
2824         if (ast_strlen_zero(context))
2825                 context = "default";
2826
2827         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2828
2829         if (!(dir = opendir(fn)))
2830                 return 0;
2831
2832         while ((de = readdir(dir))) {
2833                 if (!strncasecmp(de->d_name, "msg", 3)) {
2834                         if (shortcircuit) {
2835                                 ret = 1;
2836                                 break;
2837                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2838                                 ret++;
2839                 }
2840         }
2841
2842         closedir(dir);
2843
2844         return ret;
2845 }
2846
2847
2848 static int has_voicemail(const char *mailbox, const char *folder)
2849 {
2850         char tmp[256], *tmp2 = tmp, *mbox, *context;
2851         ast_copy_string(tmp, mailbox, sizeof(tmp));
2852         while ((mbox = strsep(&tmp2, ","))) {
2853                 if ((context = strchr(mbox, '@')))
2854                         *context++ = '\0';
2855                 else
2856                         context = "default";
2857                 if (__has_voicemail(context, mbox, folder, 1))
2858                         return 1;
2859         }
2860         return 0;
2861 }
2862
2863
2864 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2865 {
2866         char tmp[256];
2867         char *context;
2868
2869         /* If no mailbox, return immediately */
2870         if (ast_strlen_zero(mailbox))
2871                 return 0;
2872
2873         if (newmsgs)
2874                 *newmsgs = 0;
2875         if (oldmsgs)
2876                 *oldmsgs = 0;
2877
2878         if (strchr(mailbox, ',')) {
2879                 int tmpnew, tmpold;
2880                 char *mb, *cur;
2881
2882                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2883                 mb = tmp;
2884                 while ((cur = strsep(&mb, ", "))) {
2885                         if (!ast_strlen_zero(cur)) {
2886                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2887                                         return -1;
2888                                 else {
2889                                         if (newmsgs)
2890                                                 *newmsgs += tmpnew; 
2891                                         if (oldmsgs)
2892                                                 *oldmsgs += tmpold;
2893                                 }
2894                         }
2895                 }
2896                 return 0;
2897         }
2898
2899         ast_copy_string(tmp, mailbox, sizeof(tmp));
2900         
2901         if ((context = strchr(tmp, '@')))
2902                 *context++ = '\0';
2903         else
2904                 context = "default";
2905
2906         if (newmsgs)
2907                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2908         if (oldmsgs)
2909                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2910
2911         return 0;
2912 }
2913
2914 #endif
2915
2916 static void run_externnotify(char *context, char *extension)
2917 {
2918         char arguments[255];
2919         char ext_context[256] = "";
2920         int newvoicemails = 0, oldvoicemails = 0;
2921         struct ast_smdi_mwi_message *mwi_msg;
2922
2923         if (!ast_strlen_zero(context))
2924                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2925         else
2926                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2927
2928         if (smdi_iface) {
2929                 if (ast_app_has_voicemail(ext_context, NULL)) 
2930                         ast_smdi_mwi_set(smdi_iface, extension);
2931                 else
2932                         ast_smdi_mwi_unset(smdi_iface, extension);
2933
2934                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2935                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2936                         if (!strncmp(mwi_msg->cause, "INV", 3))
2937                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2938                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2939                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2940                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2941                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2942                 } else {
2943                         ast_debug(1, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2944                 }
2945         }
2946
2947         if (!ast_strlen_zero(externnotify)) {
2948                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2949                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2950                 } else {
2951                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2952                         ast_debug(1, "Executing %s\n", arguments);
2953                         ast_safe_system(arguments);
2954                 }
2955         }
2956 }
2957
2958 struct leave_vm_options {
2959         unsigned int flags;
2960         signed char record_gain;
2961 };
2962
2963 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2964 {
2965 #ifdef IMAP_STORAGE
2966         int newmsgs, oldmsgs;
2967         struct vm_state *vms = NULL;
2968 #endif
2969         char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2970         char callerid[256];
2971         FILE *txt;
2972         char date[256];
2973         int txtdes;
2974         int res = 0;
2975         int msgnum;
2976         int duration = 0;
2977         int ausemacro = 0;
2978         int ousemacro = 0;
2979         int ouseexten = 0;
2980         int rtmsgid = 0;
2981         char tmpid[16];
2982         char tmpdur[16];
2983         char priority[16];
2984         char origtime[16];
2985         char dir[PATH_MAX], tmpdir[PATH_MAX];
2986         char fn[PATH_MAX];
2987         char prefile[PATH_MAX] = "";
2988         char tempfile[PATH_MAX] = "";
2989         char ext_context[256] = "";
2990         char fmt[80];
2991         char *context;
2992         char ecodes[16] = "#";
2993         char tmp[1024] = "", *tmpptr;
2994         struct ast_vm_user *vmu;
2995         struct ast_vm_user svm;
2996         const char *category = NULL;
2997
2998         ast_copy_string(tmp, ext, sizeof(tmp));
2999         ext = tmp;
3000         if ((context = strchr(tmp, '@'))) {
3001                 *context++ = '\0';
3002                 tmpptr = strchr(context, '&');
3003         } else {
3004                 tmpptr = strchr(ext, '&');
3005         }
3006
3007         if (tmpptr)
3008                 *tmpptr++ = '\0';
3009
3010         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3011
3012         ast_debug(3, "Before find_user\n");
3013         if (!(vmu = find_user(&svm, context, ext))) {
3014                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3015                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3016                 return res;
3017         }
3018         /* Setup pre-file if appropriate */
3019         if (strcmp(vmu->context, "default"))
3020                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3021         else
3022                 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
3023         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3024                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3025         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3026                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3027         }
3028         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3029         if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
3030                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3031                 return -1;
3032         }
3033         RETRIEVE(tempfile, -1, ext, context);
3034         if (ast_fileexists(tempfile, NULL, NULL) > 0)
3035                 ast_copy_string(prefile, tempfile, sizeof(prefile));
3036         DISPOSE(tempfile, -1);
3037         /* It's easier just to try to make it than to check for its existence */
3038         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3039
3040         /* Check current or macro-calling context for special extensions */
3041         if (ast_test_flag(vmu, VM_OPERATOR)) {
3042                 if (!ast_strlen_zero(vmu->exit)) {
3043                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3044                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3045                                 ouseexten = 1;
3046                         }
3047                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3048                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3049                         ouseexten = 1;
3050                 }
3051                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3052                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3053                 ousemacro = 1;
3054                 }
3055         }
3056
3057         if (!ast_strlen_zero(vmu->exit)) {
3058                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3059                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3060         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3061                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3062         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3063                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3064                 ausemacro = 1;
3065         }
3066
3067         /* Play the beginning intro if desired */
3068         if (!ast_strlen_zero(prefile)) {
3069 #ifdef ODBC_STORAGE
3070                 int success = 
3071 #endif
3072                         RETRIEVE(prefile, -1, ext, context);
3073                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3074                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
3075                                 res = ast_waitstream(chan, ecodes);
3076 #ifdef ODBC_STORAGE
3077                         if (success == -1) {
3078                                 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
3079                                 ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
3080                                 store_file(prefile, vmu->mailbox, vmu->context, -1);
3081                         }
3082 #endif
3083                 } else {
3084                         ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
3085                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3086                 }
3087                 DISPOSE(prefile, -1);
3088                 if (res < 0) {
3089                         ast_debug(1, "Hang up during prefile playback\n");
3090                         free_user(vmu);
3091                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3092                         return -1;
3093                 }
3094         }
3095         if (res == '#') {
3096