app_voicemail.c: Fix compiler warning with IMAP build.
[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 /*** MODULEINFO
50         <defaultenabled>yes</defaultenabled>
51         <use type="module">res_adsi</use>
52         <use type="module">res_smdi</use>
53         <support_level>core</support_level>
54  ***/
55
56 /*** MAKEOPTS
57 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
58         <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
59                 <conflict>ODBC_STORAGE</conflict>
60                 <conflict>IMAP_STORAGE</conflict>
61                 <defaultenabled>yes</defaultenabled>
62                 <support_level>core</support_level>
63         </member>
64         <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
65                 <depend>generic_odbc</depend>
66                 <depend>ltdl</depend>
67                 <conflict>IMAP_STORAGE</conflict>
68                 <conflict>FILE_STORAGE</conflict>
69                 <defaultenabled>no</defaultenabled>
70                 <support_level>core</support_level>
71         </member>
72         <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
73                 <depend>imap_tk</depend>
74                 <conflict>ODBC_STORAGE</conflict>
75                 <conflict>FILE_STORAGE</conflict>
76                 <use type="external">openssl</use>
77                 <defaultenabled>no</defaultenabled>
78                 <support_level>core</support_level>
79         </member>
80 </category>
81 ***/
82
83 #include "asterisk.h"
84
85 #ifdef IMAP_STORAGE
86 #include <ctype.h>
87 #include <signal.h>
88 #include <pwd.h>
89 #ifdef USE_SYSTEM_IMAP
90 #include <imap/c-client.h>
91 #include <imap/imap4r1.h>
92 #include <imap/linkage.h>
93 #elif defined (USE_SYSTEM_CCLIENT)
94 #include <c-client/c-client.h>
95 #include <c-client/imap4r1.h>
96 #include <c-client/linkage.h>
97 #else
98 #include "c-client.h"
99 #include "imap4r1.h"
100 #include "linkage.h"
101 #endif
102 #endif
103
104 #include "asterisk/paths.h"     /* use ast_config_AST_SPOOL_DIR */
105 #include <sys/time.h>
106 #include <sys/stat.h>
107 #include <sys/mman.h>
108 #include <time.h>
109 #include <dirent.h>
110 #if defined(__FreeBSD__) || defined(__OpenBSD__)
111 #include <sys/wait.h>
112 #endif
113
114 #include "asterisk/logger.h"
115 #include "asterisk/lock.h"
116 #include "asterisk/file.h"
117 #include "asterisk/channel.h"
118 #include "asterisk/pbx.h"
119 #include "asterisk/config.h"
120 #include "asterisk/say.h"
121 #include "asterisk/module.h"
122 #include "asterisk/adsi.h"
123 #include "asterisk/app.h"
124 #include "asterisk/manager.h"
125 #include "asterisk/dsp.h"
126 #include "asterisk/localtime.h"
127 #include "asterisk/cli.h"
128 #include "asterisk/utils.h"
129 #include "asterisk/stringfields.h"
130 #include "asterisk/strings.h"
131 #include "asterisk/smdi.h"
132 #include "asterisk/astobj2.h"
133 #include "asterisk/taskprocessor.h"
134 #include "asterisk/test.h"
135 #include "asterisk/format_cache.h"
136
137 #ifdef ODBC_STORAGE
138 #include "asterisk/res_odbc.h"
139 #endif
140
141 #ifdef IMAP_STORAGE
142 #include "asterisk/threadstorage.h"
143 #endif
144
145 /*** DOCUMENTATION
146         <application name="VoiceMail" language="en_US">
147                 <synopsis>
148                         Leave a Voicemail message.
149                 </synopsis>
150                 <syntax>
151                         <parameter name="mailboxs" argsep="&amp;" required="true">
152                                 <argument name="mailbox1" argsep="@" required="true">
153                                         <argument name="mailbox" required="true" />
154                                         <argument name="context" />
155                                 </argument>
156                                 <argument name="mailbox2" argsep="@" multiple="true">
157                                         <argument name="mailbox" required="true" />
158                                         <argument name="context" />
159                                 </argument>
160                         </parameter>
161                         <parameter name="options">
162                                 <optionlist>
163                                         <option name="b">
164                                                 <para>Play the <literal>busy</literal> greeting to the calling party.</para>
165                                         </option>
166                                         <option name="d">
167                                                 <argument name="c" />
168                                                 <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
169                                                 if played during the greeting. Context defaults to the current context.</para>
170                                         </option>
171                                         <option name="g">
172                                                 <argument name="#" required="true" />
173                                                 <para>Use the specified amount of gain when recording the voicemail
174                                                 message. The units are whole-number decibels (dB). Only works on supported
175                                                 technologies, which is DAHDI only.</para>
176                                         </option>
177                                         <option name="s">
178                                                 <para>Skip the playback of instructions for leaving a message to the
179                                                 calling party.</para>
180                                         </option>
181                                         <option name="u">
182                                                 <para>Play the <literal>unavailable</literal> greeting.</para>
183                                         </option>
184                                         <option name="U">
185                                                 <para>Mark message as <literal>URGENT</literal>.</para>
186                                         </option>
187                                         <option name="P">
188                                                 <para>Mark message as <literal>PRIORITY</literal>.</para>
189                                         </option>
190                                 </optionlist>
191                         </parameter>
192                 </syntax>
193                 <description>
194                         <para>This application allows the calling party to leave a message for the specified
195                         list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
196                         the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
197                         exist.</para>
198                         <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
199                         <enumlist>
200                                 <enum name="0">
201                                         <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
202                                 </enum>
203                                 <enum name="*">
204                                         <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
205                                 </enum>
206                         </enumlist>
207                         <para>This application will set the following channel variable upon completion:</para>
208                         <variablelist>
209                                 <variable name="VMSTATUS">
210                                         <para>This indicates the status of the execution of the VoiceMail application.</para>
211                                         <value name="SUCCESS" />
212                                         <value name="USEREXIT" />
213                                         <value name="FAILED" />
214                                 </variable>
215                         </variablelist>
216                 </description>
217                 <see-also>
218                         <ref type="application">VoiceMailMain</ref>
219                 </see-also>
220         </application>
221         <application name="VoiceMailMain" language="en_US">
222                 <synopsis>
223                         Check Voicemail messages.
224                 </synopsis>
225                 <syntax>
226                         <parameter name="mailbox" required="true" argsep="@">
227                                 <argument name="mailbox" />
228                                 <argument name="context" />
229                         </parameter>
230                         <parameter name="options">
231                                 <optionlist>
232                                         <option name="p">
233                                                 <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
234                                                 the mailbox that is entered by the caller.</para>
235                                         </option>
236                                         <option name="g">
237                                                 <argument name="#" required="true" />
238                                                 <para>Use the specified amount of gain when recording a voicemail message.
239                                                 The units are whole-number decibels (dB).</para>
240                                         </option>
241                                         <option name="s">
242                                                 <para>Skip checking the passcode for the mailbox.</para>
243                                         </option>
244                                         <option name="a">
245                                                 <argument name="folder" required="true" />
246                                                 <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
247                                                 Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
248                                                 <enumlist>
249                                                         <enum name="0"><para>INBOX</para></enum>
250                                                         <enum name="1"><para>Old</para></enum>
251                                                         <enum name="2"><para>Work</para></enum>
252                                                         <enum name="3"><para>Family</para></enum>
253                                                         <enum name="4"><para>Friends</para></enum>
254                                                         <enum name="5"><para>Cust1</para></enum>
255                                                         <enum name="6"><para>Cust2</para></enum>
256                                                         <enum name="7"><para>Cust3</para></enum>
257                                                         <enum name="8"><para>Cust4</para></enum>
258                                                         <enum name="9"><para>Cust5</para></enum>
259                                                 </enumlist>
260                                         </option>
261                                 </optionlist>
262                         </parameter>
263                 </syntax>
264                 <description>
265                         <para>This application allows the calling party to check voicemail messages. A specific
266                         <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
267                         may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
268                         be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
269                         <literal>default</literal> context will be used.</para>
270                         <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
271                         or Password, and the extension exists:</para>
272                         <enumlist>
273                                 <enum name="*">
274                                         <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
275                                 </enum>
276                         </enumlist>
277                 </description>
278                 <see-also>
279                         <ref type="application">VoiceMail</ref>
280                 </see-also>
281         </application>
282         <application name="MailboxExists" language="en_US">
283                 <synopsis>
284                         Check to see if Voicemail mailbox exists.
285                 </synopsis>
286                 <syntax>
287                         <parameter name="mailbox" required="true" argsep="@">
288                                 <argument name="mailbox" required="true" />
289                                 <argument name="context" />
290                         </parameter>
291                         <parameter name="options">
292                                 <para>None options.</para>
293                         </parameter>
294                 </syntax>
295                 <description>
296                         <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note>
297                         <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
298                         <replaceable>context</replaceable> is specified, the <literal>default</literal> context
299                         will be used.</para>
300                         <para>This application will set the following channel variable upon completion:</para>
301                         <variablelist>
302                                 <variable name="VMBOXEXISTSSTATUS">
303                                         <para>This will contain the status of the execution of the MailboxExists application.
304                                         Possible values include:</para>
305                                         <value name="SUCCESS" />
306                                         <value name="FAILED" />
307                                 </variable>
308                         </variablelist>
309                 </description>
310                 <see-also>
311                         <ref type="function">VM_INFO</ref>
312                 </see-also>
313         </application>
314         <application name="VMAuthenticate" language="en_US">
315                 <synopsis>
316                         Authenticate with Voicemail passwords.
317                 </synopsis>
318                 <syntax>
319                         <parameter name="mailbox" required="true" argsep="@">
320                                 <argument name="mailbox" />
321                                 <argument name="context" />
322                         </parameter>
323                         <parameter name="options">
324                                 <optionlist>
325                                         <option name="s">
326                                                 <para>Skip playing the initial prompts.</para>
327                                         </option>
328                                 </optionlist>
329                         </parameter>
330                 </syntax>
331                 <description>
332                         <para>This application behaves the same way as the Authenticate application, but the passwords
333                         are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
334                         specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
335                         is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
336                         mailbox.</para>
337                         <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
338                         or Password, and the extension exists:</para>
339                         <enumlist>
340                                 <enum name="*">
341                                         <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
342                                 </enum>
343                         </enumlist>
344                 </description>
345         </application>
346         <application name="VoiceMailPlayMsg" language="en_US">
347                 <synopsis>
348                         Play a single voice mail msg from a mailbox by msg id.
349                 </synopsis>
350                 <syntax>
351                         <parameter name="mailbox" required="true" argsep="@">
352                                 <argument name="mailbox" />
353                                 <argument name="context" />
354                         </parameter>
355                         <parameter name="msg_id" required="true">
356                                 <para>The msg id of the msg to play back. </para>
357                         </parameter>
358                 </syntax>
359                 <description>
360                         <para>This application sets the following channel variable upon completion:</para>
361                         <variablelist>
362                                 <variable name="VOICEMAIL_PLAYBACKSTATUS">
363                                         <para>The status of the playback attempt as a text string.</para>
364                                         <value name="SUCCESS"/>
365                                         <value name="FAILED"/>
366                                 </variable>
367                         </variablelist>
368                 </description>
369         </application>
370         <application name="VMSayName" language="en_US">
371                 <synopsis>
372                         Play the name of a voicemail user
373                 </synopsis>
374                 <syntax>
375                         <parameter name="mailbox" required="true" argsep="@">
376                                 <argument name="mailbox" />
377                                 <argument name="context" />
378                         </parameter>
379                 </syntax>
380                 <description>
381                         <para>This application will say the recorded name of the voicemail user specified as the
382                         argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
383                 </description>
384         </application>
385         <function name="MAILBOX_EXISTS" language="en_US">
386                 <synopsis>
387                         Tell if a mailbox is configured.
388                 </synopsis>
389                 <syntax argsep="@">
390                         <parameter name="mailbox" required="true" />
391                         <parameter name="context" />
392                 </syntax>
393                 <description>
394                         <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note>
395                         <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
396                         If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
397                         context.</para>
398                 </description>
399                 <see-also>
400                         <ref type="function">VM_INFO</ref>
401                 </see-also>
402         </function>
403         <function name="VM_INFO" language="en_US">
404                 <synopsis>
405                         Returns the selected attribute from a mailbox.
406                 </synopsis>
407                 <syntax argsep=",">
408                         <parameter name="mailbox" argsep="@" required="true">
409                                 <argument name="mailbox" required="true" />
410                                 <argument name="context" />
411                         </parameter>
412                         <parameter name="attribute" required="true">
413                                 <optionlist>
414                                         <option name="count">
415                                                 <para>Count of messages in specified <replaceable>folder</replaceable>.
416                                                 If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
417                                         </option>
418                                         <option name="email">
419                                                 <para>E-mail address associated with the mailbox.</para>
420                                         </option>
421                                         <option name="exists">
422                                                 <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
423                                         </option>
424                                         <option name="fullname">
425                                                 <para>Full name associated with the mailbox.</para>
426                                         </option>
427                                         <option name="language">
428                                                 <para>Mailbox language if overridden, otherwise the language of the channel.</para>
429                                         </option>
430                                         <option name="locale">
431                                                 <para>Mailbox locale if overridden, otherwise global locale.</para>
432                                         </option>
433                                         <option name="pager">
434                                                 <para>Pager e-mail address associated with the mailbox.</para>
435                                         </option>
436                                         <option name="password">
437                                                 <para>Mailbox access password.</para>
438                                         </option>
439                                         <option name="tz">
440                                                 <para>Mailbox timezone if overridden, otherwise global timezone</para>
441                                         </option>
442                                 </optionlist>
443                         </parameter>
444                         <parameter name="folder" required="false">
445                                 <para>If not specified, <literal>INBOX</literal> is assumed.</para>
446                         </parameter>
447                 </syntax>
448                 <description>
449                         <para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
450                         If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
451                         context. Where the <replaceable>folder</replaceable> can be specified, common folders
452                         include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
453                         <literal>Family</literal> and <literal>Friends</literal>.</para>
454                 </description>
455         </function>
456         <manager name="VoicemailUsersList" language="en_US">
457                 <synopsis>
458                         List All Voicemail User Information.
459                 </synopsis>
460                 <syntax>
461                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
462                 </syntax>
463                 <description>
464                 </description>
465         </manager>
466         <manager name="VoicemailRefresh" language="en_US">
467                 <synopsis>
468                         Tell Asterisk to poll mailboxes for a change
469                 </synopsis>
470                 <syntax>
471                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
472                         <parameter name="Context" />
473                         <parameter name="Mailbox" />
474                 </syntax>
475                 <description>
476                         <para>Normally, MWI indicators are only sent when Asterisk itself
477                         changes a mailbox.  With external programs that modify the content
478                         of a mailbox from outside the application, an option exists called
479                         <literal>pollmailboxes</literal> that will cause voicemail to
480                         continually scan all mailboxes on a system for changes.  This can
481                         cause a large amount of load on a system.  This command allows
482                         external applications to signal when a particular mailbox has
483                         changed, thus permitting external applications to modify mailboxes
484                         and MWI to work without introducing considerable CPU load.</para>
485                         <para>If <replaceable>Context</replaceable> is not specified, all
486                         mailboxes on the system will be polled for changes.  If
487                         <replaceable>Context</replaceable> is specified, but
488                         <replaceable>Mailbox</replaceable> is omitted, then all mailboxes
489                         within <replaceable>Context</replaceable> will be polled.
490                         Otherwise, only a single mailbox will be polled for changes.</para>
491                 </description>
492         </manager>
493  ***/
494
495 #ifdef IMAP_STORAGE
496 static char imapserver[48];
497 static char imapport[8];
498 static char imapflags[128];
499 static char imapfolder[64];
500 static char imapparentfolder[64] = "\0";
501 static char greetingfolder[64];
502 static char authuser[32];
503 static char authpassword[42];
504 static int imapversion = 1;
505
506 static int expungeonhangup = 1;
507 static int imapgreetings = 0;
508 static int imap_poll_logout = 0;
509 static char delimiter = '\0';
510
511 /* mail_open cannot be protected on a stream basis */
512 ast_mutex_t mail_open_lock;
513
514 struct vm_state;
515 struct ast_vm_user;
516
517 AST_THREADSTORAGE(ts_vmstate);
518
519 /* Forward declarations for IMAP */
520 static int init_mailstream(struct vm_state *vms, int box);
521 static void write_file(char *filename, char *buffer, unsigned long len);
522 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
523 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
524 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
525 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
526 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
527 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
528 static void vmstate_insert(struct vm_state *vms);
529 static void vmstate_delete(struct vm_state *vms);
530 static void set_update(MAILSTREAM * stream);
531 static void init_vm_state(struct vm_state *vms);
532 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
533 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
534 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
535 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
536 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);
537 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);
538 static void update_messages_by_imapuser(const char *user, unsigned long number);
539 static int vm_delete(char *file);
540
541 static int imap_remove_file (char *dir, int msgnum);
542 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
543 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
544 static void check_quota(struct vm_state *vms, char *mailbox);
545 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
546 static void imap_logout(const char *mailbox_id);
547
548 struct vmstate {
549         struct vm_state *vms;
550         AST_LIST_ENTRY(vmstate) list;
551 };
552
553 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
554
555 #endif
556
557 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
558
559 #define COMMAND_TIMEOUT 5000
560 /* Don't modify these here; set your umask at runtime instead */
561 #define VOICEMAIL_DIR_MODE      0777
562 #define VOICEMAIL_FILE_MODE     0666
563 #define CHUNKSIZE       65536
564
565 #define VOICEMAIL_CONFIG "voicemail.conf"
566 #define ASTERISK_USERNAME "asterisk"
567
568 /* Define fast-forward, pause, restart, and reverse keys
569  * while listening to a voicemail message - these are
570  * strings, not characters */
571 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
572 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
573 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
574 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
575 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
576 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
577
578 /* Default mail command to mail voicemail. Change it with the
579  * mailcmd= command in voicemail.conf */
580 #define SENDMAIL "/usr/sbin/sendmail -t"
581
582 #define INTRO "vm-intro"
583
584 #define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte
585
586 #define MAXMSG 100
587 #define MAXMSGLIMIT 9999
588
589 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
590
591 #define BASELINELEN 72
592 #define BASEMAXINLINE 256
593 #ifdef IMAP_STORAGE
594 #define ENDL "\r\n"
595 #else
596 #define ENDL "\n"
597 #endif
598
599 #define MAX_DATETIME_FORMAT     512
600 #define MAX_NUM_CID_CONTEXTS 10
601
602 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
603 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
604 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
605 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
606 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
607 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
608 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
609 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
610 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
611 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
612 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
613 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
614 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
615 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
616 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
617 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
618 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
619 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
620 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
621 #define ERROR_LOCK_PATH  -100
622 #define ERROR_MAX_MSGS   -101
623 #define OPERATOR_EXIT     300
624
625 enum vm_box {
626         NEW_FOLDER =            0,
627         OLD_FOLDER =            1,
628         WORK_FOLDER =           2,
629         FAMILY_FOLDER =         3,
630         FRIENDS_FOLDER =        4,
631         GREETINGS_FOLDER =      -1
632 };
633
634 enum vm_option_flags {
635         OPT_SILENT =           (1 << 0),
636         OPT_BUSY_GREETING =    (1 << 1),
637         OPT_UNAVAIL_GREETING = (1 << 2),
638         OPT_RECORDGAIN =       (1 << 3),
639         OPT_PREPEND_MAILBOX =  (1 << 4),
640         OPT_AUTOPLAY =         (1 << 6),
641         OPT_DTMFEXIT =         (1 << 7),
642         OPT_MESSAGE_Urgent =   (1 << 8),
643         OPT_MESSAGE_PRIORITY = (1 << 9)
644 };
645
646 enum vm_option_args {
647         OPT_ARG_RECORDGAIN = 0,
648         OPT_ARG_PLAYFOLDER = 1,
649         OPT_ARG_DTMFEXIT   = 2,
650         /* This *must* be the last value in this enum! */
651         OPT_ARG_ARRAY_SIZE = 3,
652 };
653
654 enum vm_passwordlocation {
655         OPT_PWLOC_VOICEMAILCONF = 0,
656         OPT_PWLOC_SPOOLDIR      = 1,
657         OPT_PWLOC_USERSCONF     = 2,
658 };
659
660 AST_APP_OPTIONS(vm_app_options, {
661         AST_APP_OPTION('s', OPT_SILENT),
662         AST_APP_OPTION('b', OPT_BUSY_GREETING),
663         AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
664         AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
665         AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
666         AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
667         AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
668         AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
669         AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
670 });
671
672 static const char * const mailbox_folders[] = {
673 #ifdef IMAP_STORAGE
674         imapfolder,
675 #else
676         "INBOX",
677 #endif
678         "Old",
679         "Work",
680         "Family",
681         "Friends",
682         "Cust1",
683         "Cust2",
684         "Cust3",
685         "Cust4",
686         "Cust5",
687         "Deleted",
688         "Urgent",
689 };
690
691 static int load_config(int reload);
692 #ifdef TEST_FRAMEWORK
693 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
694 #endif
695 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
696
697 /*! \page vmlang Voicemail Language Syntaxes Supported
698
699         \par Syntaxes supported, not really language codes.
700         \arg \b en    - English
701         \arg \b de    - German
702         \arg \b es    - Spanish
703         \arg \b fr    - French
704         \arg \b it    - Italian
705         \arg \b nl    - Dutch
706         \arg \b pt    - Portuguese
707         \arg \b pt_BR - Portuguese (Brazil)
708         \arg \b gr    - Greek
709         \arg \b no    - Norwegian
710         \arg \b se    - Swedish
711         \arg \b tw    - Chinese (Taiwan)
712         \arg \b ua - Ukrainian
713
714 German requires the following additional soundfile:
715 \arg \b 1F      einE (feminine)
716
717 Spanish requires the following additional soundfile:
718 \arg \b 1M      un (masculine)
719
720 Dutch, Portuguese & Spanish require the following additional soundfiles:
721 \arg \b vm-INBOXs       singular of 'new'
722 \arg \b vm-Olds         singular of 'old/heard/read'
723
724 NB these are plural:
725 \arg \b vm-INBOX        nieuwe (nl)
726 \arg \b vm-Old          oude (nl)
727
728 Polish uses:
729 \arg \b vm-new-a        'new', feminine singular accusative
730 \arg \b vm-new-e        'new', feminine plural accusative
731 \arg \b vm-new-ych      'new', feminine plural genitive
732 \arg \b vm-old-a        'old', feminine singular accusative
733 \arg \b vm-old-e        'old', feminine plural accusative
734 \arg \b vm-old-ych      'old', feminine plural genitive
735 \arg \b digits/1-a      'one', not always same as 'digits/1'
736 \arg \b digits/2-ie     'two', not always same as 'digits/2'
737
738 Swedish uses:
739 \arg \b vm-nytt         singular of 'new'
740 \arg \b vm-nya          plural of 'new'
741 \arg \b vm-gammalt      singular of 'old'
742 \arg \b vm-gamla        plural of 'old'
743 \arg \b digits/ett      'one', not always same as 'digits/1'
744
745 Norwegian uses:
746 \arg \b vm-ny           singular of 'new'
747 \arg \b vm-nye          plural of 'new'
748 \arg \b vm-gammel       singular of 'old'
749 \arg \b vm-gamle        plural of 'old'
750
751 Dutch also uses:
752 \arg \b nl-om           'at'?
753
754 Spanish also uses:
755 \arg \b vm-youhaveno
756
757 Italian requires the following additional soundfile:
758
759 For vm_intro_it:
760 \arg \b vm-nuovo        new
761 \arg \b vm-nuovi        new plural
762 \arg \b vm-vecchio      old
763 \arg \b vm-vecchi       old plural
764
765 Japanese requires the following additional soundfile:
766 \arg \b jp-arimasu          there is
767 \arg \b jp-arimasen         there is not
768 \arg \b jp-oshitekudasai    please press
769 \arg \b jp-ni               article ni
770 \arg \b jp-ga               article ga
771 \arg \b jp-wa               article wa
772 \arg \b jp-wo               article wo
773
774 Chinese (Taiwan) requires the following additional soundfile:
775 \arg \b vm-tong         A class-word for call (tong1)
776 \arg \b vm-ri           A class-word for day (ri4)
777 \arg \b vm-you          You (ni3)
778 \arg \b vm-haveno   Have no (mei2 you3)
779 \arg \b vm-have     Have (you3)
780 \arg \b vm-listen   To listen (yao4 ting1)
781
782
783 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
784 spelled among others when you have to change folder. For the above reasons, vm-INBOX
785 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
786
787 */
788
789 struct baseio {
790         int iocp;
791         int iolen;
792         int linelength;
793         int ateof;
794         unsigned char iobuf[BASEMAXINLINE];
795 };
796
797 /*! Structure for linked list of users 
798  * Use ast_vm_user_destroy() to free one of these structures. */
799 struct ast_vm_user {
800         char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
801         char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
802         char password[80];               /*!< Secret pin code, numbers only */
803         char fullname[80];               /*!< Full name, for directory app */
804         char *email;                     /*!< E-mail address */
805         char *emailsubject;              /*!< E-mail subject */
806         char *emailbody;                 /*!< E-mail body */
807         char pager[80];                  /*!< E-mail address to pager (no attachment) */
808         char serveremail[80];            /*!< From: Mail address */
809         char fromstring[100];            /*!< From: Username */
810         char language[MAX_LANGUAGE];     /*!< Config: Language setting */
811         char zonetag[80];                /*!< Time zone */
812         char locale[20];                 /*!< The locale (for presentation of date/time) */
813         char callback[80];
814         char dialout[80];
815         char uniqueid[80];               /*!< Unique integer identifier */
816         char exit[80];
817         char attachfmt[20];              /*!< Attachment format */
818         unsigned int flags;              /*!< VM_ flags */
819         int saydurationm;
820         int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
821         int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
822         int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
823         int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
824         int passwordlocation;            /*!< Storage location of the password */
825 #ifdef IMAP_STORAGE
826         char imapserver[48];             /*!< IMAP server address */
827         char imapport[8];                /*!< IMAP server port */
828         char imapflags[128];             /*!< IMAP optional flags */
829         char imapuser[80];               /*!< IMAP server login */
830         char imappassword[80];           /*!< IMAP server password if authpassword not defined */
831         char imapfolder[64];             /*!< IMAP voicemail folder */
832         char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
833         int imapversion;                 /*!< If configuration changes, use the new values */
834 #endif
835         double volgain;                  /*!< Volume gain for voicemails sent via email */
836         AST_LIST_ENTRY(ast_vm_user) list;
837 };
838
839 /*! Voicemail time zones */
840 struct vm_zone {
841         AST_LIST_ENTRY(vm_zone) list;
842         char name[80];
843         char timezone[80];
844         char msg_format[512];
845 };
846
847 #define VMSTATE_MAX_MSG_ARRAY 256
848
849 /*! Voicemail mailbox state */
850 struct vm_state {
851         char curbox[80];
852         char username[80];
853         char context[80];
854         char curdir[PATH_MAX];
855         char vmbox[PATH_MAX];
856         char fn[PATH_MAX];
857         char intro[PATH_MAX];
858         int *deleted;
859         int *heard;
860         int dh_arraysize; /* used for deleted / heard allocation */
861         int curmsg;
862         int lastmsg;
863         int newmessages;
864         int oldmessages;
865         int urgentmessages;
866         int starting;
867         int repeats;
868 #ifdef IMAP_STORAGE
869         ast_mutex_t lock;
870         int updated;                         /*!< decremented on each mail check until 1 -allows delay */
871         long *msgArray;
872         unsigned msg_array_max;
873         MAILSTREAM *mailstream;
874         int vmArrayIndex;
875         char imapuser[80];                   /*!< IMAP server login */
876         char imapfolder[64];                 /*!< IMAP voicemail folder */
877         char imapserver[48];                 /*!< IMAP server address */
878         char imapport[8];                    /*!< IMAP server port */
879         char imapflags[128];                 /*!< IMAP optional flags */
880         int imapversion;
881         int interactive;
882         char introfn[PATH_MAX];              /*!< Name of prepended file */
883         unsigned int quota_limit;
884         unsigned int quota_usage;
885         struct vm_state *persist_vms;
886 #endif
887 };
888
889 #ifdef ODBC_STORAGE
890 static char odbc_database[80];
891 static char odbc_table[80];
892 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
893 #define DISPOSE(a,b) remove_file(a,b)
894 #define STORE(a,b,c,d,e,f,g,h,i,j,k) store_file(a,b,c,d)
895 #define EXISTS(a,b,c,d) (message_exists(a,b))
896 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
897 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
898 #define DELETE(a,b,c,d) (delete_file(a,b))
899 #define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
900 #else
901 #ifdef IMAP_STORAGE
902 #define DISPOSE(a,b) (imap_remove_file(a,b))
903 #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))
904 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
905 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
906 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
907 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
908 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
909 #define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
910 #else
911 #define RETRIEVE(a,b,c,d)
912 #define DISPOSE(a,b)
913 #define STORE(a,b,c,d,e,f,g,h,i,j,k)
914 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
915 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
916 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
917 #define DELETE(a,b,c,d) (vm_delete(c))
918 #define UPDATE_MSG_ID(a, b, c, d, e, f)
919 #endif
920 #endif
921
922 static char VM_SPOOL_DIR[PATH_MAX];
923
924 static char ext_pass_cmd[128];
925 static char ext_pass_check_cmd[128];
926
927 static int my_umask;
928
929 #define PWDCHANGE_INTERNAL (1 << 1)
930 #define PWDCHANGE_EXTERNAL (1 << 2)
931 static int pwdchange = PWDCHANGE_INTERNAL;
932
933 #ifdef ODBC_STORAGE
934 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
935 #else
936 # ifdef IMAP_STORAGE
937 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
938 # else
939 # define tdesc "Comedian Mail (Voicemail System)"
940 # endif
941 #endif
942
943 static char userscontext[AST_MAX_EXTENSION] = "default";
944
945 static char *addesc = "Comedian Mail";
946
947 /* Leave a message */
948 static char *app = "VoiceMail";
949
950 /* Check mail, control, etc */
951 static char *app2 = "VoiceMailMain";
952
953 static char *app3 = "MailboxExists";
954 static char *app4 = "VMAuthenticate";
955
956 static char *playmsg_app = "VoiceMailPlayMsg";
957
958 static char *sayname_app = "VMSayName";
959
960 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
961 static AST_LIST_HEAD_STATIC(zones, vm_zone);
962 static char zonetag[80];
963 static char locale[20];
964 static int maxsilence;
965 static int maxmsg;
966 static int maxdeletedmsg;
967 static int silencethreshold = 128;
968 static char serveremail[80];
969 static char mailcmd[160];       /* Configurable mail cmd */
970 static char externnotify[160]; 
971 static struct ast_smdi_interface *smdi_iface = NULL;
972 static char vmfmts[80];
973 static double volgain;
974 static int vmminsecs;
975 static int vmmaxsecs;
976 static int maxgreet;
977 static int skipms;
978 static int maxlogins;
979 static int minpassword;
980 static int passwordlocation;
981
982 /*! Poll mailboxes for changes since there is something external to
983  *  app_voicemail that may change them. */
984 static unsigned int poll_mailboxes;
985
986 /*! Polling frequency */
987 static unsigned int poll_freq;
988 /*! By default, poll every 30 seconds */
989 #define DEFAULT_POLL_FREQ 30
990
991 AST_MUTEX_DEFINE_STATIC(poll_lock);
992 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
993 static pthread_t poll_thread = AST_PTHREADT_NULL;
994 static unsigned char poll_thread_run;
995
996 /*! Subscription to MWI event subscription changes */
997 static struct stasis_subscription *mwi_sub_sub;
998
999 /*!
1000  * \brief An MWI subscription
1001  *
1002  * This is so we can keep track of which mailboxes are subscribed to.
1003  * This way, we know which mailboxes to poll when the pollmailboxes
1004  * option is being used.
1005  */
1006 struct mwi_sub {
1007         AST_RWLIST_ENTRY(mwi_sub) entry;
1008         int old_urgent;
1009         int old_new;
1010         int old_old;
1011         char *uniqueid;
1012         char mailbox[1];
1013 };
1014
1015 struct mwi_sub_task {
1016         const char *mailbox;
1017         const char *context;
1018         const char *uniqueid;
1019 };
1020
1021 static void mwi_sub_task_dtor(struct mwi_sub_task *mwist)
1022 {
1023         ast_free((void *) mwist->mailbox);
1024         ast_free((void *) mwist->context);
1025         ast_free((void *) mwist->uniqueid);
1026         ast_free(mwist);
1027 }
1028
1029 static struct ast_taskprocessor *mwi_subscription_tps;
1030
1031 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
1032
1033 /* custom audio control prompts for voicemail playback */
1034 static char listen_control_forward_key[12];
1035 static char listen_control_reverse_key[12];
1036 static char listen_control_pause_key[12];
1037 static char listen_control_restart_key[12];
1038 static char listen_control_stop_key[12];
1039
1040 /* custom password sounds */
1041 static char vm_login[80] = "vm-login";
1042 static char vm_newuser[80] = "vm-newuser";
1043 static char vm_password[80] = "vm-password";
1044 static char vm_newpassword[80] = "vm-newpassword";
1045 static char vm_passchanged[80] = "vm-passchanged";
1046 static char vm_reenterpassword[80] = "vm-reenterpassword";
1047 static char vm_mismatch[80] = "vm-mismatch";
1048 static char vm_invalid_password[80] = "vm-invalid-password";
1049 static char vm_pls_try_again[80] = "vm-pls-try-again";
1050
1051 /*
1052  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
1053  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
1054  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
1055  * app.c's __ast_play_and_record function
1056  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
1057  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
1058  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
1059  * more effort than either of the other two.
1060  */
1061 static char vm_prepend_timeout[80] = "vm-then-pound";
1062
1063 static struct ast_flags globalflags = {0};
1064
1065 static int saydurationminfo;
1066
1067 static char dialcontext[AST_MAX_CONTEXT] = "";
1068 static char callcontext[AST_MAX_CONTEXT] = "";
1069 static char exitcontext[AST_MAX_CONTEXT] = "";
1070
1071 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
1072
1073
1074 static char *emailbody = NULL;
1075 static char *emailsubject = NULL;
1076 static char *pagerbody = NULL;
1077 static char *pagersubject = NULL;
1078 static char fromstring[100];
1079 static char pagerfromstring[100];
1080 static char charset[32] = "ISO-8859-1";
1081
1082 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
1083 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
1084 static int adsiver = 1;
1085 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
1086 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
1087
1088 /* Forward declarations - generic */
1089 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
1090 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu);
1091 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);
1092 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
1093 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
1094                         char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
1095                         signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro);
1096 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
1097 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
1098 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);
1099 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);
1100 static void apply_options(struct ast_vm_user *vmu, const char *options);
1101 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);
1102 static int is_valid_dtmf(const char *key);
1103 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
1104 static int write_password_to_file(const char *secretfn, const char *password);
1105 static const char *substitute_escapes(const char *value);
1106 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);
1107 static void notify_new_state(struct ast_vm_user *vmu);
1108
1109 /*!
1110  * Place a message in the indicated folder
1111  *
1112  * \param vmu Voicemail user
1113  * \param vms Current voicemail state for the user
1114  * \param msg The message number to save
1115  * \param box The folder into which the message should be saved
1116  * \param[out] newmsg The new message number of the saved message
1117  * \param move Tells whether to copy or to move the message
1118  *
1119  * \note the "move" parameter is only honored for IMAP voicemail presently
1120  * \retval 0 Success
1121  * \retval other Failure
1122  */
1123 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move);
1124
1125 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);
1126 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot);
1127
1128 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);
1129 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);
1130 static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[]);
1131 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);
1132
1133 #ifdef TEST_FRAMEWORK
1134 static int vm_test_destroy_user(const char *context, const char *mailbox);
1135 static int vm_test_create_user(const char *context, const char *mailbox);
1136 #endif
1137
1138 /*!
1139  * \internal
1140  * \brief Parse the given mailbox_id into mailbox and context.
1141  * \since 12.0.0
1142  *
1143  * \param mailbox_id The mailbox@context string to separate.
1144  * \param mailbox Where the mailbox part will start.
1145  * \param context Where the context part will start.  ("default" if not present)
1146  *
1147  * \retval 0 on success.
1148  * \retval -1 on error.
1149  */
1150 static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
1151 {
1152         if (ast_strlen_zero(mailbox_id) || !mailbox || !context) {
1153                 return -1;
1154         }
1155         *context = mailbox_id;
1156         *mailbox = strsep(context, "@");
1157         if (ast_strlen_zero(*mailbox)) {
1158                 return -1;
1159         }
1160         if (ast_strlen_zero(*context)) {
1161                 *context = "default";
1162         }
1163         return 0;
1164 }
1165
1166 struct ao2_container *inprocess_container;
1167
1168 struct inprocess {
1169         int count;
1170         char *context;
1171         char mailbox[0];
1172 };
1173
1174 static int inprocess_hash_fn(const void *obj, const int flags)
1175 {
1176         const struct inprocess *i = obj;
1177         return atoi(i->mailbox);
1178 }
1179
1180 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
1181 {
1182         struct inprocess *i = obj, *j = arg;
1183         if (strcmp(i->mailbox, j->mailbox)) {
1184                 return 0;
1185         }
1186         return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
1187 }
1188
1189 static int inprocess_count(const char *context, const char *mailbox, int delta)
1190 {
1191         struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
1192         arg->context = arg->mailbox + strlen(mailbox) + 1;
1193         strcpy(arg->mailbox, mailbox); /* SAFE */
1194         strcpy(arg->context, context); /* SAFE */
1195         ao2_lock(inprocess_container);
1196         if ((i = ao2_find(inprocess_container, arg, 0))) {
1197                 int ret = ast_atomic_fetchadd_int(&i->count, delta);
1198                 ao2_unlock(inprocess_container);
1199                 ao2_ref(i, -1);
1200                 return ret;
1201         }
1202         if (delta < 0) {
1203                 ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
1204         }
1205         if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
1206                 ao2_unlock(inprocess_container);
1207                 return 0;
1208         }
1209         i->context = i->mailbox + strlen(mailbox) + 1;
1210         strcpy(i->mailbox, mailbox); /* SAFE */
1211         strcpy(i->context, context); /* SAFE */
1212         i->count = delta;
1213         ao2_link(inprocess_container, i);
1214         ao2_unlock(inprocess_container);
1215         ao2_ref(i, -1);
1216         return 0;
1217 }
1218
1219 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
1220 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
1221 #endif
1222
1223 /*!
1224  * \brief Strips control and non 7-bit clean characters from input string.
1225  *
1226  * \note To map control and none 7-bit characters to a 7-bit clean characters
1227  *  please use ast_str_encode_mine().
1228  */
1229 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
1230 {
1231         char *bufptr = buf;
1232         for (; *input; input++) {
1233                 if (*input < 32) {
1234                         continue;
1235                 }
1236                 *bufptr++ = *input;
1237                 if (bufptr == buf + buflen - 1) {
1238                         break;
1239                 }
1240         }
1241         *bufptr = '\0';
1242         return buf;
1243 }
1244
1245
1246 /*!
1247  * \brief Sets default voicemail system options to a voicemail user.
1248  *
1249  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
1250  * - all the globalflags
1251  * - the saydurationminfo
1252  * - the callcontext
1253  * - the dialcontext
1254  * - the exitcontext
1255  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
1256  * - volume gain.
1257  * - emailsubject, emailbody set to NULL
1258  */
1259 static void populate_defaults(struct ast_vm_user *vmu)
1260 {
1261         ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
1262         vmu->passwordlocation = passwordlocation;
1263         if (saydurationminfo) {
1264                 vmu->saydurationm = saydurationminfo;
1265         }
1266         ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
1267         ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
1268         ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
1269         ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
1270         ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
1271         if (vmminsecs) {
1272                 vmu->minsecs = vmminsecs;
1273         }
1274         if (vmmaxsecs) {
1275                 vmu->maxsecs = vmmaxsecs;
1276         }
1277         if (maxmsg) {
1278                 vmu->maxmsg = maxmsg;
1279         }
1280         if (maxdeletedmsg) {
1281                 vmu->maxdeletedmsg = maxdeletedmsg;
1282         }
1283         vmu->volgain = volgain;
1284         ast_free(vmu->email);
1285         vmu->email = NULL;
1286         ast_free(vmu->emailsubject);
1287         vmu->emailsubject = NULL;
1288         ast_free(vmu->emailbody);
1289         vmu->emailbody = NULL;
1290 #ifdef IMAP_STORAGE
1291         ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
1292         ast_copy_string(vmu->imapserver, imapserver, sizeof(vmu->imapserver));
1293         ast_copy_string(vmu->imapport, imapport, sizeof(vmu->imapport));
1294         ast_copy_string(vmu->imapflags, imapflags, sizeof(vmu->imapflags));
1295 #endif
1296 }
1297
1298 /*!
1299  * \brief Sets a a specific property value.
1300  * \param vmu The voicemail user object to work with.
1301  * \param var The name of the property to be set.
1302  * \param value The value to be set to the property.
1303  * 
1304  * The property name must be one of the understood properties. See the source for details.
1305  */
1306 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
1307 {
1308         int x;
1309         if (!strcasecmp(var, "attach")) {
1310                 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
1311         } else if (!strcasecmp(var, "attachfmt")) {
1312                 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
1313         } else if (!strcasecmp(var, "serveremail")) {
1314                 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
1315         } else if (!strcasecmp(var, "fromstring")) {
1316                 ast_copy_string(vmu->fromstring, value, sizeof(vmu->fromstring));
1317         } else if (!strcasecmp(var, "emailbody")) {
1318                 ast_free(vmu->emailbody);
1319                 vmu->emailbody = ast_strdup(substitute_escapes(value));
1320         } else if (!strcasecmp(var, "emailsubject")) {
1321                 ast_free(vmu->emailsubject);
1322                 vmu->emailsubject = ast_strdup(substitute_escapes(value));
1323         } else if (!strcasecmp(var, "language")) {
1324                 ast_copy_string(vmu->language, value, sizeof(vmu->language));
1325         } else if (!strcasecmp(var, "tz")) {
1326                 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
1327         } else if (!strcasecmp(var, "locale")) {
1328                 ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
1329 #ifdef IMAP_STORAGE
1330         } else if (!strcasecmp(var, "imapuser")) {
1331                 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
1332                 vmu->imapversion = imapversion;
1333         } else if (!strcasecmp(var, "imapserver")) {
1334                 ast_copy_string(vmu->imapserver, value, sizeof(vmu->imapserver));
1335                 vmu->imapversion = imapversion;
1336         } else if (!strcasecmp(var, "imapport")) {
1337                 ast_copy_string(vmu->imapport, value, sizeof(vmu->imapport));
1338                 vmu->imapversion = imapversion;
1339         } else if (!strcasecmp(var, "imapflags")) {
1340                 ast_copy_string(vmu->imapflags, value, sizeof(vmu->imapflags));
1341                 vmu->imapversion = imapversion;
1342         } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
1343                 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
1344                 vmu->imapversion = imapversion;
1345         } else if (!strcasecmp(var, "imapfolder")) {
1346                 ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
1347                 vmu->imapversion = imapversion;
1348         } else if (!strcasecmp(var, "imapvmshareid")) {
1349                 ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
1350                 vmu->imapversion = imapversion;
1351 #endif
1352         } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
1353                 ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
1354         } else if (!strcasecmp(var, "saycid")){
1355                 ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
1356         } else if (!strcasecmp(var, "sendvoicemail")){
1357                 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
1358         } else if (!strcasecmp(var, "review")){
1359                 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
1360         } else if (!strcasecmp(var, "tempgreetwarn")){
1361                 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);  
1362         } else if (!strcasecmp(var, "messagewrap")){
1363                 ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);    
1364         } else if (!strcasecmp(var, "operator")) {
1365                 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
1366         } else if (!strcasecmp(var, "envelope")){
1367                 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
1368         } else if (!strcasecmp(var, "moveheard")){
1369                 ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
1370         } else if (!strcasecmp(var, "sayduration")){
1371                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
1372         } else if (!strcasecmp(var, "saydurationm")){
1373                 if (sscanf(value, "%30d", &x) == 1) {
1374                         vmu->saydurationm = x;
1375                 } else {
1376                         ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
1377                 }
1378         } else if (!strcasecmp(var, "forcename")){
1379                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
1380         } else if (!strcasecmp(var, "forcegreetings")){
1381                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
1382         } else if (!strcasecmp(var, "callback")) {
1383                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
1384         } else if (!strcasecmp(var, "dialout")) {
1385                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
1386         } else if (!strcasecmp(var, "exitcontext")) {
1387                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
1388         } else if (!strcasecmp(var, "minsecs")) {
1389                 if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
1390                         vmu->minsecs = x;
1391                 } else {
1392                         ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
1393                         vmu->minsecs = vmminsecs;
1394                 }
1395         } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
1396                 vmu->maxsecs = atoi(value);
1397                 if (vmu->maxsecs <= 0) {
1398                         ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
1399                         vmu->maxsecs = vmmaxsecs;
1400                 } else {
1401                         vmu->maxsecs = atoi(value);
1402                 }
1403                 if (!strcasecmp(var, "maxmessage"))
1404                         ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
1405         } else if (!strcasecmp(var, "maxmsg")) {
1406                 vmu->maxmsg = atoi(value);
1407                 /* Accept maxmsg=0 (Greetings only voicemail) */
1408                 if (vmu->maxmsg < 0) {
1409                         ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
1410                         vmu->maxmsg = MAXMSG;
1411                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
1412                         ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
1413                         vmu->maxmsg = MAXMSGLIMIT;
1414                 }
1415         } else if (!strcasecmp(var, "nextaftercmd")) {
1416                 ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
1417         } else if (!strcasecmp(var, "backupdeleted")) {
1418                 if (sscanf(value, "%30d", &x) == 1)
1419                         vmu->maxdeletedmsg = x;
1420                 else if (ast_true(value))
1421                         vmu->maxdeletedmsg = MAXMSG;
1422                 else
1423                         vmu->maxdeletedmsg = 0;
1424
1425                 if (vmu->maxdeletedmsg < 0) {
1426                         ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
1427                         vmu->maxdeletedmsg = MAXMSG;
1428                 } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
1429                         ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
1430                         vmu->maxdeletedmsg = MAXMSGLIMIT;
1431                 }
1432         } else if (!strcasecmp(var, "volgain")) {
1433                 sscanf(value, "%30lf", &vmu->volgain);
1434         } else if (!strcasecmp(var, "passwordlocation")) {
1435                 if (!strcasecmp(value, "spooldir")) {
1436                         vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
1437                 } else {
1438                         vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
1439                 }
1440         } else if (!strcasecmp(var, "options")) {
1441                 apply_options(vmu, value);
1442         }
1443 }
1444
1445 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
1446 {
1447         int fds[2], pid = 0;
1448
1449         memset(buf, 0, len);
1450
1451         if (pipe(fds)) {
1452                 snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1453         } else {
1454                 /* good to go*/
1455                 pid = ast_safe_fork(0);
1456
1457                 if (pid < 0) {
1458                         /* ok maybe not */
1459                         close(fds[0]);
1460                         close(fds[1]);
1461                         snprintf(buf, len, "FAILURE: Fork failed");
1462                 } else if (pid) {
1463                         /* parent */
1464                         close(fds[1]);
1465                         if (read(fds[0], buf, len) < 0) {
1466                                 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1467                         }
1468                         close(fds[0]);
1469                 } else {
1470                         /*  child */
1471                         AST_DECLARE_APP_ARGS(arg,
1472                                 AST_APP_ARG(v)[20];
1473                         );
1474                         char *mycmd = ast_strdupa(command);
1475
1476                         close(fds[0]);
1477                         dup2(fds[1], STDOUT_FILENO);
1478                         close(fds[1]);
1479                         ast_close_fds_above_n(STDOUT_FILENO);
1480
1481                         AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1482
1483                         execv(arg.v[0], arg.v); 
1484                         printf("FAILURE: %s", strerror(errno));
1485                         _exit(0);
1486                 }
1487         }
1488         return buf;
1489 }
1490
1491 /*!
1492  * \brief Check that password meets minimum required length
1493  * \param vmu The voicemail user to change the password for.
1494  * \param password The password string to check
1495  *
1496  * \return zero on ok, 1 on not ok.
1497  */
1498 static int check_password(struct ast_vm_user *vmu, char *password)
1499 {
1500         /* check minimum length */
1501         if (strlen(password) < minpassword)
1502                 return 1;
1503         /* check that password does not contain '*' character */
1504         if (!ast_strlen_zero(password) && password[0] == '*')
1505                 return 1;
1506         if (!ast_strlen_zero(ext_pass_check_cmd)) {
1507                 char cmd[255], buf[255];
1508
1509                 ast_debug(1, "Verify password policies for %s\n", password);
1510
1511                 snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1512                 if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1513                         ast_debug(5, "Result: %s\n", buf);
1514                         if (!strncasecmp(buf, "VALID", 5)) {
1515                                 ast_debug(3, "Passed password check: '%s'\n", buf);
1516                                 return 0;
1517                         } else if (!strncasecmp(buf, "FAILURE", 7)) {
1518                                 ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1519                                 return 0;
1520                         } else {
1521                                 ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1522                                 return 1;
1523                         }
1524                 }
1525         }
1526         return 0;
1527 }
1528
1529 /*! 
1530  * \brief Performs a change of the voicemail passowrd in the realtime engine.
1531  * \param vmu The voicemail user to change the password for.
1532  * \param password The new value to be set to the password for this user.
1533  * 
1534  * This only works if there is a realtime engine configured.
1535  * This is called from the (top level) vm_change_password.
1536  *
1537  * \return zero on success, -1 on error.
1538  */
1539 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1540 {
1541         int res = -1;
1542         if (!strcmp(vmu->password, password)) {
1543                 /* No change (but an update would return 0 rows updated, so we opt out here) */
1544                 return 0;
1545         }
1546
1547         if (strlen(password) > 10) {
1548                 ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1549         }
1550         if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1551                 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
1552                 ast_copy_string(vmu->password, password, sizeof(vmu->password));
1553                 res = 0;
1554         }
1555         return res;
1556 }
1557
1558 /*!
1559  * \brief Destructively Parse options and apply.
1560  */
1561 static void apply_options(struct ast_vm_user *vmu, const char *options)
1562 {       
1563         char *stringp;
1564         char *s;
1565         char *var, *value;
1566         stringp = ast_strdupa(options);
1567         while ((s = strsep(&stringp, "|"))) {
1568                 value = s;
1569                 if ((var = strsep(&value, "=")) && value) {
1570                         apply_option(vmu, var, value);
1571                 }
1572         }       
1573 }
1574
1575 /*!
1576  * \brief Loads the options specific to a voicemail user.
1577  * 
1578  * This is called when a vm_user structure is being set up, such as from load_options.
1579  */
1580 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
1581 {
1582         for (; var; var = var->next) {
1583                 if (!strcasecmp(var->name, "vmsecret")) {
1584                         ast_copy_string(retval->password, var->value, sizeof(retval->password));
1585                 } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1586                         if (ast_strlen_zero(retval->password)) {
1587                                 if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
1588                                         ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
1589                                                 "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
1590                                 } else {
1591                                         ast_copy_string(retval->password, var->value, sizeof(retval->password));
1592                                 }
1593                         }
1594                 } else if (!strcasecmp(var->name, "uniqueid")) {
1595                         ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1596                 } else if (!strcasecmp(var->name, "pager")) {
1597                         ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1598                 } else if (!strcasecmp(var->name, "email")) {
1599                         ast_free(retval->email);
1600                         retval->email = ast_strdup(var->value);
1601                 } else if (!strcasecmp(var->name, "fullname")) {
1602                         ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1603                 } else if (!strcasecmp(var->name, "context")) {
1604                         ast_copy_string(retval->context, var->value, sizeof(retval->context));
1605                 } else if (!strcasecmp(var->name, "emailsubject")) {
1606                         ast_free(retval->emailsubject);
1607                         retval->emailsubject = ast_strdup(substitute_escapes(var->value));
1608                 } else if (!strcasecmp(var->name, "emailbody")) {
1609                         ast_free(retval->emailbody);
1610                         retval->emailbody = ast_strdup(substitute_escapes(var->value));
1611 #ifdef IMAP_STORAGE
1612                 } else if (!strcasecmp(var->name, "imapuser")) {
1613                         ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1614                         retval->imapversion = imapversion;
1615                 } else if (!strcasecmp(var->name, "imapserver")) {
1616                         ast_copy_string(retval->imapserver, var->value, sizeof(retval->imapserver));
1617                         retval->imapversion = imapversion;
1618                 } else if (!strcasecmp(var->name, "imapport")) {
1619                         ast_copy_string(retval->imapport, var->value, sizeof(retval->imapport));
1620                         retval->imapversion = imapversion;
1621                 } else if (!strcasecmp(var->name, "imapflags")) {
1622                         ast_copy_string(retval->imapflags, var->value, sizeof(retval->imapflags));
1623                         retval->imapversion = imapversion;
1624                 } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1625                         ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1626                         retval->imapversion = imapversion;
1627                 } else if (!strcasecmp(var->name, "imapfolder")) {
1628                         ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
1629                         retval->imapversion = imapversion;
1630                 } else if (!strcasecmp(var->name, "imapvmshareid")) {
1631                         ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1632                         retval->imapversion = imapversion;
1633 #endif
1634                 } else
1635                         apply_option(retval, var->name, var->value);
1636         }
1637 }
1638
1639 /*!
1640  * \brief Determines if a DTMF key entered is valid.
1641  * \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.
1642  *
1643  * Tests the character entered against the set of valid DTMF characters. 
1644  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1645  */
1646 static int is_valid_dtmf(const char *key)
1647 {
1648         int i;
1649         char *local_key = ast_strdupa(key);
1650
1651         for (i = 0; i < strlen(key); ++i) {
1652                 if (!strchr(VALID_DTMF, *local_key)) {
1653                         ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1654                         return 0;
1655                 }
1656                 local_key++;
1657         }
1658         return 1;
1659 }
1660
1661 /*!
1662  * \brief Finds a voicemail user from the realtime engine.
1663  * \param ivm
1664  * \param context
1665  * \param mailbox
1666  *
1667  * 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.
1668  *
1669  * \return The ast_vm_user structure for the user that was found.
1670  */
1671 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1672 {
1673         struct ast_variable *var;
1674         struct ast_vm_user *retval;
1675
1676         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1677                 if (ivm) {
1678                         memset(retval, 0, sizeof(*retval));
1679                 }
1680                 populate_defaults(retval);
1681                 if (!ivm) {
1682                         ast_set_flag(retval, VM_ALLOCED);
1683                 }
1684                 if (mailbox) {
1685                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1686                 }
1687                 if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
1688                         var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1689                 } else {
1690                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1691                 }
1692                 if (var) {
1693                         apply_options_full(retval, var);
1694                         ast_variables_destroy(var);
1695                 } else { 
1696                         if (!ivm) 
1697                                 ast_free(retval);
1698                         retval = NULL;
1699                 }       
1700         } 
1701         return retval;
1702 }
1703
1704 /*!
1705  * \brief Finds a voicemail user from the users file or the realtime engine.
1706  * \param ivm
1707  * \param context
1708  * \param mailbox
1709  * 
1710  * \return The ast_vm_user structure for the user that was found.
1711  */
1712 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1713 {
1714         /* This function could be made to generate one from a database, too */
1715         struct ast_vm_user *vmu = NULL, *cur;
1716         AST_LIST_LOCK(&users);
1717
1718         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1719                 context = "default";
1720
1721         AST_LIST_TRAVERSE(&users, cur, list) {
1722 #ifdef IMAP_STORAGE
1723                 if (cur->imapversion != imapversion) {
1724                         continue;
1725                 }
1726 #endif
1727                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1728                         break;
1729                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1730                         break;
1731         }
1732         if (cur) {
1733                 /* Make a copy, so that on a reload, we have no race */
1734                 if ((vmu = (ivm ? ivm : ast_calloc(1, sizeof(*vmu))))) {
1735                         ast_free(vmu->email);
1736                         ast_free(vmu->emailbody);
1737                         ast_free(vmu->emailsubject);
1738                         *vmu = *cur;
1739                         vmu->email = ast_strdup(cur->email);
1740                         vmu->emailbody = ast_strdup(cur->emailbody);
1741                         vmu->emailsubject = ast_strdup(cur->emailsubject);
1742                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1743                         AST_LIST_NEXT(vmu, list) = NULL;
1744                 }
1745         } else
1746                 vmu = find_user_realtime(ivm, context, mailbox);
1747         AST_LIST_UNLOCK(&users);
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 int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
2036
2037         int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
2038
2039         /* remove old allocation */
2040         if (vms->deleted) {
2041                 ast_free(vms->deleted);
2042                 vms->deleted = NULL;
2043         }
2044         if (vms->heard) {
2045                 ast_free(vms->heard);
2046                 vms->heard = NULL;
2047         }
2048         vms->dh_arraysize = 0;
2049
2050         if (arraysize > 0) {
2051                 if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
2052                         return -1;
2053                 }
2054                 if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
2055                         ast_free(vms->deleted);
2056                         vms->deleted = NULL;
2057                         return -1;
2058                 }
2059                 vms->dh_arraysize = arraysize;
2060         }
2061
2062         return 0;
2063 }
2064
2065 /* All IMAP-specific functions should go in this block. This
2066  * keeps them from being spread out all over the code */
2067 #ifdef IMAP_STORAGE
2068 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
2069 {
2070         char arg[10];
2071         struct vm_state *vms;
2072         unsigned long messageNum;
2073
2074         /* If greetings aren't stored in IMAP, just delete the file */
2075         if (msgnum < 0 && !imapgreetings) {
2076                 ast_filedelete(file, NULL);
2077                 return;
2078         }
2079
2080         if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2081                 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);
2082                 return;
2083         }
2084
2085         if (msgnum < 0) {
2086                 imap_delete_old_greeting(file, vms);
2087                 return;
2088         }
2089
2090         /* find real message number based on msgnum */
2091         /* this may be an index into vms->msgArray based on the msgnum. */
2092         messageNum = vms->msgArray[msgnum];
2093         if (messageNum == 0) {
2094                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
2095                 return;
2096         }
2097         ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
2098         /* delete message */
2099         snprintf (arg, sizeof(arg), "%lu", messageNum);
2100         ast_mutex_lock(&vms->lock);
2101         mail_setflag (vms->mailstream, arg, "\\DELETED");
2102         mail_expunge(vms->mailstream);
2103         ast_mutex_unlock(&vms->lock);
2104 }
2105
2106 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)
2107 {
2108         struct ast_channel *chan;
2109         char *cid;
2110         char *cid_name;
2111         char *cid_num;
2112         struct vm_state *vms;
2113         const char *duration_str;
2114         int duration = 0;
2115
2116         /*
2117          * First, get things initially set up. If any of this fails, then
2118          * back out before doing anything substantial
2119          */
2120         vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
2121         if (!vms) {
2122                 return;
2123         }
2124
2125         if (open_mailbox(vms, vmu, folder)) {
2126                 return;
2127         }
2128
2129         chan = ast_dummy_channel_alloc();
2130         if (!chan) {
2131                 close_mailbox(vms, vmu);
2132                 return;
2133         }
2134
2135         /*
2136          * We need to make sure the new message we save has the same
2137          * callerid, flag, and duration as the original message
2138          */
2139         cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
2140
2141         if (!ast_strlen_zero(cid)) {
2142                 ast_callerid_parse(cid, &cid_name, &cid_num);
2143                 ast_party_caller_init(ast_channel_caller(chan));
2144                 if (!ast_strlen_zero(cid_name)) {
2145                         ast_channel_caller(chan)->id.name.valid = 1;
2146                         ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
2147                 }
2148                 if (!ast_strlen_zero(cid_num)) {
2149                         ast_channel_caller(chan)->id.number.valid = 1;
2150                         ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
2151                 }
2152         }
2153
2154         duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
2155
2156         if (!ast_strlen_zero(duration_str)) {
2157                 sscanf(duration_str, "%30d", &duration);
2158         }
2159
2160         /*
2161          * IMAP messages cannot be altered once delivered. So we have to delete the
2162          * current message and then re-add it with the updated message ID.
2163          *
2164          * Furthermore, there currently is no atomic way to create a new message and to
2165          * store it in an arbitrary folder. So we have to save it to the INBOX and then
2166          * move to the appropriate folder.
2167          */
2168         if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
2169                         duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
2170                 if (folder != NEW_FOLDER) {
2171                         save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
2172                 }
2173                 vm_imap_delete(dir, msgnum, vmu);
2174         }
2175         close_mailbox(vms, vmu);
2176         ast_channel_unref(chan);
2177 }
2178
2179 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
2180 {
2181         struct vm_state *vms_p;
2182         char *file, *filename;
2183         char dest[PATH_MAX];
2184         char *attachment;
2185         int i;
2186         BODY *body;
2187         int ret = 0;
2188         int curr_mbox;
2189
2190         /* This function is only used for retrieval of IMAP greetings
2191          * regular messages are not retrieved this way, nor are greetings
2192          * if they are stored locally*/
2193         if (msgnum > -1 || !imapgreetings) {
2194                 return 0;
2195         } else {
2196                 file = strrchr(ast_strdupa(dir), '/');
2197                 if (file)
2198                         *file++ = '\0';
2199                 else {
2200                         ast_debug(1, "Failed to procure file name from directory passed.\n");
2201                         return -1;
2202                 }
2203         }
2204
2205         /* check if someone is accessing this box right now... */
2206         if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
2207                 !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2208                 /* Unlike when retrieving a message, it is reasonable not to be able to find a 
2209                 * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
2210                 * that's all we need to do.
2211                 */
2212                 if (!(vms_p = create_vm_state_from_user(vmu))) {
2213                         ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
2214                         return -1;
2215                 }
2216         }
2217
2218         /* Greetings will never have a prepended message */
2219         *vms_p->introfn = '\0';
2220
2221         ast_mutex_lock(&vms_p->lock);
2222
2223         /* get the current mailbox so that we can point the mailstream back to it later */
2224         curr_mbox = get_folder_by_name(vms_p->curbox);
2225
2226         if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
2227                 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2228                 ast_mutex_unlock(&vms_p->lock);
2229                 return -1;
2230         }
2231
2232         /*XXX Yuck, this could probably be done a lot better */
2233         for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
2234                 mail_fetchstructure(vms_p->mailstream, i + 1, &body);
2235                 /* We have the body, now we extract the file name of the first attachment. */
2236                 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2237                         attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
2238                 } else {
2239                         ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
2240                         ret = -1;
2241                         break;
2242                 }
2243                 filename = strsep(&attachment, ".");
2244                 if (!strcmp(filename, file)) {
2245                         ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
2246                         vms_p->msgArray[vms_p->curmsg] = i + 1;
2247                         create_dirpath(dest, sizeof(dest), vmu->context, vms_p->username, "");
2248                         save_body(body, vms_p, "2", attachment, 0);
2249                         ret = 0;
2250                         break;
2251                 }
2252         }
2253
2254         if (curr_mbox != -1) {
2255                 /* restore previous mbox stream */
2256                 if (init_mailstream(vms_p, curr_mbox) || !vms_p->mailstream) {
2257                         ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2258                         ret = -1;
2259                 }
2260         }
2261         ast_mutex_unlock(&vms_p->lock);
2262         return ret;
2263 }
2264
2265 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
2266 {
2267         BODY *body;
2268         char *header_content;
2269         char *attachedfilefmt;
2270         char buf[80];
2271         struct vm_state *vms;
2272         char text_file[PATH_MAX];
2273         FILE *text_file_ptr;
2274         int res = 0;
2275         struct ast_vm_user *vmu;
2276         int curr_mbox;
2277
2278         if (!(vmu = find_user(NULL, context, mailbox))) {
2279                 ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
2280                 return -1;
2281         }
2282
2283         if (msgnum < 0) {
2284                 if (imapgreetings) {
2285                         res = imap_retrieve_greeting(dir, msgnum, vmu);
2286                         goto exit;
2287                 } else {
2288                         res = 0;
2289                         goto exit;
2290                 }
2291         }
2292
2293         /* Before anything can happen, we need a vm_state so that we can
2294          * actually access the imap server through the vms->mailstream
2295          */
2296         if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2297                 /* This should not happen. If it does, then I guess we'd
2298                  * need to create the vm_state, extract which mailbox to
2299                  * open, and then set up the msgArray so that the correct
2300                  * IMAP message could be accessed. If I have seen correctly
2301                  * though, the vms should be obtainable from the vmstates list
2302                  * and should have its msgArray properly set up.
2303                  */
2304                 ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
2305                 res = -1;
2306                 goto exit;
2307         }
2308
2309         /* Ensure we have the correct mailbox open and have a valid mailstream for it */
2310         curr_mbox = get_folder_by_name(vms->curbox);
2311         if (curr_mbox < 0) {
2312                 ast_debug(3, "Mailbox folder curbox not set, defaulting to Inbox\n");
2313                 curr_mbox = 0;
2314         }
2315         init_mailstream(vms, curr_mbox);
2316         if (!vms->mailstream) {
2317                 ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmu->mailbox);
2318                 res = -1;
2319                 goto exit;
2320         }
2321
2322         make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
2323         snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
2324
2325         /* Don't try to retrieve a message from IMAP if it already is on the file system */
2326         if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
2327                 res = 0;
2328                 goto exit;
2329         }
2330
2331         ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
2332         if (vms->msgArray[msgnum] == 0) {
2333                 ast_log(LOG_WARNING, "Trying to access unknown message\n");
2334                 res = -1;
2335                 goto exit;
2336         }
2337
2338         /* This will only work for new messages... */
2339         ast_mutex_lock(&vms->lock);
2340         header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
2341         ast_mutex_unlock(&vms->lock);
2342         /* empty string means no valid header */
2343         if (ast_strlen_zero(header_content)) {
2344                 ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
2345                 res = -1;
2346                 goto exit;
2347         }
2348
2349         ast_mutex_lock(&vms->lock);
2350         mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
2351         ast_mutex_unlock(&vms->lock);
2352
2353         /* We have the body, now we extract the file name of the first attachment. */
2354         if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2355                 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
2356         } else {
2357                 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
2358                 res = -1;
2359                 goto exit;
2360         }
2361         
2362         /* Find the format of the attached file */
2363
2364         strsep(&attachedfilefmt, ".");
2365         if (!attachedfilefmt) {
2366                 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
2367                 res = -1;
2368                 goto exit;
2369         }
2370         
2371         save_body(body, vms, "2", attachedfilefmt, 0);
2372         if (save_body(body, vms, "3", attachedfilefmt, 1)) {
2373                 *vms->introfn = '\0';
2374         }
2375
2376         /* Get info from headers!! */
2377         snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
2378
2379         if (!(text_file_ptr = fopen(text_file, "w"))) {
2380                 ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
2381                 goto exit;
2382         }
2383
2384         fprintf(text_file_ptr, "%s\n", "[message]");
2385
2386         if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
2387                 fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2388         }
2389         if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
2390                 fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2391         }
2392         if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
2393                 fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2394         }
2395         if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
2396                 fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2397         }
2398         if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
2399                 fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2400         }
2401         if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
2402                 fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2403         }
2404         if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
2405                 fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2406         }
2407         if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
2408                 fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
2409         }
2410         fclose(text_file_ptr);
2411
2412 exit:
2413         free_user(vmu);
2414         return res;
2415 }
2416
2417 static int folder_int(const char *folder)
2418 {
2419         /*assume a NULL folder means INBOX*/
2420         if (!folder) {
2421                 return 0;
2422         }
2423         if (!strcasecmp(folder, imapfolder)) {
2424                 return 0;
2425         } else if (!strcasecmp(folder, "Old")) {
2426                 return 1;
2427         } else if (!strcasecmp(folder, "Work")) {
2428                 return 2;
2429         } else if (!strcasecmp(folder, "Family")) {
2430                 return 3;
2431         } else if (!strcasecmp(folder, "Friends")) {
2432                 return 4;
2433         } else if (!strcasecmp(folder, "Cust1")) {
2434                 return 5;
2435         } else if (!strcasecmp(folder, "Cust2")) {
2436                 return 6;
2437         } else if (!strcasecmp(folder, "Cust3")) {
2438                 return 7;
2439         } else if (!strcasecmp(folder, "Cust4")) {
2440                 return 8;
2441         } else if (!strcasecmp(folder, "Cust5")) {
2442                 return 9;
2443         } else if (!strcasecmp(folder, "Urgent")) {
2444                 return 11;
2445         } else { /*assume they meant INBOX if folder is not found otherwise*/
2446                 return 0;
2447         }
2448 }
2449
2450 static int __messagecount(const char *context, const char *mailbox, const char *folder)
2451 {
2452         SEARCHPGM *pgm;
2453         SEARCHHEADER *hdr;
2454
2455         struct ast_vm_user *vmu, vmus;
2456         struct vm_state *vms_p;
2457         int ret = 0;
2458         int fold = folder_int(folder);
2459         int urgent = 0;
2460         
2461         /* If URGENT, then look at INBOX */
2462         if (fold == 11) {
2463                 fold = NEW_FOLDER;
2464                 urgent = 1;
2465         }
2466
2467         if (ast_strlen_zero(mailbox))
2468                 return 0;
2469
2470         /* We have to get the user before we can open the stream! */
2471         memset(&vmus, 0, sizeof(vmus));
2472         vmu = find_user(&vmus, context, mailbox);
2473         if (!vmu) {
2474                 ast_log(AST_LOG_WARNING, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2475                 free_user(vmu);
2476                 return -1;
2477         } else {
2478                 /* No IMAP account available */
2479                 if (vmu->imapuser[0] == '\0') {
2480                         ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2481                         free_user(vmu);
2482                         return -1;
2483                 }
2484         }
2485
2486         /* No IMAP account available */
2487         if (vmu->imapuser[0] == '\0') {
2488                 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2489                 free_user(vmu);
2490                 return -1;
2491         }
2492
2493         /* check if someone is accessing this box right now... */
2494         vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2495         if (!vms_p) {
2496                 vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2497         }
2498         if (vms_p) {
2499                 ast_debug(3, "Returning before search - user is logged in\n");
2500                 if (fold == 0) { /* INBOX */
2501                         free_user(vmu);
2502                         return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2503                 }
2504                 if (fold == 1) { /* Old messages */
2505                         free_user(vmu);
2506                         return vms_p->oldmessages;
2507                 }
2508         }
2509
2510         /* add one if not there... */
2511         vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2512         if (!vms_p) {
2513                 vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2514         }
2515
2516         if (!vms_p) {
2517                 vms_p = create_vm_state_from_user(vmu);
2518         }
2519         ret = init_mailstream(vms_p, fold);
2520         if (!vms_p->mailstream) {
2521                 ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2522                 free_user(vmu);
2523                 return -1;
2524         }
2525         if (ret == 0) {
2526                 ast_mutex_lock(&vms_p->lock);
2527                 pgm = mail_newsearchpgm ();
2528                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2529                 hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2530                 pgm->header = hdr;
2531                 if (fold != OLD_FOLDER) {
2532                         pgm->unseen = 1;
2533                         pgm->seen = 0;
2534                 }
2535                 /* In the special case where fold is 1 (old messages) we have to do things a bit
2536                  * differently. Old messages are stored in the INBOX but are marked as "seen"
2537                  */
2538                 else {
2539                         pgm->unseen = 0;
2540                         pgm->seen = 1;
2541                 }
2542                 /* look for urgent messages */
2543                 if (fold == NEW_FOLDER) {
2544                         if (urgent) {
2545                                 pgm->flagged = 1;
2546                                 pgm->unflagged = 0;
2547                         } else {
2548                                 pgm->flagged = 0;
2549                                 pgm->unflagged = 1;
2550                         }
2551                 }
2552                 pgm->undeleted = 1;
2553                 pgm->deleted = 0;
2554
2555                 vms_p->vmArrayIndex = 0;
2556                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2557                 if (fold == 0 && urgent == 0)
2558                         vms_p->newmessages = vms_p->vmArrayIndex;
2559                 if (fold == 1)
2560                         vms_p->oldmessages = vms_p->vmArrayIndex;
2561                 if (fold == 0 && urgent == 1)
2562                         vms_p->urgentmessages = vms_p->vmArrayIndex;
2563                 /*Freeing the searchpgm also frees the searchhdr*/
2564                 mail_free_searchpgm(&pgm);
2565                 ast_mutex_unlock(&vms_p->lock);
2566                 free_user(vmu);
2567                 vms_p->updated = 0;
2568                 return vms_p->vmArrayIndex;
2569         } else {
2570                 ast_mutex_lock(&vms_p->lock);
2571                 mail_ping(vms_p->mailstream);
2572                 ast_mutex_unlock(&vms_p->lock);
2573         }
2574         free_user(vmu);
2575         return 0;
2576 }
2577
2578 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2579 {
2580         /* Check if mailbox is full */
2581         check_quota(vms, vmu->imapfolder);
2582         if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2583                 ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2584                 if (chan) {
2585                         ast_play_and_wait(chan, "vm-mailboxfull");
2586                 }
2587                 return -1;
2588         }
2589
2590         /* Check if we have exceeded maxmsg */
2591         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));
2592         if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2593                 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2594                 if (chan) {
2595                         ast_play_and_wait(chan, "vm-mailboxfull");
2596                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2597                 }
2598                 return -1;
2599         }
2600
2601         return 0;
2602 }
2603
2604 /*!
2605  * \brief Gets the number of messages that exist in a mailbox folder.
2606  * \param mailbox_id
2607  * \param folder
2608  * 
2609  * This method is used when IMAP backend is used.
2610  * \return The number of messages in this mailbox folder (zero or more).
2611  */
2612 static int messagecount(const char *mailbox_id, const char *folder)
2613 {
2614         char *context;
2615         char *mailbox;
2616
2617         if (ast_strlen_zero(mailbox_id)
2618                 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
2619                 return 0;
2620         }
2621
2622         if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2623                 return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2624         } else {
2625                 return __messagecount(context, mailbox, folder);
2626         }
2627 }
2628
2629 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)
2630 {
2631         char *myserveremail = serveremail;
2632         char fn[PATH_MAX];
2633         char introfn[PATH_MAX];
2634         char mailbox[256];
2635         char *stringp;
2636         FILE *p = NULL;
2637         char tmp[80] = "/tmp/astmail-XXXXXX";
2638         long len;
2639         void *buf;
2640         int tempcopy = 0;
2641         STRING str;
2642         int ret; /* for better error checking */
2643         char *imap_flags = NIL;
2644         int msgcount;
2645         int box = NEW_FOLDER;
2646
2647         snprintf(mailbox, sizeof(mailbox), "%s@%s", vmu->mailbox, vmu->context);
2648         msgcount = messagecount(mailbox, "INBOX") + messagecount(mailbox, "Old");
2649
2650         /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2651         if (msgnum < 0) {
2652                 if(!imapgreetings) {
2653                         return 0;
2654                 } else {
2655                         box = GREETINGS_FOLDER;
2656                 }
2657         }
2658
2659         if (imap_check_limits(chan, vms, vmu, msgcount)) {
2660                 return -1;
2661         }
2662
2663         /* Set urgent flag for IMAP message */
2664         if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2665                 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2666                 imap_flags = "\\FLAGGED";
2667         }
2668
2669         /* Attach only the first format */
2670         fmt = ast_strdupa(fmt);
2671         stringp = fmt;
2672         strsep(&stringp, "|");
2673
2674         if (!ast_strlen_zero(vmu->serveremail))
2675                 myserveremail = vmu->serveremail;
2676
2677         if (msgnum > -1)
2678                 make_file(fn, sizeof(fn), dir, msgnum);
2679         else
2680                 ast_copy_string (fn, dir, sizeof(fn));
2681
2682         snprintf(introfn, sizeof(introfn), "%sintro", fn);
2683         if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2684                 *introfn = '\0';
2685         }
2686
2687         if (ast_strlen_zero(vmu->email)) {
2688                 /* We need the vmu->email to be set when we call make_email_file, but
2689                  * if we keep it set, a duplicate e-mail will be created. So at the end
2690                  * of this function, we will revert back to an empty string if tempcopy
2691                  * is 1.
2692                  */
2693                 vmu->email = ast_strdup(vmu->imapuser);
2694                 tempcopy = 1;
2695         }
2696
2697         if (!strcmp(fmt, "wav49"))
2698                 fmt = "WAV";
2699         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2700
2701         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2702            command hangs. */
2703         if (!(p = vm_mkftemp(tmp))) {
2704                 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2705                 if (tempcopy) {
2706                         ast_free(vmu->email);
2707                         vmu->email = NULL;
2708                 }
2709                 return -1;
2710         }
2711
2712         if (msgnum < 0 && imapgreetings) {
2713                 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
2714                         ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
2715                         return -1;
2716                 }
2717                 imap_delete_old_greeting(fn, vms);
2718         }
2719
2720         make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
2721                 chan ? S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL) : NULL,
2722                 chan ? S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL) : NULL,
2723                 fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
2724         /* read mail file to memory */
2725         len = ftell(p);
2726         rewind(p);
2727         if (!(buf = ast_malloc(len + 1))) {
2728                 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2729                 fclose(p);
2730                 if (tempcopy)
2731                         *(vmu->email) = '\0';
2732                 return -1;
2733         }
2734         if (fread(buf, 1, len, p) != len) {
2735                 if (ferror(p)) {
2736                         ast_log(LOG_ERROR, "Error while reading mail file: %s\n", tmp);
2737                         return -1;
2738                 }
2739         }
2740         ((char *) buf)[len] = '\0';
2741         INIT(&str, mail_string, buf, len);
2742         ret = init_mailstream(vms, box);
2743         if (ret == 0) {
2744                 imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
2745                 ast_mutex_lock(&vms->lock);
2746                 if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
2747                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2748                 ast_mutex_unlock(&vms->lock);
2749                 fclose(p);
2750                 unlink(tmp);
2751                 ast_free(buf);
2752         } else {
2753                 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
2754                 fclose(p);
2755                 unlink(tmp);
2756                 ast_free(buf);
2757                 return -1;
2758         }
2759         ast_debug(3, "%s stored\n", fn);
2760
2761         if (tempcopy)
2762                 *(vmu->email) = '\0';
2763         inprocess_count(vmu->mailbox, vmu->context, -1);
2764         return 0;
2765
2766 }
2767
2768 /*!
2769  * \brief Gets the number of messages that exist in the inbox folder.
2770  * \param mailbox_context
2771  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
2772  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
2773  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
2774  * 
2775  * This method is used when IMAP backend is used.
2776  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
2777  *
2778  * \return zero on success, -1 on error.
2779  */
2780
2781 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2782 {
2783         char tmp[PATH_MAX] = "";
2784         char *mailboxnc;
2785         char *context;
2786         char *mb;
2787         char *cur;
2788         if (newmsgs)
2789                 *newmsgs = 0;
2790         if (oldmsgs)
2791                 *oldmsgs = 0;
2792         if (urgentmsgs)
2793                 *urgentmsgs = 0;
2794
2795         ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
2796         /* If no mailbox, return immediately */
2797         if (ast_strlen_zero(mailbox_context))
2798                 return 0;
2799
2800         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2801         context = strchr(tmp, '@');
2802         if (strchr(mailbox_context, ',')) {
2803                 int tmpnew, tmpold, tmpurgent;
2804                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2805                 mb = tmp;
2806                 while ((cur = strsep(&mb, ", "))) {
2807                         if (!ast_strlen_zero(cur)) {
2808                                 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2809                                         return -1;
2810                                 else {
2811                                         if (newmsgs)
2812                                                 *newmsgs += tmpnew; 
2813                                         if (oldmsgs)
2814                                                 *oldmsgs += tmpold;
2815                                         if (urgentmsgs)
2816                                                 *urgentmsgs += tmpurgent;
2817                                 }
2818                         }
2819                 }
2820                 return 0;
2821         }
2822         if (context) {
2823                 *context = '\0';
2824                 mailboxnc = tmp;
2825                 context++;
2826         } else {
2827                 context = "default";
2828                 mailboxnc = (char *) mailbox_context;
2829         }
2830
2831         if (newmsgs) {
2832                 struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
2833                 if (!vmu) {
2834                         ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2835                         return -1;
2836                 }
2837                 if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
2838                         free_user(vmu);
2839                         return -1;
2840                 }
2841                 free_user(vmu);
2842         }
2843         if (oldmsgs) {
2844                 if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
2845                         return -1;
2846                 }
2847         }
2848         if (urgentmsgs) {
2849                 if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
2850                         return -1;
2851                 }
2852         }
2853         return 0;
2854 }
2855
2856 /** 
2857  * \brief Determines if the given folder has messages.
2858  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
2859  * \param folder the folder to look in
2860  *
2861  * This function is used when the mailbox is stored in an IMAP back end.
2862  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
2863  * \return 1 if the folder has one or more messages. zero otherwise.
2864  */
2865
2866 static int has_voicemail(const char *mailbox, const char *folder)
2867 {
2868         char tmp[256], *tmp2, *box, *context;
2869         ast_copy_string(tmp, mailbox, sizeof(tmp));
2870         tmp2 = tmp;
2871         if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
2872                 while ((box = strsep(&tmp2, ",&"))) {
2873                         if (!ast_strlen_zero(box)) {
2874                                 if (has_voicemail(box, folder)) {
2875                                         return 1;
2876                                 }
2877                         }
2878                 }
2879         }
2880         if ((context = strchr(tmp, '@'))) {
2881                 *context++ = '\0';
2882         } else {
2883                 context = "default";
2884         }
2885         return __messagecount(context, tmp, folder) ? 1 : 0;
2886 }
2887
2888 /*!
2889  * \brief Copies a message from one mailbox to another.
2890  * \param chan
2891  * \param vmu
2892  * \param imbox
2893  * \param msgnum
2894  * \param duration
2895  * \param recip
2896  * \param fmt
2897  * \param dir
2898  *
2899  * This works with IMAP storage based mailboxes.
2900  *
2901  * \return zero on success, -1 on error.
2902  */
2903 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)
2904 {
2905         struct vm_state *sendvms = NULL;
2906         char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2907         if (msgnum >= recip->maxmsg) {
2908                 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2909                 return -1;
2910         }
2911         if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2912                 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2913                 return -1;
2914         }
2915         if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
2916                 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2917                 return -1;
2918         }
2919         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2920         ast_mutex_lock(&sendvms->lock);
2921         if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
2922                 ast_mutex_unlock(&sendvms->lock);
2923                 return 0;
2924         }
2925         ast_mutex_unlock(&sendvms->lock);
2926         ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2927         return -1;
2928 }
2929
2930 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
2931 {
2932         char tmp[256], *t = tmp;
2933         size_t left = sizeof(tmp);
2934         
2935         if (box == OLD_FOLDER) {
2936                 ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
2937         } else {
2938                 ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
2939         }
2940
2941         if (box == NEW_FOLDER) {
2942                 ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
2943         } else {
2944                 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
2945         }
2946
2947         /* Build up server information */
2948         ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
2949
2950         /* Add authentication user if present */
2951         if (!ast_strlen_zero(authuser))
2952                 ast_build_string(&t, &left, "/authuser=%s", authuser);
2953
2954         /* Add flags if present */
2955         if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
2956                 ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
2957         }
2958
2959         /* End with username */
2960 #if 1
2961         ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
2962 #else
2963         ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
2964 #endif
2965         if (box == NEW_FOLDER || box == OLD_FOLDER)
2966                 snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
2967         else if (box == GREETINGS_FOLDER)
2968                 snprintf(spec, len, "%s%s", tmp, greetingfolder);
2969         else {  /* Other folders such as Friends, Family, etc... */
2970                 if (!ast_strlen_zero(imapparentfolder)) {
2971                         /* imapparentfolder would typically be set to INBOX */
2972                         snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
2973                 } else {
2974                         snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
2975                 }
2976         }
2977 }
2978
2979 static int init_mailstream(struct vm_state *vms, int box)
2980 {
2981         MAILSTREAM *stream = NIL;
2982         long debug;
2983         char tmp[256];
2984
2985         if (!vms) {
2986                 ast_log(LOG_ERROR, "vm_state is NULL!\n");
2987                 return -1;
2988         }
2989         ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
2990         if (vms->mailstream == NIL || !vms->mailstream) {
2991                 ast_debug(1, "mailstream not set.\n");
2992         } else {
2993                 stream = vms->mailstream;
2994         }
2995         /* debug = T;  user wants protocol telemetry? */
2996         debug = NIL;  /* NO protocol telemetry? */
2997
2998         if (delimiter == '\0') {                /* did not probe the server yet */
2999                 char *cp;
3000 #ifdef USE_SYSTEM_IMAP
3001 #include <imap/linkage.c>
3002 #elif defined(USE_SYSTEM_CCLIENT)
3003 #include <c-client/linkage.c>
3004 #else
3005 #include "linkage.c"
3006 #endif
3007                 /* Connect to INBOX first to get folders delimiter */
3008                 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
3009                 ast_mutex_lock(&vms->lock);
3010                 ast_mutex_lock(&mail_open_lock);
3011                 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3012                 ast_mutex_unlock(&mail_open_lock);
3013                 ast_mutex_unlock(&vms->lock);
3014                 if (stream == NIL) {
3015                         ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
3016                         return -1;
3017                 }
3018                 get_mailbox_delimiter(vms, stream);
3019                 /* update delimiter in imapfolder */
3020                 for (cp = vms->imapfolder; *cp; cp++)
3021                         if (*cp == '/')
3022                                 *cp = delimiter;
3023         }
3024         /* Now connect to the target folder */
3025         imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
3026         ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
3027         ast_mutex_lock(&vms->lock);
3028         ast_mutex_lock(&mail_open_lock);
3029         vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3030         /* Create the folder if it dosn't exist */
3031         if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
3032                 mail_create(vms->mailstream, tmp);
3033         }
3034         ast_mutex_unlock(&mail_open_lock);
3035         ast_mutex_unlock(&vms->lock);
3036         if (vms->mailstream == NIL) {
3037                 return -1;
3038         } else {
3039                 return 0;
3040         }
3041 }
3042
3043 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
3044 {
3045         SEARCHPGM *pgm;
3046         SEARCHHEADER *hdr;
3047         int urgent = 0;
3048
3049         /* If Urgent, then look at INBOX */
3050         if (box == 11) {
3051                 box = NEW_FOLDER;
3052                 urgent = 1;
3053         }
3054
3055         ast_co