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