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