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