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