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