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