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