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