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