Merged revisions 147193 via svnmerge from
[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         } else {
853                 ast_log(LOG_WARNING, "Unknown option '%s' specified for mailbox '%s'.\n", var, vmu->mailbox);
854         }
855 }
856
857 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
858 {
859         int fds[2], pid = 0;
860
861         memset(buf, 0, len);
862
863         if (pipe(fds)) {
864                 snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
865         } else {
866                 /* good to go*/
867                 pid = ast_safe_fork(0);
868
869                 if (pid < 0) {
870                         /* ok maybe not */
871                         close(fds[0]);
872                         close(fds[1]);
873                         snprintf(buf, len, "FAILURE: Fork failed");
874                 } else if (pid) {
875                         /* parent */
876                         close(fds[1]);
877                         read(fds[0], buf, len);
878                         close(fds[0]);
879                 } else {
880                         /*  child */
881                         AST_DECLARE_APP_ARGS(arg,
882                                 AST_APP_ARG(v)[20];
883                         );
884                         char *mycmd = ast_strdupa(command);
885
886                         close(fds[0]);
887                         dup2(fds[1], STDOUT_FILENO);
888                         close(fds[1]);
889                         ast_close_fds_above_n(STDOUT_FILENO);
890
891                         AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
892
893                         execv(arg.v[0], arg.v); 
894                         printf("FAILURE: %s", strerror(errno));
895                         _exit(0);
896                 }
897         }
898         return buf;
899 }
900
901 /*!
902  * \brief Check that password meets minimum required length
903  * \param vmu The voicemail user to change the password for.
904  * \param password The password string to check
905  *
906  * \return zero on ok, 1 on not ok.
907  */
908 static int check_password(struct ast_vm_user *vmu, char *password)
909 {
910         /* check minimum length */
911         if (strlen(password) < minpassword)
912                 return 1;
913         if (!ast_strlen_zero(ext_pass_check_cmd)) {
914                 char cmd[255], buf[255];
915
916                 ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
917
918                 snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
919                 if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
920                         ast_debug(5, "Result: %s\n", buf);
921                         if (!strncasecmp(buf, "VALID", 5)) {
922                                 ast_debug(3, "Passed password check: '%s'\n", buf);
923                                 return 0;
924                         } else if (!strncasecmp(buf, "FAILURE", 7)) {
925                                 ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
926                                 return 0;
927                         } else {
928                                 ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
929                                 return 1;
930                         }
931                 }
932         }
933         return 0;
934 }
935
936 /*! 
937  * \brief Performs a change of the voicemail passowrd in the realtime engine.
938  * \param vmu The voicemail user to change the password for.
939  * \param password The new value to be set to the password for this user.
940  * 
941  * This only works if the voicemail user has a unique id, and if there is a realtime engine configured.
942  * This is called from the (top level) vm_change_password.
943  *
944  * \return zero on success, -1 on error.
945  */
946 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
947 {
948         int res;
949         if (!ast_strlen_zero(vmu->uniqueid)) {
950                 if (strlen(password) > 10) {
951                         ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
952                 }
953                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, SENTINEL);
954                 if (res > 0) {
955                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
956                         res = 0;
957                 } else if (!res) {
958                         res = -1;
959                 }
960                 return res;
961         }
962         return -1;
963 }
964
965 /*!
966  * \brief Destructively Parse options and apply.
967  */
968 static void apply_options(struct ast_vm_user *vmu, const char *options)
969 {       
970         char *stringp;
971         char *s;
972         char *var, *value;
973         stringp = ast_strdupa(options);
974         while ((s = strsep(&stringp, "|"))) {
975                 value = s;
976                 if ((var = strsep(&value, "=")) && value) {
977                         apply_option(vmu, var, value);
978                 }
979         }       
980 }
981
982 /*!
983  * \brief Loads the options specific to a voicemail user.
984  * 
985  * This is called when a vm_user structure is being set up, such as from load_options.
986  */
987 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
988 {
989         struct ast_variable *tmp;
990         tmp = var;
991         while (tmp) {
992                 if (!strcasecmp(tmp->name, "vmsecret")) {
993                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
994                 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
995                         if (ast_strlen_zero(retval->password))
996                                 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
997                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
998                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
999                 } else if (!strcasecmp(tmp->name, "pager")) {
1000                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
1001                 } else if (!strcasecmp(tmp->name, "email")) {
1002                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
1003                 } else if (!strcasecmp(tmp->name, "fullname")) {
1004                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
1005                 } else if (!strcasecmp(tmp->name, "context")) {
1006                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
1007 #ifdef IMAP_STORAGE
1008                 } else if (!strcasecmp(tmp->name, "imapuser")) {
1009                         ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
1010                 } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
1011                         ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
1012 #endif
1013                 } else
1014                         apply_option(retval, tmp->name, tmp->value);
1015                 tmp = tmp->next;
1016         } 
1017 }
1018
1019 /*!
1020  * \brief Determines if a DTMF key entered is valid.
1021  * \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.
1022  *
1023  * Tests the character entered against the set of valid DTMF characters. 
1024  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1025  */
1026 static int is_valid_dtmf(const char *key)
1027 {
1028         int i;
1029         char *local_key = ast_strdupa(key);
1030
1031         for (i = 0; i < strlen(key); ++i) {
1032                 if (!strchr(VALID_DTMF, *local_key)) {
1033                         ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1034                         return 0;
1035                 }
1036                 local_key++;
1037         }
1038         return 1;
1039 }
1040
1041 /*!
1042  * \brief Finds a voicemail user from the realtime engine.
1043  * \param ivm
1044  * \param context
1045  * \param mailbox
1046  *
1047  * 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.
1048  *
1049  * \return The ast_vm_user structure for the user that was found.
1050  */
1051 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1052 {
1053         struct ast_variable *var;
1054         struct ast_vm_user *retval;
1055
1056         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1057                 if (!ivm)
1058                         ast_set_flag(retval, VM_ALLOCED);       
1059                 else
1060                         memset(retval, 0, sizeof(*retval));
1061                 if (mailbox) 
1062                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1063                 populate_defaults(retval);
1064                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
1065                         var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1066                 else
1067                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1068                 if (var) {
1069                         apply_options_full(retval, var);
1070                         ast_variables_destroy(var);
1071                 } else { 
1072                         if (!ivm) 
1073                                 ast_free(retval);
1074                         retval = NULL;
1075                 }       
1076         } 
1077         return retval;
1078 }
1079
1080 /*!
1081  * \brief Finds a voicemail user from the users file or the realtime engine.
1082  * \param ivm
1083  * \param context
1084  * \param mailbox
1085  * 
1086  * \return The ast_vm_user structure for the user that was found.
1087  */
1088 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1089 {
1090         /* This function could be made to generate one from a database, too */
1091         struct ast_vm_user *vmu=NULL, *cur;
1092         AST_LIST_LOCK(&users);
1093
1094         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1095                 context = "default";
1096
1097         AST_LIST_TRAVERSE(&users, cur, list) {
1098                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1099                         break;
1100                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1101                         break;
1102         }
1103         if (cur) {
1104                 /* Make a copy, so that on a reload, we have no race */
1105                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
1106                         memcpy(vmu, cur, sizeof(*vmu));
1107                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1108                         AST_LIST_NEXT(vmu, list) = NULL;
1109                 }
1110         } else
1111                 vmu = find_user_realtime(ivm, context, mailbox);
1112         AST_LIST_UNLOCK(&users);
1113         return vmu;
1114 }
1115
1116 /*!
1117  * \brief Resets a user password to a specified password.
1118  * \param context
1119  * \param mailbox
1120  * \param newpass
1121  *
1122  * This does the actual change password work, called by the vm_change_password() function.
1123  *
1124  * \return zero on success, -1 on error.
1125  */
1126 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1127 {
1128         /* This function could be made to generate one from a database, too */
1129         struct ast_vm_user *cur;
1130         int res = -1;
1131         AST_LIST_LOCK(&users);
1132         AST_LIST_TRAVERSE(&users, cur, list) {
1133                 if ((!context || !strcasecmp(context, cur->context)) &&
1134                         (!strcasecmp(mailbox, cur->mailbox)))
1135                                 break;
1136         }
1137         if (cur) {
1138                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
1139                 res = 0;
1140         }
1141         AST_LIST_UNLOCK(&users);
1142         return res;
1143 }
1144
1145 /*! 
1146  * \brief The handler for the change password option.
1147  * \param vmu The voicemail user to work with.
1148  * \param newpassword The new password (that has been gathered from the appropriate prompting).
1149  * 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.
1150  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1151  */
1152 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1153 {
1154         struct ast_config   *cfg=NULL;
1155         struct ast_variable *var=NULL;
1156         struct ast_category *cat=NULL;
1157         char *category=NULL, *value=NULL, *new=NULL;
1158         const char *tmp=NULL;
1159         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1160         if (!change_password_realtime(vmu, newpassword))
1161                 return;
1162
1163         /* check voicemail.conf */
1164         if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1165                 while ((category = ast_category_browse(cfg, category))) {
1166                         if (!strcasecmp(category, vmu->context)) {
1167                                 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1168                                         ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1169                                         break;
1170                                 }
1171                                 value = strstr(tmp,",");
1172                                 if (!value) {
1173                                         ast_log(AST_LOG_WARNING, "variable has bad format.\n");
1174                                         break;
1175                                 }
1176                                 new = alloca((strlen(value)+strlen(newpassword)+1));
1177                                 sprintf(new,"%s%s", newpassword, value);
1178                                 if (!(cat = ast_category_get(cfg, category))) {
1179                                         ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1180                                         break;
1181                                 }
1182                                 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1183                         }
1184                 }
1185                 /* save the results */
1186                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1187                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1188                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
1189         }
1190         category = NULL;
1191         var = NULL;
1192         /* check users.conf and update the password stored for the mailbox*/
1193         /* if no vmsecret entry exists create one. */
1194         if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1195                 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1196                 while ((category = ast_category_browse(cfg, category))) {
1197                         ast_debug(4, "users.conf: %s\n", category);
1198                         if (!strcasecmp(category, vmu->mailbox)) {
1199                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
1200                                         ast_debug(3, "looks like we need to make vmsecret!\n");
1201                                         var = ast_variable_new("vmsecret", newpassword, "");
1202                                 } 
1203                                 new = alloca(strlen(newpassword)+1);
1204                                 sprintf(new, "%s", newpassword);
1205                                 if (!(cat = ast_category_get(cfg, category))) {
1206                                         ast_debug(4, "failed to get category!\n");
1207                                         break;
1208                                 }
1209                                 if (!var)               
1210                                         ast_variable_update(cat, "vmsecret", new, NULL, 0);
1211                                 else
1212                                         ast_variable_append(cat, var);
1213                         }
1214                 }
1215                 /* save the results and clean things up */
1216                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
1217                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1218                 config_text_file_save("users.conf", cfg, "AppVoicemail");
1219         }
1220 }
1221
1222 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1223 {
1224         char buf[255];
1225         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
1226         if (!ast_safe_system(buf)) {
1227                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1228                 /* Reset the password in memory, too */
1229                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1230         }
1231 }
1232
1233 /*! 
1234  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1235  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1236  * \param len The length of the path string that was written out.
1237  * 
1238  * The path is constructed as 
1239  *      VM_SPOOL_DIRcontext/ext/folder
1240  *
1241  * \return zero on success, -1 on error.
1242  */
1243 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1244 {
1245         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1246 }
1247
1248 /*! 
1249  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1250  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1251  * \param len The length of the path string that was written out.
1252  * 
1253  * The path is constructed as 
1254  *      VM_SPOOL_DIRcontext/ext/folder
1255  *
1256  * \return zero on success, -1 on error.
1257  */
1258 static int make_file(char *dest, const int len, const char *dir, const int num)
1259 {
1260         return snprintf(dest, len, "%s/msg%04d", dir, num);
1261 }
1262
1263 /* same as mkstemp, but return a FILE * */
1264 static FILE *vm_mkftemp(char *template)
1265 {
1266         FILE *p = NULL;
1267         int pfd = mkstemp(template);
1268         chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1269         if (pfd > -1) {
1270                 p = fdopen(pfd, "w+");
1271                 if (!p) {
1272                         close(pfd);
1273                         pfd = -1;
1274                 }
1275         }
1276         return p;
1277 }
1278
1279 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1280  * \param dest    String. base directory.
1281  * \param len     Length of dest.
1282  * \param context String. Ignored if is null or empty string.
1283  * \param ext     String. Ignored if is null or empty string.
1284  * \param folder  String. Ignored if is null or empty string. 
1285  * \return -1 on failure, 0 on success.
1286  */
1287 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1288 {
1289         mode_t  mode = VOICEMAIL_DIR_MODE;
1290         int res;
1291
1292         make_dir(dest, len, context, ext, folder);
1293         if ((res = ast_mkdir(dest, mode))) {
1294                 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1295                 return -1;
1296         }
1297         return 0;
1298 }
1299
1300 static const char *mbox(int id)
1301 {
1302         static const char *msgs[] = {
1303 #ifdef IMAP_STORAGE
1304                 imapfolder,
1305 #else
1306                 "INBOX",
1307 #endif
1308                 "Old",
1309                 "Work",
1310                 "Family",
1311                 "Friends",
1312                 "Cust1",
1313                 "Cust2",
1314                 "Cust3",
1315                 "Cust4",
1316                 "Cust5",
1317                 "Deleted",
1318                 "Urgent"
1319         };
1320         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
1321 }
1322
1323 static void free_user(struct ast_vm_user *vmu)
1324 {
1325         if (ast_test_flag(vmu, VM_ALLOCED))
1326                 ast_free(vmu);
1327 }
1328
1329 /* All IMAP-specific functions should go in this block. This
1330  * keeps them from being spread out all over the code */
1331 #ifdef IMAP_STORAGE
1332 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
1333 {
1334         char arg[10];
1335         struct vm_state *vms;
1336         unsigned long messageNum;
1337
1338         /* Greetings aren't stored in IMAP, so we can't delete them there */
1339         if (msgnum < 0) {
1340                 return;
1341         }
1342
1343         if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1344                 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);
1345                 return;
1346         }
1347
1348         /* find real message number based on msgnum */
1349         /* this may be an index into vms->msgArray based on the msgnum. */
1350         messageNum = vms->msgArray[msgnum];
1351         if (messageNum == 0) {
1352                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1353                 return;
1354         }
1355         if (option_debug > 2)
1356                 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1357         /* delete message */
1358         snprintf (arg, sizeof(arg), "%lu",messageNum);
1359         mail_setflag (vms->mailstream,arg,"\\DELETED");
1360 }
1361
1362 static int imap_retrieve_greeting (const char *dir, const int msgnum, struct ast_vm_user *vmu)
1363 {
1364         struct vm_state *vms_p;
1365         char *file, *filename;
1366         char *attachment;
1367         int ret = 0, i;
1368         BODY *body;
1369
1370         /* This function is only used for retrieval of IMAP greetings
1371          * regular messages are not retrieved this way, nor are greetings
1372          * if they are stored locally*/
1373         if (msgnum > -1 || !imapgreetings) {
1374                 return 0;
1375         } else {
1376                 file = strrchr(ast_strdupa(dir), '/');
1377                 if (file)
1378                         *file++ = '\0';
1379                 else {
1380                         ast_debug (1, "Failed to procure file name from directory passed.\n");
1381                         return -1;
1382                 }
1383         }
1384
1385         /* check if someone is accessing this box right now... */
1386         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))) {
1387                 ast_log(AST_LOG_ERROR, "Voicemail state not found!\n");
1388                 return -1;
1389         }
1390         
1391         /* Greetings will never have a prepended message */
1392         *vms_p->introfn = '\0';
1393         
1394         ret = init_mailstream(vms_p, GREETINGS_FOLDER);
1395         if (!vms_p->mailstream) {
1396                 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
1397                 return -1;
1398         }
1399
1400         /*XXX Yuck, this could probably be done a lot better */
1401         for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
1402                 mail_fetchstructure(vms_p->mailstream, i + 1, &body);
1403                 /* We have the body, now we extract the file name of the first attachment. */
1404                 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1405                         attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
1406                 } else {
1407                         ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
1408                         return -1;
1409                 }
1410                 filename = strsep(&attachment, ".");
1411                 if (!strcmp(filename, file)) {
1412                         ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
1413                         vms_p->msgArray[vms_p->curmsg] = i + 1;
1414                         save_body(body, vms_p, "2", attachment, 0);
1415                         return 0;
1416                 }
1417         }
1418
1419         return -1;
1420 }
1421
1422 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
1423 {
1424         BODY *body;
1425         char *header_content;
1426         char *attachedfilefmt;
1427         char buf[80];
1428         struct vm_state *vms;
1429         char text_file[PATH_MAX];
1430         FILE *text_file_ptr;
1431         int res = 0;
1432         struct ast_vm_user *vmu;
1433
1434         if (!(vmu = find_user(NULL, context, mailbox))) {
1435                 ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
1436                 return -1;
1437         }
1438         
1439         if (msgnum < 0) {
1440                 if (imapgreetings) {
1441                         res = imap_retrieve_greeting(dir, msgnum, vmu);
1442                         goto exit;
1443                 } else {
1444                         res = 0;
1445                         goto exit;
1446                 }
1447         }
1448
1449         /* Before anything can happen, we need a vm_state so that we can
1450          * actually access the imap server through the vms->mailstream
1451          */
1452         if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1453                 /* This should not happen. If it does, then I guess we'd
1454                  * need to create the vm_state, extract which mailbox to
1455                  * open, and then set up the msgArray so that the correct
1456                  * IMAP message could be accessed. If I have seen correctly
1457                  * though, the vms should be obtainable from the vmstates list
1458                  * and should have its msgArray properly set up.
1459                  */
1460                 ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
1461                 res = -1;
1462                 goto exit;
1463         }
1464         
1465         make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
1466         snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
1467
1468         /* Don't try to retrieve a message from IMAP if it already is on the file system */
1469         if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
1470                 res = 0;
1471                 goto exit;
1472         }
1473
1474         if (option_debug > 2)
1475                 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
1476         if (vms->msgArray[msgnum] == 0) {
1477                 ast_log (LOG_WARNING,"Trying to access unknown message\n");
1478                 res = -1;
1479                 goto exit;
1480         }
1481
1482         /* This will only work for new messages... */
1483         header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
1484         /* empty string means no valid header */
1485         if (ast_strlen_zero(header_content)) {
1486                 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
1487                 res = -1;
1488                 goto exit;
1489         }
1490
1491         mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
1492         
1493         /* We have the body, now we extract the file name of the first attachment. */
1494         if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1495                 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
1496         } else {
1497                 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
1498                 res = -1;
1499                 goto exit;
1500         }
1501         
1502         /* Find the format of the attached file */
1503
1504         strsep(&attachedfilefmt, ".");
1505         if (!attachedfilefmt) {
1506                 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
1507                 res = -1;
1508                 goto exit;
1509         }
1510         
1511         save_body(body, vms, "2", attachedfilefmt, 0);
1512         if (save_body(body, vms, "3", attachedfilefmt, 1)) {
1513                 *vms->introfn = '\0';
1514         }
1515
1516         /* Get info from headers!! */
1517         snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
1518
1519         if (!(text_file_ptr = fopen(text_file, "w"))) {
1520                 ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
1521         }
1522
1523         fprintf(text_file_ptr, "%s\n", "[message]");
1524
1525         get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
1526         fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
1527         get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
1528         fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
1529         get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
1530         fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
1531         get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
1532         fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
1533         get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
1534         fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
1535         get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
1536         fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
1537         fclose(text_file_ptr);
1538
1539 exit:
1540         free_user(vmu);
1541         return res;
1542 }
1543
1544 static int folder_int(const char *folder)
1545 {
1546         /*assume a NULL folder means INBOX*/
1547         if (!folder)
1548                 return 0;
1549 #ifdef IMAP_STORAGE
1550         if (!strcasecmp(folder, imapfolder))
1551 #else
1552         if (!strcasecmp(folder, "INBOX"))
1553 #endif
1554                 return 0;
1555         else if (!strcasecmp(folder, "Old"))
1556                 return 1;
1557         else if (!strcasecmp(folder, "Work"))
1558                 return 2;
1559         else if (!strcasecmp(folder, "Family"))
1560                 return 3;
1561         else if (!strcasecmp(folder, "Friends"))
1562                 return 4;
1563         else if (!strcasecmp(folder, "Cust1"))
1564                 return 5;
1565         else if (!strcasecmp(folder, "Cust2"))
1566                 return 6;
1567         else if (!strcasecmp(folder, "Cust3"))
1568                 return 7;
1569         else if (!strcasecmp(folder, "Cust4"))
1570                 return 8;
1571         else if (!strcasecmp(folder, "Cust5"))
1572                 return 9;
1573         else /*assume they meant INBOX if folder is not found otherwise*/
1574                 return 0;
1575 }
1576
1577 /*!
1578  * \brief Gets the number of messages that exist in a mailbox folder.
1579  * \param context
1580  * \param mailbox
1581  * \param folder
1582  * 
1583  * This method is used when IMAP backend is used.
1584  * \return The number of messages in this mailbox folder (zero or more).
1585  */
1586 static int messagecount(const char *context, const char *mailbox, const char *folder)
1587 {
1588         SEARCHPGM *pgm;
1589         SEARCHHEADER *hdr;
1590
1591         struct ast_vm_user *vmu, vmus;
1592         struct vm_state *vms_p;
1593         int ret = 0;
1594         int fold = folder_int(folder);
1595         int urgent = 0;
1596         
1597         if (ast_strlen_zero(mailbox))
1598                 return 0;
1599
1600         /* We have to get the user before we can open the stream! */
1601         vmu = find_user(&vmus, context, mailbox);
1602         if (!vmu) {
1603                 ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
1604                 return -1;
1605         } else {
1606                 /* No IMAP account available */
1607                 if (vmu->imapuser[0] == '\0') {
1608                         ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
1609                         return -1;
1610                 }
1611         }
1612         
1613         /* No IMAP account available */
1614         if (vmu->imapuser[0] == '\0') {
1615                 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
1616                 free_user(vmu);
1617                 return -1;
1618         }
1619
1620         /* check if someone is accessing this box right now... */
1621         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
1622         if (!vms_p) {
1623                 vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
1624         }
1625         if (vms_p) {
1626                 ast_debug(3, "Returning before search - user is logged in\n");
1627                 if (fold == 0) { /* INBOX */
1628                         return vms_p->newmessages;
1629                 }
1630                 if (fold == 1) { /* Old messages */
1631                         return vms_p->oldmessages;
1632                 }
1633                 if (fold == 11) {/*Urgent messages*/
1634                         return vms_p->urgentmessages;
1635                 }
1636         }
1637
1638         /* add one if not there... */
1639         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
1640         if (!vms_p) {
1641                 vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
1642         }
1643
1644         /* If URGENT, then look at INBOX */
1645         if (fold == 11) {
1646                 fold = NEW_FOLDER;
1647                 urgent = 1;
1648         }
1649
1650         if (!vms_p) {
1651                 ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
1652                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
1653                         return -1;
1654                 }
1655                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
1656                 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
1657                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
1658                 ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
1659                 vms_p->updated = 1;
1660                 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
1661                 init_vm_state(vms_p);
1662                 vmstate_insert(vms_p);
1663         }
1664         ret = init_mailstream(vms_p, fold);
1665         if (!vms_p->mailstream) {
1666                 ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
1667                 return -1;
1668         }
1669         if (ret == 0) {
1670                 pgm = mail_newsearchpgm ();
1671                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
1672                 pgm->header = hdr;
1673                 if (fold != 1) {
1674                         pgm->unseen = 1;
1675                         pgm->seen = 0;
1676                 }
1677                 /* In the special case where fold is 1 (old messages) we have to do things a bit
1678                  * differently. Old messages are stored in the INBOX but are marked as "seen"
1679                  */
1680                 else {
1681                         pgm->unseen = 0;
1682                         pgm->seen = 1;
1683                 }
1684                 /* look for urgent messages */
1685                 if (urgent) {
1686                         pgm->flagged = 1;
1687                         pgm->unflagged = 0;
1688                 }
1689                 pgm->undeleted = 1;
1690                 pgm->deleted = 0;
1691
1692                 vms_p->vmArrayIndex = 0;
1693                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
1694                 if (fold == 0 && urgent == 0)
1695                         vms_p->newmessages = vms_p->vmArrayIndex;
1696                 if (fold == 1)
1697                         vms_p->oldmessages = vms_p->vmArrayIndex;
1698                 if (fold == 0 && urgent == 1)
1699                         vms_p->urgentmessages = vms_p->vmArrayIndex;
1700                 /*Freeing the searchpgm also frees the searchhdr*/
1701                 mail_free_searchpgm(&pgm);
1702                 vms_p->updated = 0;
1703                 return vms_p->vmArrayIndex;
1704         } else {  
1705                 mail_ping(vms_p->mailstream);
1706         }
1707         return 0;
1708 }
1709
1710 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)
1711 {
1712         char *myserveremail = serveremail;
1713         char fn[PATH_MAX];
1714         char introfn[PATH_MAX];
1715         char mailbox[256];
1716         char *stringp;
1717         FILE *p=NULL;
1718         char tmp[80] = "/tmp/astmail-XXXXXX";
1719         long len;
1720         void *buf;
1721         int tempcopy = 0;
1722         STRING str;
1723         int ret; /* for better error checking */
1724         char *imap_flags = NIL;
1725
1726         /* Set urgent flag for IMAP message */
1727         if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
1728                 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
1729                 imap_flags="\\FLAGGED";
1730         }
1731         
1732         /* Attach only the first format */
1733         fmt = ast_strdupa(fmt);
1734         stringp = fmt;
1735         strsep(&stringp, "|");
1736
1737         if (!ast_strlen_zero(vmu->serveremail))
1738                 myserveremail = vmu->serveremail;
1739
1740         if (msgnum > -1)
1741                 make_file(fn, sizeof(fn), dir, msgnum);
1742         else
1743                 ast_copy_string (fn, dir, sizeof(fn));
1744
1745         snprintf(introfn, sizeof(introfn), "%sintro", fn);
1746         if (ast_fileexists(introfn, NULL, NULL) <= 0) {
1747                 *introfn = '\0';
1748         }
1749         
1750         if (ast_strlen_zero(vmu->email)) {
1751                 /* We need the vmu->email to be set when we call make_email_file, but
1752                  * if we keep it set, a duplicate e-mail will be created. So at the end
1753                  * of this function, we will revert back to an empty string if tempcopy
1754                  * is 1.
1755                  */
1756                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
1757                 tempcopy = 1;
1758         }
1759
1760         if (!strcmp(fmt, "wav49"))
1761                 fmt = "WAV";
1762         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
1763
1764         /* Make a temporary file instead of piping directly to sendmail, in case the mail
1765            command hangs. */
1766         if (!(p = vm_mkftemp(tmp))) {
1767                 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
1768                 if (tempcopy)
1769                         *(vmu->email) = '\0';
1770                 return -1;
1771         }
1772
1773         if (msgnum < 0 && imapgreetings) {
1774                 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
1775                         ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
1776                         return -1;
1777                 }
1778                 imap_delete_old_greeting(fn, vms);
1779         }
1780         
1781         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);
1782         /* read mail file to memory */          
1783         len = ftell(p);
1784         rewind(p);
1785         if (!(buf = ast_malloc(len + 1))) {
1786                 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
1787                 fclose(p);
1788                 if (tempcopy)
1789                         *(vmu->email) = '\0';
1790                 return -1;
1791         }
1792         fread(buf, len, 1, p);
1793         ((char *)buf)[len] = '\0';
1794         INIT(&str, mail_string, buf, len);
1795         ret = init_mailstream(vms, NEW_FOLDER);
1796         if (ret == 0) {
1797                 imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
1798                 if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
1799                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
1800                 fclose(p);
1801                 unlink(tmp);
1802                 ast_free(buf);
1803         } else {
1804                 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
1805                 fclose(p);
1806                 unlink(tmp);
1807                 ast_free(buf);
1808                 return -1;
1809         }
1810         ast_debug(3, "%s stored\n", fn);
1811         
1812         if (tempcopy)
1813                 *(vmu->email) = '\0';
1814         
1815         return 0;
1816
1817 }
1818
1819 /*!
1820  * \brief Gets the number of messages that exist in the inbox folder.
1821  * \param mailbox_context
1822  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
1823  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
1824  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
1825  * 
1826  * This method is used when IMAP backend is used.
1827  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
1828  *
1829  * \return zero on success, -1 on error.
1830  */
1831
1832 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
1833 {
1834         char tmp[PATH_MAX] = "";
1835         char *mailboxnc;
1836         char *context;
1837         char *mb;
1838         char *cur;
1839         if (newmsgs)
1840                 *newmsgs = 0;
1841         if (oldmsgs)
1842                 *oldmsgs = 0;
1843         if (urgentmsgs)
1844                 *urgentmsgs = 0;
1845
1846         ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
1847         /* If no mailbox, return immediately */
1848         if (ast_strlen_zero(mailbox_context))
1849                 return 0;
1850         
1851         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
1852         context = strchr(tmp, '@');
1853         if (strchr(mailbox_context, ',')) {
1854                 int tmpnew, tmpold, tmpurgent;
1855                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
1856                 mb = tmp;
1857                 while ((cur = strsep(&mb, ", "))) {
1858                         if (!ast_strlen_zero(cur)) {
1859                                 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
1860                                         return -1;
1861                                 else {
1862                                         if (newmsgs)
1863                                                 *newmsgs += tmpnew; 
1864                                         if (oldmsgs)
1865                                                 *oldmsgs += tmpold;
1866                                         if (urgentmsgs)
1867                                                 *urgentmsgs += tmpurgent;
1868                                 }
1869                         }
1870                 }
1871                 return 0;
1872         }
1873         if (context) {
1874                 *context = '\0';
1875                 mailboxnc = tmp;
1876                 context++;
1877         } else {
1878                 context = "default";
1879                 mailboxnc = (char *)mailbox_context;
1880         }
1881         if (newmsgs) {
1882                 if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
1883                         return -1;
1884         }
1885         if (oldmsgs) {
1886                 if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
1887                         return -1;
1888         }
1889         if (urgentmsgs) {
1890                 if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
1891                         return -1;
1892         }
1893         return 0;
1894 }
1895
1896 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
1897 {
1898         return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
1899 }
1900
1901 /** 
1902  * \brief Determines if the given folder has messages.
1903  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
1904  * \param folder the folder to look in
1905  *
1906  * This function is used when the mailbox is stored in an IMAP back end.
1907  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
1908  * \return 1 if the folder has one or more messages. zero otherwise.
1909  */
1910
1911 static int has_voicemail(const char *mailbox, const char *folder)
1912 {
1913         char tmp[256], *tmp2, *box, *context;
1914         ast_copy_string(tmp, mailbox, sizeof(tmp));
1915         tmp2 = tmp;
1916         if (strchr(tmp2, ',')) {
1917                 while ((box = strsep(&tmp2, ","))) {
1918                         if (!ast_strlen_zero(box)) {
1919                                 if (has_voicemail(box, folder))
1920                                         return 1;
1921                         }
1922                 }
1923         }
1924         if ((context= strchr(tmp, '@')))
1925                 *context++ = '\0';
1926         else
1927                 context = "default";
1928         return messagecount(context, tmp, folder) ? 1 : 0;
1929 }
1930
1931 /*!
1932  * \brief Copies a message from one mailbox to another.
1933  * \param chan
1934  * \param vmu
1935  * \param imbox
1936  * \param msgnum
1937  * \param duration
1938  * \param recip
1939  * \param fmt
1940  * \param dir
1941  *
1942  * This works with IMAP storage based mailboxes.
1943  *
1944  * \return zero on success, -1 on error.
1945  */
1946 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)
1947 {
1948         struct vm_state *sendvms = NULL, *destvms = NULL;
1949         char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
1950         if (msgnum >= recip->maxmsg) {
1951                 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
1952                 return -1;
1953         }
1954         if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
1955                 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
1956                 return -1;
1957         }
1958         if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
1959                 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
1960                 return -1;
1961         }
1962         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
1963         if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
1964                 return 0;
1965         ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
1966         return -1;
1967 }
1968
1969 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
1970 {
1971         char tmp[256], *t = tmp;
1972         size_t left = sizeof(tmp);
1973         
1974         if (box == OLD_FOLDER) {
1975                 ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
1976         } else {
1977                 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
1978         }
1979
1980         if (box == NEW_FOLDER) {
1981                 ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
1982         } else {
1983                 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
1984         }
1985
1986         /* Build up server information */
1987         ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
1988
1989         /* Add authentication user if present */
1990         if (!ast_strlen_zero(authuser))
1991                 ast_build_string(&t, &left, "/authuser=%s", authuser);
1992
1993         /* Add flags if present */
1994         if (!ast_strlen_zero(imapflags))
1995                 ast_build_string(&t, &left, "/%s", imapflags);
1996
1997         /* End with username */
1998         ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
1999         if (box == NEW_FOLDER || box == OLD_FOLDER)
2000                 snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
2001         else if (box == GREETINGS_FOLDER)
2002                 snprintf(spec, len, "%s%s", tmp, greetingfolder);
2003         else {  /* Other folders such as Friends, Family, etc... */
2004                 if (!ast_strlen_zero(imapparentfolder)) {
2005                         /* imapparentfolder would typically be set to INBOX */
2006                         snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
2007                 } else {
2008                         snprintf(spec, len, "%s%s", tmp, mbox(box));
2009                 }
2010         }
2011 }
2012
2013 static int init_mailstream(struct vm_state *vms, int box)
2014 {
2015         MAILSTREAM *stream = NIL;
2016         long debug;
2017         char tmp[256];
2018         
2019         if (!vms) {
2020                 ast_log (LOG_ERROR,"vm_state is NULL!\n");
2021                 return -1;
2022         }
2023         if (option_debug > 2)
2024                 ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
2025         if (vms->mailstream == NIL || !vms->mailstream) {
2026                 if (option_debug)
2027                         ast_log (LOG_DEBUG,"mailstream not set.\n");
2028         } else {
2029                 stream = vms->mailstream;
2030         }
2031         /* debug = T;  user wants protocol telemetry? */
2032         debug = NIL;  /* NO protocol telemetry? */
2033
2034         if (delimiter == '\0') {                /* did not probe the server yet */
2035                 char *cp;
2036 #ifdef USE_SYSTEM_IMAP
2037 #include <imap/linkage.c>
2038 #elif defined(USE_SYSTEM_CCLIENT)
2039 #include <c-client/linkage.c>
2040 #else
2041 #include "linkage.c"
2042 #endif
2043                 /* Connect to INBOX first to get folders delimiter */
2044                 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
2045                 ast_mutex_lock(&vms->lock);
2046                 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2047                 ast_mutex_unlock(&vms->lock);
2048                 if (stream == NIL) {
2049                         ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
2050                         return -1;
2051                 }
2052                 get_mailbox_delimiter(stream);
2053                 /* update delimiter in imapfolder */
2054                 for (cp = imapfolder; *cp; cp++)
2055                         if (*cp == '/')
2056                                 *cp = delimiter;
2057         }
2058         /* Now connect to the target folder */
2059         imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
2060         if (option_debug > 2)
2061                 ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
2062         ast_mutex_lock(&vms->lock);
2063         vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2064         ast_mutex_unlock(&vms->lock);
2065         if (vms->mailstream == NIL) {
2066                 return -1;
2067         } else {
2068                 return 0;
2069         }
2070 }
2071
2072 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
2073 {
2074         SEARCHPGM *pgm;
2075         SEARCHHEADER *hdr;
2076         int ret, urgent = 0;
2077
2078         /* If Urgent, then look at INBOX */
2079         if (box == 11) {
2080                 box = NEW_FOLDER;
2081                 urgent = 1;
2082         }
2083
2084         ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
2085         ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
2086
2087         if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
2088                 ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
2089                 return -1;
2090         }
2091         
2092         create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2093         
2094         /* Check Quota */
2095         if  (box == 0)  {
2096                 ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
2097                 check_quota(vms,(char *)mbox(box));
2098         }
2099
2100         pgm = mail_newsearchpgm();
2101
2102         /* Check IMAP folder for Asterisk messages only... */
2103         hdr = mail_newsearchheader("X-Asterisk-VM-Extension", vmu->mailbox);
2104         pgm->header = hdr;
2105         pgm->deleted = 0;
2106         pgm->undeleted = 1;
2107
2108         /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
2109         if (box == NEW_FOLDER && urgent == 1) {
2110                 pgm->unseen = 1;
2111                 pgm->seen = 0;
2112                 pgm->flagged = 1;
2113                 pgm->unflagged = 0;
2114         } else if (box == NEW_FOLDER && urgent == 0) {
2115                 pgm->unseen = 1;
2116                 pgm->seen = 0;
2117                 pgm->flagged = 0;
2118                 pgm->unflagged = 1;
2119         } else if (box == OLD_FOLDER) {
2120                 pgm->seen = 1;
2121                 pgm->unseen = 0;
2122         }
2123
2124         ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
2125
2126         vms->vmArrayIndex = 0;
2127         mail_search_full (vms->mailstream, NULL, pgm, NIL);
2128         vms->lastmsg = vms->vmArrayIndex - 1;
2129         mail_free_searchpgm(&pgm);
2130
2131         return 0;
2132 }
2133
2134 static void write_file(char *filename, char *buffer, unsigned long len)
2135 {
2136         FILE *output;
2137
2138         output = fopen (filename, "w");
2139         fwrite (buffer, len, 1, output);
2140         fclose (output);
2141 }
2142
2143 static void update_messages_by_imapuser(const char *user, unsigned long number)
2144 {
2145         struct vmstate *vlist = NULL;
2146
2147         AST_LIST_LOCK(&vmstates);
2148         AST_LIST_TRAVERSE(&vmstates, vlist, list) {
2149                 if (!vlist->vms) {
2150                         ast_debug(3, "error: vms is NULL for %s\n", user);
2151                         continue;
2152                 }
2153                 if (!vlist->vms->imapuser) {
2154                         ast_debug(3, "error: imapuser is NULL for %s\n", user);
2155                         continue;
2156                 }
2157                 ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vlist->vms->vmArrayIndex, vlist->vms->interactive);
2158                 vlist->vms->msgArray[vlist->vms->vmArrayIndex++] = number;
2159         }
2160         AST_LIST_UNLOCK(&vmstates);
2161 }
2162
2163 void mm_searched(MAILSTREAM *stream, unsigned long number)
2164 {
2165         char *mailbox = stream->mailbox, buf[1024] = "", *user;
2166
2167         if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
2168                 return;
2169
2170         update_messages_by_imapuser(user, number);
2171 }
2172
2173 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
2174 {
2175         struct ast_variable *var;
2176         struct ast_vm_user *vmu;
2177
2178         vmu = ast_calloc(1, sizeof *vmu);
2179         if (!vmu)
2180                 return NULL;
2181         ast_set_flag(vmu, VM_ALLOCED);
2182         populate_defaults(vmu);
2183
2184         var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
2185         if (var) {
2186                 apply_options_full(vmu, var);
2187                 ast_variables_destroy(var);
2188                 return vmu;
2189         } else {
2190                 free(vmu);
2191                 return NULL;
2192         }
2193 }
2194
2195 /* Interfaces to C-client */
2196
2197 void mm_exists(MAILSTREAM * stream, unsigned long number)
2198 {
2199         /* mail_ping will callback here if new mail! */
2200         ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
2201         if (number == 0) return;
2202         set_update(stream);
2203 }
2204
2205
2206 void mm_expunged(MAILSTREAM * stream, unsigned long number)
2207 {
2208         /* mail_ping will callback here if expunged mail! */
2209         ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
2210         if (number == 0) return;
2211         set_update(stream);
2212 }
2213
2214
2215 void mm_flags(MAILSTREAM * stream, unsigned long number)
2216 {
2217         /* mail_ping will callback here if read mail! */
2218         ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
2219         if (number == 0) return;
2220         set_update(stream);
2221 }
2222
2223
2224 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
2225 {
2226         ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
2227         mm_log (string, errflg);
2228 }
2229
2230
2231 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2232 {
2233         if (delimiter == '\0') {
2234                 delimiter = delim;
2235         }
2236
2237         ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
2238         if (attributes & LATT_NOINFERIORS)
2239                 ast_debug(5, "no inferiors\n");
2240         if (attributes & LATT_NOSELECT)
2241                 ast_debug(5, "no select\n");
2242         if (attributes & LATT_MARKED)
2243                 ast_debug(5, "marked\n");
2244         if (attributes & LATT_UNMARKED)
2245                 ast_debug(5, "unmarked\n");
2246 }
2247
2248
2249 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2250 {
2251         ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
2252         if (attributes & LATT_NOINFERIORS)
2253                 ast_debug(5, "no inferiors\n");
2254         if (attributes & LATT_NOSELECT)
2255                 ast_debug(5, "no select\n");
2256         if (attributes & LATT_MARKED)
2257                 ast_debug(5, "marked\n");
2258         if (attributes & LATT_UNMARKED)
2259                 ast_debug(5, "unmarked\n");
2260 }
2261
2262
2263 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
2264 {
2265         ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
2266         if (status->flags & SA_MESSAGES)
2267                 ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
2268         if (status->flags & SA_RECENT)
2269                 ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
2270         if (status->flags & SA_UNSEEN)
2271                 ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
2272         if (status->flags & SA_UIDVALIDITY)
2273                 ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
2274         if (status->flags & SA_UIDNEXT)
2275                 ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
2276         ast_log(AST_LOG_NOTICE, "\n");
2277 }
2278
2279
2280 void mm_log(char *string, long errflg)
2281 {
2282         switch ((short) errflg) {
2283                 case NIL:
2284                         ast_debug(1,"IMAP Info: %s\n", string);
2285                         break;
2286                 case PARSE:
2287                 case WARN:
2288                         ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
2289                         break;
2290                 case ERROR:
2291                         ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
2292                         break;
2293         }
2294 }
2295
2296
2297 void mm_dlog(char *string)
2298 {
2299         ast_log(AST_LOG_NOTICE, "%s\n", string);
2300 }
2301
2302
2303 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
2304 {
2305         struct ast_vm_user *vmu;
2306
2307         ast_debug(4, "Entering callback mm_login\n");
2308
2309         ast_copy_string(user, mb->user, MAILTMPLEN);
2310
2311         /* We should only do this when necessary */
2312         if (!ast_strlen_zero(authpassword)) {
2313                 ast_copy_string(pwd, authpassword, MAILTMPLEN);
2314         } else {
2315                 AST_LIST_TRAVERSE(&users, vmu, list) {
2316                         if (!strcasecmp(mb->user, vmu->imapuser)) {
2317                                 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2318                                 break;
2319                         }
2320                 }
2321                 if (!vmu) {
2322                         if ((vmu = find_user_realtime_imapuser(mb->user))) {
2323                                 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2324                                 free_user(vmu);
2325                         }
2326                 }
2327         }
2328 }
2329
2330
2331 void mm_critical(MAILSTREAM * stream)
2332 {
2333 }
2334
2335
2336 void mm_nocritical(MAILSTREAM * stream)
2337 {
2338 }
2339
2340
2341 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
2342 {
2343         kill (getpid (), SIGSTOP);
2344         return NIL;
2345 }
2346
2347
2348 void mm_fatal(char *string)
2349 {
2350         ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
2351 }
2352
2353 /* C-client callback to handle quota */
2354 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
2355 {
2356         struct vm_state *vms;
2357         char *mailbox = stream->mailbox, *user;
2358         char buf[1024] = "";
2359         unsigned long usage = 0, limit = 0;
2360         
2361         while (pquota) {
2362                 usage = pquota->usage;
2363                 limit = pquota->limit;
2364                 pquota = pquota->next;
2365         }
2366         
2367         if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 2))) {
2368                 ast_log(AST_LOG_ERROR, "No state found.\n");
2369                 return;
2370         }
2371
2372         ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
2373
2374         vms->quota_usage = usage;
2375         vms->quota_limit = limit;
2376 }
2377
2378 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
2379 {
2380         char *start, *eol_pnt;
2381         int taglen;
2382
2383         if (ast_strlen_zero(header) || ast_strlen_zero(tag))
2384                 return NULL;
2385
2386         taglen = strlen(tag) + 1;
2387         if (taglen < 1)
2388                 return NULL;
2389
2390         if (!(start = strstr(header, tag)))
2391                 return NULL;
2392
2393         /* Since we can be called multiple times we should clear our buffer */
2394         memset(buf, 0, len);
2395
2396         ast_copy_string(buf, start+taglen, len);
2397         if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
2398                 *eol_pnt = '\0';
2399         return buf;
2400 }
2401
2402 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
2403 {
2404         char *start, *quote, *eol_pnt;
2405
2406         if (ast_strlen_zero(mailbox))
2407                 return NULL;
2408
2409         if (!(start = strstr(mailbox, "/user=")))
2410                 return NULL;
2411
2412         ast_copy_string(buf, start+6, len);
2413
2414         if (!(quote = strchr(buf, '\"'))) {
2415                 if (!(eol_pnt = strchr(buf, '/')))
2416                         eol_pnt = strchr(buf,'}');
2417                 *eol_pnt = '\0';
2418                 return buf;
2419         } else {
2420                 eol_pnt = strchr(buf+1,'\"');
2421                 *eol_pnt = '\0';
2422                 return buf+1;
2423         }
2424 }
2425
2426 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
2427 {
2428         struct vm_state *vms_p;
2429
2430         if (option_debug > 4)
2431                 ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2432         if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
2433                 return NULL;
2434         ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
2435         ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2436         ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
2437         vms_p->mailstream = NIL; /* save for access from interactive entry point */
2438         if (option_debug > 4)
2439                 ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2440         vms_p->updated = 1;
2441         /* set mailbox to INBOX! */
2442         ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2443         init_vm_state(vms_p);
2444         vmstate_insert(vms_p);
2445         return vms_p;
2446 }
2447
2448 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
2449 {
2450         struct vmstate *vlist = NULL;
2451
2452         AST_LIST_LOCK(&vmstates);
2453         AST_LIST_TRAVERSE(&vmstates, vlist, list) {
2454                 if (!vlist->vms) {
2455                         ast_debug(3, "error: vms is NULL for %s\n", user);
2456                         continue;
2457                 }
2458                 if (!vlist->vms->imapuser) {
2459                         ast_debug(3, "error: imapuser is NULL for %s\n", user);
2460                         continue;
2461                 }
2462
2463                 if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
2464                         AST_LIST_UNLOCK(&vmstates);
2465                         return vlist->vms;
2466                 }
2467         }
2468         AST_LIST_UNLOCK(&vmstates);
2469
2470         ast_debug(3, "%s not found in vmstates\n", user);
2471
2472         return NULL;
2473 }
2474
2475 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
2476 {
2477
2478         struct vmstate *vlist = NULL;
2479         const char *local_context = S_OR(context, "default");
2480
2481         AST_LIST_LOCK(&vmstates);
2482         AST_LIST_TRAVERSE(&vmstates, vlist, list) {
2483                 if (!vlist->vms) {
2484                         ast_debug(3, "error: vms is NULL for %s\n", mailbox);
2485                         continue;
2486                 }
2487                 if (!vlist->vms->username || !vlist->vms->context) {
2488                         ast_debug(3, "error: username is NULL for %s\n", mailbox);
2489                         continue;
2490                 }
2491
2492                 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);
2493                 
2494                 if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
2495                         ast_debug(3, "Found it!\n");
2496                         AST_LIST_UNLOCK(&vmstates);
2497                         return vlist->vms;
2498                 }
2499         }
2500         AST_LIST_UNLOCK(&vmstates);
2501
2502         ast_debug(3, "%s not found in vmstates\n", mailbox);
2503
2504         return NULL;
2505 }
2506
2507 static void vmstate_insert(struct vm_state *vms) 
2508 {
2509         struct vmstate *v;
2510         struct vm_state *altvms;
2511
2512         /* If interactive, it probably already exists, and we should
2513            use the one we already have since it is more up to date.
2514            We can compare the username to find the duplicate */
2515         if (vms->interactive == 1) {
2516                 altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
2517                 if (altvms) {   
2518                         ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
2519                         vms->newmessages = altvms->newmessages;
2520                         vms->oldmessages = altvms->oldmessages;
2521                         vms->vmArrayIndex = altvms->vmArrayIndex;
2522                         vms->lastmsg = altvms->lastmsg;
2523                         vms->curmsg = altvms->curmsg;
2524                         /* get a pointer to the persistent store */
2525                         vms->persist_vms = altvms;
2526                         /* Reuse the mailstream? */
2527                         vms->mailstream = altvms->mailstream;
2528                         /* vms->mailstream = NIL; */
2529                 }
2530         }
2531
2532         if (!(v = ast_calloc(1, sizeof(*v))))
2533                 return;
2534         
2535         v->vms = vms;
2536
2537         ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
2538
2539         AST_LIST_LOCK(&vmstates);
2540         AST_LIST_INSERT_TAIL(&vmstates, v, list);
2541         AST_LIST_UNLOCK(&vmstates);
2542 }
2543
2544 static void vmstate_delete(struct vm_state *vms) 
2545 {
2546         struct vmstate *vc = NULL;
2547         struct vm_state *altvms = NULL;
2548
2549         /* If interactive, we should copy pertinent info
2550            back to the persistent state (to make update immediate) */
2551         if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
2552                 ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
2553                 altvms->newmessages = vms->newmessages;
2554                 altvms->oldmessages = vms->oldmessages;
2555                 altvms->updated = 1;
2556         }
2557         
2558         ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
2559         
2560         AST_LIST_LOCK(&vmstates);
2561         AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
2562                 if (vc->vms == vms) {
2563                         AST_LIST_REMOVE_CURRENT(list);
2564                         break;
2565                 }
2566         }
2567         AST_LIST_TRAVERSE_SAFE_END
2568         AST_LIST_UNLOCK(&vmstates);
2569         
2570         if (vc) {
2571                 ast_mutex_destroy(&vc->vms->lock);
2572                 ast_free(vc);
2573         }
2574         else
2575                 ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
2576 }
2577
2578 static void set_update(MAILSTREAM * stream) 
2579 {
2580         struct vm_state *vms;
2581         char *mailbox = stream->mailbox, *user;
2582         char buf[1024] = "";
2583
2584         if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
2585                 if (user && option_debug > 2)
2586                         ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
2587                 return;
2588         }
2589
2590         ast_debug(3, "User %s mailbox set for update.\n", user);
2591
2592         vms->updated = 1; /* Set updated flag since mailbox changed */
2593 }
2594
2595 static void init_vm_state(struct vm_state *vms) 
2596 {
2597         int x;
2598         vms->vmArrayIndex = 0;
2599         for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
2600                 vms->msgArray[x] = 0;
2601         }
2602         ast_mutex_init(&vms->lock);
2603 }
2604
2605 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
2606 {
2607         char *body_content;
2608         char *body_decoded;
2609         char *fn = is_intro ? vms->introfn : vms->fn;
2610         unsigned long len;
2611         unsigned long newlen;
2612         char filename[256];
2613         
2614         if (!body || body == NIL)
2615                 return -1;
2616
2617         body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
2618         if (body_content != NIL) {
2619                 snprintf(filename, sizeof(filename), "%s.%s", fn, format);
2620                 /* ast_debug(1,body_content); */
2621                 body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
2622                 /* If the body of the file is empty, return an error */
2623                 if (!newlen) {
2624                         return -1;
2625                 }
2626                 write_file(filename, (char *) body_decoded, newlen);
2627         } else {
2628                 ast_debug(5, "Body of message is NULL.\n");
2629                 return -1;
2630         }
2631         return 0;
2632 }
2633
2634 /*! 
2635  * \brief Get delimiter via mm_list callback 
2636  * \param stream
2637  *
2638  * Determines the delimiter character that is used by the underlying IMAP based mail store.
2639  */
2640 static void get_mailbox_delimiter(MAILSTREAM *stream) {
2641         char tmp[50];
2642         snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
2643         mail_list(stream, tmp, "*");
2644 }
2645
2646 /*! 
2647  * \brief Check Quota for user 
2648  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
2649  * \param mailbox the mailbox to check the quota for.
2650  *
2651  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
2652  */
2653 static void check_quota(struct vm_state *vms, char *mailbox) {
2654         mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
2655         ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
2656         if (vms && vms->mailstream != NULL) {
2657                 imap_getquotaroot(vms->mailstream, mailbox);
2658         } else {
2659                 ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
2660         }
2661 }
2662
2663 #endif /* IMAP_STORAGE */
2664
2665 /*! \brief Lock file path
2666     only return failure if ast_lock_path returns 'timeout',
2667    not if the path does not exist or any other reason
2668 */
2669 static int vm_lock_path(const char *path)
2670 {
2671         switch (ast_lock_path(path)) {
2672         case AST_LOCK_TIMEOUT:
2673                 return -1;
2674         default:
2675                 return 0;
2676         }
2677 }
2678
2679
2680 #ifdef ODBC_STORAGE
2681 struct generic_prepare_struct {
2682         char *sql;
2683         int argc;
2684         char **argv;
2685 };
2686
2687 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
2688 {
2689         struct generic_prepare_struct *gps = data;
2690         int res, i;
2691         SQLHSTMT stmt;
2692
2693         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2694         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2695                 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
2696                 return NULL;
2697         }
2698         res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
2699         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2700                 ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
2701                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2702                 return NULL;
2703         }
2704         for (i = 0; i < gps->argc; i++)
2705                 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
2706
2707         return stmt;
2708 }
2709
2710 /*!
2711  * \brief Retrieves a file from an ODBC data store.
2712  * \param dir the path to the file to be retreived.
2713  * \param msgnum the message number, such as within a mailbox folder.
2714  * 
2715  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
2716  * 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.
2717  *
2718  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
2719  * The output is the message information file with the name msgnum and the extension .txt
2720  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
2721  * 
2722  * \return 0 on success, -1 on error.
2723  */
2724 static int retrieve_file(char *dir, int msgnum)
2725 {
2726         int x = 0;
2727         int res;
2728         int fd=-1;
2729         size_t fdlen = 0;
2730         void *fdm = MAP_FAILED;
2731         SQLSMALLINT colcount=0;
2732         SQLHSTMT stmt;
2733         char sql[PATH_MAX];
2734         char fmt[80]="";
2735         char *c;
2736         char coltitle[256];
2737         SQLSMALLINT collen;
2738         SQLSMALLINT datatype;
2739         SQLSMALLINT decimaldigits;
2740         SQLSMALLINT nullable;
2741         SQLULEN colsize;
2742         SQLLEN colsize2;
2743         FILE *f=NULL;
2744         char rowdata[80];
2745         char fn[PATH_MAX];
2746         char full_fn[PATH_MAX];
2747         char msgnums[80];
2748         char *argv[] = { dir, msgnums };
2749         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2750
2751         struct odbc_obj *obj;
2752         obj = ast_odbc_request_obj(odbc_database, 0);
2753         if (obj) {
2754                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2755                 c = strchr(fmt, '|');
2756                 if (c)
2757                         *c = '\0';
2758                 if (!strcasecmp(fmt, "wav49"))
2759                         strcpy(fmt, "WAV");
2760                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
2761                 if (msgnum > -1)
2762                         make_file(fn, sizeof(fn), dir, msgnum);
2763                 else
2764                         ast_copy_string(fn, dir, sizeof(fn));
2765
2766                 /* Create the information file */
2767                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
2768                 
2769                 if (!(f = fopen(full_fn, "w+"))) {
2770                         ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
2771                         goto yuck;
2772                 }
2773                 
2774                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
2775                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
2776                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2777                 if (!stmt) {
2778                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2779                         ast_odbc_release_obj(obj);
2780                         goto yuck;
2781                 }
2782                 res = SQLFetch(stmt);
2783                 if (res == SQL_NO_DATA) {
2784                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2785                         ast_odbc_release_obj(obj);
2786                         goto yuck;
2787                 } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2788                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2789                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2790                         ast_odbc_release_obj(obj);
2791                         goto yuck;
2792                 }
2793                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
2794                 if (fd < 0) {
2795                         ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
2796                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2797                         ast_odbc_release_obj(obj);
2798                         goto yuck;
2799                 }
2800                 res = SQLNumResultCols(stmt, &colcount);
2801                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
2802                         ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
2803                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2804                         ast_odbc_release_obj(obj);
2805                         goto yuck;
2806                 }
2807                 if (f) 
2808                         fprintf(f, "[message]\n");
2809                 for (x=0;x<colcount;x++) {
2810                         rowdata[0] = '\0';
2811                         collen = sizeof(coltitle);
2812                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
2813                                                 &datatype, &colsize, &decimaldigits, &nullable);
2814                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2815                                 ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
2816                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2817                                 ast_odbc_release_obj(obj);
2818                                 goto yuck;
2819                         }
2820                         if (!strcasecmp(coltitle, "recording")) {
2821                                 off_t offset;
2822                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
2823                                 fdlen = colsize2;
2824                                 if (fd > -1) {
2825                                         char tmp[1]="";
2826                                         lseek(fd, fdlen - 1, SEEK_SET);
2827                                         if (write(fd, tmp, 1) != 1) {
2828                                                 close(fd);
2829                                                 fd = -1;
2830                                                 continue;
2831                                         }
2832                                         /* Read out in small chunks */
2833                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
2834                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
2835                                                         ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
2836                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2837                                                         ast_odbc_release_obj(obj);
2838                                                         goto yuck;
2839                                                 } else {
2840                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
2841                                                         munmap(fdm, CHUNKSIZE);
2842                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2843                                                                 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2844                                                                 unlink(full_fn);
2845                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2846                                                                 ast_odbc_release_obj(obj);
2847                                                                 goto yuck;
2848                                                         }
2849                                                 }
2850                                         }
2851                                         truncate(full_fn, fdlen);
2852                                 }
2853                         } else {
2854                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2855                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2856                                         ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
2857                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2858                                         ast_odbc_release_obj(obj);
2859                                         goto yuck;
2860                                 }
2861                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
2862                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
2863                         }
2864                 }
2865                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2866                 ast_odbc_release_obj(obj);
2867         } else
2868                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2869 yuck:   
2870         if (f)
2871                 fclose(f);
2872         if (fd > -1)
2873                 close(fd);
2874         return x - 1;
2875 }
2876
2877 /*!
2878  * \brief Determines the highest message number in use for a given user and mailbox folder.
2879  * \param vmu 
2880  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
2881  *
2882  * This method is used when mailboxes are stored in an ODBC back end.
2883  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
2884  *
2885  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
2886  */
2887 static int last_message_index(struct ast_vm_user *vmu, char *dir)
2888 {
2889         int x = 0;
2890         int res;
2891         SQLHSTMT stmt;
2892         char sql[PATH_MAX];
2893         char rowdata[20];
2894         char *argv[] = { dir };
2895         struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
2896
2897         struct odbc_obj *obj;
2898         obj = ast_odbc_request_obj(odbc_database, 0);
2899         if (obj) {
2900                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
2901                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2902                 if (!stmt) {
2903                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2904                         ast_odbc_release_obj(obj);
2905                         goto yuck;
2906                 }
2907                 res = SQLFetch(stmt);
2908                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2909                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2910                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2911                         ast_odbc_release_obj(obj);
2912                         goto yuck;
2913                 }
2914                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2915                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2916                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2917                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2918                         ast_odbc_release_obj(obj);
2919                         goto yuck;
2920                 }
2921                 if (sscanf(rowdata, "%d", &x) != 1)
2922                         ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
2923                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2924                 ast_odbc_release_obj(obj);
2925         } else
2926                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2927 yuck:   
2928         return x - 1;
2929 }
2930
2931 /*!
2932  * \brief Determines if the specified message exists.
2933  * \param dir the folder the mailbox folder to look for messages. 
2934  * \param msgnum the message index to query for.
2935  *
2936  * This method is used when mailboxes are stored in an ODBC back end.
2937  *
2938  * \return greater than zero if the message exists, zero when the message does not exist or on error.
2939  */
2940 static int message_exists(char *dir, int msgnum)
2941 {
2942         int x = 0;
2943         int res;
2944         SQLHSTMT stmt;
2945         char sql[PATH_MAX];
2946         char rowdata[20];
2947         char msgnums[20];
2948         char *argv[] = { dir, msgnums };
2949         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2950
2951         struct odbc_obj *obj;
2952         obj = ast_odbc_request_obj(odbc_database, 0);
2953         if (obj) {
2954                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
2955                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
2956                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2957                 if (!stmt) {
2958                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2959                         ast_odbc_release_obj(obj);
2960                         goto yuck;
2961                 }
2962                 res = SQLFetch(stmt);
2963                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2964                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2965                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2966                         ast_odbc_release_obj(obj);
2967                         goto yuck;
2968                 }
2969                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2970                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2971                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2972                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2973                         ast_odbc_release_obj(obj);
2974                         goto yuck;
2975                 }
2976                 if (sscanf(rowdata, "%d", &x) != 1)
2977                         ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
2978                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2979                 ast_odbc_release_obj(obj);
2980         } else
2981                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2982 yuck:   
2983         return x;
2984 }
2985
2986 /*!
2987  * \brief returns the one-based count for messages.
2988  * \param vmu
2989  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
2990  *
2991  * This method is used when mailboxes are stored in an ODBC back end.
2992  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
2993  * one-based messages.
2994  * This method just calls last_message_index and returns +1 of its value.
2995  *
2996  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
2997  */
2998 static int count_messages(struct ast_vm_user *vmu, char *dir)
2999 {
3000         return last_message_index(vmu, dir) + 1;
3001 }
3002
3003 /*!
3004  * \brief Deletes a message from the mailbox folder.
3005  * \param sdir The mailbox folder to work in.
3006  * \param smsg The message index to be deleted.
3007  *
3008  * This method is used when mailboxes are stored in an ODBC back end.
3009  * The specified message is directly deleted from the database 'voicemessages' table.
3010  * 
3011  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
3012  */
3013 static void delete_file(char *sdir, int smsg)
3014 {
3015         SQLHSTMT stmt;
3016         char sql[PATH_MAX];
3017         char msgnums[20];
3018         char *argv[] = { sdir, msgnums };
3019         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3020
3021         struct odbc_obj *obj;
3022         obj = ast_odbc_request_obj(odbc_database, 0);
3023         if (obj) {
3024                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3025                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
3026                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3027                 if (!stmt)
3028                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3029                 else
3030                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3031                 ast_odbc_release_obj(obj);
3032         } else
3033                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3034         return; 
3035 }
3036
3037 /*!
3038  * \brief Copies a voicemail from one mailbox to another.
3039  * \param sdir the folder for which to look for the message to be copied.
3040  * \param smsg the index of the message to be copied.
3041  * \param ddir the destination folder to copy the message into.
3042  * \param dmsg the index to be used for the copied message.
3043  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
3044  * \param dmailboxcontext The context for the destination user.
3045  *
3046  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
3047  */
3048 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
3049 {
3050         SQLHSTMT stmt;
3051         char sql[512];
3052         char msgnums[20];
3053         char msgnumd[20];
3054         struct odbc_obj *obj;
3055         char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
3056         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3057
3058         delete_file(ddir, dmsg);
3059         obj = ast_odbc_request_obj(odbc_database, 0);
3060         if (obj) {
3061                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3062                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3063                 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);
3064                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3065                 if (!stmt)
3066                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
3067                 else
3068                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3069                 ast_odbc_release_obj(obj);
3070         } else
3071                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3072         return; 
3073 }
3074
3075 struct insert_data {
3076         char *sql;
3077         char *dir;
3078         char *msgnums;
3079         void *data;
3080         SQLLEN datalen;
3081         SQLLEN indlen;
3082         const char *context;
3083         const char *macrocontext;
3084         const char *callerid;
3085         const char *origtime;
3086         const char *duration;
3087         char *mailboxuser;
3088         char *mailboxcontext;
3089         const char *category;
3090         const char *flag;
3091 };
3092
3093 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
3094 {
3095         struct insert_data *data = vdata;
3096         int res;
3097         SQLHSTMT stmt;
3098
3099         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
3100         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3101                 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
3102                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3103                 return NULL;
3104         }
3105
3106         SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
3107         SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
3108         SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
3109         SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
3110         SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
3111         SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
3112         SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
3113         SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
3114         SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
3115         SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
3116         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
3117         if (!ast_strlen_zero(data->category)) {
3118                 SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
3119         }
3120         res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
3121         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3122                 ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
3123                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3124                 return NULL;
3125         }
3126
3127         return stmt;
3128 }
3129
3130 /*!
3131  * \brief Stores a voicemail into the database.
3132  * \param dir the folder the mailbox folder to store the message.
3133  * \param mailboxuser the user owning the mailbox folder.
3134  * \param mailboxcontext
3135  * \param msgnum the message index for the message to be stored.
3136  *
3137  * This method is used when mailboxes are stored in an ODBC back end.
3138  * The message sound file and information file is looked up on the file system. 
3139  * A SQL query is invoked to store the message into the (MySQL) database.
3140  *
3141  * \return the zero on success -1 on error.
3142  */
3143 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
3144 {
3145         int res = 0;
3146         int fd = -1;
3147         void *fdm = MAP_FAILED;
3148         size_t fdlen = -1;
3149         SQLHSTMT stmt;
3150         char sql[PATH_MAX];
3151         char msgnums[20];
3152         char fn[PATH_MAX];
3153         char full_fn[PATH_MAX];
3154         char fmt[80]="";
3155         char *c;
3156         struct ast_config *cfg=NULL;
3157         struct odbc_obj *obj;
3158         struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext };
3159         struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
3160
3161         delete_file(dir, msgnum);
3162         if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
3163                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3164                 return -1;
3165         }
3166
3167         do {
3168                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
3169                 c = strchr(fmt, '|');
3170                 if (c)
3171                         *c = '\0';
3172                 if (!strcasecmp(fmt, "wav49"))
3173                         strcpy(fmt, "WAV");
3174                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
3175                 if (msgnum > -1)
3176                         make_file(fn, sizeof(fn), dir, msgnum);
3177                 else
3178                         ast_copy_string(fn, dir, sizeof(fn));
3179                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3180                 cfg = ast_config_load(full_fn, config_flags);
3181                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3182                 fd = open(full_fn, O_RDWR);
3183                 if (fd < 0) {
3184                         ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
3185                         res = -1;
3186                         break;
3187                 }
3188                 if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
3189                         if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
3190                                 idata.context = "";
3191                         }
3192                         if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
3193                                 idata.macrocontext = "";
3194                         }
3195                         if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
3196                                 idata.callerid = "";
3197                         }
3198                         if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
3199                                 idata.origtime = "";
3200                         }
3201                         if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
3202                                 idata.duration = "";
3203                         }
3204                         if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
3205                                 idata.category = "";
3206                         }
3207                         if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
3208                                 idata.flag = "";
3209                         }
3210                 }
3211                 fdlen = lseek(fd, 0, SEEK_END);
3212                 lseek(fd, 0, SEEK_SET);
3213                 printf("Length is %zd\n", fdlen);
3214                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
3215                 if (fdm == MAP_FAILED) {
3216                         ast_log(AST_LOG_WARNING, "Memory map failed!\n");
3217                         res = -1;
3218                         break;
3219                 } 
3220                 idata.data = fdm;
3221                 idata.datalen = idata.indlen = fdlen;
3222
3223                 if (!ast_strlen_zero(idata.category)) 
3224                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
3225                 else
3226                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
3227
3228                 if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
3229                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3230                 } else {
3231                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3232                         res = -1;
3233                 }
3234         } while (0);
3235         if (obj) {
3236                 ast_odbc_release_obj(obj);
3237         }
3238         if (cfg)
3239                 ast_config_destroy(cfg);
3240         if (fdm != MAP_FAILED)
3241                 munmap(fdm, fdlen);
3242         if (fd > -1)
3243                 close(fd);
3244         return res;
3245 }
3246
3247 /*!
3248  * \brief Renames a message in a mailbox folder.
3249  * \param sdir The folder of the message to be renamed.
3250  * \param smsg The index of the message to be renamed.
3251  * \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.
3252  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
3253  * \param ddir The destination folder for the message to be renamed into
3254  * \param dmsg The destination message for the message to be renamed.
3255  *
3256  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
3257  * 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.
3258  * 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.
3259  */
3260 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
3261 {
3262         SQLHSTMT stmt;
3263         char sql[PATH_MAX];
3264         char msgnums[20];
3265         char msgnumd[20];
3266         struct odbc_obj *obj;
3267         char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
3268         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3269
3270         delete_file(ddir, dmsg);
3271         obj = ast_odbc_request_obj(odbc_database, 0);
3272         if (obj) {
3273                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3274                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3275                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
3276                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3277                 if (!stmt)
3278                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3279                 else
3280                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3281                 ast_odbc_release_obj(obj);
3282         } else
3283                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3284         return; 
3285 }
3286
3287 /*!
3288  * \brief Removes a voicemail message file.
3289  * \param dir the path to the message file.
3290  * \param msgnum the unique number for the message within the mailbox.
3291  *
3292  * Removes the message content file and the information file.
3293  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
3294  * Typical use is to clean up after a RETRIEVE operation. 
3295  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
3296  * \return zero on success, -1 on error.
3297  */
3298 static int remove_file(char *dir, int msgnum)
3299 {
3300         char fn[PATH_MAX];
3301         char full_fn[PATH_MAX];
3302         char msgnums[80];
3303         
3304         if (msgnum > -1) {
3305                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3306                 make_file(fn, sizeof(fn), dir, msgnum);
3307         } else
3308                 ast_copy_string(fn, dir, sizeof(fn));
3309         ast_filedelete(fn, NULL);       
3310         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3311         unlink(full_fn);
3312         return 0;
3313 }
3314 #else
3315 #ifndef IMAP_STORAGE
3316 /*!
3317  * \brief Find all .txt files - even if they are not in sequence from 0000.
3318  * \param vmu
3319  * \param dir
3320  *
3321  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
3322  *
3323  * \return the count of messages, zero or more.
3324  */
3325 static int count_messages(struct ast_vm_user *vmu, char *dir)
3326 {
3327
3328         int vmcount = 0;
3329         DIR *vmdir = NULL;
3330         struct dirent *vment = NULL;
3331
3332         if (vm_lock_path(dir))
3333                 return ERROR_LOCK_PATH;
3334
3335         if ((vmdir = opendir(dir))) {
3336                 while ((vment = readdir(vmdir))) {
3337                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
3338                                 vmcount++;
3339                         }
3340                 }
3341                 closedir(vmdir);
3342         }
3343         ast_unlock_path(dir);
3344         
3345         return vmcount;
3346 }
3347
3348 /*!
3349  * \brief Renames a message in a mailbox folder.
3350  * \param sfn The path to the mailbox information and data file to be renamed.
3351  * \param dfn The path for where the message data and information files will be renamed to.
3352  *
3353  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
3354  */
3355 static void rename_file(char *sfn, char *dfn)
3356 {
3357         char stxt[PATH_MAX];
3358         char dtxt[PATH_MAX];
3359         ast_filerename(sfn,dfn,NULL);
3360         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
3361         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
3362         if (ast_check_realtime("voicemail_data")) {
3363                 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
3364         }
3365         rename(stxt, dtxt);
3366 }
3367
3368 /*! 
3369  * \brief Determines the highest message number in use for a given user and mailbox folder.
3370  * \param vmu 
3371  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3372  *
3373  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
3374  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
3375  *
3376  * \note Should always be called with a lock already set on dir.
3377  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
3378  */
3379 static int last_message_index(struct ast_vm_user *vmu, char *dir)
3380 {
3381         int x;
3382         unsigned char map[MAXMSGLIMIT] = "";
3383         DIR *msgdir;
3384         struct dirent *msgdirent;
3385         int msgdirint;
3386
3387         /* Reading the entire directory into a file map scales better than
3388          * doing a stat repeatedly on a predicted sequence.  I suspect this
3389          * is partially due to stat(2) internally doing a readdir(2) itself to
3390          * find each file. */
3391         msgdir = opendir(dir);
3392         while ((msgdirent = readdir(msgdir))) {
3393                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
3394                         map[msgdirint] = 1;
3395         }
3396         closedir(msgdir);
3397
3398         for (x = 0; x < vmu->maxmsg; x++) {
3399                 if (map[x] == 0)
3400                         break;
3401         }
3402
3403         return x - 1;
3404 }
3405
3406 #endif /* #ifndef IMAP_STORAGE */
3407 #endif /* #else of #ifdef ODBC_STORAGE */
3408 #ifndef IMAP_STORAGE
3409 /*!
3410  * \brief Utility function to copy a file.
3411  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
3412  * \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.
3413  *
3414  * 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.
3415  * The copy operation copies up to 4096 bytes at once.
3416  *
3417  * \return zero on success, -1 on error.
3418  */
3419 static int copy(char *infile, char *outfile)
3420 {
3421         int ifd;
3422         int ofd;
3423         int res;
3424         int len;
3425         char buf[4096];
3426
3427 #ifdef HARDLINK_WHEN_POSSIBLE
3428         /* Hard link if possible; saves disk space & is faster */
3429         if (link(infile, outfile)) {
3430 #endif
3431                 if ((ifd = open(infile, O_RDONLY)) < 0) {
3432                         ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
3433                         return -1;
3434                 }
3435                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
3436                         ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
3437                         close(ifd);
3438                         return -1;
3439                 }
3440                 do {
3441                         len = read(ifd, buf, sizeof(buf));
3442                         if (len < 0) {
3443                                 ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
3444                                 close(ifd);
3445                                 close(ofd);
3446                                 unlink(outfile);
3447                         }
3448                         if (len) {
3449                                 res = write(ofd, buf, len);
3450                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
3451                                         ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
3452                                         close(ifd);
3453                                         close(ofd);
3454                                         unlink(outfile);
3455                                 }
3456                         }
3457                 } while (len);
3458                 close(ifd);
3459                 close(ofd);
3460                 return 0;
3461 #ifdef HARDLINK_WHEN_POSSIBLE
3462         } else {
3463                 /* Hard link succeeded */
3464                 return 0;
3465         }
3466 #endif
3467 }
3468
3469 /*!
3470  * \brief Copies a voicemail information (envelope) file.
3471  * \param frompath
3472  * \param topath 
3473  *
3474  * Every voicemail has the data (.wav) file, and the information file.
3475  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
3476  * This is used by the COPY macro when not using IMAP storage.
3477  */
3478 static void copy_plain_file(char *frompath, char *topath)
3479 {
3480         char frompath2[PATH_MAX], topath2[PATH_MAX];
3481         struct ast_variable *tmp,*var = NULL;
3482         const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
3483         ast_filecopy(frompath, topath, NULL);
3484         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
3485         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
3486         if (ast_check_realtime("voicemail_data")) {
3487                 var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
3488                 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
3489                 for (tmp = var; tmp; tmp = tmp->next) {
3490                         if (!strcasecmp(tmp->name, "origmailbox")) {
3491                                 origmailbox = tmp->value;
3492                         } else if (!strcasecmp(tmp->name, "context")) {
3493                                 context = tmp->value;
3494                         } else if (!strcasecmp(tmp->name, "macrocontext")) {
3495                                 macrocontext = tmp->value;
3496                         } else if (!strcasecmp(tmp->name, "exten")) {
3497                                 exten = tmp->value;
3498                         } else if (!strcasecmp(tmp->name, "priority")) {
3499                                 priority = tmp->value;
3500                         } else if (!strcasecmp(tmp->name, "callerchan")) {
3501                                 callerchan = tmp->value;
3502                         } else if (!strcasecmp(tmp->name, "callerid")) {
3503                                 callerid = tmp->value;
3504                         } else if (!strcasecmp(tmp->name, "origdate")) {
3505                                 origdate = tmp->value;
3506                         } else if (!strcasecmp(tmp->name, "origtime")) {
3507                                 origtime = tmp->value;
3508                         } else if (!strcasecmp(tmp->name, "category")) {
3509                                 category = tmp->value;
3510                         } else if (!strcasecmp(tmp->name, "duration")) {
3511                                 duration = tmp->value;
3512                         }
3513                 }
3514                 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);
3515         }
3516         copy(frompath2, topath2);
3517         ast_variables_destroy(var);
3518 }
3519
3520 /*! 
3521  * \brief Removes the voicemail sound and information file.
3522  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
3523  *
3524  * This is used by the DELETE macro when voicemails are stored on the file system.
3525  *
3526  * \return zero on success, -1 on error.
3527  */
3528 static int vm_delete(char *file)
3529 {
3530         char *txt;
3531         int txtsize = 0;
3532
3533         txtsize = (strlen(file) + 5)*sizeof(char);
3534         txt = alloca(txtsize);
3535         /* Sprintf here would safe because we alloca'd exactly the right length,
3536          * but trying to eliminate all sprintf's anyhow
3537          */
3538         if (ast_check_realtime("voicemail_data")) {
3539                 ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
3540         }
3541         snprintf(txt, txtsize, "%s.txt", file);
3542         unlink(txt);
3543         return ast_filedelete(file, NULL);
3544 }
3545 #endif
3546
3547 /*!
3548  * \brief utility used by inchar(), for base_encode()
3549  */
3550 static int inbuf(struct baseio *bio, FILE *fi)
3551 {
3552         int l;
3553
3554         if (bio->ateof)
3555                 return 0;
3556
3557         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
3558                 if (ferror(fi))
3559                         return -1;
3560
3561                 bio->ateof = 1;
3562                 return 0;
3563         }
3564
3565         bio->iolen= l;
3566         bio->iocp= 0;
3567
3568         return 1;
3569 }
3570
3571 /*!
3572  * \brief utility used by base_encode()
3573  */
3574 static int inchar(struct baseio *bio, FILE *fi)
3575 {
3576         if (bio->iocp>=bio->iolen) {
3577                 if (!inbuf(bio, fi))
3578                         return EOF;
3579         }
3580
3581         return bio->iobuf[bio->iocp++];
3582 }
3583
3584 /*!
3585  * \brief utility used by base_encode()
3586  */
3587 static int ochar(struct baseio *bio, int c, FILE *so)
3588 {
3589         if (bio->linelength >= BASELINELEN) {
3590                 if (fputs(eol,so) == EOF)
3591                         return -1;
3592
3593                 bio->linelength= 0;
3594         }
3595
3596         if (putc(((unsigned char)c),so) == EOF)
3597                 return -1;
3598
3599         bio->linelength++;
3600
3601         return 1;
3602 }
3603
3604 /*!
3605  * \brief Performs a base 64 encode algorithm on the contents of a File
3606  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
3607  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
3608  *
3609  * 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 ?
3610  *
3611  * \return zero on success, -1 on error.
3612  */
3613 static int base_encode(char *filename, FILE *so)
3614 {
3615         static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
3616                 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
3617                 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
3618                 '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
3619         int i,hiteof= 0;
3620         FILE *fi;
3621         struct baseio bio;
3622
3623         memset(&bio, 0, sizeof(bio));
3624         bio.iocp = BASEMAXINLINE;
3625
3626         if (!(fi = fopen(filename, "rb"))) {
3627                 ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
3628                 return -1;
3629         }
3630
3631         while (!hiteof){
3632                 unsigned char igroup[3], ogroup[4];
3633                 int c,n;
3634
3635                 igroup[0]= igroup[1]= igroup[2]= 0;
3636
3637                 for (n= 0;n<3;n++) {
3638                         if ((c = inchar(&bio, fi)) == EOF) {
3639                                 hiteof= 1;
3640                                 break;
3641                         }
3642
3643                         igroup[n]= (unsigned char)c;
3644                 }
3645
3646                 if (n> 0) {
3647                         ogroup[0]= dtable[igroup[0]>>2];
3648                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
3649                         ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
3650                         ogroup[3]= dtable[igroup[2]&0x3F];
3651
3652                         if (n<3) {
3653                                 ogroup[3]= '=';
3654
3655                                 if (n<2)
3656         &nbs