Fix memory leaks in app_voicemail when using IMAP storage or realtime config
[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                         memset(retval, 0, sizeof(*retval));
1582                 }
1583                 populate_defaults(retval);
1584                 if (!ivm) {
1585                         ast_set_flag(retval, VM_ALLOCED);
1586                 }
1587                 if (mailbox) {
1588                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1589                 }
1590                 if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
1591                         var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1592                 } else {
1593                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1594                 }
1595                 if (var) {
1596                         apply_options_full(retval, var);
1597                         ast_variables_destroy(var);
1598                 } else { 
1599                         if (!ivm) 
1600                                 ast_free(retval);
1601                         retval = NULL;
1602                 }       
1603         } 
1604         return retval;
1605 }
1606
1607 /*!
1608  * \brief Finds a voicemail user from the users file or the realtime engine.
1609  * \param ivm
1610  * \param context
1611  * \param mailbox
1612  * 
1613  * \return The ast_vm_user structure for the user that was found.
1614  */
1615 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1616 {
1617         /* This function could be made to generate one from a database, too */
1618         struct ast_vm_user *vmu = NULL, *cur;
1619         AST_LIST_LOCK(&users);
1620
1621         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1622                 context = "default";
1623
1624         AST_LIST_TRAVERSE(&users, cur, list) {
1625 #ifdef IMAP_STORAGE
1626                 if (cur->imapversion != imapversion) {
1627                         continue;
1628                 }
1629 #endif
1630                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1631                         break;
1632                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1633                         break;
1634         }
1635         if (cur) {
1636                 /* Make a copy, so that on a reload, we have no race */
1637                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
1638                         *vmu = *cur;
1639                         if (!ivm) {
1640                                 vmu->emailbody = ast_strdup(cur->emailbody);
1641                                 vmu->emailsubject = ast_strdup(cur->emailsubject);
1642                         }
1643                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1644                         AST_LIST_NEXT(vmu, list) = NULL;
1645                 }
1646         } else
1647                 vmu = find_user_realtime(ivm, context, mailbox);
1648         AST_LIST_UNLOCK(&users);
1649         return vmu;
1650 }
1651
1652 /*!
1653  * \brief Resets a user password to a specified password.
1654  * \param context
1655  * \param mailbox
1656  * \param newpass
1657  *
1658  * This does the actual change password work, called by the vm_change_password() function.
1659  *
1660  * \return zero on success, -1 on error.
1661  */
1662 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1663 {
1664         /* This function could be made to generate one from a database, too */
1665         struct ast_vm_user *cur;
1666         int res = -1;
1667         AST_LIST_LOCK(&users);
1668         AST_LIST_TRAVERSE(&users, cur, list) {
1669                 if ((!context || !strcasecmp(context, cur->context)) &&
1670                         (!strcasecmp(mailbox, cur->mailbox)))
1671                                 break;
1672         }
1673         if (cur) {
1674                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
1675                 res = 0;
1676         }
1677         AST_LIST_UNLOCK(&users);
1678         return res;
1679 }
1680
1681 /*! 
1682  * \brief The handler for the change password option.
1683  * \param vmu The voicemail user to work with.
1684  * \param newpassword The new password (that has been gathered from the appropriate prompting).
1685  * 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.
1686  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1687  */
1688 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1689 {
1690         struct ast_config   *cfg = NULL;
1691         struct ast_variable *var = NULL;
1692         struct ast_category *cat = NULL;
1693         char *category = NULL, *value = NULL, *new = NULL;
1694         const char *tmp = NULL;
1695         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1696         char secretfn[PATH_MAX] = "";
1697         int found = 0;
1698
1699         if (!change_password_realtime(vmu, newpassword))
1700                 return;
1701
1702         /* check if we should store the secret in the spool directory next to the messages */
1703         switch (vmu->passwordlocation) {
1704         case OPT_PWLOC_SPOOLDIR:
1705                 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
1706                 if (write_password_to_file(secretfn, newpassword) == 0) {
1707                         ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
1708                         ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
1709                         reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1710                         ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1711                         break;
1712                 } else {
1713                         ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
1714                 }
1715                 /* Fall-through */
1716         case OPT_PWLOC_VOICEMAILCONF:
1717                 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1718                         while ((category = ast_category_browse(cfg, category))) {
1719                                 if (!strcasecmp(category, vmu->context)) {
1720                                         if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1721                                                 ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1722                                                 break;
1723                                         }
1724                                         value = strstr(tmp, ",");
1725                                         if (!value) {
1726                                                 new = ast_alloca(strlen(newpassword)+1);
1727                                                 sprintf(new, "%s", newpassword);
1728                                         } else {
1729                                                 new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
1730                                                 sprintf(new, "%s%s", newpassword, value);
1731                                         }
1732                                         if (!(cat = ast_category_get(cfg, category))) {
1733                                                 ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1734                                                 break;
1735                                         }
1736                                         ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1737                                         found = 1;
1738                                 }
1739                         }
1740                         /* save the results */
1741                         if (found) {
1742                                 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
1743                                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1744                                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1745                                 ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
1746                                 break;
1747                         }
1748                 }
1749                 /* Fall-through */
1750         case OPT_PWLOC_USERSCONF:
1751                 /* check users.conf and update the password stored for the mailbox */
1752                 /* if no vmsecret entry exists create one. */
1753                 if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1754                         ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1755                         for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
1756                                 ast_debug(4, "users.conf: %s\n", category);
1757                                 if (!strcasecmp(category, vmu->mailbox)) {
1758                                         if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
1759                                                 ast_debug(3, "looks like we need to make vmsecret!\n");
1760                                                 var = ast_variable_new("vmsecret", newpassword, "");
1761                                         } else {
1762                                                 var = NULL;
1763                                         }
1764                                         new = ast_alloca(strlen(newpassword) + 1);
1765                                         sprintf(new, "%s", newpassword);
1766                                         if (!(cat = ast_category_get(cfg, category))) {
1767                                                 ast_debug(4, "failed to get category!\n");
1768                                                 ast_free(var);
1769                                                 break;
1770                                         }
1771                                         if (!var) {
1772                                                 ast_variable_update(cat, "vmsecret", new, NULL, 0);
1773                                         } else {
1774                                                 ast_variable_append(cat, var);
1775                                         }
1776                                         found = 1;
1777                                         break;
1778                                 }
1779                         }
1780                         /* save the results and clean things up */
1781                         if (found) {
1782                                 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
1783                                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1784                                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1785                                 ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
1786                         }
1787                 }
1788         }
1789 }
1790
1791 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1792 {
1793         char buf[255];
1794         snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
1795         ast_debug(1, "External password: %s\n",buf);
1796         if (!ast_safe_system(buf)) {
1797                 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
1798                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1799                 /* Reset the password in memory, too */
1800                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1801         }
1802 }
1803
1804 /*! 
1805  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1806  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1807  * \param len The length of the path string that was written out.
1808  * \param context
1809  * \param ext 
1810  * \param folder 
1811  * 
1812  * The path is constructed as 
1813  *      VM_SPOOL_DIRcontext/ext/folder
1814  *
1815  * \return zero on success, -1 on error.
1816  */
1817 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1818 {
1819         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1820 }
1821
1822 /*! 
1823  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1824  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1825  * \param len The length of the path string that was written out.
1826  * \param dir 
1827  * \param num 
1828  * 
1829  * The path is constructed as 
1830  *      VM_SPOOL_DIRcontext/ext/folder
1831  *
1832  * \return zero on success, -1 on error.
1833  */
1834 static int make_file(char *dest, const int len, const char *dir, const int num)
1835 {
1836         return snprintf(dest, len, "%s/msg%04d", dir, num);
1837 }
1838
1839 /* same as mkstemp, but return a FILE * */
1840 static FILE *vm_mkftemp(char *template)
1841 {
1842         FILE *p = NULL;
1843         int pfd = mkstemp(template);
1844         chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1845         if (pfd > -1) {
1846                 p = fdopen(pfd, "w+");
1847                 if (!p) {
1848                         close(pfd);
1849                         pfd = -1;
1850                 }
1851         }
1852         return p;
1853 }
1854
1855 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1856  * \param dest    String. base directory.
1857  * \param len     Length of dest.
1858  * \param context String. Ignored if is null or empty string.
1859  * \param ext     String. Ignored if is null or empty string.
1860  * \param folder  String. Ignored if is null or empty string. 
1861  * \return -1 on failure, 0 on success.
1862  */
1863 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1864 {
1865         mode_t  mode = VOICEMAIL_DIR_MODE;
1866         int res;
1867
1868         make_dir(dest, len, context, ext, folder);
1869         if ((res = ast_mkdir(dest, mode))) {
1870                 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1871                 return -1;
1872         }
1873         return 0;
1874 }
1875
1876 static const char *mbox(struct ast_vm_user *vmu, int id)
1877 {
1878 #ifdef IMAP_STORAGE
1879         if (vmu && id == 0) {
1880                 return vmu->imapfolder;
1881         }
1882 #endif
1883         return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
1884 }
1885
1886 static const char *vm_index_to_foldername(int id)
1887 {
1888         return mbox(NULL, id);
1889 }
1890
1891
1892 static int get_folder_by_name(const char *name)
1893 {
1894         size_t i;
1895
1896         for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
1897                 if (strcasecmp(name, mailbox_folders[i]) == 0) {
1898                         return i;
1899                 }
1900         }
1901
1902         return -1;
1903 }
1904
1905 static void free_user(struct ast_vm_user *vmu)
1906 {
1907         if (ast_test_flag(vmu, VM_ALLOCED)) {
1908
1909                 ast_free(vmu->emailbody);
1910                 vmu->emailbody = NULL;
1911
1912                 ast_free(vmu->emailsubject);
1913                 vmu->emailsubject = NULL;
1914
1915                 ast_free(vmu);
1916         }
1917 }
1918
1919 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
1920
1921         int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
1922
1923         /* remove old allocation */
1924         if (vms->deleted) {
1925                 ast_free(vms->deleted);
1926                 vms->deleted = NULL;
1927         }
1928         if (vms->heard) {
1929                 ast_free(vms->heard);
1930                 vms->heard = NULL;
1931         }
1932         vms->dh_arraysize = 0;
1933
1934         if (arraysize > 0) {
1935                 if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
1936                         return -1;
1937                 }
1938                 if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
1939                         ast_free(vms->deleted);
1940                         vms->deleted = NULL;
1941                         return -1;
1942                 }
1943                 vms->dh_arraysize = arraysize;
1944         }
1945
1946         return 0;
1947 }
1948
1949 /* All IMAP-specific functions should go in this block. This
1950  * keeps them from being spread out all over the code */
1951 #ifdef IMAP_STORAGE
1952 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
1953 {
1954         char arg[10];
1955         struct vm_state *vms;
1956         unsigned long messageNum;
1957
1958         /* If greetings aren't stored in IMAP, just delete the file */
1959         if (msgnum < 0 && !imapgreetings) {
1960                 ast_filedelete(file, NULL);
1961                 return;
1962         }
1963
1964         if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1965                 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);
1966                 return;
1967         }
1968
1969         /* find real message number based on msgnum */
1970         /* this may be an index into vms->msgArray based on the msgnum. */
1971         messageNum = vms->msgArray[msgnum];
1972         if (messageNum == 0) {
1973                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
1974                 return;
1975         }
1976         ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
1977         /* delete message */
1978         snprintf (arg, sizeof(arg), "%lu", messageNum);
1979         ast_mutex_lock(&vms->lock);
1980         mail_setflag (vms->mailstream, arg, "\\DELETED");
1981         mail_expunge(vms->mailstream);
1982         ast_mutex_unlock(&vms->lock);
1983 }
1984
1985 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)
1986 {
1987         struct ast_channel *chan;
1988         char *cid;
1989         char *cid_name;
1990         char *cid_num;
1991         struct vm_state *vms;
1992         const char *duration_str;
1993         int duration = 0;
1994
1995         /*
1996          * First, get things initially set up. If any of this fails, then
1997          * back out before doing anything substantial
1998          */
1999         vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
2000         if (!vms) {
2001                 return;
2002         }
2003
2004         if (open_mailbox(vms, vmu, folder)) {
2005                 return;
2006         }
2007
2008         chan = ast_dummy_channel_alloc();
2009         if (!chan) {
2010                 close_mailbox(vms, vmu);
2011                 return;
2012         }
2013
2014         /*
2015          * We need to make sure the new message we save has the same
2016          * callerid, flag, and duration as the original message
2017          */
2018         cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
2019
2020         if (!ast_strlen_zero(cid)) {
2021                 ast_callerid_parse(cid, &cid_name, &cid_num);
2022                 ast_party_caller_init(ast_channel_caller(chan));
2023                 if (!ast_strlen_zero(cid_name)) {
2024                         ast_channel_caller(chan)->id.name.valid = 1;
2025                         ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
2026                 }
2027                 if (!ast_strlen_zero(cid_num)) {
2028                         ast_channel_caller(chan)->id.number.valid = 1;
2029                         ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
2030                 }
2031         }
2032
2033         duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
2034
2035         if (!ast_strlen_zero(duration_str)) {
2036                 sscanf(duration_str, "%30d", &duration);
2037         }
2038
2039         /*
2040          * IMAP messages cannot be altered once delivered. So we have to delete the
2041          * current message and then re-add it with the updated message ID.
2042          *
2043          * Furthermore, there currently is no atomic way to create a new message and to
2044          * store it in an arbitrary folder. So we have to save it to the INBOX and then
2045          * move to the appropriate folder.
2046          */
2047         if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
2048                         duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
2049                 if (folder != NEW_FOLDER) {
2050                         save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
2051                 }
2052                 vm_imap_delete(dir, msgnum, vmu);
2053         }
2054         close_mailbox(vms, vmu);
2055         ast_channel_unref(chan);
2056 }
2057
2058 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
2059 {
2060         struct vm_state *vms_p;
2061         char *file, *filename;
2062         char *attachment;
2063         int i;
2064         BODY *body;
2065
2066         /* This function is only used for retrieval of IMAP greetings
2067          * regular messages are not retrieved this way, nor are greetings
2068          * if they are stored locally*/
2069         if (msgnum > -1 || !imapgreetings) {
2070                 return 0;
2071         } else {
2072                 file = strrchr(ast_strdupa(dir), '/');
2073                 if (file)
2074                         *file++ = '\0';
2075                 else {
2076                         ast_debug(1, "Failed to procure file name from directory passed.\n");
2077                         return -1;
2078                 }
2079         }
2080
2081         /* check if someone is accessing this box right now... */
2082         if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
2083                 !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2084                 /* Unlike when retrieving a message, it is reasonable not to be able to find a 
2085                 * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
2086                 * that's all we need to do.
2087                 */
2088                 if (!(vms_p = create_vm_state_from_user(vmu))) {
2089                         ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
2090                         return -1;
2091                 }
2092         }
2093
2094         /* Greetings will never have a prepended message */
2095         *vms_p->introfn = '\0';
2096
2097         ast_mutex_lock(&vms_p->lock);
2098         if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
2099                 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2100                 ast_mutex_unlock(&vms_p->lock);
2101                 return -1;
2102         }
2103
2104         /*XXX Yuck, this could probably be done a lot better */
2105         for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
2106                 mail_fetchstructure(vms_p->mailstream, i + 1, &body);
2107                 /* We have the body, now we extract the file name of the first attachment. */
2108                 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2109                         attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
2110                 } else {
2111                         ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
2112                         ast_mutex_unlock(&vms_p->lock);
2113                         return -1;
2114                 }
2115                 filename = strsep(&attachment, ".");
2116                 if (!strcmp(filename, file)) {
2117                         ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
2118                         vms_p->msgArray[vms_p->curmsg] = i + 1;
2119                         save_body(body, vms_p, "2", attachment, 0);
2120                         ast_mutex_unlock(&vms_p->lock);
2121                         return 0;
2122                 }
2123         }
2124         ast_mutex_unlock(&vms_p->lock);
2125
2126         return -1;
2127 }
2128
2129 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
2130 {
2131         BODY *body;
2132         char *header_content;
2133         char *attachedfilefmt;
2134         char buf[80];
2135         struct vm_state *vms;
2136         char text_file[PATH_MAX];
2137         FILE *text_file_ptr;
2138         int res = 0;
2139         struct ast_vm_user *vmu;
2140
2141         if (!(vmu = find_user(NULL, context, mailbox))) {
2142                 ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
2143                 return -1;
2144         }
2145         
2146         if (msgnum < 0) {
2147                 if (imapgreetings) {
2148                         res = imap_retrieve_greeting(dir, msgnum, vmu);
2149                         goto exit;
2150                 } else {
2151                         res = 0;
2152                         goto exit;
2153                 }
2154         }
2155
2156         /* Before anything can happen, we need a vm_state so that we can
2157          * actually access the imap server through the vms->mailstream
2158          */
2159         if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2160                 /* This should not happen. If it does, then I guess we'd
2161                  * need to create the vm_state, extract which mailbox to
2162                  * open, and then set up the msgArray so that the correct
2163                  * IMAP message could be accessed. If I have seen correctly
2164                  * though, the vms should be obtainable from the vmstates list
2165                  * and should have its msgArray properly set up.
2166                  */
2167                 ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
2168                 res = -1;
2169                 goto exit;
2170         }
2171
2172         make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
2173         snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
2174
2175         /* Don't try to retrieve a message from IMAP if it already is on the file system */
2176         if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
2177                 res = 0;
2178                 goto exit;
2179         }
2180
2181         ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
2182         if (vms->msgArray[msgnum] == 0) {
2183                 ast_log(LOG_WARNING, "Trying to access unknown message\n");
2184                 res = -1;
2185                 goto exit;
2186         }
2187
2188         /* This will only work for new messages... */
2189         ast_mutex_lock(&vms->lock);
2190         header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
2191         ast_mutex_unlock(&vms->lock);
2192         /* empty string means no valid header */
2193         if (ast_strlen_zero(header_content)) {
2194                 ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
2195                 res = -1;
2196                 goto exit;
2197         }
2198
2199         ast_mutex_lock(&vms->lock);
2200         mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
2201         ast_mutex_unlock(&vms->lock);
2202
2203         /* We have the body, now we extract the file name of the first attachment. */
2204         if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2205                 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
2206         } else {
2207                 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
2208                 res = -1;
2209                 goto exit;
2210         }
2211         
2212         /* Find the format of the attached file */
2213
2214         strsep(&attachedfilefmt, ".");
2215         if (!attachedfilefmt) {
2216                 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
2217                 res = -1;
2218                 goto exit;
2219         }
2220         
2221         save_body(body, vms, "2", attachedfilefmt, 0);
2222         if (save_body(body, vms, "3", attachedfilefmt, 1)) {
2223                 *vms->introfn = '\0';
2224         }
2225
2226         /* Get info from headers!! */
2227         snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
2228
2229         if (!(text_file_ptr = fopen(text_file, "w"))) {
2230                 ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
2231         }
2232
2233         fprintf(text_file_ptr, "%s\n", "[message]");
2234
2235         if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
2236                 fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2237         }
2238         if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
2239                 fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2240         }
2241         if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
2242                 fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2243         }
2244         if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
2245                 fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2246         }
2247         if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
2248                 fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2249         }
2250         if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
2251                 fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2252         }
2253         if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
2254                 fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2255         }
2256         if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
2257                 fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
2258         }
2259         fclose(text_file_ptr);
2260
2261 exit:
2262         free_user(vmu);
2263         return res;
2264 }
2265
2266 static int folder_int(const char *folder)
2267 {
2268         /*assume a NULL folder means INBOX*/
2269         if (!folder) {
2270                 return 0;
2271         }
2272         if (!strcasecmp(folder, imapfolder)) {
2273                 return 0;
2274         } else if (!strcasecmp(folder, "Old")) {
2275                 return 1;
2276         } else if (!strcasecmp(folder, "Work")) {
2277                 return 2;
2278         } else if (!strcasecmp(folder, "Family")) {
2279                 return 3;
2280         } else if (!strcasecmp(folder, "Friends")) {
2281                 return 4;
2282         } else if (!strcasecmp(folder, "Cust1")) {
2283                 return 5;
2284         } else if (!strcasecmp(folder, "Cust2")) {
2285                 return 6;
2286         } else if (!strcasecmp(folder, "Cust3")) {
2287                 return 7;
2288         } else if (!strcasecmp(folder, "Cust4")) {
2289                 return 8;
2290         } else if (!strcasecmp(folder, "Cust5")) {
2291                 return 9;
2292         } else if (!strcasecmp(folder, "Urgent")) {
2293                 return 11;
2294         } else { /*assume they meant INBOX if folder is not found otherwise*/
2295                 return 0;
2296         }
2297 }
2298
2299 static int __messagecount(const char *context, const char *mailbox, const char *folder)
2300 {
2301         SEARCHPGM *pgm;
2302         SEARCHHEADER *hdr;
2303
2304         struct ast_vm_user *vmu, vmus;
2305         struct vm_state *vms_p;
2306         int ret = 0;
2307         int fold = folder_int(folder);
2308         int urgent = 0;
2309         
2310         /* If URGENT, then look at INBOX */
2311         if (fold == 11) {
2312                 fold = NEW_FOLDER;
2313                 urgent = 1;
2314         }
2315
2316         if (ast_strlen_zero(mailbox))
2317                 return 0;
2318
2319         /* We have to get the user before we can open the stream! */
2320         vmu = find_user(&vmus, context, mailbox);
2321         if (!vmu) {
2322                 ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2323                 return -1;
2324         } else {
2325                 /* No IMAP account available */
2326                 if (vmu->imapuser[0] == '\0') {
2327                         ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2328                         return -1;
2329                 }
2330         }
2331
2332         /* No IMAP account available */
2333         if (vmu->imapuser[0] == '\0') {
2334                 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2335                 free_user(vmu);
2336                 return -1;
2337         }
2338
2339         /* check if someone is accessing this box right now... */
2340         vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2341         if (!vms_p) {
2342                 vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2343         }
2344         if (vms_p) {
2345                 ast_debug(3, "Returning before search - user is logged in\n");
2346                 if (fold == 0) { /* INBOX */
2347                         return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2348                 }
2349                 if (fold == 1) { /* Old messages */
2350                         return vms_p->oldmessages;
2351                 }
2352         }
2353
2354         /* add one if not there... */
2355         vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2356         if (!vms_p) {
2357                 vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2358         }
2359
2360         if (!vms_p) {
2361                 vms_p = create_vm_state_from_user(vmu);
2362         }
2363         ret = init_mailstream(vms_p, fold);
2364         if (!vms_p->mailstream) {
2365                 ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2366                 return -1;
2367         }
2368         if (ret == 0) {
2369                 ast_mutex_lock(&vms_p->lock);
2370                 pgm = mail_newsearchpgm ();
2371                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2372                 hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2373                 pgm->header = hdr;
2374                 if (fold != OLD_FOLDER) {
2375                         pgm->unseen = 1;
2376                         pgm->seen = 0;
2377                 }
2378                 /* In the special case where fold is 1 (old messages) we have to do things a bit
2379                  * differently. Old messages are stored in the INBOX but are marked as "seen"
2380                  */
2381                 else {
2382                         pgm->unseen = 0;
2383                         pgm->seen = 1;
2384                 }
2385                 /* look for urgent messages */
2386                 if (fold == NEW_FOLDER) {
2387                         if (urgent) {
2388                                 pgm->flagged = 1;
2389                                 pgm->unflagged = 0;
2390                         } else {
2391                                 pgm->flagged = 0;
2392                                 pgm->unflagged = 1;
2393                         }
2394                 }
2395                 pgm->undeleted = 1;
2396                 pgm->deleted = 0;
2397
2398                 vms_p->vmArrayIndex = 0;
2399                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2400                 if (fold == 0 && urgent == 0)
2401                         vms_p->newmessages = vms_p->vmArrayIndex;
2402                 if (fold == 1)
2403                         vms_p->oldmessages = vms_p->vmArrayIndex;
2404                 if (fold == 0 && urgent == 1)
2405                         vms_p->urgentmessages = vms_p->vmArrayIndex;
2406                 /*Freeing the searchpgm also frees the searchhdr*/
2407                 mail_free_searchpgm(&pgm);
2408                 ast_mutex_unlock(&vms_p->lock);
2409                 vms_p->updated = 0;
2410                 return vms_p->vmArrayIndex;
2411         } else {
2412                 ast_mutex_lock(&vms_p->lock);
2413                 mail_ping(vms_p->mailstream);
2414                 ast_mutex_unlock(&vms_p->lock);
2415         }
2416         return 0;
2417 }
2418
2419 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2420 {
2421         /* Check if mailbox is full */
2422         check_quota(vms, vmu->imapfolder);
2423         if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2424                 ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2425                 if (chan) {
2426                         ast_play_and_wait(chan, "vm-mailboxfull");
2427                 }
2428                 return -1;
2429         }
2430
2431         /* Check if we have exceeded maxmsg */
2432         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));
2433         if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2434                 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2435                 if (chan) {
2436                         ast_play_and_wait(chan, "vm-mailboxfull");
2437                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2438                 }
2439                 return -1;
2440         }
2441
2442         return 0;
2443 }
2444
2445 /*!
2446  * \brief Gets the number of messages that exist in a mailbox folder.
2447  * \param context
2448  * \param mailbox
2449  * \param folder
2450  * 
2451  * This method is used when IMAP backend is used.
2452  * \return The number of messages in this mailbox folder (zero or more).
2453  */
2454 static int messagecount(const char *context, const char *mailbox, const char *folder)
2455 {
2456         if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2457                 return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2458         } else {
2459                 return __messagecount(context, mailbox, folder);
2460         }
2461 }
2462
2463 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)
2464 {
2465         char *myserveremail = serveremail;
2466         char fn[PATH_MAX];
2467         char introfn[PATH_MAX];
2468         char mailbox[256];
2469         char *stringp;
2470         FILE *p = NULL;
2471         char tmp[80] = "/tmp/astmail-XXXXXX";
2472         long len;
2473         void *buf;
2474         int tempcopy = 0;
2475         STRING str;
2476         int ret; /* for better error checking */
2477         char *imap_flags = NIL;
2478         int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
2479         int box = NEW_FOLDER;
2480
2481         /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2482         if (msgnum < 0) {
2483                 if(!imapgreetings) {
2484                         return 0;
2485                 } else {
2486                         box = GREETINGS_FOLDER;
2487                 }
2488         }
2489
2490         if (imap_check_limits(chan, vms, vmu, msgcount)) {
2491                 return -1;
2492         }
2493
2494         /* Set urgent flag for IMAP message */
2495         if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2496                 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2497                 imap_flags = "\\FLAGGED";
2498         }
2499
2500         /* Attach only the first format */
2501         fmt = ast_strdupa(fmt);
2502         stringp = fmt;
2503         strsep(&stringp, "|");
2504
2505         if (!ast_strlen_zero(vmu->serveremail))
2506                 myserveremail = vmu->serveremail;
2507
2508         if (msgnum > -1)
2509                 make_file(fn, sizeof(fn), dir, msgnum);
2510         else
2511                 ast_copy_string (fn, dir, sizeof(fn));
2512
2513         snprintf(introfn, sizeof(introfn), "%sintro", fn);
2514         if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2515                 *introfn = '\0';
2516         }
2517
2518         if (ast_strlen_zero(vmu->email)) {
2519                 /* We need the vmu->email to be set when we call make_email_file, but
2520                  * if we keep it set, a duplicate e-mail will be created. So at the end
2521                  * of this function, we will revert back to an empty string if tempcopy
2522                  * is 1.
2523                  */
2524                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2525                 tempcopy = 1;
2526         }
2527
2528         if (!strcmp(fmt, "wav49"))
2529                 fmt = "WAV";
2530         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2531
2532         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2533            command hangs. */
2534         if (!(p = vm_mkftemp(tmp))) {
2535                 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2536                 if (tempcopy)
2537                         *(vmu->email) = '\0';
2538                 return -1;
2539         }
2540
2541         if (msgnum < 0 && imapgreetings) {
2542                 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
2543                         ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
2544                         return -1;
2545                 }
2546                 imap_delete_old_greeting(fn, vms);
2547         }
2548
2549         make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
2550                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
2551                 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
2552                 fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
2553         /* read mail file to memory */
2554         len = ftell(p);
2555         rewind(p);
2556         if (!(buf = ast_malloc(len + 1))) {
2557                 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2558                 fclose(p);
2559                 if (tempcopy)
2560                         *(vmu->email) = '\0';
2561                 return -1;
2562         }
2563         if (fread(buf, len, 1, p) < len) {
2564                 if (ferror(p)) {
2565                         ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
2566                         return -1;
2567                 }
2568         }
2569         ((char *) buf)[len] = '\0';
2570         INIT(&str, mail_string, buf, len);
2571         ret = init_mailstream(vms, box);
2572         if (ret == 0) {
2573                 imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
2574                 ast_mutex_lock(&vms->lock);
2575                 if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
2576                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2577                 ast_mutex_unlock(&vms->lock);
2578                 fclose(p);
2579                 unlink(tmp);
2580                 ast_free(buf);
2581         } else {
2582                 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
2583                 fclose(p);
2584                 unlink(tmp);
2585                 ast_free(buf);
2586                 return -1;
2587         }
2588         ast_debug(3, "%s stored\n", fn);
2589
2590         if (tempcopy)
2591                 *(vmu->email) = '\0';
2592         inprocess_count(vmu->mailbox, vmu->context, -1);
2593         return 0;
2594
2595 }
2596
2597 /*!
2598  * \brief Gets the number of messages that exist in the inbox folder.
2599  * \param mailbox_context
2600  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
2601  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
2602  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
2603  * 
2604  * This method is used when IMAP backend is used.
2605  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
2606  *
2607  * \return zero on success, -1 on error.
2608  */
2609
2610 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2611 {
2612         char tmp[PATH_MAX] = "";
2613         char *mailboxnc;
2614         char *context;
2615         char *mb;
2616         char *cur;
2617         if (newmsgs)
2618                 *newmsgs = 0;
2619         if (oldmsgs)
2620                 *oldmsgs = 0;
2621         if (urgentmsgs)
2622                 *urgentmsgs = 0;
2623
2624         ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
2625         /* If no mailbox, return immediately */
2626         if (ast_strlen_zero(mailbox_context))
2627                 return 0;
2628
2629         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2630         context = strchr(tmp, '@');
2631         if (strchr(mailbox_context, ',')) {
2632                 int tmpnew, tmpold, tmpurgent;
2633                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2634                 mb = tmp;
2635                 while ((cur = strsep(&mb, ", "))) {
2636                         if (!ast_strlen_zero(cur)) {
2637                                 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2638                                         return -1;
2639                                 else {
2640                                         if (newmsgs)
2641                                                 *newmsgs += tmpnew; 
2642                                         if (oldmsgs)
2643                                                 *oldmsgs += tmpold;
2644                                         if (urgentmsgs)
2645                                                 *urgentmsgs += tmpurgent;
2646                                 }
2647                         }
2648                 }
2649                 return 0;
2650         }
2651         if (context) {
2652                 *context = '\0';
2653                 mailboxnc = tmp;
2654                 context++;
2655         } else {
2656                 context = "default";
2657                 mailboxnc = (char *) mailbox_context;
2658         }
2659
2660         if (newmsgs) {
2661                 struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
2662                 if (!vmu) {
2663                         ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2664                         return -1;
2665                 }
2666                 if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
2667                         free_user(vmu);
2668                         return -1;
2669                 }
2670                 free_user(vmu);
2671         }
2672         if (oldmsgs) {
2673                 if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
2674                         return -1;
2675                 }
2676         }
2677         if (urgentmsgs) {
2678                 if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
2679                         return -1;
2680                 }
2681         }
2682         return 0;
2683 }
2684
2685 /** 
2686  * \brief Determines if the given folder has messages.
2687  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
2688  * \param folder the folder to look in
2689  *
2690  * This function is used when the mailbox is stored in an IMAP back end.
2691  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
2692  * \return 1 if the folder has one or more messages. zero otherwise.
2693  */
2694
2695 static int has_voicemail(const char *mailbox, const char *folder)
2696 {
2697         char tmp[256], *tmp2, *box, *context;
2698         ast_copy_string(tmp, mailbox, sizeof(tmp));
2699         tmp2 = tmp;
2700         if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
2701                 while ((box = strsep(&tmp2, ",&"))) {
2702                         if (!ast_strlen_zero(box)) {
2703                                 if (has_voicemail(box, folder)) {
2704                                         return 1;
2705                                 }
2706                         }
2707                 }
2708         }
2709         if ((context = strchr(tmp, '@'))) {
2710                 *context++ = '\0';
2711         } else {
2712                 context = "default";
2713         }
2714         return __messagecount(context, tmp, folder) ? 1 : 0;
2715 }
2716
2717 /*!
2718  * \brief Copies a message from one mailbox to another.
2719  * \param chan
2720  * \param vmu
2721  * \param imbox
2722  * \param msgnum
2723  * \param duration
2724  * \param recip
2725  * \param fmt
2726  * \param dir
2727  *
2728  * This works with IMAP storage based mailboxes.
2729  *
2730  * \return zero on success, -1 on error.
2731  */
2732 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)
2733 {
2734         struct vm_state *sendvms = NULL;
2735         char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2736         if (msgnum >= recip->maxmsg) {
2737                 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2738                 return -1;
2739         }
2740         if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2741                 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2742                 return -1;
2743         }
2744         if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
2745                 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2746                 return -1;
2747         }
2748         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2749         ast_mutex_lock(&sendvms->lock);
2750         if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
2751                 ast_mutex_unlock(&sendvms->lock);
2752                 return 0;
2753         }
2754         ast_mutex_unlock(&sendvms->lock);
2755         ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2756         return -1;
2757 }
2758
2759 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
2760 {
2761         char tmp[256], *t = tmp;
2762         size_t left = sizeof(tmp);
2763         
2764         if (box == OLD_FOLDER) {
2765                 ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
2766         } else {
2767                 ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
2768         }
2769
2770         if (box == NEW_FOLDER) {
2771                 ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
2772         } else {
2773                 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
2774         }
2775
2776         /* Build up server information */
2777         ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
2778
2779         /* Add authentication user if present */
2780         if (!ast_strlen_zero(authuser))
2781                 ast_build_string(&t, &left, "/authuser=%s", authuser);
2782
2783         /* Add flags if present */
2784         if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
2785                 ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
2786         }
2787
2788         /* End with username */
2789 #if 1
2790         ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
2791 #else
2792         ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
2793 #endif
2794         if (box == NEW_FOLDER || box == OLD_FOLDER)
2795                 snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
2796         else if (box == GREETINGS_FOLDER)
2797                 snprintf(spec, len, "%s%s", tmp, greetingfolder);
2798         else {  /* Other folders such as Friends, Family, etc... */
2799                 if (!ast_strlen_zero(imapparentfolder)) {
2800                         /* imapparentfolder would typically be set to INBOX */
2801                         snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
2802                 } else {
2803                         snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
2804                 }
2805         }
2806 }
2807
2808 static int init_mailstream(struct vm_state *vms, int box)
2809 {
2810         MAILSTREAM *stream = NIL;
2811         long debug;
2812         char tmp[256];
2813
2814         if (!vms) {
2815                 ast_log(LOG_ERROR, "vm_state is NULL!\n");
2816                 return -1;
2817         }
2818         ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
2819         if (vms->mailstream == NIL || !vms->mailstream) {
2820                 ast_debug(1, "mailstream not set.\n");
2821         } else {
2822                 stream = vms->mailstream;
2823         }
2824         /* debug = T;  user wants protocol telemetry? */
2825         debug = NIL;  /* NO protocol telemetry? */
2826
2827         if (delimiter == '\0') {                /* did not probe the server yet */
2828                 char *cp;
2829 #ifdef USE_SYSTEM_IMAP
2830 #include <imap/linkage.c>
2831 #elif defined(USE_SYSTEM_CCLIENT)
2832 #include <c-client/linkage.c>
2833 #else
2834 #include "linkage.c"
2835 #endif
2836                 /* Connect to INBOX first to get folders delimiter */
2837                 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
2838                 ast_mutex_lock(&vms->lock);
2839                 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2840                 ast_mutex_unlock(&vms->lock);
2841                 if (stream == NIL) {
2842                         ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
2843                         return -1;
2844                 }
2845                 get_mailbox_delimiter(vms, stream);
2846                 /* update delimiter in imapfolder */
2847                 for (cp = vms->imapfolder; *cp; cp++)
2848                         if (*cp == '/')
2849                                 *cp = delimiter;
2850         }
2851         /* Now connect to the target folder */
2852         imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
2853         ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
2854         ast_mutex_lock(&vms->lock);
2855         vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2856         ast_mutex_unlock(&vms->lock);
2857         if (vms->mailstream == NIL) {
2858                 return -1;
2859         } else {
2860                 return 0;
2861         }
2862 }
2863
2864 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
2865 {
2866         SEARCHPGM *pgm;
2867         SEARCHHEADER *hdr;
2868         int urgent = 0;
2869
2870         /* If Urgent, then look at INBOX */
2871         if (box == 11) {
2872                 box = NEW_FOLDER;
2873                 urgent = 1;
2874         }
2875
2876         ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
2877         ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
2878         ast_copy_string(vms->imapserver, vmu->imapserver, sizeof(vms->imapserver));
2879         ast_copy_string(vms->imapport, vmu->imapport, sizeof(vms->imapport));
2880         ast_copy_string(vms->imapflags, vmu->imapflags, sizeof(vms->imapflags));
2881         vms->imapversion = vmu->imapversion;
2882         ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
2883
2884         if (init_mailstream(vms, box) || !vms->mailstream) {
2885                 ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
2886                 return -1;
2887         }
2888
2889         create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2890
2891         /* Check Quota */
2892         if  (box == 0)  {
2893                 ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
2894                 check_quota(vms, (char *) mbox(vmu, box));
2895         }
2896
2897         ast_mutex_lock(&vms->lock);
2898         pgm = mail_newsearchpgm();
2899
2900         /* Check IMAP folder for Asterisk messages only... */
2901         hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
2902         hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
2903         pgm->header = hdr;
2904         pgm->deleted = 0;
2905         pgm->undeleted = 1;
2906
2907         /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
2908         if (box == NEW_FOLDER && urgent == 1) {
2909                 pgm->unseen = 1;
2910                 pgm->seen = 0;
2911                 pgm->flagged = 1;
2912                 pgm->unflagged = 0;
2913         } else if (box == NEW_FOLDER && urgent == 0) {
2914                 pgm->unseen = 1;
2915                 pgm->seen = 0;
2916                 pgm->flagged = 0;
2917                 pgm->unflagged = 1;
2918         } else if (box == OLD_FOLDER) {
2919                 pgm->seen = 1;
2920                 pgm->unseen = 0;
2921         }
2922
2923         ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
2924
2925         vms->vmArrayIndex = 0;
2926         mail_search_full (vms->mailstream, NULL, pgm, NIL);
2927         vms->lastmsg = vms->vmArrayIndex - 1;
2928         mail_free_searchpgm(&pgm);
2929         /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
2930          * ensure to allocate enough space to account for all of them. Warn if old messages
2931          * have not been checked first as that is required.
2932          */
2933         if (box == 0 && !vms->dh_arraysize) {
2934                 ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
2935         }
2936         if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
2937                 ast_mutex_unlock(&vms->lock);
2938                 return -1;
2939         }
2940
2941         ast_mutex_unlock(&vms->lock);
2942         return 0;
2943 }
2944
2945 static void write_file(char *filename, char *buffer, unsigned long len)
2946 {
2947         FILE *output;
2948
2949         output = fopen (filename, "w");
2950         if (fwrite(buffer, len, 1, output) != 1) {
2951                 if (ferror(output)) {
2952                         ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
2953                 }
2954         }
2955         fclose (output);
2956 }
2957
2958 static void update_messages_by_imapuser(const char *user, unsigned long number)
2959 {
2960         struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
2961
2962         if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
2963                 return;
2964         }
2965
2966         ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
2967         vms->msgArray[vms->vmArrayIndex++] = number;
2968 }
2969
2970 void mm_searched(MAILSTREAM *stream, unsigned long number)
2971 {
2972         char *mailbox = stream->mailbox, buf[1024] = "", *user;
2973
2974         if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
2975                 return;
2976
2977         update_messages_by_imapuser(user, number);
2978 }
2979
2980 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
2981 {
2982         struct ast_variable *var;
2983         struct ast_vm_user *vmu;
2984
2985         vmu = ast_calloc(1, sizeof *vmu);
2986         if (!vmu)
2987                 return NULL;
2988
2989         populate_defaults(vmu);
2990         ast_set_flag(vmu, VM_ALLOCED);
2991
2992         var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
2993         if (var) {
2994                 apply_options_full(vmu, var);
2995                 ast_variables_destroy(var);
2996                 return vmu;
2997         } else {
2998                 ast_free(vmu);
2999                 return NULL;
3000         }
3001 }
3002
3003 /* Interfaces to C-client */
3004
3005 void mm_exists(MAILSTREAM * stream, unsigned long number)
3006 {
3007         /* mail_ping will callback here if new mail! */
3008         ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
3009         if (number == 0) return;
3010         set_update(stream);
3011 }
3012
3013
3014 void mm_expunged(MAILSTREAM * stream, unsigned long number)
3015 {
3016         /* mail_ping will callback here if expunged mail! */
3017         ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
3018         if (number == 0) return;
3019         set_update(stream);
3020 }
3021
3022
3023 void mm_flags(MAILSTREAM * stream, unsigned long number)
3024 {
3025         /* mail_ping will callback here if read mail! */
3026         ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
3027         if (number == 0) return;
3028         set_update(stream);
3029 }
3030
3031
3032 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
3033 {
3034         ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
3035         mm_log (string, errflg);
3036 }
3037
3038
3039 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3040 {
3041         if (delimiter == '\0') {
3042                 delimiter = delim;
3043         }
3044
3045         ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3046         if (attributes & LATT_NOINFERIORS)
3047                 ast_debug(5, "no inferiors\n");
3048         if (attributes & LATT_NOSELECT)
3049                 ast_debug(5, "no select\n");
3050         if (attributes & LATT_MARKED)
3051                 ast_debug(5, "marked\n");
3052         if (attributes & LATT_UNMARKED)
3053                 ast_debug(5, "unmarked\n");
3054 }
3055
3056
3057 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3058 {
3059         ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3060         if (attributes & LATT_NOINFERIORS)
3061                 ast_debug(5, "no inferiors\n");
3062         if (attributes & LATT_NOSELECT)
3063                 ast_debug(5, "no select\n");
3064         if (attributes & LATT_MARKED)
3065                 ast_debug(5, "marked\n");