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