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