Fudges for wav16, just like wav49
[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_or_iodbc</depend>
51                 <depend>ltdl</depend>
52                 <use>unixodbc</use>
53                 <use>iodbc</use>
54                 <conflict>IMAP_STORAGE</conflict>
55                 <defaultenabled>no</defaultenabled>
56         </member>
57         <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
58                 <depend>imap_tk</depend>
59                 <conflict>ODBC_STORAGE</conflict>
60                 <use>ssl</use>
61                 <defaultenabled>no</defaultenabled>
62         </member>
63 </category>
64  ***/
65
66 /* It is important to include the IMAP_STORAGE related headers
67  * before asterisk.h since asterisk.h includes logger.h. logger.h
68  * and c-client.h have conflicting definitions for AST_LOG_WARNING and
69  * AST_LOG_DEBUG, so it's important that we use Asterisk's definitions
70  * here instead of the c-client's 
71  */
72 #ifdef IMAP_STORAGE
73 #include <ctype.h>
74 #include <signal.h>
75 #include <pwd.h>
76 #ifdef USE_SYSTEM_IMAP
77 #include <imap/c-client.h>
78 #include <imap/imap4r1.h>
79 #include <imap/linkage.h>
80 #elif defined (USE_SYSTEM_CCLIENT)
81 #include <c-client/c-client.h>
82 #include <c-client/imap4r1.h>
83 #include <c-client/linkage.h>
84 #else
85 #include "c-client.h"
86 #include "imap4r1.h"
87 #include "linkage.h"
88 #endif
89 #endif
90
91 #include "asterisk.h"
92
93 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
94
95 #include "asterisk/paths.h"     /* use ast_config_AST_SPOOL_DIR */
96 #include <sys/time.h>
97 #include <sys/stat.h>
98 #include <sys/mman.h>
99 #include <time.h>
100 #include <dirent.h>
101
102 #include "asterisk/lock.h"
103 #include "asterisk/file.h"
104 #include "asterisk/channel.h"
105 #include "asterisk/pbx.h"
106 #include "asterisk/config.h"
107 #include "asterisk/say.h"
108 #include "asterisk/module.h"
109 #include "asterisk/adsi.h"
110 #include "asterisk/app.h"
111 #include "asterisk/manager.h"
112 #include "asterisk/dsp.h"
113 #include "asterisk/localtime.h"
114 #include "asterisk/cli.h"
115 #include "asterisk/utils.h"
116 #include "asterisk/stringfields.h"
117 #include "asterisk/smdi.h"
118 #include "asterisk/event.h"
119 #include "asterisk/taskprocessor.h"
120
121 #ifdef ODBC_STORAGE
122 #include "asterisk/res_odbc.h"
123 #endif
124
125 #ifdef IMAP_STORAGE
126 static char imapserver[48];
127 static char imapport[8];
128 static char imapflags[128];
129 static char imapfolder[64];
130 static char imapparentfolder[64] = "\0";
131 static char greetingfolder[64];
132 static char authuser[32];
133 static char authpassword[42];
134
135 static int expungeonhangup = 1;
136 static int imapgreetings = 0;
137 static char delimiter = '\0';
138
139 struct vm_state;
140 struct ast_vm_user;
141
142 /* Forward declarations for IMAP */
143 static int init_mailstream(struct vm_state *vms, int box);
144 static void write_file(char *filename, char *buffer, unsigned long len);
145 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
146 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu);
147 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
148 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
149 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
150 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
151 static void vmstate_insert(struct vm_state *vms);
152 static void vmstate_delete(struct vm_state *vms);
153 static void set_update(MAILSTREAM * stream);
154 static void init_vm_state(struct vm_state *vms);
155 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
156 static void get_mailbox_delimiter(MAILSTREAM *stream);
157 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
158 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
159 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, const char *flag);
160 static void update_messages_by_imapuser(const char *user, unsigned long number);
161
162 static int imap_remove_file (char *dir, int msgnum);
163 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
164 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
165 static void check_quota(struct vm_state *vms, char *mailbox);
166 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
167 struct vmstate {
168         struct vm_state *vms;
169         AST_LIST_ENTRY(vmstate) list;
170 };
171
172 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
173
174 #endif
175
176 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
177
178 #define COMMAND_TIMEOUT 5000
179 /* Don't modify these here; set your umask at runtime instead */
180 #define VOICEMAIL_DIR_MODE      0777
181 #define VOICEMAIL_FILE_MODE     0666
182 #define CHUNKSIZE       65536
183
184 #define VOICEMAIL_CONFIG "voicemail.conf"
185 #define ASTERISK_USERNAME "asterisk"
186
187 /* Define fast-forward, pause, restart, and reverse keys
188    while listening to a voicemail message - these are
189    strings, not characters */
190 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
191 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
192 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
193 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
194 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
195 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
196
197 /* Default mail command to mail voicemail. Change it with the
198     mailcmd= command in voicemail.conf */
199 #define SENDMAIL "/usr/sbin/sendmail -t"
200
201 #define INTRO "vm-intro"
202
203 #define MAXMSG 100
204 #define MAXMSGLIMIT 9999
205
206 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
207
208 #define BASELINELEN 72
209 #define BASEMAXINLINE 256
210 #define eol "\r\n"
211
212 #define MAX_DATETIME_FORMAT     512
213 #define MAX_NUM_CID_CONTEXTS 10
214
215 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
216 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
217 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
218 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
219 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
220 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
221 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
222 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
223 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
224 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
225 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
226 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
227 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
228 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
229 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
230 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
231 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
232 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
233 #define ERROR_LOCK_PATH  -100
234
235
236 enum {
237         NEW_FOLDER,
238         OLD_FOLDER,
239         WORK_FOLDER,
240         FAMILY_FOLDER,
241         FRIENDS_FOLDER,
242         GREETINGS_FOLDER
243 } vm_box;
244
245 enum {
246         OPT_SILENT =           (1 << 0),
247         OPT_BUSY_GREETING =    (1 << 1),
248         OPT_UNAVAIL_GREETING = (1 << 2),
249         OPT_RECORDGAIN =       (1 << 3),
250         OPT_PREPEND_MAILBOX =  (1 << 4),
251         OPT_AUTOPLAY =         (1 << 6),
252         OPT_DTMFEXIT =         (1 << 7),
253         OPT_MESSAGE_Urgent =   (1 << 8),
254         OPT_MESSAGE_PRIORITY = (1 << 9)
255 } vm_option_flags;
256
257 enum {
258         OPT_ARG_RECORDGAIN = 0,
259         OPT_ARG_PLAYFOLDER = 1,
260         OPT_ARG_DTMFEXIT   = 2,
261         /* This *must* be the last value in this enum! */
262         OPT_ARG_ARRAY_SIZE = 3,
263 } vm_option_args;
264
265 AST_APP_OPTIONS(vm_app_options, {
266         AST_APP_OPTION('s', OPT_SILENT),
267         AST_APP_OPTION('b', OPT_BUSY_GREETING),
268         AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
269         AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
270         AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
271         AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
272         AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
273         AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
274         AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
275 });
276
277 static int load_config(int reload);
278
279 /*! \page vmlang Voicemail Language Syntaxes Supported
280
281         \par Syntaxes supported, not really language codes.
282         \arg \b en    - English
283         \arg \b de    - German
284         \arg \b es    - Spanish
285         \arg \b fr    - French
286         \arg \b it    - Italian
287         \arg \b nl    - Dutch
288         \arg \b pt    - Portuguese
289         \arg \b pt_BR - Portuguese (Brazil)
290         \arg \b gr    - Greek
291         \arg \b no    - Norwegian
292         \arg \b se    - Swedish
293         \arg \b tw    - Chinese (Taiwan)
294         \arg \b ua - Ukrainian
295
296 German requires the following additional soundfile:
297 \arg \b 1F      einE (feminine)
298
299 Spanish requires the following additional soundfile:
300 \arg \b 1M      un (masculine)
301
302 Dutch, Portuguese & Spanish require the following additional soundfiles:
303 \arg \b vm-INBOXs       singular of 'new'
304 \arg \b vm-Olds         singular of 'old/heard/read'
305
306 NB these are plural:
307 \arg \b vm-INBOX        nieuwe (nl)
308 \arg \b vm-Old          oude (nl)
309
310 Polish uses:
311 \arg \b vm-new-a        'new', feminine singular accusative
312 \arg \b vm-new-e        'new', feminine plural accusative
313 \arg \b vm-new-ych      'new', feminine plural genitive
314 \arg \b vm-old-a        'old', feminine singular accusative
315 \arg \b vm-old-e        'old', feminine plural accusative
316 \arg \b vm-old-ych      'old', feminine plural genitive
317 \arg \b digits/1-a      'one', not always same as 'digits/1'
318 \arg \b digits/2-ie     'two', not always same as 'digits/2'
319
320 Swedish uses:
321 \arg \b vm-nytt         singular of 'new'
322 \arg \b vm-nya          plural of 'new'
323 \arg \b vm-gammalt      singular of 'old'
324 \arg \b vm-gamla        plural of 'old'
325 \arg \b digits/ett      'one', not always same as 'digits/1'
326
327 Norwegian uses:
328 \arg \b vm-ny           singular of 'new'
329 \arg \b vm-nye          plural of 'new'
330 \arg \b vm-gammel       singular of 'old'
331 \arg \b vm-gamle        plural of 'old'
332
333 Dutch also uses:
334 \arg \b nl-om           'at'?
335
336 Spanish also uses:
337 \arg \b vm-youhaveno
338
339 Ukrainian requires the following additional soundfile:
340 \arg \b vm-nove         'nove'
341 \arg \b vm-stare        'stare'
342 \arg \b digits/ua/1e    'odne'
343
344 Italian requires the following additional soundfile:
345
346 For vm_intro_it:
347 \arg \b vm-nuovo        new
348 \arg \b vm-nuovi        new plural
349 \arg \b vm-vecchio      old
350 \arg \b vm-vecchi       old plural
351
352 Chinese (Taiwan) requires the following additional soundfile:
353 \arg \b vm-tong         A class-word for call (tong1)
354 \arg \b vm-ri           A class-word for day (ri4)
355 \arg \b vm-you          You (ni3)
356 \arg \b vm-haveno   Have no (mei2 you3)
357 \arg \b vm-have     Have (you3)
358 \arg \b vm-listen   To listen (yao4 ting1)
359
360
361 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
362 spelled among others when you have to change folder. For the above reasons, vm-INBOX
363 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
364
365 */
366
367 struct baseio {
368         int iocp;
369         int iolen;
370         int linelength;
371         int ateof;
372         unsigned char iobuf[BASEMAXINLINE];
373 };
374
375 /*! Structure for linked list of users 
376  * Use ast_vm_user_destroy() to free one of these structures. */
377 struct ast_vm_user {
378         char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
379         char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
380         char password[80];               /*!< Secret pin code, numbers only */
381         char fullname[80];               /*!< Full name, for directory app */
382         char email[80];                  /*!< E-mail address */
383         char pager[80];                  /*!< E-mail address to pager (no attachment) */
384         char serveremail[80];            /*!< From: Mail address */
385         char mailcmd[160];               /*!< Configurable mail command */
386         char language[MAX_LANGUAGE];     /*!< Config: Language setting */
387         char zonetag[80];                /*!< Time zone */
388         char callback[80];
389         char dialout[80];
390         char uniqueid[80];               /*!< Unique integer identifier */
391         char exit[80];
392         char attachfmt[20];              /*!< Attachment format */
393         unsigned int flags;              /*!< VM_ flags */      
394         int saydurationm;
395         int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
396         int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
397         int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
398 #ifdef IMAP_STORAGE
399         char imapuser[80];               /*!< IMAP server login */
400         char imappassword[80];           /*!< IMAP server password if authpassword not defined */
401 #endif
402         double volgain;                  /*!< Volume gain for voicemails sent via email */
403         AST_LIST_ENTRY(ast_vm_user) list;
404 };
405
406 /*! Voicemail time zones */
407 struct vm_zone {
408         AST_LIST_ENTRY(vm_zone) list;
409         char name[80];
410         char timezone[80];
411         char msg_format[512];
412 };
413
414 #define VMSTATE_MAX_MSG_ARRAY 256
415
416 /*! Voicemail mailbox state */
417 struct vm_state {
418         char curbox[80];
419         char username[80];
420         char context[80];
421         char curdir[PATH_MAX];
422         char vmbox[PATH_MAX];
423         char fn[PATH_MAX];
424         char intro[PATH_MAX];
425         int *deleted;
426         int *heard;
427         int curmsg;
428         int lastmsg;
429         int newmessages;
430         int oldmessages;
431         int urgentmessages;
432         int starting;
433         int repeats;
434 #ifdef IMAP_STORAGE
435         ast_mutex_t lock;
436         int updated;                         /*!< decremented on each mail check until 1 -allows delay */
437         long msgArray[VMSTATE_MAX_MSG_ARRAY];
438         MAILSTREAM *mailstream;
439         int vmArrayIndex;
440         char imapuser[80];                   /*!< IMAP server login */
441         int interactive;
442         char introfn[PATH_MAX];              /*!< Name of prepended file */
443         unsigned int quota_limit;
444         unsigned int quota_usage;
445         struct vm_state *persist_vms;
446 #endif
447 };
448
449 #ifdef ODBC_STORAGE
450 static char odbc_database[80];
451 static char odbc_table[80];
452 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
453 #define DISPOSE(a,b) remove_file(a,b)
454 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
455 #define EXISTS(a,b,c,d) (message_exists(a,b))
456 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
457 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
458 #define DELETE(a,b,c,d) (delete_file(a,b))
459 #else
460 #ifdef IMAP_STORAGE
461 #define DISPOSE(a,b) (imap_remove_file(a,b))
462 #define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
463 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
464 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
465 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
466 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
467 #define DELETE(a,b,c,d) (vm_imap_delete(b,d))
468 #else
469 #define RETRIEVE(a,b,c,d)
470 #define DISPOSE(a,b)
471 #define STORE(a,b,c,d,e,f,g,h,i,j)
472 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
473 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
474 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
475 #define DELETE(a,b,c,d) (vm_delete(c))
476 #endif
477 #endif
478
479 static char VM_SPOOL_DIR[PATH_MAX];
480
481 static char ext_pass_cmd[128];
482 static char ext_pass_check_cmd[128];
483
484 int my_umask;
485
486 #define PWDCHANGE_INTERNAL (1 << 1)
487 #define PWDCHANGE_EXTERNAL (1 << 2)
488 static int pwdchange = PWDCHANGE_INTERNAL;
489
490 #ifdef ODBC_STORAGE
491 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
492 #else
493 # ifdef IMAP_STORAGE
494 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
495 # else
496 # define tdesc "Comedian Mail (Voicemail System)"
497 # endif
498 #endif
499
500 static char userscontext[AST_MAX_EXTENSION] = "default";
501
502 static char *addesc = "Comedian Mail";
503
504 static char *synopsis_vm = "Leave a Voicemail message";
505
506 static char *descrip_vm =
507         "  VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
508         "application allows the calling party to leave a message for the specified\n"
509         "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
510         "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
511         "specified mailbox does not exist.\n"
512         "  The Voicemail application will exit if any of the following DTMF digits are\n"
513         "received:\n"
514         "    0 - Jump to the 'o' extension in the current dialplan context.\n"
515         "    * - Jump to the 'a' extension in the current dialplan context.\n"
516         "  This application will set the following channel variable upon completion:\n"
517         "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
518         "               application. The possible values are:\n"
519         "               SUCCESS | USEREXIT | FAILED\n\n"
520         "  Options:\n"
521         "    b    - Play the 'busy' greeting to the calling party.\n"
522         "    d([c]) - Accept digits for a new extension in context c, if played during\n"
523         "             the greeting.  Context defaults to the current context.\n"
524         "    g(#) - Use the specified amount of gain when recording the voicemail\n"
525         "           message. The units are whole-number decibels (dB).\n"
526         "           Only works on supported technologies, which is DAHDI only.\n"
527         "    s    - Skip the playback of instructions for leaving a message to the\n"
528         "           calling party.\n"
529         "    u    - Play the 'unavailable' greeting.\n"
530         "    U    - Mark message as Urgent.\n"
531         "    P    - Mark message as PRIORITY.\n";
532
533 static char *synopsis_vmain = "Check Voicemail messages";
534
535 static char *descrip_vmain =
536         "  VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
537         "calling party to check voicemail messages. A specific mailbox, and optional\n"
538         "corresponding context, may be specified. If a mailbox is not provided, the\n"
539         "calling party will be prompted to enter one. If a context is not specified,\n"
540         "the 'default' context will be used.\n\n"
541         "  Options:\n"
542         "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
543         "           is entered by the caller.\n"
544         "    g(#) - Use the specified amount of gain when recording a voicemail\n"
545         "           message. The units are whole-number decibels (dB).\n"
546         "    s    - Skip checking the passcode for the mailbox.\n"
547         "    a(#) - Skip folder prompt and go directly to folder specified.\n"
548         "           Defaults to INBOX\n";
549
550 static char *synopsis_vm_box_exists =
551 "Check to see if Voicemail mailbox exists";
552
553 static char *descrip_vm_box_exists =
554         "  MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
555         "mailbox exists. If no voicemail context is specified, the 'default' context\n"
556         "will be used.\n"
557         "  This application will set the following channel variable upon completion:\n"
558         "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
559         "                        MailboxExists application. Possible values include:\n"
560         "                        SUCCESS | FAILED\n\n"
561         "  Options: (none)\n";
562
563 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
564
565 static char *descrip_vmauthenticate =
566         "  VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
567         "same way as the Authenticate application, but the passwords are taken from\n"
568         "voicemail.conf.\n"
569         "  If the mailbox is specified, only that mailbox's password will be considered\n"
570         "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
571         "be set with the authenticated mailbox.\n\n"
572         "  Options:\n"
573         "    s - Skip playing the initial prompts.\n";
574
575 /* Leave a message */
576 static char *app = "VoiceMail";
577
578 /* Check mail, control, etc */
579 static char *app2 = "VoiceMailMain";
580
581 static char *app3 = "MailboxExists";
582 static char *app4 = "VMAuthenticate";
583
584 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
585 static AST_LIST_HEAD_STATIC(zones, vm_zone);
586 static int maxsilence;
587 static int maxmsg;
588 static int maxdeletedmsg;
589 static int silencethreshold = 128;
590 static char serveremail[80];
591 static char mailcmd[160];       /* Configurable mail cmd */
592 static char externnotify[160]; 
593 static struct ast_smdi_interface *smdi_iface = NULL;
594 static char vmfmts[80];
595 static double volgain;
596 static int vmminsecs;
597 static int vmmaxsecs;
598 static int maxgreet;
599 static int skipms;
600 static int maxlogins;
601 static int minpassword;
602
603 /*! Poll mailboxes for changes since there is something external to
604  *  app_voicemail that may change them. */
605 static unsigned int poll_mailboxes;
606
607 /*! Polling frequency */
608 static unsigned int poll_freq;
609 /*! By default, poll every 30 seconds */
610 #define DEFAULT_POLL_FREQ 30
611
612 AST_MUTEX_DEFINE_STATIC(poll_lock);
613 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
614 static pthread_t poll_thread = AST_PTHREADT_NULL;
615 static unsigned char poll_thread_run;
616
617 /*! Subscription to ... MWI event subscriptions */
618 static struct ast_event_sub *mwi_sub_sub;
619 /*! Subscription to ... MWI event un-subscriptions */
620 static struct ast_event_sub *mwi_unsub_sub;
621
622 /*!
623  * \brief An MWI subscription
624  *
625  * This is so we can keep track of which mailboxes are subscribed to.
626  * This way, we know which mailboxes to poll when the pollmailboxes
627  * option is being used.
628  */
629 struct mwi_sub {
630         AST_RWLIST_ENTRY(mwi_sub) entry;
631         int old_urgent;
632         int old_new;
633         int old_old;
634         uint32_t uniqueid;
635         char mailbox[1];
636 };
637
638 struct mwi_sub_task {
639         const char *mailbox;
640         const char *context;
641         uint32_t uniqueid;
642 };
643
644 static struct ast_taskprocessor *mwi_subscription_tps;
645
646 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
647
648 /* custom audio control prompts for voicemail playback */
649 static char listen_control_forward_key[12];
650 static char listen_control_reverse_key[12];
651 static char listen_control_pause_key[12];
652 static char listen_control_restart_key[12];
653 static char listen_control_stop_key[12];
654
655 /* custom password sounds */
656 static char vm_password[80] = "vm-password";
657 static char vm_newpassword[80] = "vm-newpassword";
658 static char vm_passchanged[80] = "vm-passchanged";
659 static char vm_reenterpassword[80] = "vm-reenterpassword";
660 static char vm_mismatch[80] = "vm-mismatch";
661 static char vm_invalid_password[80] = "vm-invalid-password";
662
663 static struct ast_flags globalflags = {0};
664
665 static int saydurationminfo;
666
667 static char dialcontext[AST_MAX_CONTEXT] = "";
668 static char callcontext[AST_MAX_CONTEXT] = "";
669 static char exitcontext[AST_MAX_CONTEXT] = "";
670
671 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
672
673
674 static char *emailbody = NULL;
675 static char *emailsubject = NULL;
676 static char *pagerbody = NULL;
677 static char *pagersubject = NULL;
678 static char fromstring[100];
679 static char pagerfromstring[100];
680 static char charset[32] = "ISO-8859-1";
681
682 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
683 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
684 static int adsiver = 1;
685 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
686
687 /* Forward declarations - generic */
688 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
689 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);
690 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
691 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
692                         char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
693                         signed char record_gain, struct vm_state *vms, char *flag);
694 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
695 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
696 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
697 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 *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag);
698 static void apply_options(struct ast_vm_user *vmu, const char *options);
699 static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
700 static int is_valid_dtmf(const char *key);
701
702 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
703 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
704 #endif
705
706 static char *strip_control(const char *input, char *buf, size_t buflen)
707 {
708         char *bufptr = buf;
709         for (; *input; input++) {
710                 if (*input < 32) {
711                         continue;
712                 }
713                 *bufptr++ = *input;
714                 if (bufptr == buf + buflen - 1) {
715                         break;
716                 }
717         }
718         *bufptr = '\0';
719         return buf;
720 }
721
722
723 /*!
724  * \brief Sets default voicemail system options to a voicemail user.
725  *
726  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
727  * - all the globalflags
728  * - the saydurationminfo
729  * - the callcontext
730  * - the dialcontext
731  * - the exitcontext
732  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
733  * - volume gain.
734  */
735 static void populate_defaults(struct ast_vm_user *vmu)
736 {
737         ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);     
738         if (saydurationminfo)
739                 vmu->saydurationm = saydurationminfo;
740         ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
741         ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
742         ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
743         if (vmmaxsecs)
744                 vmu->maxsecs = vmmaxsecs;
745         if (maxmsg)
746                 vmu->maxmsg = maxmsg;
747         if (maxdeletedmsg)
748                 vmu->maxdeletedmsg = maxdeletedmsg;
749         vmu->volgain = volgain;
750 }
751
752 /*!
753  * \brief Sets a a specific property value.
754  * \param vmu The voicemail user object to work with.
755  * \param var The name of the property to be set.
756  * \param value The value to be set to the property.
757  * 
758  * The property name must be one of the understood properties. See the source for details.
759  */
760 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
761 {
762         int x;
763         if (!strcasecmp(var, "attach")) {
764                 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
765         } else if (!strcasecmp(var, "attachfmt")) {
766                 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
767         } else if (!strcasecmp(var, "serveremail")) {
768                 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
769         } else if (!strcasecmp(var, "language")) {
770                 ast_copy_string(vmu->language, value, sizeof(vmu->language));
771         } else if (!strcasecmp(var, "tz")) {
772                 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
773 #ifdef IMAP_STORAGE
774         } else if (!strcasecmp(var, "imapuser")) {
775                 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
776         } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
777                 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
778 #endif
779         } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
780                 ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
781         } else if (!strcasecmp(var, "saycid")){
782                 ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
783         } else if (!strcasecmp(var,"sendvoicemail")){
784                 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
785         } else if (!strcasecmp(var, "review")){
786                 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
787         } else if (!strcasecmp(var, "tempgreetwarn")){
788                 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);  
789         } else if (!strcasecmp(var, "messagewrap")){
790                 ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);    
791         } else if (!strcasecmp(var, "operator")) {
792                 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
793         } else if (!strcasecmp(var, "envelope")){
794                 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
795         } else if (!strcasecmp(var, "moveheard")){
796                 ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
797         } else if (!strcasecmp(var, "sayduration")){
798                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
799         } else if (!strcasecmp(var, "saydurationm")){
800                 if (sscanf(value, "%d", &x) == 1) {
801                         vmu->saydurationm = x;
802                 } else {
803                         ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
804                 }
805         } else if (!strcasecmp(var, "forcename")){
806                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
807         } else if (!strcasecmp(var, "forcegreetings")){
808                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
809         } else if (!strcasecmp(var, "callback")) {
810                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
811         } else if (!strcasecmp(var, "dialout")) {
812                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
813         } else if (!strcasecmp(var, "exitcontext")) {
814                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
815         } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
816                 if (vmu->maxsecs <= 0) {
817                         ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
818                         vmu->maxsecs = vmmaxsecs;
819                 } else {
820                         vmu->maxsecs = atoi(value);
821                 }
822                 if (!strcasecmp(var, "maxmessage"))
823                         ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
824         } else if (!strcasecmp(var, "maxmsg")) {
825                 vmu->maxmsg = atoi(value);
826                 if (vmu->maxmsg <= 0) {
827                         ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
828                         vmu->maxmsg = MAXMSG;
829                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
830                         ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
831                         vmu->maxmsg = MAXMSGLIMIT;
832                 }
833         } else if (!strcasecmp(var, "backupdeleted")) {
834                 if (sscanf(value, "%d", &x) == 1)
835                         vmu->maxdeletedmsg = x;
836                 else if (ast_true(value))
837                         vmu->maxdeletedmsg = MAXMSG;
838                 else
839                         vmu->maxdeletedmsg = 0;
840
841                 if (vmu->maxdeletedmsg < 0) {
842                         ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
843                         vmu->maxdeletedmsg = MAXMSG;
844                 } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
845                         ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
846                         vmu->maxdeletedmsg = MAXMSGLIMIT;
847                 }
848         } else if (!strcasecmp(var, "volgain")) {
849                 sscanf(value, "%lf", &vmu->volgain);
850         } else if (!strcasecmp(var, "options")) {
851                 apply_options(vmu, value);
852         }
853 }
854
855 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
856 {
857         int fds[2], pid = 0;
858
859         memset(buf, 0, len);
860
861         if (pipe(fds)) {
862                 snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
863         } else {
864                 /* good to go*/
865                 pid = ast_safe_fork(0);
866
867                 if (pid < 0) {
868                         /* ok maybe not */
869                         close(fds[0]);
870                         close(fds[1]);
871                         snprintf(buf, len, "FAILURE: Fork failed");
872                 } else if (pid) {
873                         /* parent */
874                         close(fds[1]);
875                         read(fds[0], buf, len);
876                         close(fds[0]);
877                 } else {
878                         /*  child */
879                         AST_DECLARE_APP_ARGS(arg,
880                                 AST_APP_ARG(v)[20];
881                         );
882                         char *mycmd = ast_strdupa(command);
883
884                         close(fds[0]);
885                         dup2(fds[1], STDOUT_FILENO);
886                         close(fds[1]);
887                         ast_close_fds_above_n(STDOUT_FILENO);
888
889                         AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
890
891                         execv(arg.v[0], arg.v); 
892                         printf("FAILURE: %s", strerror(errno));
893                         _exit(0);
894                 }
895         }
896         return buf;
897 }
898
899 /*!
900  * \brief Check that password meets minimum required length
901  * \param vmu The voicemail user to change the password for.
902  * \param password The password string to check
903  *
904  * \return zero on ok, 1 on not ok.
905  */
906 static int check_password(struct ast_vm_user *vmu, char *password)
907 {
908         /* check minimum length */
909         if (strlen(password) < minpassword)
910                 return 1;
911         if (!ast_strlen_zero(ext_pass_check_cmd)) {
912                 char cmd[255], buf[255];
913
914                 ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
915
916                 snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
917                 if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
918                         ast_debug(5, "Result: %s\n", buf);
919                         if (!strncasecmp(buf, "VALID", 5)) {
920                                 ast_debug(3, "Passed password check: '%s'\n", buf);
921                                 return 0;
922                         } else if (!strncasecmp(buf, "FAILURE", 7)) {
923                                 ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
924                                 return 0;
925                         } else {
926                                 ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
927                                 return 1;
928                         }
929                 }
930         }
931         return 0;
932 }
933
934 /*! 
935  * \brief Performs a change of the voicemail passowrd in the realtime engine.
936  * \param vmu The voicemail user to change the password for.
937  * \param password The new value to be set to the password for this user.
938  * 
939  * This only works if the voicemail user has a unique id, and if there is a realtime engine configured.
940  * This is called from the (top level) vm_change_password.
941  *
942  * \return zero on success, -1 on error.
943  */
944 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
945 {
946         int res;
947         if (!ast_strlen_zero(vmu->uniqueid)) {
948                 if (strlen(password) > 10) {
949                         ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
950                 }
951                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, SENTINEL);
952                 if (res > 0) {
953                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
954                         res = 0;
955                 } else if (!res) {
956                         res = -1;
957                 }
958                 return res;
959         }
960         return -1;
961 }
962
963 /*!
964  * \brief Destructively Parse options and apply.
965  */
966 static void apply_options(struct ast_vm_user *vmu, const char *options)
967 {       
968         char *stringp;
969         char *s;
970         char *var, *value;
971         stringp = ast_strdupa(options);
972         while ((s = strsep(&stringp, "|"))) {
973                 value = s;
974                 if ((var = strsep(&value, "=")) && value) {
975                         apply_option(vmu, var, value);
976                 }
977         }       
978 }
979
980 /*!
981  * \brief Loads the options specific to a voicemail user.
982  * 
983  * This is called when a vm_user structure is being set up, such as from load_options.
984  */
985 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
986 {
987         struct ast_variable *tmp;
988         tmp = var;
989         while (tmp) {
990                 if (!strcasecmp(tmp->name, "vmsecret")) {
991                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
992                 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
993                         if (ast_strlen_zero(retval->password))
994                                 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
995                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
996                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
997                 } else if (!strcasecmp(tmp->name, "pager")) {
998                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
999                 } else if (!strcasecmp(tmp->name, "email")) {
1000                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
1001                 } else if (!strcasecmp(tmp->name, "fullname")) {
1002                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
1003                 } else if (!strcasecmp(tmp->name, "context")) {
1004                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
1005 #ifdef IMAP_STORAGE
1006                 } else if (!strcasecmp(tmp->name, "imapuser")) {
1007                         ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
1008                 } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
1009                         ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
1010 #endif
1011                 } else
1012                         apply_option(retval, tmp->name, tmp->value);
1013                 tmp = tmp->next;
1014         } 
1015 }
1016
1017 /*!
1018  * \brief Determines if a DTMF key entered is valid.
1019  * \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
1020  *
1021  * Tests the character entered against the set of valid DTMF characters. 
1022  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1023  */
1024 static int is_valid_dtmf(const char *key)
1025 {
1026         int i;
1027         char *local_key = ast_strdupa(key);
1028
1029         for (i = 0; i < strlen(key); ++i) {
1030                 if (!strchr(VALID_DTMF, *local_key)) {
1031                         ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1032                         return 0;
1033                 }
1034                 local_key++;
1035         }
1036         return 1;
1037 }
1038
1039 /*!
1040  * \brief Finds a voicemail user from the realtime engine.
1041  * \param ivm
1042  * \param context
1043  * \param mailbox
1044  *
1045  * This is called as a fall through case when the normal find_user() was not able to find a user. That is, the default it so look in the usual voicemail users file first.
1046  *
1047  * \return The ast_vm_user structure for the user that was found.
1048  */
1049 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1050 {
1051         struct ast_variable *var;
1052         struct ast_vm_user *retval;
1053
1054         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1055                 if (!ivm)
1056                         ast_set_flag(retval, VM_ALLOCED);       
1057                 else
1058                         memset(retval, 0, sizeof(*retval));
1059                 if (mailbox) 
1060                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1061                 populate_defaults(retval);
1062                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
1063                         var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1064                 else
1065                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1066                 if (var) {
1067                         apply_options_full(retval, var);
1068                         ast_variables_destroy(var);
1069                 } else { 
1070                         if (!ivm) 
1071                                 ast_free(retval);
1072                         retval = NULL;
1073                 }       
1074         } 
1075         return retval;
1076 }
1077
1078 /*!
1079  * \brief Finds a voicemail user from the users file or the realtime engine.
1080  * \param ivm
1081  * \param context
1082  * \param mailbox
1083  * 
1084  * \return The ast_vm_user structure for the user that was found.
1085  */
1086 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1087 {
1088         /* This function could be made to generate one from a database, too */
1089         struct ast_vm_user *vmu=NULL, *cur;
1090         AST_LIST_LOCK(&users);
1091
1092         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1093                 context = "default";
1094
1095         AST_LIST_TRAVERSE(&users, cur, list) {
1096                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1097                         break;
1098                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1099                         break;
1100         }
1101         if (cur) {
1102                 /* Make a copy, so that on a reload, we have no race */
1103                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
1104                         memcpy(vmu, cur, sizeof(*vmu));
1105                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1106                         AST_LIST_NEXT(vmu, list) = NULL;
1107                 }
1108         } else
1109                 vmu = find_user_realtime(ivm, context, mailbox);
1110         AST_LIST_UNLOCK(&users);
1111         return vmu;
1112 }
1113
1114 /*!
1115  * \brief Resets a user password to a specified password.
1116  * \param context
1117  * \param mailbox
1118  * \param newpass
1119  *
1120  * This does the actual change password work, called by the vm_change_password() function.
1121  *
1122  * \return zero on success, -1 on error.
1123  */
1124 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1125 {
1126         /* This function could be made to generate one from a database, too */
1127         struct ast_vm_user *cur;
1128         int res = -1;
1129         AST_LIST_LOCK(&users);
1130         AST_LIST_TRAVERSE(&users, cur, list) {
1131                 if ((!context || !strcasecmp(context, cur->context)) &&
1132                         (!strcasecmp(mailbox, cur->mailbox)))
1133                                 break;
1134         }
1135         if (cur) {
1136                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
1137                 res = 0;
1138         }
1139         AST_LIST_UNLOCK(&users);
1140         return res;
1141 }
1142
1143 /*! 
1144  * \brief The handler for the change password option.
1145  * \param vmu The voicemail user to work with.
1146  * \param newpassword The new password (that has been gathered from the appropriate prompting).
1147  * This is called when a new user logs in for the first time and the option to force them to change their password is set.
1148  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1149  */
1150 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1151 {
1152         struct ast_config   *cfg=NULL;
1153         struct ast_variable *var=NULL;
1154         struct ast_category *cat=NULL;
1155         char *category=NULL, *value=NULL, *new=NULL;
1156         const char *tmp=NULL;
1157         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1158         if (!change_password_realtime(vmu, newpassword))
1159                 return;
1160
1161         /* check voicemail.conf */
1162         if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1163                 while ((category = ast_category_browse(cfg, category))) {
1164                         if (!strcasecmp(category, vmu->context)) {
1165                                 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1166                                         ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1167                                         break;
1168                                 }
1169                                 value = strstr(tmp,",");
1170                                 if (!value) {
1171                                         ast_log(AST_LOG_WARNING, "variable has bad format.\n");
1172                                         break;
1173                                 }
1174                                 new = alloca((strlen(value)+strlen(newpassword)+1));
1175                                 sprintf(new,"%s%s", newpassword, value);
1176                                 if (!(cat = ast_category_get(cfg, category))) {
1177                                         ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1178                                         break;
1179                                 }
1180                                 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1181                         }
1182                 }
1183                 /* save the results */
1184                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1185                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1186                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
1187         }
1188         category = NULL;
1189         var = NULL;
1190         /* check users.conf and update the password stored for the mailbox*/
1191         /* if no vmsecret entry exists create one. */
1192         if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1193                 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1194                 while ((category = ast_category_browse(cfg, category))) {
1195                         ast_debug(4, "users.conf: %s\n", category);
1196                         if (!strcasecmp(category, vmu->mailbox)) {
1197                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
1198                                         ast_debug(3, "looks like we need to make vmsecret!\n");
1199                                         var = ast_variable_new("vmsecret", newpassword, "");
1200                                 } 
1201                                 new = alloca(strlen(newpassword)+1);
1202                                 sprintf(new, "%s", newpassword);
1203                                 if (!(cat = ast_category_get(cfg, category))) {
1204                                         ast_debug(4, "failed to get category!\n");
1205                                         break;
1206                                 }
1207                                 if (!var)               
1208                                         ast_variable_update(cat, "vmsecret", new, NULL, 0);
1209                                 else
1210                                         ast_variable_append(cat, var);
1211                         }
1212                 }
1213                 /* save the results and clean things up */
1214                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
1215                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1216                 config_text_file_save("users.conf", cfg, "AppVoicemail");
1217         }
1218 }
1219
1220 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1221 {
1222         char buf[255];
1223         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
1224         if (!ast_safe_system(buf)) {
1225                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1226                 /* Reset the password in memory, too */
1227                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1228         }
1229 }
1230
1231 /*! 
1232  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1233  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1234  * \param len The length of the path string that was written out.
1235  * 
1236  * The path is constructed as 
1237  *      VM_SPOOL_DIRcontext/ext/folder
1238  *
1239  * \return zero on success, -1 on error.
1240  */
1241 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1242 {
1243         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1244 }
1245
1246 /*! 
1247  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1248  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1249  * \param len The length of the path string that was written out.
1250  * 
1251  * The path is constructed as 
1252  *      VM_SPOOL_DIRcontext/ext/folder
1253  *
1254  * \return zero on success, -1 on error.
1255  */
1256 static int make_file(char *dest, const int len, const char *dir, const int num)
1257 {
1258         return snprintf(dest, len, "%s/msg%04d", dir, num);
1259 }
1260
1261 /* same as mkstemp, but return a FILE * */
1262 static FILE *vm_mkftemp(char *template)
1263 {
1264         FILE *p = NULL;
1265         int pfd = mkstemp(template);
1266         chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1267         if (pfd > -1) {
1268                 p = fdopen(pfd, "w+");
1269                 if (!p) {
1270                         close(pfd);
1271                         pfd = -1;
1272                 }
1273         }
1274         return p;
1275 }
1276
1277 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1278  * \param dest    String. base directory.
1279  * \param len     Length of dest.
1280  * \param context String. Ignored if is null or empty string.
1281  * \param ext     String. Ignored if is null or empty string.
1282  * \param folder  String. Ignored if is null or empty string. 
1283  * \return -1 on failure, 0 on success.
1284  */
1285 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1286 {
1287         mode_t  mode = VOICEMAIL_DIR_MODE;
1288         int res;
1289
1290         make_dir(dest, len, context, ext, folder);
1291         if ((res = ast_mkdir(dest, mode))) {
1292                 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1293                 return -1;
1294         }
1295         return 0;
1296 }
1297
1298 static const char *mbox(int id)
1299 {
1300         static const char *msgs[] = {
1301 #ifdef IMAP_STORAGE
1302                 imapfolder,
1303 #else
1304                 "INBOX",
1305 #endif
1306                 "Old",
1307                 "Work",
1308                 "Family",
1309                 "Friends",
1310                 "Cust1",
1311                 "Cust2",
1312                 "Cust3",
1313                 "Cust4",
1314                 "Cust5",
1315                 "Deleted",
1316                 "Urgent"
1317         };
1318         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
1319 }
1320
1321 static void free_user(struct ast_vm_user *vmu)
1322 {
1323         if (ast_test_flag(vmu, VM_ALLOCED))
1324                 ast_free(vmu);
1325 }
1326
1327 /* All IMAP-specific functions should go in this block. This
1328  * keeps them from being spread out all over the code */
1329 #ifdef IMAP_STORAGE
1330 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
1331 {
1332         char arg[10];
1333         struct vm_state *vms;
1334         unsigned long messageNum;
1335
1336         /* Greetings aren't stored in IMAP, so we can't delete them there */
1337         if (msgnum < 0) {
1338                 return;
1339         }
1340
1341         if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1342                 ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
1343                 return;
1344         }
1345
1346         /* find real message number based on msgnum */
1347         /* this may be an index into vms->msgArray based on the msgnum. */
1348         messageNum = vms->msgArray[msgnum];
1349         if (messageNum == 0) {
1350                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1351                 return;
1352         }
1353         if (option_debug > 2)
1354                 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1355         /* delete message */
1356         snprintf (arg, sizeof(arg), "%lu",messageNum);
1357         mail_setflag (vms->mailstream,arg,"\\DELETED");
1358 }
1359
1360 static int imap_retrieve_greeting (const char *dir, const int msgnum, struct ast_vm_user *vmu)
1361 {
1362         struct vm_state *vms_p;
1363         char *file, *filename;
1364         char *attachment;
1365         int ret = 0, i;
1366         BODY *body;
1367
1368         /* This function is only used for retrieval of IMAP greetings
1369          * regular messages are not retrieved this way, nor are greetings
1370          * if they are stored locally*/
1371         if (msgnum > -1 || !imapgreetings) {
1372                 return 0;
1373         } else {
1374                 file = strrchr(ast_strdupa(dir), '/');
1375                 if (file)
1376                         *file++ = '\0';
1377                 else {
1378                         ast_debug (1, "Failed to procure file name from directory passed.\n");
1379                         return -1;
1380                 }
1381         }
1382
1383         /* check if someone is accessing this box right now... */
1384         if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) ||!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1385                 ast_log(AST_LOG_ERROR, "Voicemail state not found!\n");
1386                 return -1;
1387         }
1388         
1389         /* Greetings will never have a prepended message */
1390         *vms_p->introfn = '\0';
1391         
1392         ret = init_mailstream(vms_p, GREETINGS_FOLDER);
1393         if (!vms_p->mailstream) {
1394                 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
1395                 return -1;
1396         }
1397
1398         /*XXX Yuck, this could probably be done a lot better */
1399         for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
1400                 mail_fetchstructure(vms_p->mailstream, i + 1, &body);
1401                 /* We have the body, now we extract the file name of the first attachment. */
1402                 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1403                         attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
1404                 } else {
1405                         ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
1406                         return -1;
1407                 }
1408                 filename = strsep(&attachment, ".");
1409                 if (!strcmp(filename, file)) {
1410                         ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
1411                         vms_p->msgArray[vms_p->curmsg] = i + 1;
1412                         save_body(body, vms_p, "2", attachment, 0);
1413                         return 0;
1414                 }
1415         }
1416
1417         return -1;
1418 }
1419
1420 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
1421 {
1422         BODY *body;
1423         char *header_content;
1424         char *attachedfilefmt;
1425         char buf[80];
1426         struct vm_state *vms;
1427         char text_file[PATH_MAX];
1428         FILE *text_file_ptr;
1429         int res = 0;
1430         struct ast_vm_user *vmu;
1431
1432         if (!(vmu = find_user(NULL, context, mailbox))) {
1433                 ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
1434                 return -1;
1435         }
1436         
1437         if (msgnum < 0) {
1438                 if (imapgreetings) {
1439                         res = imap_retrieve_greeting(dir, msgnum, vmu);
1440                         goto exit;
1441                 } else {
1442                         res = 0;
1443                         goto exit;
1444                 }
1445         }
1446
1447         /* Before anything can happen, we need a vm_state so that we can
1448          * actually access the imap server through the vms->mailstream
1449          */
1450         if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1451                 /* This should not happen. If it does, then I guess we'd
1452                  * need to create the vm_state, extract which mailbox to
1453                  * open, and then set up the msgArray so that the correct
1454                  * IMAP message could be accessed. If I have seen correctly
1455                  * though, the vms should be obtainable from the vmstates list
1456                  * and should have its msgArray properly set up.
1457                  */
1458                 ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
1459                 res = -1;
1460                 goto exit;
1461         }
1462         
1463         make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
1464         snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
1465
1466         /* Don't try to retrieve a message from IMAP if it already is on the file system */
1467         if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
1468                 res = 0;
1469                 goto exit;
1470         }
1471
1472         if (option_debug > 2)
1473                 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
1474         if (vms->msgArray[msgnum] == 0) {
1475                 ast_log (LOG_WARNING,"Trying to access unknown message\n");
1476                 res = -1;
1477                 goto exit;
1478         }
1479
1480         /* This will only work for new messages... */
1481         header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
1482         /* empty string means no valid header */
1483         if (ast_strlen_zero(header_content)) {
1484                 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
1485                 res = -1;
1486                 goto exit;
1487         }
1488
1489         mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
1490         
1491         /* We have the body, now we extract the file name of the first attachment. */
1492         if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1493                 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
1494         } else {
1495                 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
1496                 res = -1;
1497                 goto exit;
1498         }
1499         
1500         /* Find the format of the attached file */
1501
1502         strsep(&attachedfilefmt, ".");
1503         if (!attachedfilefmt) {
1504                 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
1505                 res = -1;
1506                 goto exit;
1507         }
1508         
1509         save_body(body, vms, "2", attachedfilefmt, 0);
1510         if (save_body(body, vms, "3", attachedfilefmt, 1)) {
1511                 *vms->introfn = '\0';
1512         }
1513
1514         /* Get info from headers!! */
1515         snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
1516
1517         if (!(text_file_ptr = fopen(text_file, "w"))) {
1518                 ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
1519         }
1520
1521         fprintf(text_file_ptr, "%s\n", "[message]");
1522
1523         get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
1524         fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
1525         get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
1526         fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
1527         get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
1528         fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
1529         get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
1530         fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
1531         get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
1532         fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
1533         get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
1534         fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
1535         fclose(text_file_ptr);
1536
1537 exit:
1538         free_user(vmu);
1539         return res;
1540 }
1541
1542 static int folder_int(const char *folder)
1543 {
1544         /*assume a NULL folder means INBOX*/
1545         if (!folder)
1546                 return 0;
1547 #ifdef IMAP_STORAGE
1548         if (!strcasecmp(folder, imapfolder))
1549 #else
1550         if (!strcasecmp(folder, "INBOX"))
1551 #endif
1552                 return 0;
1553         else if (!strcasecmp(folder, "Old"))
1554                 return 1;
1555         else if (!strcasecmp(folder, "Work"))
1556                 return 2;
1557         else if (!strcasecmp(folder, "Family"))
1558                 return 3;
1559         else if (!strcasecmp(folder, "Friends"))
1560                 return 4;
1561         else if (!strcasecmp(folder, "Cust1"))
1562                 return 5;
1563         else if (!strcasecmp(folder, "Cust2"))
1564                 return 6;
1565         else if (!strcasecmp(folder, "Cust3"))
1566                 return 7;
1567         else if (!strcasecmp(folder, "Cust4"))
1568                 return 8;
1569         else if (!strcasecmp(folder, "Cust5"))
1570                 return 9;
1571         else /*assume they meant INBOX if folder is not found otherwise*/
1572                 return 0;
1573 }
1574
1575 /*!
1576  * \brief Gets the number of messages that exist in a mailbox folder.
1577  * \param context
1578  * \param mailbox
1579  * \param folder
1580  * 
1581  * This method is used when IMAP backend is used.
1582  * \return The number of messages in this mailbox folder (zero or more).
1583  */
1584 static int messagecount(const char *context, const char *mailbox, const char *folder)
1585 {
1586         SEARCHPGM *pgm;
1587         SEARCHHEADER *hdr;
1588
1589         struct ast_vm_user *vmu, vmus;
1590         struct vm_state *vms_p;
1591         int ret = 0;
1592         int fold = folder_int(folder);
1593         int urgent = 0;
1594         
1595         if (ast_strlen_zero(mailbox))
1596                 return 0;
1597
1598         /* We have to get the user before we can open the stream! */
1599         vmu = find_user(&vmus, context, mailbox);
1600         if (!vmu) {
1601                 ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
1602                 return -1;
1603         } else {
1604                 /* No IMAP account available */
1605                 if (vmu->imapuser[0] == '\0') {
1606                         ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
1607                         return -1;
1608                 }
1609         }
1610         
1611         /* No IMAP account available */
1612         if (vmu->imapuser[0] == '\0') {
1613                 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
1614                 free_user(vmu);
1615                 return -1;
1616         }
1617
1618         /* check if someone is accessing this box right now... */
1619         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
1620         if (!vms_p) {
1621                 vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
1622         }
1623         if (vms_p) {
1624                 ast_debug(3, "Returning before search - user is logged in\n");
1625                 if (fold == 0) { /* INBOX */
1626                         return vms_p->newmessages;
1627                 }
1628                 if (fold == 1) { /* Old messages */
1629                         return vms_p->oldmessages;
1630                 }
1631                 if (fold == 11) {/*Urgent messages*/
1632                         return vms_p->urgentmessages;
1633                 }
1634         }
1635
1636         /* add one if not there... */
1637         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
1638         if (!vms_p) {
1639                 vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
1640         }
1641
1642         /* If URGENT, then look at INBOX */
1643         if (fold == 11) {
1644                 fold = NEW_FOLDER;
1645                 urgent = 1;
1646         }
1647
1648         if (!vms_p) {
1649                 ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
1650                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
1651                         return -1;
1652                 }
1653                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
1654                 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
1655                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
1656                 ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
1657                 vms_p->updated = 1;
1658                 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
1659                 init_vm_state(vms_p);
1660                 vmstate_insert(vms_p);
1661         }
1662         ret = init_mailstream(vms_p, fold);
1663         if (!vms_p->mailstream) {
1664                 ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
1665                 return -1;
1666         }
1667         if (ret == 0) {
1668                 pgm = mail_newsearchpgm ();
1669                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
1670                 pgm->header = hdr;
1671                 if (fold != 1) {
1672                         pgm->unseen = 1;
1673                         pgm->seen = 0;
1674                 }
1675                 /* In the special case where fold is 1 (old messages) we have to do things a bit
1676                  * differently. Old messages are stored in the INBOX but are marked as "seen"
1677                  */
1678                 else {
1679                         pgm->unseen = 0;
1680                         pgm->seen = 1;
1681                 }
1682                 /* look for urgent messages */
1683                 if (urgent) {
1684                         pgm->flagged = 1;
1685                         pgm->unflagged = 0;
1686                 }
1687                 pgm->undeleted = 1;
1688                 pgm->deleted = 0;
1689
1690                 vms_p->vmArrayIndex = 0;
1691                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
1692                 if (fold == 0 && urgent == 0)
1693                         vms_p->newmessages = vms_p->vmArrayIndex;
1694                 if (fold == 1)
1695                         vms_p->oldmessages = vms_p->vmArrayIndex;
1696                 if (fold == 0 && urgent == 1)
1697                         vms_p->urgentmessages = vms_p->vmArrayIndex;
1698                 /*Freeing the searchpgm also frees the searchhdr*/
1699                 mail_free_searchpgm(&pgm);
1700                 vms_p->updated = 0;
1701                 return vms_p->vmArrayIndex;
1702         } else {  
1703                 mail_ping(vms_p->mailstream);
1704         }
1705         return 0;
1706 }
1707
1708 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, const char *flag)
1709 {
1710         char *myserveremail = serveremail;
1711         char fn[PATH_MAX];
1712         char introfn[PATH_MAX];
1713         char mailbox[256];
1714         char *stringp;
1715         FILE *p=NULL;
1716         char tmp[80] = "/tmp/astmail-XXXXXX";
1717         long len;
1718         void *buf;
1719         int tempcopy = 0;
1720         STRING str;
1721         int ret; /* for better error checking */
1722         char *imap_flags = NIL;
1723
1724         /* Set urgent flag for IMAP message */
1725         if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
1726                 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
1727                 imap_flags="\\FLAGGED";
1728         }
1729         
1730         /* Attach only the first format */
1731         fmt = ast_strdupa(fmt);
1732         stringp = fmt;
1733         strsep(&stringp, "|");
1734
1735         if (!ast_strlen_zero(vmu->serveremail))
1736                 myserveremail = vmu->serveremail;
1737
1738         if (msgnum > -1)
1739                 make_file(fn, sizeof(fn), dir, msgnum);
1740         else
1741                 ast_copy_string (fn, dir, sizeof(fn));
1742
1743         snprintf(introfn, sizeof(introfn), "%sintro", fn);
1744         if (ast_fileexists(introfn, NULL, NULL) <= 0) {
1745                 *introfn = '\0';
1746         }
1747         
1748         if (ast_strlen_zero(vmu->email)) {
1749                 /* We need the vmu->email to be set when we call make_email_file, but
1750                  * if we keep it set, a duplicate e-mail will be created. So at the end
1751                  * of this function, we will revert back to an empty string if tempcopy
1752                  * is 1.
1753                  */
1754                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
1755                 tempcopy = 1;
1756         }
1757
1758         if (!strcmp(fmt, "wav49")) {
1759                 fmt = "WAV";
1760         } else if (!strcmp(fmt, "wav16")) {
1761                 fmt = "Wav";
1762         }
1763         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
1764
1765         /* Make a temporary file instead of piping directly to sendmail, in case the mail
1766            command hangs. */
1767         if (!(p = vm_mkftemp(tmp))) {
1768                 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
1769                 if (tempcopy)
1770                         *(vmu->email) = '\0';
1771                 return -1;
1772         }
1773
1774         if (msgnum < 0 && imapgreetings) {
1775                 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
1776                         ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
1777                         return -1;
1778                 }
1779                 imap_delete_old_greeting(fn, vms);
1780         }
1781         
1782         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, introfn, fmt, duration, 1, chan, NULL, 1, flag);
1783         /* read mail file to memory */          
1784         len = ftell(p);
1785         rewind(p);
1786         if (!(buf = ast_malloc(len + 1))) {
1787                 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
1788                 fclose(p);
1789                 if (tempcopy)
1790                         *(vmu->email) = '\0';
1791                 return -1;
1792         }
1793         fread(buf, len, 1, p);
1794         ((char *)buf)[len] = '\0';
1795         INIT(&str, mail_string, buf, len);
1796         ret = init_mailstream(vms, NEW_FOLDER);
1797         if (ret == 0) {
1798                 imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
1799                 if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
1800                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
1801                 fclose(p);
1802                 unlink(tmp);
1803                 ast_free(buf);
1804         } else {
1805                 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
1806                 fclose(p);
1807                 unlink(tmp);
1808                 ast_free(buf);
1809                 return -1;
1810         }
1811         ast_debug(3, "%s stored\n", fn);
1812         
1813         if (tempcopy)
1814                 *(vmu->email) = '\0';
1815         
1816         return 0;
1817
1818 }
1819
1820 /*!
1821  * \brief Gets the number of messages that exist in the inbox folder.
1822  * \param mailbox_context
1823  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
1824  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
1825  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
1826  * 
1827  * This method is used when IMAP backend is used.
1828  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
1829  *
1830  * \return zero on success, -1 on error.
1831  */
1832
1833 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
1834 {
1835         char tmp[PATH_MAX] = "";
1836         char *mailboxnc;
1837         char *context;
1838         char *mb;
1839         char *cur;
1840         if (newmsgs)
1841                 *newmsgs = 0;
1842         if (oldmsgs)
1843                 *oldmsgs = 0;
1844         if (urgentmsgs)
1845                 *urgentmsgs = 0;
1846
1847         ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
1848         /* If no mailbox, return immediately */
1849         if (ast_strlen_zero(mailbox_context))
1850                 return 0;
1851         
1852         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
1853         context = strchr(tmp, '@');
1854         if (strchr(mailbox_context, ',')) {
1855                 int tmpnew, tmpold, tmpurgent;
1856                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
1857                 mb = tmp;
1858                 while ((cur = strsep(&mb, ", "))) {
1859                         if (!ast_strlen_zero(cur)) {
1860                                 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
1861                                         return -1;
1862                                 else {
1863                                         if (newmsgs)
1864                                                 *newmsgs += tmpnew; 
1865                                         if (oldmsgs)
1866                                                 *oldmsgs += tmpold;
1867                                         if (urgentmsgs)
1868                                                 *urgentmsgs += tmpurgent;
1869                                 }
1870                         }
1871                 }
1872                 return 0;
1873         }
1874         if (context) {
1875                 *context = '\0';
1876                 mailboxnc = tmp;
1877                 context++;
1878         } else {
1879                 context = "default";
1880                 mailboxnc = (char *)mailbox_context;
1881         }
1882         if (newmsgs) {
1883                 if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
1884                         return -1;
1885         }
1886         if (oldmsgs) {
1887                 if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
1888                         return -1;
1889         }
1890         if (urgentmsgs) {
1891                 if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
1892                         return -1;
1893         }
1894         return 0;
1895 }
1896
1897 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
1898 {
1899         return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
1900 }
1901
1902 /** 
1903  * \brief Determines if the given folder has messages.
1904  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
1905  * \param folder the folder to look in
1906  *
1907  * This function is used when the mailbox is stored in an IMAP back end.
1908  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
1909  * \return 1 if the folder has one or more messages. zero otherwise.
1910  */
1911
1912 static int has_voicemail(const char *mailbox, const char *folder)
1913 {
1914         char tmp[256], *tmp2, *box, *context;
1915         ast_copy_string(tmp, mailbox, sizeof(tmp));
1916         tmp2 = tmp;
1917         if (strchr(tmp2, ',')) {
1918                 while ((box = strsep(&tmp2, ","))) {
1919                         if (!ast_strlen_zero(box)) {
1920                                 if (has_voicemail(box, folder))
1921                                         return 1;
1922                         }
1923                 }
1924         }
1925         if ((context= strchr(tmp, '@')))
1926                 *context++ = '\0';
1927         else
1928                 context = "default";
1929         return messagecount(context, tmp, folder) ? 1 : 0;
1930 }
1931
1932 /*!
1933  * \brief Copies a message from one mailbox to another.
1934  * \param chan
1935  * \param vmu
1936  * \param imbox
1937  * \param msgnum
1938  * \param duration
1939  * \param recip
1940  * \param fmt
1941  * \param dir
1942  *
1943  * This works with IMAP storage based mailboxes.
1944  *
1945  * \return zero on success, -1 on error.
1946  */
1947 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, char *flag)
1948 {
1949         struct vm_state *sendvms = NULL, *destvms = NULL;
1950         char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
1951         if (msgnum >= recip->maxmsg) {
1952                 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
1953                 return -1;
1954         }
1955         if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
1956                 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
1957                 return -1;
1958         }
1959         if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
1960                 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
1961                 return -1;
1962         }
1963         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
1964         if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
1965                 return 0;
1966         ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
1967         return -1;
1968 }
1969
1970 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
1971 {
1972         char tmp[256], *t = tmp;
1973         size_t left = sizeof(tmp);
1974         
1975         if (box == OLD_FOLDER) {
1976                 ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
1977         } else {
1978                 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
1979         }
1980
1981         if (box == NEW_FOLDER) {
1982                 ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
1983         } else {
1984                 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
1985         }
1986
1987         /* Build up server information */
1988         ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
1989
1990         /* Add authentication user if present */
1991         if (!ast_strlen_zero(authuser))
1992                 ast_build_string(&t, &left, "/authuser=%s", authuser);
1993
1994         /* Add flags if present */
1995         if (!ast_strlen_zero(imapflags))
1996                 ast_build_string(&t, &left, "/%s", imapflags);
1997
1998         /* End with username */
1999         ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
2000         if (box == NEW_FOLDER || box == OLD_FOLDER)
2001                 snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
2002         else if (box == GREETINGS_FOLDER)
2003                 snprintf(spec, len, "%s%s", tmp, greetingfolder);
2004         else {  /* Other folders such as Friends, Family, etc... */
2005                 if (!ast_strlen_zero(imapparentfolder)) {
2006                         /* imapparentfolder would typically be set to INBOX */
2007                         snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
2008                 } else {
2009                         snprintf(spec, len, "%s%s", tmp, mbox(box));
2010                 }
2011         }
2012 }
2013
2014 static int init_mailstream(struct vm_state *vms, int box)
2015 {
2016         MAILSTREAM *stream = NIL;
2017         long debug;
2018         char tmp[256];
2019         
2020         if (!vms) {
2021                 ast_log (LOG_ERROR,"vm_state is NULL!\n");
2022                 return -1;
2023         }
2024         if (option_debug > 2)
2025                 ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
2026         if (vms->mailstream == NIL || !vms->mailstream) {
2027                 if (option_debug)
2028                         ast_log (LOG_DEBUG,"mailstream not set.\n");
2029         } else {
2030                 stream = vms->mailstream;
2031         }
2032         /* debug = T;  user wants protocol telemetry? */
2033         debug = NIL;  /* NO protocol telemetry? */
2034
2035         if (delimiter == '\0') {                /* did not probe the server yet */
2036                 char *cp;
2037 #ifdef USE_SYSTEM_IMAP
2038 #include <imap/linkage.c>
2039 #elif defined(USE_SYSTEM_CCLIENT)
2040 #include <c-client/linkage.c>
2041 #else
2042 #include "linkage.c"
2043 #endif
2044                 /* Connect to INBOX first to get folders delimiter */
2045                 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
2046                 ast_mutex_lock(&vms->lock);
2047                 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2048                 ast_mutex_unlock(&vms->lock);
2049                 if (stream == NIL) {
2050                         ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
2051                         return -1;
2052                 }
2053                 get_mailbox_delimiter(stream);
2054                 /* update delimiter in imapfolder */
2055                 for (cp = imapfolder; *cp; cp++)
2056                         if (*cp == '/')
2057                                 *cp = delimiter;
2058         }
2059         /* Now connect to the target folder */
2060         imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
2061         if (option_debug > 2)
2062                 ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
2063         ast_mutex_lock(&vms->lock);
2064         vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2065         ast_mutex_unlock(&vms->lock);
2066         if (vms->mailstream == NIL) {
2067                 return -1;
2068         } else {
2069                 return 0;
2070         }
2071 }
2072
2073 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
2074 {
2075         SEARCHPGM *pgm;
2076         SEARCHHEADER *hdr;
2077         int ret, urgent = 0;
2078
2079         /* If Urgent, then look at INBOX */
2080         if (box == 11) {
2081                 box = NEW_FOLDER;
2082                 urgent = 1;
2083         }
2084
2085         ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
2086         ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
2087
2088         if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
2089                 ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
2090                 return -1;
2091         }
2092         
2093         create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2094         
2095         /* Check Quota */
2096         if  (box == 0)  {
2097                 ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
2098                 check_quota(vms,(char *)mbox(box));
2099         }
2100
2101         pgm = mail_newsearchpgm();
2102
2103         /* Check IMAP folder for Asterisk messages only... */
2104         hdr = mail_newsearchheader("X-Asterisk-VM-Extension", vmu->mailbox);
2105         pgm->header = hdr;
2106         pgm->deleted = 0;
2107         pgm->undeleted = 1;
2108
2109         /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
2110         if (box == NEW_FOLDER && urgent == 1) {
2111                 pgm->unseen = 1;
2112                 pgm->seen = 0;
2113                 pgm->flagged = 1;
2114                 pgm->unflagged = 0;
2115         } else if (box == NEW_FOLDER && urgent == 0) {
2116                 pgm->unseen = 1;
2117                 pgm->seen = 0;
2118                 pgm->flagged = 0;
2119                 pgm->unflagged = 1;
2120         } else if (box == OLD_FOLDER) {
2121                 pgm->seen = 1;
2122                 pgm->unseen = 0;
2123         }
2124
2125         ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
2126
2127         vms->vmArrayIndex = 0;
2128         mail_search_full (vms->mailstream, NULL, pgm, NIL);
2129         vms->lastmsg = vms->vmArrayIndex - 1;
2130         mail_free_searchpgm(&pgm);
2131
2132         return 0;
2133 }
2134
2135 static void write_file(char *filename, char *buffer, unsigned long len)
2136 {
2137         FILE *output;
2138
2139         output = fopen (filename, "w");
2140         fwrite (buffer, len, 1, output);
2141         fclose (output);
2142 }
2143
2144 static void update_messages_by_imapuser(const char *user, unsigned long number)
2145 {
2146         struct vmstate *vlist = NULL;
2147
2148         AST_LIST_LOCK(&vmstates);
2149         AST_LIST_TRAVERSE(&vmstates, vlist, list) {
2150                 if (!vlist->vms) {
2151                         ast_debug(3, "error: vms is NULL for %s\n", user);
2152                         continue;
2153                 }
2154                 if (!vlist->vms->imapuser) {
2155                         ast_debug(3, "error: imapuser is NULL for %s\n", user);
2156                         continue;
2157                 }
2158                 ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vlist->vms->vmArrayIndex, vlist->vms->interactive);
2159                 vlist->vms->msgArray[vlist->vms->vmArrayIndex++] = number;
2160         }
2161         AST_LIST_UNLOCK(&vmstates);
2162 }
2163
2164 void mm_searched(MAILSTREAM *stream, unsigned long number)
2165 {
2166         char *mailbox = stream->mailbox, buf[1024] = "", *user;
2167
2168         if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
2169                 return;
2170
2171         update_messages_by_imapuser(user, number);
2172 }
2173
2174 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
2175 {
2176         struct ast_variable *var;
2177         struct ast_vm_user *vmu;
2178
2179         vmu = ast_calloc(1, sizeof *vmu);
2180         if (!vmu)
2181                 return NULL;
2182         ast_set_flag(vmu, VM_ALLOCED);
2183         populate_defaults(vmu);
2184
2185         var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
2186         if (var) {
2187                 apply_options_full(vmu, var);
2188                 ast_variables_destroy(var);
2189                 return vmu;
2190         } else {
2191                 free(vmu);
2192                 return NULL;
2193         }
2194 }
2195
2196 /* Interfaces to C-client */
2197
2198 void mm_exists(MAILSTREAM * stream, unsigned long number)
2199 {
2200         /* mail_ping will callback here if new mail! */
2201         ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
2202         if (number == 0) return;
2203         set_update(stream);
2204 }
2205
2206
2207 void mm_expunged(MAILSTREAM * stream, unsigned long number)
2208 {
2209         /* mail_ping will callback here if expunged mail! */
2210         ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
2211         if (number == 0) return;
2212         set_update(stream);
2213 }
2214
2215
2216 void mm_flags(MAILSTREAM * stream, unsigned long number)
2217 {
2218         /* mail_ping will callback here if read mail! */
2219         ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
2220         if (number == 0) return;
2221         set_update(stream);
2222 }
2223
2224
2225 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
2226 {
2227         ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
2228         mm_log (string, errflg);
2229 }
2230
2231
2232 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2233 {
2234         if (delimiter == '\0') {
2235                 delimiter = delim;
2236         }
2237
2238         ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
2239         if (attributes & LATT_NOINFERIORS)
2240                 ast_debug(5, "no inferiors\n");
2241         if (attributes & LATT_NOSELECT)
2242                 ast_debug(5, "no select\n");
2243         if (attributes & LATT_MARKED)
2244                 ast_debug(5, "marked\n");
2245         if (attributes & LATT_UNMARKED)
2246                 ast_debug(5, "unmarked\n");
2247 }
2248
2249
2250 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2251 {
2252         ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
2253         if (attributes & LATT_NOINFERIORS)
2254                 ast_debug(5, "no inferiors\n");
2255         if (attributes & LATT_NOSELECT)
2256                 ast_debug(5, "no select\n");
2257         if (attributes & LATT_MARKED)
2258                 ast_debug(5, "marked\n");
2259         if (attributes & LATT_UNMARKED)
2260                 ast_debug(5, "unmarked\n");
2261 }
2262
2263
2264 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
2265 {
2266         ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
2267         if (status->flags & SA_MESSAGES)
2268                 ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
2269         if (status->flags & SA_RECENT)
2270                 ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
2271         if (status->flags & SA_UNSEEN)
2272                 ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
2273         if (status->flags & SA_UIDVALIDITY)
2274                 ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
2275         if (status->flags & SA_UIDNEXT)
2276                 ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
2277         ast_log(AST_LOG_NOTICE, "\n");
2278 }
2279
2280
2281 void mm_log(char *string, long errflg)
2282 {
2283         switch ((short) errflg) {
2284                 case NIL:
2285                         ast_debug(1,"IMAP Info: %s\n", string);
2286                         break;
2287                 case PARSE:
2288                 case WARN:
2289                         ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
2290                         break;
2291                 case ERROR:
2292                         ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
2293                         break;
2294         }
2295 }
2296
2297
2298 void mm_dlog(char *string)
2299 {
2300         ast_log(AST_LOG_NOTICE, "%s\n", string);
2301 }
2302
2303
2304 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
2305 {
2306         struct ast_vm_user *vmu;
2307
2308         ast_debug(4, "Entering callback mm_login\n");
2309
2310         ast_copy_string(user, mb->user, MAILTMPLEN);
2311
2312         /* We should only do this when necessary */
2313         if (!ast_strlen_zero(authpassword)) {
2314                 ast_copy_string(pwd, authpassword, MAILTMPLEN);
2315         } else {
2316                 AST_LIST_TRAVERSE(&users, vmu, list) {
2317                         if (!strcasecmp(mb->user, vmu->imapuser)) {
2318                                 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2319                                 break;
2320                         }
2321                 }
2322                 if (!vmu) {
2323                         if ((vmu = find_user_realtime_imapuser(mb->user))) {
2324                                 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2325                                 free_user(vmu);
2326                         }
2327                 }
2328         }
2329 }
2330
2331
2332 void mm_critical(MAILSTREAM * stream)
2333 {
2334 }
2335
2336
2337 void mm_nocritical(MAILSTREAM * stream)
2338 {
2339 }
2340
2341
2342 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
2343 {
2344         kill (getpid (), SIGSTOP);
2345         return NIL;
2346 }
2347
2348
2349 void mm_fatal(char *string)
2350 {
2351         ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
2352 }
2353
2354 /* C-client callback to handle quota */
2355 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
2356 {
2357         struct vm_state *vms;
2358         char *mailbox = stream->mailbox, *user;
2359         char buf[1024] = "";
2360         unsigned long usage = 0, limit = 0;
2361         
2362         while (pquota) {
2363                 usage = pquota->usage;
2364                 limit = pquota->limit;
2365                 pquota = pquota->next;
2366         }
2367         
2368         if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 2))) {
2369                 ast_log(AST_LOG_ERROR, "No state found.\n");
2370                 return;
2371         }
2372
2373         ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
2374
2375         vms->quota_usage = usage;
2376         vms->quota_limit = limit;
2377 }
2378
2379 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
2380 {
2381         char *start, *eol_pnt;
2382         int taglen;
2383
2384         if (ast_strlen_zero(header) || ast_strlen_zero(tag))
2385                 return NULL;
2386
2387         taglen = strlen(tag) + 1;
2388         if (taglen < 1)
2389                 return NULL;
2390
2391         if (!(start = strstr(header, tag)))
2392                 return NULL;
2393
2394         /* Since we can be called multiple times we should clear our buffer */
2395         memset(buf, 0, len);
2396
2397         ast_copy_string(buf, start+taglen, len);
2398         if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
2399                 *eol_pnt = '\0';
2400         return buf;
2401 }
2402
2403 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
2404 {
2405         char *start, *quote, *eol_pnt;
2406
2407         if (ast_strlen_zero(mailbox))
2408                 return NULL;
2409
2410         if (!(start = strstr(mailbox, "/user=")))
2411                 return NULL;
2412
2413         ast_copy_string(buf, start+6, len);
2414
2415         if (!(quote = strchr(buf, '\"'))) {
2416                 if (!(eol_pnt = strchr(buf, '/')))
2417                         eol_pnt = strchr(buf,'}');
2418                 *eol_pnt = '\0';
2419                 return buf;
2420         } else {
2421                 eol_pnt = strchr(buf+1,'\"');
2422                 *eol_pnt = '\0';
2423                 return buf+1;
2424         }
2425 }
2426
2427 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
2428 {
2429         struct vm_state *vms_p;
2430
2431         if (option_debug > 4)
2432                 ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2433         if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
2434                 return NULL;
2435         ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
2436         ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2437         ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
2438         vms_p->mailstream = NIL; /* save for access from interactive entry point */
2439         if (option_debug > 4)
2440                 ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2441         vms_p->updated = 1;
2442         /* set mailbox to INBOX! */
2443         ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2444         init_vm_state(vms_p);
2445         vmstate_insert(vms_p);
2446         return vms_p;
2447 }
2448
2449 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
2450 {
2451         struct vmstate *vlist = NULL;
2452
2453         AST_LIST_LOCK(&vmstates);
2454         AST_LIST_TRAVERSE(&vmstates, vlist, list) {
2455                 if (!vlist->vms) {
2456                         ast_debug(3, "error: vms is NULL for %s\n", user);
2457                         continue;
2458                 }
2459                 if (!vlist->vms->imapuser) {
2460                         ast_debug(3, "error: imapuser is NULL for %s\n", user);
2461                         continue;
2462                 }
2463
2464                 if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
2465                         AST_LIST_UNLOCK(&vmstates);
2466                         return vlist->vms;
2467                 }
2468         }
2469         AST_LIST_UNLOCK(&vmstates);
2470
2471         ast_debug(3, "%s not found in vmstates\n", user);
2472
2473         return NULL;
2474 }
2475
2476 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
2477 {
2478
2479         struct vmstate *vlist = NULL;
2480         const char *local_context = S_OR(context, "default");
2481
2482         AST_LIST_LOCK(&vmstates);
2483         AST_LIST_TRAVERSE(&vmstates, vlist, list) {
2484                 if (!vlist->vms) {
2485                         ast_debug(3, "error: vms is NULL for %s\n", mailbox);
2486                         continue;
2487                 }
2488                 if (!vlist->vms->username || !vlist->vms->context) {
2489                         ast_debug(3, "error: username is NULL for %s\n", mailbox);
2490                         continue;
2491                 }
2492
2493                 ast_debug(3, "comparing mailbox %s@%s (i=%d) to vmstate mailbox %s@%s (i=%d)\n", mailbox, local_context, interactive, vlist->vms->username, vlist->vms->context, vlist->vms->interactive);
2494                 
2495                 if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
2496                         ast_debug(3, "Found it!\n");
2497                         AST_LIST_UNLOCK(&vmstates);
2498                         return vlist->vms;
2499                 }
2500         }
2501         AST_LIST_UNLOCK(&vmstates);
2502
2503         ast_debug(3, "%s not found in vmstates\n", mailbox);
2504
2505         return NULL;
2506 }
2507
2508 static void vmstate_insert(struct vm_state *vms) 
2509 {
2510         struct vmstate *v;
2511         struct vm_state *altvms;
2512
2513         /* If interactive, it probably already exists, and we should
2514            use the one we already have since it is more up to date.
2515            We can compare the username to find the duplicate */
2516         if (vms->interactive == 1) {
2517                 altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
2518                 if (altvms) {   
2519                         ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
2520                         vms->newmessages = altvms->newmessages;
2521                         vms->oldmessages = altvms->oldmessages;
2522                         vms->vmArrayIndex = altvms->vmArrayIndex;
2523                         vms->lastmsg = altvms->lastmsg;
2524                         vms->curmsg = altvms->curmsg;
2525                         /* get a pointer to the persistent store */
2526                         vms->persist_vms = altvms;
2527                         /* Reuse the mailstream? */
2528                         vms->mailstream = altvms->mailstream;
2529                         /* vms->mailstream = NIL; */
2530                 }
2531         }
2532
2533         if (!(v = ast_calloc(1, sizeof(*v))))
2534                 return;
2535         
2536         v->vms = vms;
2537
2538         ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
2539
2540         AST_LIST_LOCK(&vmstates);
2541         AST_LIST_INSERT_TAIL(&vmstates, v, list);
2542         AST_LIST_UNLOCK(&vmstates);
2543 }
2544
2545 static void vmstate_delete(struct vm_state *vms) 
2546 {
2547         struct vmstate *vc = NULL;
2548         struct vm_state *altvms = NULL;
2549
2550         /* If interactive, we should copy pertinent info
2551            back to the persistent state (to make update immediate) */
2552         if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
2553                 ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
2554                 altvms->newmessages = vms->newmessages;
2555                 altvms->oldmessages = vms->oldmessages;
2556                 altvms->updated = 1;
2557         }
2558         
2559         ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
2560         
2561         AST_LIST_LOCK(&vmstates);
2562         AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
2563                 if (vc->vms == vms) {
2564                         AST_LIST_REMOVE_CURRENT(list);
2565                         break;
2566                 }
2567         }
2568         AST_LIST_TRAVERSE_SAFE_END
2569         AST_LIST_UNLOCK(&vmstates);
2570         
2571         if (vc) {
2572                 ast_mutex_destroy(&vc->vms->lock);
2573                 ast_free(vc);
2574         }
2575         else
2576                 ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
2577 }
2578
2579 static void set_update(MAILSTREAM * stream) 
2580 {
2581         struct vm_state *vms;
2582         char *mailbox = stream->mailbox, *user;
2583         char buf[1024] = "";
2584
2585         if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
2586                 if (user && option_debug > 2)
2587                         ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
2588                 return;
2589         }
2590
2591         ast_debug(3, "User %s mailbox set for update.\n", user);
2592
2593         vms->updated = 1; /* Set updated flag since mailbox changed */
2594 }
2595
2596 static void init_vm_state(struct vm_state *vms) 
2597 {
2598         int x;
2599         vms->vmArrayIndex = 0;
2600         for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
2601                 vms->msgArray[x] = 0;
2602         }
2603         ast_mutex_init(&vms->lock);
2604 }
2605
2606 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
2607 {
2608         char *body_content;
2609         char *body_decoded;
2610         char *fn = is_intro ? vms->introfn : vms->fn;
2611         unsigned long len;
2612         unsigned long newlen;
2613         char filename[256];
2614         
2615         if (!body || body == NIL)
2616                 return -1;
2617
2618         body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
2619         if (body_content != NIL) {
2620                 snprintf(filename, sizeof(filename), "%s.%s", fn, format);
2621                 /* ast_debug(1,body_content); */
2622                 body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
2623                 /* If the body of the file is empty, return an error */
2624                 if (!newlen) {
2625                         return -1;
2626                 }
2627                 write_file(filename, (char *) body_decoded, newlen);
2628         } else {
2629                 ast_debug(5, "Body of message is NULL.\n");
2630                 return -1;
2631         }
2632         return 0;
2633 }
2634
2635 /*! 
2636  * \brief Get delimiter via mm_list callback 
2637  * \param stream
2638  *
2639  * Determines the delimiter character that is used by the underlying IMAP based mail store.
2640  */
2641 static void get_mailbox_delimiter(MAILSTREAM *stream) {
2642         char tmp[50];
2643         snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
2644         mail_list(stream, tmp, "*");
2645 }
2646
2647 /*! 
2648  * \brief Check Quota for user 
2649  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
2650  * \param mailbox the mailbox to check the quota for.
2651  *
2652  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
2653  */
2654 static void check_quota(struct vm_state *vms, char *mailbox) {
2655         mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
2656         ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
2657         if (vms && vms->mailstream != NULL) {
2658                 imap_getquotaroot(vms->mailstream, mailbox);
2659         } else {
2660                 ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
2661         }
2662 }
2663
2664 #endif /* IMAP_STORAGE */
2665
2666 /*! \brief Lock file path
2667     only return failure if ast_lock_path returns 'timeout',
2668    not if the path does not exist or any other reason
2669 */
2670 static int vm_lock_path(const char *path)
2671 {
2672         switch (ast_lock_path(path)) {
2673         case AST_LOCK_TIMEOUT:
2674                 return -1;
2675         default:
2676                 return 0;
2677         }
2678 }
2679
2680
2681 #ifdef ODBC_STORAGE
2682 struct generic_prepare_struct {
2683         char *sql;
2684         int argc;
2685         char **argv;
2686 };
2687
2688 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
2689 {
2690         struct generic_prepare_struct *gps = data;
2691         int res, i;
2692         SQLHSTMT stmt;
2693
2694         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2695         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2696                 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
2697                 return NULL;
2698         }
2699         res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
2700         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2701                 ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
2702                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2703                 return NULL;
2704         }
2705         for (i = 0; i < gps->argc; i++)
2706                 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
2707
2708         return stmt;
2709 }
2710
2711 /*!
2712  * \brief Retrieves a file from an ODBC data store.
2713  * \param dir the path to the file to be retreived.
2714  * \param msgnum the message number, such as within a mailbox folder.
2715  * 
2716  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
2717  * The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
2718  *
2719  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
2720  * The output is the message information file with the name msgnum and the extension .txt
2721  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
2722  * 
2723  * \return 0 on success, -1 on error.
2724  */
2725 static int retrieve_file(char *dir, int msgnum)
2726 {
2727         int x = 0;
2728         int res;
2729         int fd=-1;
2730         size_t fdlen = 0;
2731         void *fdm = MAP_FAILED;
2732         SQLSMALLINT colcount=0;
2733         SQLHSTMT stmt;
2734         char sql[PATH_MAX];
2735         char fmt[80]="";
2736         char *c;
2737         char coltitle[256];
2738         SQLSMALLINT collen;
2739         SQLSMALLINT datatype;
2740         SQLSMALLINT decimaldigits;
2741         SQLSMALLINT nullable;
2742         SQLULEN colsize;
2743         SQLLEN colsize2;
2744         FILE *f=NULL;
2745         char rowdata[80];
2746         char fn[PATH_MAX];
2747         char full_fn[PATH_MAX];
2748         char msgnums[80];
2749         char *argv[] = { dir, msgnums };
2750         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2751
2752         struct odbc_obj *obj;
2753         obj = ast_odbc_request_obj(odbc_database, 0);
2754         if (obj) {
2755                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2756                 c = strchr(fmt, '|');
2757                 if (c)
2758                         *c = '\0';
2759                 if (!strcasecmp(fmt, "wav49")) {
2760                         strcpy(fmt, "WAV");
2761                 } else if (!strcasecmp(fmt, "wav16")) {
2762                         strcpy(fmt, "Wav");
2763                 }
2764                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
2765                 if (msgnum > -1)
2766                         make_file(fn, sizeof(fn), dir, msgnum);
2767                 else
2768                         ast_copy_string(fn, dir, sizeof(fn));
2769
2770                 /* Create the information file */
2771                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
2772                 
2773                 if (!(f = fopen(full_fn, "w+"))) {
2774                         ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
2775                         goto yuck;
2776                 }
2777                 
2778                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
2779                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
2780                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2781                 if (!stmt) {
2782                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2783                         ast_odbc_release_obj(obj);
2784                         goto yuck;
2785                 }
2786                 res = SQLFetch(stmt);
2787                 if (res == SQL_NO_DATA) {
2788                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2789                         ast_odbc_release_obj(obj);
2790                         goto yuck;
2791                 } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2792                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2793                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2794                         ast_odbc_release_obj(obj);
2795                         goto yuck;
2796                 }
2797                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
2798                 if (fd < 0) {
2799                         ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
2800                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2801                         ast_odbc_release_obj(obj);
2802                         goto yuck;
2803                 }
2804                 res = SQLNumResultCols(stmt, &colcount);
2805                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
2806                         ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
2807                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2808                         ast_odbc_release_obj(obj);
2809                         goto yuck;
2810                 }
2811                 if (f) 
2812                         fprintf(f, "[message]\n");
2813                 for (x=0;x<colcount;x++) {
2814                         rowdata[0] = '\0';
2815                         collen = sizeof(coltitle);
2816                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
2817                                                 &datatype, &colsize, &decimaldigits, &nullable);
2818                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2819                                 ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
2820                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2821                                 ast_odbc_release_obj(obj);
2822                                 goto yuck;
2823                         }
2824                         if (!strcasecmp(coltitle, "recording")) {
2825                                 off_t offset;
2826                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
2827                                 fdlen = colsize2;
2828                                 if (fd > -1) {
2829                                         char tmp[1]="";
2830                                         lseek(fd, fdlen - 1, SEEK_SET);
2831                                         if (write(fd, tmp, 1) != 1) {
2832                                                 close(fd);
2833                                                 fd = -1;
2834                                                 continue;
2835                                         }
2836                                         /* Read out in small chunks */
2837                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
2838                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
2839                                                         ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
2840                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2841                                                         ast_odbc_release_obj(obj);
2842                                                         goto yuck;
2843                                                 } else {
2844                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
2845                                                         munmap(fdm, CHUNKSIZE);
2846                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2847                                                                 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2848                                                                 unlink(full_fn);
2849                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2850                                                                 ast_odbc_release_obj(obj);
2851                                                                 goto yuck;
2852                                                         }
2853                                                 }
2854                                         }
2855                                         truncate(full_fn, fdlen);
2856                                 }
2857                         } else {
2858                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2859                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2860                                         ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
2861                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2862                                         ast_odbc_release_obj(obj);
2863                                         goto yuck;
2864                                 }
2865                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
2866                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
2867                         }
2868                 }
2869                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2870                 ast_odbc_release_obj(obj);
2871         } else
2872                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2873 yuck:   
2874         if (f)
2875                 fclose(f);
2876         if (fd > -1)
2877                 close(fd);
2878         return x - 1;
2879 }
2880
2881 /*!
2882  * \brief Determines the highest message number in use for a given user and mailbox folder.
2883  * \param vmu 
2884  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
2885  *
2886  * This method is used when mailboxes are stored in an ODBC back end.
2887  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
2888  *
2889  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
2890  */
2891 static int last_message_index(struct ast_vm_user *vmu, char *dir)
2892 {
2893         int x = 0;
2894         int res;
2895         SQLHSTMT stmt;
2896         char sql[PATH_MAX];
2897         char rowdata[20];
2898         char *argv[] = { dir };
2899         struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
2900
2901         struct odbc_obj *obj;
2902         obj = ast_odbc_request_obj(odbc_database, 0);
2903         if (obj) {
2904                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
2905                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2906                 if (!stmt) {
2907                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2908                         ast_odbc_release_obj(obj);
2909                         goto yuck;
2910                 }
2911                 res = SQLFetch(stmt);
2912                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2913                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2914                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2915                         ast_odbc_release_obj(obj);
2916                         goto yuck;
2917                 }
2918                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2919                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2920                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2921                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2922                         ast_odbc_release_obj(obj);
2923                         goto yuck;
2924                 }
2925                 if (sscanf(rowdata, "%d", &x) != 1)
2926                         ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
2927                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2928                 ast_odbc_release_obj(obj);
2929         } else
2930                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2931 yuck:   
2932         return x - 1;
2933 }
2934
2935 /*!
2936  * \brief Determines if the specified message exists.
2937  * \param dir the folder the mailbox folder to look for messages. 
2938  * \param msgnum the message index to query for.
2939  *
2940  * This method is used when mailboxes are stored in an ODBC back end.
2941  *
2942  * \return greater than zero if the message exists, zero when the message does not exist or on error.
2943  */
2944 static int message_exists(char *dir, int msgnum)
2945 {
2946         int x = 0;
2947         int res;
2948         SQLHSTMT stmt;
2949         char sql[PATH_MAX];
2950         char rowdata[20];
2951         char msgnums[20];
2952         char *argv[] = { dir, msgnums };
2953         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2954
2955         struct odbc_obj *obj;
2956         obj = ast_odbc_request_obj(odbc_database, 0);
2957         if (obj) {
2958                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
2959                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
2960                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2961                 if (!stmt) {
2962                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2963                         ast_odbc_release_obj(obj);
2964                         goto yuck;
2965                 }
2966                 res = SQLFetch(stmt);
2967                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2968                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2969                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2970                         ast_odbc_release_obj(obj);
2971                         goto yuck;
2972                 }
2973                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2974                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2975                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2976                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2977                         ast_odbc_release_obj(obj);
2978                         goto yuck;
2979                 }
2980                 if (sscanf(rowdata, "%d", &x) != 1)
2981                         ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
2982                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2983                 ast_odbc_release_obj(obj);
2984         } else
2985                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2986 yuck:   
2987         return x;
2988 }
2989
2990 /*!
2991  * \brief returns the one-based count for messages.
2992  * \param vmu
2993  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
2994  *
2995  * This method is used when mailboxes are stored in an ODBC back end.
2996  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
2997  * one-based messages.
2998  * This method just calls last_message_index and returns +1 of its value.
2999  *
3000  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
3001  */
3002 static int count_messages(struct ast_vm_user *vmu, char *dir)
3003 {
3004         return last_message_index(vmu, dir) + 1;
3005 }
3006
3007 /*!
3008  * \brief Deletes a message from the mailbox folder.
3009  * \param sdir The mailbox folder to work in.
3010  * \param smsg The message index to be deleted.
3011  *
3012  * This method is used when mailboxes are stored in an ODBC back end.
3013  * The specified message is directly deleted from the database 'voicemessages' table.
3014  * 
3015  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
3016  */
3017 static void delete_file(char *sdir, int smsg)
3018 {
3019         SQLHSTMT stmt;
3020         char sql[PATH_MAX];
3021         char msgnums[20];
3022         char *argv[] = { sdir, msgnums };
3023         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3024
3025         struct odbc_obj *obj;
3026         obj = ast_odbc_request_obj(odbc_database, 0);
3027         if (obj) {
3028                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3029                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
3030                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3031                 if (!stmt)
3032                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3033                 else
3034                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3035                 ast_odbc_release_obj(obj);
3036         } else
3037                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3038         return; 
3039 }
3040
3041 /*!
3042  * \brief Copies a voicemail from one mailbox to another.
3043  * \param sdir the folder for which to look for the message to be copied.
3044  * \param smsg the index of the message to be copied.
3045  * \param ddir the destination folder to copy the message into.
3046  * \param dmsg the index to be used for the copied message.
3047  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
3048  * \param dmailboxcontext The context for the destination user.
3049  *
3050  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
3051  */
3052 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
3053 {
3054         SQLHSTMT stmt;
3055         char sql[512];
3056         char msgnums[20];
3057         char msgnumd[20];
3058         struct odbc_obj *obj;
3059         char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
3060         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3061
3062         delete_file(ddir, dmsg);
3063         obj = ast_odbc_request_obj(odbc_database, 0);
3064         if (obj) {
3065                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3066                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3067                 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext, flag) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
3068                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3069                 if (!stmt)
3070                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
3071                 else
3072                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3073                 ast_odbc_release_obj(obj);
3074         } else
3075                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3076         return; 
3077 }
3078
3079 struct insert_data {
3080         char *sql;
3081         char *dir;
3082         char *msgnums;
3083         void *data;
3084         SQLLEN datalen;
3085         SQLLEN indlen;
3086         const char *context;
3087         const char *macrocontext;
3088         const char *callerid;
3089         const char *origtime;
3090         const char *duration;
3091         char *mailboxuser;
3092         char *mailboxcontext;
3093         const char *category;
3094         const char *flag;
3095 };
3096
3097 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
3098 {
3099         struct insert_data *data = vdata;
3100         int res;
3101         SQLHSTMT stmt;
3102
3103         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
3104         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3105                 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
3106                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3107                 return NULL;
3108         }
3109
3110         SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
3111         SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
3112         SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
3113         SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
3114         SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
3115         SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
3116         SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
3117         SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
3118         SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
3119         SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
3120         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
3121         if (!ast_strlen_zero(data->category)) {
3122                 SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
3123         }
3124         res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
3125         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3126                 ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
3127                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3128                 return NULL;
3129         }
3130
3131         return stmt;
3132 }
3133
3134 /*!
3135  * \brief Stores a voicemail into the database.
3136  * \param dir the folder the mailbox folder to store the message.
3137  * \param mailboxuser the user owning the mailbox folder.
3138  * \param mailboxcontext
3139  * \param msgnum the message index for the message to be stored.
3140  *
3141  * This method is used when mailboxes are stored in an ODBC back end.
3142  * The message sound file and information file is looked up on the file system. 
3143  * A SQL query is invoked to store the message into the (MySQL) database.
3144  *
3145  * \return the zero on success -1 on error.
3146  */
3147 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
3148 {
3149         int res = 0;
3150         int fd = -1;
3151         void *fdm = MAP_FAILED;
3152         size_t fdlen = -1;
3153         SQLHSTMT stmt;
3154         char sql[PATH_MAX];
3155         char msgnums[20];
3156         char fn[PATH_MAX];
3157         char full_fn[PATH_MAX];
3158         char fmt[80]="";
3159         char *c;
3160         struct ast_config *cfg=NULL;
3161         struct odbc_obj *obj;
3162         struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext };
3163         struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
3164
3165         delete_file(dir, msgnum);
3166         if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
3167                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3168                 return -1;
3169         }
3170
3171         do {
3172                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
3173                 c = strchr(fmt, '|');
3174                 if (c)
3175                         *c = '\0';
3176                 if (!strcasecmp(fmt, "wav49")) {
3177                         strcpy(fmt, "WAV");
3178                 } else if (!strcasecmp(fmt, "wav16")) {
3179                         strcpy(fmt, "Wav");
3180                 }
3181                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
3182                 if (msgnum > -1)
3183                         make_file(fn, sizeof(fn), dir, msgnum);
3184                 else
3185                         ast_copy_string(fn, dir, sizeof(fn));
3186                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3187                 cfg = ast_config_load(full_fn, config_flags);
3188                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3189                 fd = open(full_fn, O_RDWR);
3190                 if (fd < 0) {
3191                         ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
3192                         res = -1;
3193                         break;
3194                 }
3195                 if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
3196                         if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
3197                                 idata.context = "";
3198                         }
3199                         if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
3200                                 idata.macrocontext = "";
3201                         }
3202                         if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
3203                                 idata.callerid = "";
3204                         }
3205                         if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
3206                                 idata.origtime = "";
3207                         }
3208                         if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
3209                                 idata.duration = "";
3210                         }
3211                         if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
3212                                 idata.category = "";
3213                         }
3214                         if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
3215                                 idata.flag = "";
3216                         }
3217                 }
3218                 fdlen = lseek(fd, 0, SEEK_END);
3219                 lseek(fd, 0, SEEK_SET);
3220                 printf("Length is %zd\n", fdlen);
3221                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
3222                 if (fdm == MAP_FAILED) {
3223                         ast_log(AST_LOG_WARNING, "Memory map failed!\n");
3224                         res = -1;
3225                         break;
3226                 } 
3227                 idata.data = fdm;
3228                 idata.datalen = idata.indlen = fdlen;
3229
3230                 if (!ast_strlen_zero(idata.category)) 
3231                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
3232                 else
3233                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
3234
3235                 if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
3236                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3237                 } else {
3238                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3239                         res = -1;
3240                 }
3241         } while (0);
3242         if (obj) {
3243                 ast_odbc_release_obj(obj);
3244         }
3245         if (cfg)
3246                 ast_config_destroy(cfg);
3247         if (fdm != MAP_FAILED)
3248                 munmap(fdm, fdlen);
3249         if (fd > -1)
3250                 close(fd);
3251         return res;
3252 }
3253
3254 /*!
3255  * \brief Renames a message in a mailbox folder.
3256  * \param sdir The folder of the message to be renamed.
3257  * \param smsg The index of the message to be renamed.
3258  * \param mailboxuser The user to become the owner of the message after it is renamed. Usually this will be the same as the original owner.
3259  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
3260  * \param ddir The destination folder for the message to be renamed into
3261  * \param dmsg The destination message for the message to be renamed.
3262  *
3263  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
3264  * The is usually used to resequence the messages in the mailbox, such as to delete messag index 0, it would be called successively to slide all the other messages down one index.
3265  * But in theory, because the SQL query performs an update on (dir, msgnum, mailboxuser, mailboxcontext) in the database, it should be possible to have the message relocated to another mailbox or context as well.
3266  */
3267 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
3268 {
3269         SQLHSTMT stmt;
3270         char sql[PATH_MAX];
3271         char msgnums[20];
3272         char msgnumd[20];
3273         struct odbc_obj *obj;
3274         char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
3275         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3276
3277         delete_file(ddir, dmsg);
3278         obj = ast_odbc_request_obj(odbc_database, 0);
3279         if (obj) {
3280                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3281                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3282                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
3283                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3284                 if (!stmt)
3285                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3286                 else
3287                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3288                 ast_odbc_release_obj(obj);
3289         } else
3290                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3291         return; 
3292 }
3293
3294 /*!
3295  * \brief Removes a voicemail message file.
3296  * \param dir the path to the message file.
3297  * \param msgnum the unique number for the message within the mailbox.
3298  *
3299  * Removes the message content file and the information file.
3300  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
3301  * Typical use is to clean up after a RETRIEVE operation. 
3302  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
3303  * \return zero on success, -1 on error.
3304  */
3305 static int remove_file(char *dir, int msgnum)
3306 {
3307         char fn[PATH_MAX];
3308         char full_fn[PATH_MAX];
3309         char msgnums[80];
3310         
3311         if (msgnum > -1) {
3312                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3313                 make_file(fn, sizeof(fn), dir, msgnum);
3314         } else
3315                 ast_copy_string(fn, dir, sizeof(fn));
3316         ast_filedelete(fn, NULL);       
3317         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3318         unlink(full_fn);
3319         return 0;
3320 }
3321 #else
3322 #ifndef IMAP_STORAGE
3323 /*!
3324  * \brief Find all .txt files - even if they are not in sequence from 0000.
3325  * \param vmu
3326  * \param dir
3327  *
3328  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
3329  *
3330  * \return the count of messages, zero or more.
3331  */
3332 static int count_messages(struct ast_vm_user *vmu, char *dir)
3333 {
3334
3335         int vmcount = 0;
3336         DIR *vmdir = NULL;
3337         struct dirent *vment = NULL;
3338
3339         if (vm_lock_path(dir))
3340                 return ERROR_LOCK_PATH;
3341
3342         if ((vmdir = opendir(dir))) {
3343                 while ((vment = readdir(vmdir))) {
3344                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
3345                                 vmcount++;
3346                         }
3347                 }
3348                 closedir(vmdir);
3349         }
3350         ast_unlock_path(dir);
3351         
3352         return vmcount;
3353 }
3354
3355 /*!
3356  * \brief Renames a message in a mailbox folder.
3357  * \param sfn The path to the mailbox information and data file to be renamed.
3358  * \param dfn The path for where the message data and information files will be renamed to.
3359  *
3360  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
3361  */
3362 static void rename_file(char *sfn, char *dfn)
3363 {
3364         char stxt[PATH_MAX];
3365         char dtxt[PATH_MAX];
3366         ast_filerename(sfn,dfn,NULL);
3367         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
3368         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
3369         if (ast_check_realtime("voicemail_data")) {
3370                 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
3371         }
3372         rename(stxt, dtxt);
3373 }
3374
3375 /*! 
3376  * \brief Determines the highest message number in use for a given user and mailbox folder.
3377  * \param vmu 
3378  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3379  *
3380  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
3381  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
3382  *
3383  * \note Should always be called with a lock already set on dir.
3384  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
3385  */
3386 static int last_message_index(struct ast_vm_user *vmu, char *dir)
3387 {
3388         int x;
3389         unsigned char map[MAXMSGLIMIT] = "";
3390         DIR *msgdir;
3391         struct dirent *msgdirent;
3392         int msgdirint;
3393
3394         /* Reading the entire directory into a file map scales better than
3395          * doing a stat repeatedly on a predicted sequence.  I suspect this
3396          * is partially due to stat(2) internally doing a readdir(2) itself to
3397          * find each file. */
3398         msgdir = opendir(dir);
3399         while ((msgdirent = readdir(msgdir))) {
3400                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
3401                         map[msgdirint] = 1;
3402         }
3403         closedir(msgdir);
3404
3405         for (x = 0; x < vmu->maxmsg; x++) {
3406                 if (map[x] == 0)
3407                         break;
3408         }
3409
3410         return x - 1;
3411 }
3412
3413 #endif /* #ifndef IMAP_STORAGE */
3414 #endif /* #else of #ifdef ODBC_STORAGE */
3415 #ifndef IMAP_STORAGE
3416 /*!
3417  * \brief Utility function to copy a file.
3418  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
3419  * \param outfile The path for which to copy the file to. The directory permissions must allow the creation (or truncation) of the file, and allow for opening the file in write only mode.
3420  *
3421  * When the compiler option HARDLINK_WHEN_POSSIBLE is set, the copy operation will attempt to use the hard link facility instead of copy the file (to save disk space). If the link operation fails, it falls back to the copy operation.
3422  * The copy operation copies up to 4096 bytes at once.
3423  *
3424  * \return zero on success, -1 on error.
3425  */
3426 static int copy(char *infile, char *outfile)
3427 {
3428         int ifd;
3429         int ofd;
3430         int res;
3431         int len;
3432         char buf[4096];
3433
3434 #ifdef HARDLINK_WHEN_POSSIBLE
3435         /* Hard link if possible; saves disk space & is faster */
3436         if (link(infile, outfile)) {
3437 #endif
3438                 if ((ifd = open(infile, O_RDONLY)) < 0) {
3439                         ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
3440                         return -1;
3441                 }
3442                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
3443                         ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
3444                         close(ifd);
3445                         return -1;
3446                 }
3447                 do {
3448                         len = read(ifd, buf, sizeof(buf));
3449                         if (len < 0) {
3450                                 ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
3451                                 close(ifd);
3452                                 close(ofd);
3453                                 unlink(outfile);
3454                         }
3455                         if (len) {
3456                                 res = write(ofd, buf, len);
3457                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
3458                                         ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
3459                                         close(ifd);
3460                                         close(ofd);
3461                                         unlink(outfile);
3462                                 }
3463                         }
3464                 } while (len);
3465                 close(ifd);
3466                 close(ofd);
3467                 return 0;
3468 #ifdef HARDLINK_WHEN_POSSIBLE
3469         } else {
3470                 /* Hard link succeeded */
3471                 return 0;
3472         }
3473 #endif
3474 }
3475
3476 /*!
3477  * \brief Copies a voicemail information (envelope) file.
3478  * \param frompath
3479  * \param topath 
3480  *
3481  * Every voicemail has the data (.wav) file, and the information file.
3482  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
3483  * This is used by the COPY macro when not using IMAP storage.
3484  */
3485 static void copy_plain_file(char *frompath, char *topath)
3486 {
3487         char frompath2[PATH_MAX], topath2[PATH_MAX];
3488         struct ast_variable *tmp,*var = NULL;
3489         const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
3490         ast_filecopy(frompath, topath, NULL);
3491         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
3492         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
3493         if (ast_check_realtime("voicemail_data")) {
3494                 var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
3495                 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
3496                 for (tmp = var; tmp; tmp = tmp->next) {
3497                         if (!strcasecmp(tmp->name, "origmailbox")) {
3498                                 origmailbox = tmp->value;
3499                         } else if (!strcasecmp(tmp->name, "context")) {
3500                                 context = tmp->value;
3501                         } else if (!strcasecmp(tmp->name, "macrocontext")) {
3502                                 macrocontext = tmp->value;
3503                         } else if (!strcasecmp(tmp->name, "exten")) {
3504                                 exten = tmp->value;
3505                         } else if (!strcasecmp(tmp->name, "priority")) {
3506                                 priority = tmp->value;
3507                         } else if (!strcasecmp(tmp->name, "callerchan")) {
3508                                 callerchan = tmp->value;
3509                         } else if (!strcasecmp(tmp->name, "callerid")) {
3510                                 callerid = tmp->value;
3511                         } else if (!strcasecmp(tmp->name, "origdate")) {
3512                                 origdate = tmp->value;
3513                         } else if (!strcasecmp(tmp->name, "origtime")) {
3514                                 origtime = tmp->value;
3515                         } else if (!strcasecmp(tmp->name, "category")) {
3516                                 category = tmp->value;
3517                         } else if (!strcasecmp(tmp->name, "duration")) {
3518                                 duration = tmp->value;
3519                         }
3520                 }
3521                 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, SENTINEL);
3522         }
3523         copy(frompath2, topath2);
3524         ast_variables_destroy(var);
3525 }
3526
3527 /*! 
3528  * \brief Removes the voicemail sound and information file.
3529  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
3530  *
3531  * This is used by the DELETE macro when voicemails are stored on the file system.
3532  *
3533  * \return zero on success, -1 on error.
3534  */
3535 static int vm_delete(char *file)
3536 {
3537         char *txt;
3538         int txtsize = 0;
3539
3540         txtsize = (strlen(file) + 5)*sizeof(char);
3541         txt = alloca(txtsize);
3542         /* Sprintf here would safe because we alloca'd exactly the right length,
3543          * but trying to eliminate all sprintf's anyhow
3544          */
3545         if (ast_check_realtime("voicemail_data")) {
3546                 ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
3547         }
3548         snprintf(txt, txtsize, "%s.txt", file);
3549         unlink(txt);
3550         return ast_filedelete(file, NULL);
3551 }
3552 #endif
3553
3554 /*!
3555  * \brief utility used by inchar(), for base_encode()
3556  */
3557 static int inbuf(struct baseio *bio, FILE *fi)
3558 {
3559         int l;
3560
3561         if (bio->ateof)
3562                 return 0;
3563
3564         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
3565                 if (ferror(fi))
3566                         return -1;
3567
3568                 bio->ateof = 1;
3569                 return 0;
3570         }
3571
3572         bio->iolen= l;
3573         bio->iocp= 0;
3574
3575         return 1;
3576 }
3577
3578 /*!
3579  * \brief utility used by base_encode()
3580  */
3581 static int inchar(struct baseio *bio, FILE *fi)
3582 {
3583         if (bio->iocp>=bio->iolen) {
3584                 if (!inbuf(bio, fi))
3585                         return EOF;
3586         }
3587
3588         return bio->iobuf[bio->iocp++];
3589 }
3590
3591 /*!
3592  * \brief utility used by base_encode()
3593  */
3594 static int ochar(struct baseio *bio, int c, FILE *so)
3595 {
3596         if (bio->linelength >= BASELINELEN) {
3597                 if (fputs(eol,so) == EOF)
3598                         return -1;
3599
3600                 bio->linelength= 0;
3601         }
3602
3603         if (putc(((unsigned char)c),so) == EOF)
3604                 return -1;
3605
3606         bio->linelength++;
3607
3608         return 1;
3609 }
3610
3611 /*!
3612  * \brief Performs a base 64 encode algorithm on the contents of a File
3613  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
3614  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
3615  *
3616  * TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ?
3617  *
3618  * \return zero on success, -1 on error.
3619  */
3620 static int base_encode(char *filename, FILE *so)
3621 {
3622         static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
3623                 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
3624                 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
3625                 '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
3626         int i,hiteof= 0;
3627         FILE *fi;
3628         struct baseio bio;
3629
3630         memset(&bio, 0, sizeof(bio));
3631         bio.iocp = BASEMAXINLINE;
3632
3633         if (!(fi = fopen(filename, "rb"))) {
3634                 ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
3635                 return -1;
3636         }
3637
3638         while (!hiteof){
3639                 unsigned char igroup[3], ogroup[4];
3640                 int c,n;
3641
3642                 igroup[0]= igroup[1]= igroup[2]= 0;
3643
3644                 for (n= 0;n<3;n++) {
3645                         if ((c = inchar(&bio, fi)) == EOF) {
3646                                 hiteof= 1;
3647                                 break;
3648                         }
3649
3650                         igroup[n]= (unsigned char)c;
3651                 }
3652
3653                 if (n> 0) {
3654                         ogroup[0]= dtable[igroup[0]>>2];
3655                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
3656                         ogroup[2]= dtable[((igro