Sun Oct 16 2011 08:41:29

Asterisk developer's documentation


app_voicemail.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \author Mark Spencer <markster@digium.com>
00022  * \brief Comedian Mail - Voicemail System
00023  *
00024  * \extref unixODBC (http://www.unixodbc.org/)
00025  * \extref A source distribution of University of Washington's IMAP c-client
00026  *         (http://www.washington.edu/imap/)
00027  *
00028  * \par See also
00029  * \arg \ref Config_vm
00030  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
00031  * \ingroup applications
00032  * \note This module requires res_adsi to load. This needs to be optional
00033  * during compilation.
00034  *
00035  * \note This file is now almost impossible to work with, due to all \#ifdefs.
00036  *       Feels like the database code before realtime. Someone - please come up
00037  *       with a plan to clean this up.
00038  */
00039 
00040 /*** MODULEINFO
00041    <use>res_adsi</use>
00042    <use>res_smdi</use>
00043    <support_level>core</support_level>
00044  ***/
00045 
00046 /*** MAKEOPTS
00047 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
00048    <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
00049       <conflict>ODBC_STORAGE</conflict>
00050       <conflict>IMAP_STORAGE</conflict>
00051       <defaultenabled>yes</defaultenabled>
00052    </member>
00053    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00054       <depend>generic_odbc</depend>
00055       <depend>ltdl</depend>
00056       <conflict>IMAP_STORAGE</conflict>
00057       <conflict>FILE_STORAGE</conflict>
00058       <defaultenabled>no</defaultenabled>
00059    </member>
00060    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00061       <depend>imap_tk</depend>
00062       <conflict>ODBC_STORAGE</conflict>
00063       <conflict>FILE_STORAGE</conflict>
00064       <use>openssl</use>
00065       <defaultenabled>no</defaultenabled>
00066    </member>
00067 </category>
00068 ***/
00069 
00070 #include "asterisk.h"
00071 
00072 #ifdef IMAP_STORAGE
00073 #include <ctype.h>
00074 #include <signal.h>
00075 #include <pwd.h>
00076 #ifdef USE_SYSTEM_IMAP
00077 #include <imap/c-client.h>
00078 #include <imap/imap4r1.h>
00079 #include <imap/linkage.h>
00080 #elif defined (USE_SYSTEM_CCLIENT)
00081 #include <c-client/c-client.h>
00082 #include <c-client/imap4r1.h>
00083 #include <c-client/linkage.h>
00084 #else
00085 #include "c-client.h"
00086 #include "imap4r1.h"
00087 #include "linkage.h"
00088 #endif
00089 #endif
00090 
00091 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 334453 $")
00092 
00093 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00094 #include <sys/time.h>
00095 #include <sys/stat.h>
00096 #include <sys/mman.h>
00097 #include <time.h>
00098 #include <dirent.h>
00099 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00100 #include <sys/wait.h>
00101 #endif
00102 
00103 #include "asterisk/logger.h"
00104 #include "asterisk/lock.h"
00105 #include "asterisk/file.h"
00106 #include "asterisk/channel.h"
00107 #include "asterisk/pbx.h"
00108 #include "asterisk/config.h"
00109 #include "asterisk/say.h"
00110 #include "asterisk/module.h"
00111 #include "asterisk/adsi.h"
00112 #include "asterisk/app.h"
00113 #include "asterisk/manager.h"
00114 #include "asterisk/dsp.h"
00115 #include "asterisk/localtime.h"
00116 #include "asterisk/cli.h"
00117 #include "asterisk/utils.h"
00118 #include "asterisk/stringfields.h"
00119 #include "asterisk/smdi.h"
00120 #include "asterisk/astobj2.h"
00121 #include "asterisk/event.h"
00122 #include "asterisk/taskprocessor.h"
00123 #include "asterisk/test.h"
00124 
00125 #ifdef ODBC_STORAGE
00126 #include "asterisk/res_odbc.h"
00127 #endif
00128 
00129 #ifdef IMAP_STORAGE
00130 #include "asterisk/threadstorage.h"
00131 #endif
00132 
00133 /*** DOCUMENTATION
00134    <application name="VoiceMail" language="en_US">
00135       <synopsis>
00136          Leave a Voicemail message.
00137       </synopsis>
00138       <syntax>
00139          <parameter name="mailboxs" argsep="&amp;" required="true">
00140             <argument name="mailbox1" argsep="@" required="true">
00141                <argument name="mailbox" required="true" />
00142                <argument name="context" />
00143             </argument>
00144             <argument name="mailbox2" argsep="@" multiple="true">
00145                <argument name="mailbox" required="true" />
00146                <argument name="context" />
00147             </argument>
00148          </parameter>
00149          <parameter name="options">
00150             <optionlist>
00151                <option name="b">
00152                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00153                </option>
00154                <option name="d">
00155                   <argument name="c" />
00156                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00157                   if played during the greeting. Context defaults to the current context.</para>
00158                </option>
00159                <option name="g">
00160                   <argument name="#" required="true" />
00161                   <para>Use the specified amount of gain when recording the voicemail
00162                   message. The units are whole-number decibels (dB). Only works on supported
00163                   technologies, which is DAHDI only.</para>
00164                </option>
00165                <option name="s">
00166                   <para>Skip the playback of instructions for leaving a message to the
00167                   calling party.</para>
00168                </option>
00169                <option name="u">
00170                   <para>Play the <literal>unavailable</literal> greeting.</para>
00171                </option>
00172                <option name="U">
00173                   <para>Mark message as <literal>URGENT</literal>.</para>
00174                </option>
00175                <option name="P">
00176                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00177                </option>
00178             </optionlist>
00179          </parameter>
00180       </syntax>
00181       <description>
00182          <para>This application allows the calling party to leave a message for the specified
00183          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00184          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00185          exist.</para>
00186          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00187          <enumlist>
00188             <enum name="0">
00189                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00190             </enum>
00191             <enum name="*">
00192                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00193             </enum>
00194          </enumlist>
00195          <para>This application will set the following channel variable upon completion:</para>
00196          <variablelist>
00197             <variable name="VMSTATUS">
00198                <para>This indicates the status of the execution of the VoiceMail application.</para>
00199                <value name="SUCCESS" />
00200                <value name="USEREXIT" />
00201                <value name="FAILED" />
00202             </variable>
00203          </variablelist>
00204       </description>
00205       <see-also>
00206          <ref type="application">VoiceMailMain</ref>
00207       </see-also>
00208    </application>
00209    <application name="VoiceMailMain" language="en_US">
00210       <synopsis>
00211          Check Voicemail messages.
00212       </synopsis>
00213       <syntax>
00214          <parameter name="mailbox" required="true" argsep="@">
00215             <argument name="mailbox" />
00216             <argument name="context" />
00217          </parameter>
00218          <parameter name="options">
00219             <optionlist>
00220                <option name="p">
00221                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00222                   the mailbox that is entered by the caller.</para>
00223                </option>
00224                <option name="g">
00225                   <argument name="#" required="true" />
00226                   <para>Use the specified amount of gain when recording a voicemail message.
00227                   The units are whole-number decibels (dB).</para>
00228                </option>
00229                <option name="s">
00230                   <para>Skip checking the passcode for the mailbox.</para>
00231                </option>
00232                <option name="a">
00233                   <argument name="folder" required="true" />
00234                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00235                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00236                   <enumlist>
00237                      <enum name="0"><para>INBOX</para></enum>
00238                      <enum name="1"><para>Old</para></enum>
00239                      <enum name="2"><para>Work</para></enum>
00240                      <enum name="3"><para>Family</para></enum>
00241                      <enum name="4"><para>Friends</para></enum>
00242                      <enum name="5"><para>Cust1</para></enum>
00243                      <enum name="6"><para>Cust2</para></enum>
00244                      <enum name="7"><para>Cust3</para></enum>
00245                      <enum name="8"><para>Cust4</para></enum>
00246                      <enum name="9"><para>Cust5</para></enum>
00247                   </enumlist>
00248                </option>
00249             </optionlist>
00250          </parameter>
00251       </syntax>
00252       <description>
00253          <para>This application allows the calling party to check voicemail messages. A specific
00254          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00255          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00256          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00257          <literal>default</literal> context will be used.</para>
00258          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00259          or Password, and the extension exists:</para>
00260          <enumlist>
00261             <enum name="*">
00262                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00263             </enum>
00264          </enumlist>
00265       </description>
00266       <see-also>
00267          <ref type="application">VoiceMail</ref>
00268       </see-also>
00269    </application>
00270    <application name="MailboxExists" language="en_US">
00271       <synopsis>
00272          Check to see if Voicemail mailbox exists.
00273       </synopsis>
00274       <syntax>
00275          <parameter name="mailbox" required="true" argsep="@">
00276             <argument name="mailbox" required="true" />
00277             <argument name="context" />
00278          </parameter>
00279          <parameter name="options">
00280             <para>None options.</para>
00281          </parameter>
00282       </syntax>
00283       <description>
00284          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00285          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00286          will be used.</para>
00287          <para>This application will set the following channel variable upon completion:</para>
00288          <variablelist>
00289             <variable name="VMBOXEXISTSSTATUS">
00290                <para>This will contain the status of the execution of the MailboxExists application.
00291                Possible values include:</para>
00292                <value name="SUCCESS" />
00293                <value name="FAILED" />
00294             </variable>
00295          </variablelist>
00296       </description>
00297    </application>
00298    <application name="VMAuthenticate" language="en_US">
00299       <synopsis>
00300          Authenticate with Voicemail passwords.
00301       </synopsis>
00302       <syntax>
00303          <parameter name="mailbox" required="true" argsep="@">
00304             <argument name="mailbox" />
00305             <argument name="context" />
00306          </parameter>
00307          <parameter name="options">
00308             <optionlist>
00309                <option name="s">
00310                   <para>Skip playing the initial prompts.</para>
00311                </option>
00312             </optionlist>
00313          </parameter>
00314       </syntax>
00315       <description>
00316          <para>This application behaves the same way as the Authenticate application, but the passwords
00317          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00318          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00319          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00320          mailbox.</para>
00321          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00322          or Password, and the extension exists:</para>
00323          <enumlist>
00324             <enum name="*">
00325                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00326             </enum>
00327          </enumlist>
00328       </description>
00329    </application>
00330    <application name="VMSayName" language="en_US">
00331       <synopsis>
00332          Play the name of a voicemail user
00333       </synopsis>
00334       <syntax>
00335          <parameter name="mailbox" required="true" argsep="@">
00336             <argument name="mailbox" />
00337             <argument name="context" />
00338          </parameter>
00339       </syntax>
00340       <description>
00341          <para>This application will say the recorded name of the voicemail user specified as the
00342          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00343       </description>
00344    </application>
00345    <function name="MAILBOX_EXISTS" language="en_US">
00346       <synopsis>
00347          Tell if a mailbox is configured.
00348       </synopsis>
00349       <syntax argsep="@">
00350          <parameter name="mailbox" required="true" />
00351          <parameter name="context" />
00352       </syntax>
00353       <description>
00354          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00355          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00356          context.</para>
00357       </description>
00358    </function>
00359    <manager name="VoicemailUsersList" language="en_US">
00360       <synopsis>
00361          List All Voicemail User Information.
00362       </synopsis>
00363       <syntax>
00364          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00365       </syntax>
00366       <description>
00367       </description>
00368    </manager>
00369  ***/
00370 
00371 #ifdef IMAP_STORAGE
00372 static char imapserver[48];
00373 static char imapport[8];
00374 static char imapflags[128];
00375 static char imapfolder[64];
00376 static char imapparentfolder[64] = "\0";
00377 static char greetingfolder[64];
00378 static char authuser[32];
00379 static char authpassword[42];
00380 static int imapversion = 1;
00381 
00382 static int expungeonhangup = 1;
00383 static int imapgreetings = 0;
00384 static char delimiter = '\0';
00385 
00386 struct vm_state;
00387 struct ast_vm_user;
00388 
00389 AST_THREADSTORAGE(ts_vmstate);
00390 
00391 /* Forward declarations for IMAP */
00392 static int init_mailstream(struct vm_state *vms, int box);
00393 static void write_file(char *filename, char *buffer, unsigned long len);
00394 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00395 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00396 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00397 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00398 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00399 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00400 static void vmstate_insert(struct vm_state *vms);
00401 static void vmstate_delete(struct vm_state *vms);
00402 static void set_update(MAILSTREAM * stream);
00403 static void init_vm_state(struct vm_state *vms);
00404 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00405 static void get_mailbox_delimiter(MAILSTREAM *stream);
00406 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00407 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00408 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
00409 static void update_messages_by_imapuser(const char *user, unsigned long number);
00410 static int vm_delete(char *file);
00411 
00412 static int imap_remove_file (char *dir, int msgnum);
00413 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00414 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00415 static void check_quota(struct vm_state *vms, char *mailbox);
00416 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00417 struct vmstate {
00418    struct vm_state *vms;
00419    AST_LIST_ENTRY(vmstate) list;
00420 };
00421 
00422 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00423 
00424 #endif
00425 
00426 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00427 
00428 #define COMMAND_TIMEOUT 5000
00429 /* Don't modify these here; set your umask at runtime instead */
00430 #define  VOICEMAIL_DIR_MODE   0777
00431 #define  VOICEMAIL_FILE_MODE  0666
00432 #define  CHUNKSIZE   65536
00433 
00434 #define VOICEMAIL_CONFIG "voicemail.conf"
00435 #define ASTERISK_USERNAME "asterisk"
00436 
00437 /* Define fast-forward, pause, restart, and reverse keys
00438    while listening to a voicemail message - these are
00439    strings, not characters */
00440 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00441 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00442 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00443 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00444 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00445 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00446 
00447 /* Default mail command to mail voicemail. Change it with the
00448     mailcmd= command in voicemail.conf */
00449 #define SENDMAIL "/usr/sbin/sendmail -t"
00450 
00451 #define INTRO "vm-intro"
00452 
00453 #define MAXMSG 100
00454 #define MAXMSGLIMIT 9999
00455 
00456 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00457 
00458 #define BASELINELEN 72
00459 #define BASEMAXINLINE 256
00460 #ifdef IMAP_STORAGE
00461 #define ENDL "\r\n"
00462 #else
00463 #define ENDL "\n"
00464 #endif
00465 
00466 #define MAX_DATETIME_FORMAT   512
00467 #define MAX_NUM_CID_CONTEXTS 10
00468 
00469 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00470 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00471 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00472 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00473 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00474 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00475 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00476 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00477 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00478 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00479 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00480 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00481 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00482 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00483 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00484 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00485 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00486 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00487 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00488 #define ERROR_LOCK_PATH  -100
00489 #define OPERATOR_EXIT     300
00490 
00491 
00492 enum vm_box {
00493    NEW_FOLDER,
00494    OLD_FOLDER,
00495    WORK_FOLDER,
00496    FAMILY_FOLDER,
00497    FRIENDS_FOLDER,
00498    GREETINGS_FOLDER
00499 };
00500 
00501 enum vm_option_flags {
00502    OPT_SILENT =           (1 << 0),
00503    OPT_BUSY_GREETING =    (1 << 1),
00504    OPT_UNAVAIL_GREETING = (1 << 2),
00505    OPT_RECORDGAIN =       (1 << 3),
00506    OPT_PREPEND_MAILBOX =  (1 << 4),
00507    OPT_AUTOPLAY =         (1 << 6),
00508    OPT_DTMFEXIT =         (1 << 7),
00509    OPT_MESSAGE_Urgent =   (1 << 8),
00510    OPT_MESSAGE_PRIORITY = (1 << 9)
00511 };
00512 
00513 enum vm_option_args {
00514    OPT_ARG_RECORDGAIN = 0,
00515    OPT_ARG_PLAYFOLDER = 1,
00516    OPT_ARG_DTMFEXIT   = 2,
00517    /* This *must* be the last value in this enum! */
00518    OPT_ARG_ARRAY_SIZE = 3,
00519 };
00520 
00521 enum vm_passwordlocation {
00522    OPT_PWLOC_VOICEMAILCONF = 0,
00523    OPT_PWLOC_SPOOLDIR      = 1,
00524    OPT_PWLOC_USERSCONF     = 2,
00525 };
00526 
00527 AST_APP_OPTIONS(vm_app_options, {
00528    AST_APP_OPTION('s', OPT_SILENT),
00529    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00530    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00531    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00532    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00533    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00534    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00535    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00536    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00537 });
00538 
00539 static int load_config(int reload);
00540 
00541 /*! \page vmlang Voicemail Language Syntaxes Supported
00542 
00543    \par Syntaxes supported, not really language codes.
00544    \arg \b en    - English
00545    \arg \b de    - German
00546    \arg \b es    - Spanish
00547    \arg \b fr    - French
00548    \arg \b it    - Italian
00549    \arg \b nl    - Dutch
00550    \arg \b pt    - Portuguese
00551    \arg \b pt_BR - Portuguese (Brazil)
00552    \arg \b gr    - Greek
00553    \arg \b no    - Norwegian
00554    \arg \b se    - Swedish
00555    \arg \b tw    - Chinese (Taiwan)
00556    \arg \b ua - Ukrainian
00557 
00558 German requires the following additional soundfile:
00559 \arg \b 1F  einE (feminine)
00560 
00561 Spanish requires the following additional soundfile:
00562 \arg \b 1M      un (masculine)
00563 
00564 Dutch, Portuguese & Spanish require the following additional soundfiles:
00565 \arg \b vm-INBOXs singular of 'new'
00566 \arg \b vm-Olds      singular of 'old/heard/read'
00567 
00568 NB these are plural:
00569 \arg \b vm-INBOX  nieuwe (nl)
00570 \arg \b vm-Old    oude (nl)
00571 
00572 Polish uses:
00573 \arg \b vm-new-a  'new', feminine singular accusative
00574 \arg \b vm-new-e  'new', feminine plural accusative
00575 \arg \b vm-new-ych   'new', feminine plural genitive
00576 \arg \b vm-old-a  'old', feminine singular accusative
00577 \arg \b vm-old-e  'old', feminine plural accusative
00578 \arg \b vm-old-ych   'old', feminine plural genitive
00579 \arg \b digits/1-a   'one', not always same as 'digits/1'
00580 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00581 
00582 Swedish uses:
00583 \arg \b vm-nytt      singular of 'new'
00584 \arg \b vm-nya    plural of 'new'
00585 \arg \b vm-gammalt   singular of 'old'
00586 \arg \b vm-gamla  plural of 'old'
00587 \arg \b digits/ett   'one', not always same as 'digits/1'
00588 
00589 Norwegian uses:
00590 \arg \b vm-ny     singular of 'new'
00591 \arg \b vm-nye    plural of 'new'
00592 \arg \b vm-gammel singular of 'old'
00593 \arg \b vm-gamle  plural of 'old'
00594 
00595 Dutch also uses:
00596 \arg \b nl-om     'at'?
00597 
00598 Spanish also uses:
00599 \arg \b vm-youhaveno
00600 
00601 Italian requires the following additional soundfile:
00602 
00603 For vm_intro_it:
00604 \arg \b vm-nuovo  new
00605 \arg \b vm-nuovi  new plural
00606 \arg \b vm-vecchio   old
00607 \arg \b vm-vecchi old plural
00608 
00609 Chinese (Taiwan) requires the following additional soundfile:
00610 \arg \b vm-tong      A class-word for call (tong1)
00611 \arg \b vm-ri     A class-word for day (ri4)
00612 \arg \b vm-you    You (ni3)
00613 \arg \b vm-haveno   Have no (mei2 you3)
00614 \arg \b vm-have     Have (you3)
00615 \arg \b vm-listen   To listen (yao4 ting1)
00616 
00617 
00618 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00619 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00620 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00621 
00622 */
00623 
00624 struct baseio {
00625    int iocp;
00626    int iolen;
00627    int linelength;
00628    int ateof;
00629    unsigned char iobuf[BASEMAXINLINE];
00630 };
00631 
00632 /*! Structure for linked list of users 
00633  * Use ast_vm_user_destroy() to free one of these structures. */
00634 struct ast_vm_user {
00635    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00636    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00637    char password[80];               /*!< Secret pin code, numbers only */
00638    char fullname[80];               /*!< Full name, for directory app */
00639    char email[80];                  /*!< E-mail address */
00640    char *emailsubject;              /*!< E-mail subject */
00641    char *emailbody;                 /*!< E-mail body */
00642    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00643    char serveremail[80];            /*!< From: Mail address */
00644    char mailcmd[160];               /*!< Configurable mail command */
00645    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00646    char zonetag[80];                /*!< Time zone */
00647    char locale[20];                 /*!< The locale (for presentation of date/time) */
00648    char callback[80];
00649    char dialout[80];
00650    char uniqueid[80];               /*!< Unique integer identifier */
00651    char exit[80];
00652    char attachfmt[20];              /*!< Attachment format */
00653    unsigned int flags;              /*!< VM_ flags */ 
00654    int saydurationm;
00655    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00656    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00657    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00658    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00659    int passwordlocation;            /*!< Storage location of the password */
00660 #ifdef IMAP_STORAGE
00661    char imapuser[80];               /*!< IMAP server login */
00662    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00663    char imapfolder[64];             /*!< IMAP voicemail folder */
00664    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00665    int imapversion;                 /*!< If configuration changes, use the new values */
00666 #endif
00667    double volgain;                  /*!< Volume gain for voicemails sent via email */
00668    AST_LIST_ENTRY(ast_vm_user) list;
00669 };
00670 
00671 /*! Voicemail time zones */
00672 struct vm_zone {
00673    AST_LIST_ENTRY(vm_zone) list;
00674    char name[80];
00675    char timezone[80];
00676    char msg_format[512];
00677 };
00678 
00679 #define VMSTATE_MAX_MSG_ARRAY 256
00680 
00681 /*! Voicemail mailbox state */
00682 struct vm_state {
00683    char curbox[80];
00684    char username[80];
00685    char context[80];
00686    char curdir[PATH_MAX];
00687    char vmbox[PATH_MAX];
00688    char fn[PATH_MAX];
00689    char intro[PATH_MAX];
00690    int *deleted;
00691    int *heard;
00692    int dh_arraysize; /* used for deleted / heard allocation */
00693    int curmsg;
00694    int lastmsg;
00695    int newmessages;
00696    int oldmessages;
00697    int urgentmessages;
00698    int starting;
00699    int repeats;
00700 #ifdef IMAP_STORAGE
00701    ast_mutex_t lock;
00702    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00703    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00704    MAILSTREAM *mailstream;
00705    int vmArrayIndex;
00706    char imapuser[80];                   /*!< IMAP server login */
00707    char imapfolder[64];                 /*!< IMAP voicemail folder */
00708    int imapversion;
00709    int interactive;
00710    char introfn[PATH_MAX];              /*!< Name of prepended file */
00711    unsigned int quota_limit;
00712    unsigned int quota_usage;
00713    struct vm_state *persist_vms;
00714 #endif
00715 };
00716 
00717 #ifdef ODBC_STORAGE
00718 static char odbc_database[80];
00719 static char odbc_table[80];
00720 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00721 #define DISPOSE(a,b) remove_file(a,b)
00722 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00723 #define EXISTS(a,b,c,d) (message_exists(a,b))
00724 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00725 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00726 #define DELETE(a,b,c,d) (delete_file(a,b))
00727 #else
00728 #ifdef IMAP_STORAGE
00729 #define DISPOSE(a,b) (imap_remove_file(a,b))
00730 #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))
00731 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00732 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00733 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00734 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00735 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00736 #else
00737 #define RETRIEVE(a,b,c,d)
00738 #define DISPOSE(a,b)
00739 #define STORE(a,b,c,d,e,f,g,h,i,j)
00740 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00741 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00742 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00743 #define DELETE(a,b,c,d) (vm_delete(c))
00744 #endif
00745 #endif
00746 
00747 static char VM_SPOOL_DIR[PATH_MAX];
00748 
00749 static char ext_pass_cmd[128];
00750 static char ext_pass_check_cmd[128];
00751 
00752 static int my_umask;
00753 
00754 #define PWDCHANGE_INTERNAL (1 << 1)
00755 #define PWDCHANGE_EXTERNAL (1 << 2)
00756 static int pwdchange = PWDCHANGE_INTERNAL;
00757 
00758 #ifdef ODBC_STORAGE
00759 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00760 #else
00761 # ifdef IMAP_STORAGE
00762 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00763 # else
00764 # define tdesc "Comedian Mail (Voicemail System)"
00765 # endif
00766 #endif
00767 
00768 static char userscontext[AST_MAX_EXTENSION] = "default";
00769 
00770 static char *addesc = "Comedian Mail";
00771 
00772 /* Leave a message */
00773 static char *app = "VoiceMail";
00774 
00775 /* Check mail, control, etc */
00776 static char *app2 = "VoiceMailMain";
00777 
00778 static char *app3 = "MailboxExists";
00779 static char *app4 = "VMAuthenticate";
00780 
00781 static char *sayname_app = "VMSayName";
00782 
00783 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00784 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00785 static char zonetag[80];
00786 static char locale[20];
00787 static int maxsilence;
00788 static int maxmsg;
00789 static int maxdeletedmsg;
00790 static int silencethreshold = 128;
00791 static char serveremail[80];
00792 static char mailcmd[160];  /* Configurable mail cmd */
00793 static char externnotify[160]; 
00794 static struct ast_smdi_interface *smdi_iface = NULL;
00795 static char vmfmts[80];
00796 static double volgain;
00797 static int vmminsecs;
00798 static int vmmaxsecs;
00799 static int maxgreet;
00800 static int skipms;
00801 static int maxlogins;
00802 static int minpassword;
00803 static int passwordlocation;
00804 
00805 /*! Poll mailboxes for changes since there is something external to
00806  *  app_voicemail that may change them. */
00807 static unsigned int poll_mailboxes;
00808 
00809 /*! Polling frequency */
00810 static unsigned int poll_freq;
00811 /*! By default, poll every 30 seconds */
00812 #define DEFAULT_POLL_FREQ 30
00813 
00814 AST_MUTEX_DEFINE_STATIC(poll_lock);
00815 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00816 static pthread_t poll_thread = AST_PTHREADT_NULL;
00817 static unsigned char poll_thread_run;
00818 
00819 /*! Subscription to ... MWI event subscriptions */
00820 static struct ast_event_sub *mwi_sub_sub;
00821 /*! Subscription to ... MWI event un-subscriptions */
00822 static struct ast_event_sub *mwi_unsub_sub;
00823 
00824 /*!
00825  * \brief An MWI subscription
00826  *
00827  * This is so we can keep track of which mailboxes are subscribed to.
00828  * This way, we know which mailboxes to poll when the pollmailboxes
00829  * option is being used.
00830  */
00831 struct mwi_sub {
00832    AST_RWLIST_ENTRY(mwi_sub) entry;
00833    int old_urgent;
00834    int old_new;
00835    int old_old;
00836    uint32_t uniqueid;
00837    char mailbox[1];
00838 };
00839 
00840 struct mwi_sub_task {
00841    const char *mailbox;
00842    const char *context;
00843    uint32_t uniqueid;
00844 };
00845 
00846 static struct ast_taskprocessor *mwi_subscription_tps;
00847 
00848 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00849 
00850 /* custom audio control prompts for voicemail playback */
00851 static char listen_control_forward_key[12];
00852 static char listen_control_reverse_key[12];
00853 static char listen_control_pause_key[12];
00854 static char listen_control_restart_key[12];
00855 static char listen_control_stop_key[12];
00856 
00857 /* custom password sounds */
00858 static char vm_password[80] = "vm-password";
00859 static char vm_newpassword[80] = "vm-newpassword";
00860 static char vm_passchanged[80] = "vm-passchanged";
00861 static char vm_reenterpassword[80] = "vm-reenterpassword";
00862 static char vm_mismatch[80] = "vm-mismatch";
00863 static char vm_invalid_password[80] = "vm-invalid-password";
00864 static char vm_pls_try_again[80] = "vm-pls-try-again";
00865 
00866 /*
00867  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00868  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00869  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00870  * app.c's __ast_play_and_record function
00871  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00872  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00873  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00874  * more effort than either of the other two.
00875  */
00876 static char vm_prepend_timeout[80] = "vm-then-pound";
00877 
00878 static struct ast_flags globalflags = {0};
00879 
00880 static int saydurationminfo;
00881 
00882 static char dialcontext[AST_MAX_CONTEXT] = "";
00883 static char callcontext[AST_MAX_CONTEXT] = "";
00884 static char exitcontext[AST_MAX_CONTEXT] = "";
00885 
00886 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00887 
00888 
00889 static char *emailbody = NULL;
00890 static char *emailsubject = NULL;
00891 static char *pagerbody = NULL;
00892 static char *pagersubject = NULL;
00893 static char fromstring[100];
00894 static char pagerfromstring[100];
00895 static char charset[32] = "ISO-8859-1";
00896 
00897 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00898 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00899 static int adsiver = 1;
00900 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00901 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00902 
00903 /* Forward declarations - generic */
00904 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00905 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);
00906 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00907 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00908          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00909          signed char record_gain, struct vm_state *vms, char *flag);
00910 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00911 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00912 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);
00913 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);
00914 static void apply_options(struct ast_vm_user *vmu, const char *options);
00915 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);
00916 static int is_valid_dtmf(const char *key);
00917 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00918 static int write_password_to_file(const char *secretfn, const char *password);
00919 static const char *substitute_escapes(const char *value);
00920 static void free_user(struct ast_vm_user *vmu);
00921 
00922 struct ao2_container *inprocess_container;
00923 
00924 struct inprocess {
00925    int count;
00926    char *context;
00927    char mailbox[0];
00928 };
00929 
00930 static int inprocess_hash_fn(const void *obj, const int flags)
00931 {
00932    const struct inprocess *i = obj;
00933    return atoi(i->mailbox);
00934 }
00935 
00936 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00937 {
00938    struct inprocess *i = obj, *j = arg;
00939    if (strcmp(i->mailbox, j->mailbox)) {
00940       return 0;
00941    }
00942    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00943 }
00944 
00945 static int inprocess_count(const char *context, const char *mailbox, int delta)
00946 {
00947    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00948    arg->context = arg->mailbox + strlen(mailbox) + 1;
00949    strcpy(arg->mailbox, mailbox); /* SAFE */
00950    strcpy(arg->context, context); /* SAFE */
00951    ao2_lock(inprocess_container);
00952    if ((i = ao2_find(inprocess_container, arg, 0))) {
00953       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00954       ao2_unlock(inprocess_container);
00955       ao2_ref(i, -1);
00956       return ret;
00957    }
00958    if (delta < 0) {
00959       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00960    }
00961    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00962       ao2_unlock(inprocess_container);
00963       return 0;
00964    }
00965    i->context = i->mailbox + strlen(mailbox) + 1;
00966    strcpy(i->mailbox, mailbox); /* SAFE */
00967    strcpy(i->context, context); /* SAFE */
00968    i->count = delta;
00969    ao2_link(inprocess_container, i);
00970    ao2_unlock(inprocess_container);
00971    ao2_ref(i, -1);
00972    return 0;
00973 }
00974 
00975 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00976 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00977 #endif
00978 
00979 /*!
00980  * \brief Strips control and non 7-bit clean characters from input string.
00981  *
00982  * \note To map control and none 7-bit characters to a 7-bit clean characters
00983  *  please use ast_str_encode_mine().
00984  */
00985 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00986 {
00987    char *bufptr = buf;
00988    for (; *input; input++) {
00989       if (*input < 32) {
00990          continue;
00991       }
00992       *bufptr++ = *input;
00993       if (bufptr == buf + buflen - 1) {
00994          break;
00995       }
00996    }
00997    *bufptr = '\0';
00998    return buf;
00999 }
01000 
01001 
01002 /*!
01003  * \brief Sets default voicemail system options to a voicemail user.
01004  *
01005  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01006  * - all the globalflags
01007  * - the saydurationminfo
01008  * - the callcontext
01009  * - the dialcontext
01010  * - the exitcontext
01011  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01012  * - volume gain
01013  * - emailsubject, emailbody set to NULL
01014  */
01015 static void populate_defaults(struct ast_vm_user *vmu)
01016 {
01017    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01018    vmu->passwordlocation = passwordlocation;
01019    if (saydurationminfo) {
01020       vmu->saydurationm = saydurationminfo;
01021    }
01022    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01023    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01024    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01025    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01026    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01027    if (vmminsecs) {
01028       vmu->minsecs = vmminsecs;
01029    }
01030    if (vmmaxsecs) {
01031       vmu->maxsecs = vmmaxsecs;
01032    }
01033    if (maxmsg) {
01034       vmu->maxmsg = maxmsg;
01035    }
01036    if (maxdeletedmsg) {
01037       vmu->maxdeletedmsg = maxdeletedmsg;
01038    }
01039    vmu->volgain = volgain;
01040    ast_free(vmu->emailsubject);
01041    vmu->emailsubject = NULL;
01042    ast_free(vmu->emailbody);
01043    vmu->emailbody = NULL;
01044 #ifdef IMAP_STORAGE
01045    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01046 #endif
01047 }
01048 
01049 /*!
01050  * \brief Sets a a specific property value.
01051  * \param vmu The voicemail user object to work with.
01052  * \param var The name of the property to be set.
01053  * \param value The value to be set to the property.
01054  * 
01055  * The property name must be one of the understood properties. See the source for details.
01056  */
01057 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01058 {
01059    int x;
01060    if (!strcasecmp(var, "attach")) {
01061       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01062    } else if (!strcasecmp(var, "attachfmt")) {
01063       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01064    } else if (!strcasecmp(var, "serveremail")) {
01065       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01066    } else if (!strcasecmp(var, "emailbody")) {
01067       vmu->emailbody = ast_strdup(substitute_escapes(value));
01068    } else if (!strcasecmp(var, "emailsubject")) {
01069       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01070    } else if (!strcasecmp(var, "language")) {
01071       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01072    } else if (!strcasecmp(var, "tz")) {
01073       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01074    } else if (!strcasecmp(var, "locale")) {
01075       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01076 #ifdef IMAP_STORAGE
01077    } else if (!strcasecmp(var, "imapuser")) {
01078       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01079       vmu->imapversion = imapversion;
01080    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01081       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01082       vmu->imapversion = imapversion;
01083    } else if (!strcasecmp(var, "imapfolder")) {
01084       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01085    } else if (!strcasecmp(var, "imapvmshareid")) {
01086       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01087       vmu->imapversion = imapversion;
01088 #endif
01089    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01090       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01091    } else if (!strcasecmp(var, "saycid")){
01092       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01093    } else if (!strcasecmp(var, "sendvoicemail")){
01094       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01095    } else if (!strcasecmp(var, "review")){
01096       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01097    } else if (!strcasecmp(var, "tempgreetwarn")){
01098       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01099    } else if (!strcasecmp(var, "messagewrap")){
01100       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01101    } else if (!strcasecmp(var, "operator")) {
01102       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01103    } else if (!strcasecmp(var, "envelope")){
01104       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01105    } else if (!strcasecmp(var, "moveheard")){
01106       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01107    } else if (!strcasecmp(var, "sayduration")){
01108       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01109    } else if (!strcasecmp(var, "saydurationm")){
01110       if (sscanf(value, "%30d", &x) == 1) {
01111          vmu->saydurationm = x;
01112       } else {
01113          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01114       }
01115    } else if (!strcasecmp(var, "forcename")){
01116       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01117    } else if (!strcasecmp(var, "forcegreetings")){
01118       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01119    } else if (!strcasecmp(var, "callback")) {
01120       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01121    } else if (!strcasecmp(var, "dialout")) {
01122       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01123    } else if (!strcasecmp(var, "exitcontext")) {
01124       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01125    } else if (!strcasecmp(var, "minsecs")) {
01126       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01127          vmu->minsecs = x;
01128       } else {
01129          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01130          vmu->minsecs = vmminsecs;
01131       }
01132    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01133       vmu->maxsecs = atoi(value);
01134       if (vmu->maxsecs <= 0) {
01135          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01136          vmu->maxsecs = vmmaxsecs;
01137       } else {
01138          vmu->maxsecs = atoi(value);
01139       }
01140       if (!strcasecmp(var, "maxmessage"))
01141          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01142    } else if (!strcasecmp(var, "maxmsg")) {
01143       vmu->maxmsg = atoi(value);
01144       /* Accept maxmsg=0 (Greetings only voicemail) */
01145       if (vmu->maxmsg < 0) {
01146          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01147          vmu->maxmsg = MAXMSG;
01148       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01149          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01150          vmu->maxmsg = MAXMSGLIMIT;
01151       }
01152    } else if (!strcasecmp(var, "nextaftercmd")) {
01153       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01154    } else if (!strcasecmp(var, "backupdeleted")) {
01155       if (sscanf(value, "%30d", &x) == 1)
01156          vmu->maxdeletedmsg = x;
01157       else if (ast_true(value))
01158          vmu->maxdeletedmsg = MAXMSG;
01159       else
01160          vmu->maxdeletedmsg = 0;
01161 
01162       if (vmu->maxdeletedmsg < 0) {
01163          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01164          vmu->maxdeletedmsg = MAXMSG;
01165       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01166          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01167          vmu->maxdeletedmsg = MAXMSGLIMIT;
01168       }
01169    } else if (!strcasecmp(var, "volgain")) {
01170       sscanf(value, "%30lf", &vmu->volgain);
01171    } else if (!strcasecmp(var, "passwordlocation")) {
01172       if (!strcasecmp(value, "spooldir")) {
01173          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01174       } else {
01175          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01176       }
01177    } else if (!strcasecmp(var, "options")) {
01178       apply_options(vmu, value);
01179    }
01180 }
01181 
01182 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01183 {
01184    int fds[2], pid = 0;
01185 
01186    memset(buf, 0, len);
01187 
01188    if (pipe(fds)) {
01189       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01190    } else {
01191       /* good to go*/
01192       pid = ast_safe_fork(0);
01193 
01194       if (pid < 0) {
01195          /* ok maybe not */
01196          close(fds[0]);
01197          close(fds[1]);
01198          snprintf(buf, len, "FAILURE: Fork failed");
01199       } else if (pid) {
01200          /* parent */
01201          close(fds[1]);
01202          if (read(fds[0], buf, len) < 0) {
01203             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01204          }
01205          close(fds[0]);
01206       } else {
01207          /*  child */
01208          AST_DECLARE_APP_ARGS(arg,
01209             AST_APP_ARG(v)[20];
01210          );
01211          char *mycmd = ast_strdupa(command);
01212 
01213          close(fds[0]);
01214          dup2(fds[1], STDOUT_FILENO);
01215          close(fds[1]);
01216          ast_close_fds_above_n(STDOUT_FILENO);
01217 
01218          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01219 
01220          execv(arg.v[0], arg.v); 
01221          printf("FAILURE: %s", strerror(errno));
01222          _exit(0);
01223       }
01224    }
01225    return buf;
01226 }
01227 
01228 /*!
01229  * \brief Check that password meets minimum required length
01230  * \param vmu The voicemail user to change the password for.
01231  * \param password The password string to check
01232  *
01233  * \return zero on ok, 1 on not ok.
01234  */
01235 static int check_password(struct ast_vm_user *vmu, char *password)
01236 {
01237    /* check minimum length */
01238    if (strlen(password) < minpassword)
01239       return 1;
01240    /* check that password does not contain '*' character */
01241    if (!ast_strlen_zero(password) && password[0] == '*')
01242       return 1;
01243    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01244       char cmd[255], buf[255];
01245 
01246       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01247 
01248       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01249       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01250          ast_debug(5, "Result: %s\n", buf);
01251          if (!strncasecmp(buf, "VALID", 5)) {
01252             ast_debug(3, "Passed password check: '%s'\n", buf);
01253             return 0;
01254          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01255             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01256             return 0;
01257          } else {
01258             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01259             return 1;
01260          }
01261       }
01262    }
01263    return 0;
01264 }
01265 
01266 /*! 
01267  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01268  * \param vmu The voicemail user to change the password for.
01269  * \param password The new value to be set to the password for this user.
01270  * 
01271  * This only works if there is a realtime engine configured.
01272  * This is called from the (top level) vm_change_password.
01273  *
01274  * \return zero on success, -1 on error.
01275  */
01276 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01277 {
01278    int res = -1;
01279    if (!strcmp(vmu->password, password)) {
01280       /* No change (but an update would return 0 rows updated, so we opt out here) */
01281       return 0;
01282    }
01283 
01284    if (strlen(password) > 10) {
01285       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01286    }
01287    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01288       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01289       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01290       res = 0;
01291    }
01292    return res;
01293 }
01294 
01295 /*!
01296  * \brief Destructively Parse options and apply.
01297  */
01298 static void apply_options(struct ast_vm_user *vmu, const char *options)
01299 {  
01300    char *stringp;
01301    char *s;
01302    char *var, *value;
01303    stringp = ast_strdupa(options);
01304    while ((s = strsep(&stringp, "|"))) {
01305       value = s;
01306       if ((var = strsep(&value, "=")) && value) {
01307          apply_option(vmu, var, value);
01308       }
01309    }  
01310 }
01311 
01312 /*!
01313  * \brief Loads the options specific to a voicemail user.
01314  * 
01315  * This is called when a vm_user structure is being set up, such as from load_options.
01316  */
01317 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01318 {
01319    for (; var; var = var->next) {
01320       if (!strcasecmp(var->name, "vmsecret")) {
01321          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01322       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01323          if (ast_strlen_zero(retval->password)) {
01324             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01325                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01326                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01327             } else {
01328                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01329             }
01330          }
01331       } else if (!strcasecmp(var->name, "uniqueid")) {
01332          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01333       } else if (!strcasecmp(var->name, "pager")) {
01334          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01335       } else if (!strcasecmp(var->name, "email")) {
01336          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01337       } else if (!strcasecmp(var->name, "fullname")) {
01338          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01339       } else if (!strcasecmp(var->name, "context")) {
01340          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01341       } else if (!strcasecmp(var->name, "emailsubject")) {
01342          ast_free(retval->emailsubject);
01343          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01344       } else if (!strcasecmp(var->name, "emailbody")) {
01345          ast_free(retval->emailbody);
01346          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01347 #ifdef IMAP_STORAGE
01348       } else if (!strcasecmp(var->name, "imapuser")) {
01349          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01350          retval->imapversion = imapversion;
01351       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01352          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01353          retval->imapversion = imapversion;
01354       } else if (!strcasecmp(var->name, "imapfolder")) {
01355          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01356       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01357          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01358          retval->imapversion = imapversion;
01359 #endif
01360       } else
01361          apply_option(retval, var->name, var->value);
01362    }
01363 }
01364 
01365 /*!
01366  * \brief Determines if a DTMF key entered is valid.
01367  * \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.
01368  *
01369  * Tests the character entered against the set of valid DTMF characters. 
01370  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01371  */
01372 static int is_valid_dtmf(const char *key)
01373 {
01374    int i;
01375    char *local_key = ast_strdupa(key);
01376 
01377    for (i = 0; i < strlen(key); ++i) {
01378       if (!strchr(VALID_DTMF, *local_key)) {
01379          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01380          return 0;
01381       }
01382       local_key++;
01383    }
01384    return 1;
01385 }
01386 
01387 /*!
01388  * \brief Finds a voicemail user from the realtime engine.
01389  * \param ivm
01390  * \param context
01391  * \param mailbox
01392  *
01393  * 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.
01394  *
01395  * \return The ast_vm_user structure for the user that was found.
01396  */
01397 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01398 {
01399    struct ast_variable *var;
01400    struct ast_vm_user *retval;
01401 
01402    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01403       if (!ivm)
01404          ast_set_flag(retval, VM_ALLOCED);   
01405       else
01406          memset(retval, 0, sizeof(*retval));
01407       if (mailbox) 
01408          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01409       populate_defaults(retval);
01410       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01411          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01412       else
01413          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01414       if (var) {
01415          apply_options_full(retval, var);
01416          ast_variables_destroy(var);
01417       } else { 
01418          if (!ivm) 
01419             free_user(retval);
01420          retval = NULL;
01421       }  
01422    } 
01423    return retval;
01424 }
01425 
01426 /*!
01427  * \brief Finds a voicemail user from the users file or the realtime engine.
01428  * \param ivm
01429  * \param context
01430  * \param mailbox
01431  * 
01432  * \return The ast_vm_user structure for the user that was found.
01433  */
01434 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01435 {
01436    /* This function could be made to generate one from a database, too */
01437    struct ast_vm_user *vmu = NULL, *cur;
01438    AST_LIST_LOCK(&users);
01439 
01440    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01441       context = "default";
01442 
01443    AST_LIST_TRAVERSE(&users, cur, list) {
01444 #ifdef IMAP_STORAGE
01445       if (cur->imapversion != imapversion) {
01446          continue;
01447       }
01448 #endif
01449       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01450          break;
01451       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01452          break;
01453    }
01454    if (cur) {
01455       /* Make a copy, so that on a reload, we have no race */
01456       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01457          *vmu = *cur;
01458          if (!ivm) {
01459             vmu->emailbody = ast_strdup(cur->emailbody);
01460             vmu->emailsubject = ast_strdup(cur->emailsubject);
01461          }
01462          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01463          AST_LIST_NEXT(vmu, list) = NULL;
01464       }
01465    } else
01466       vmu = find_user_realtime(ivm, context, mailbox);
01467    AST_LIST_UNLOCK(&users);
01468    return vmu;
01469 }
01470 
01471 /*!
01472  * \brief Resets a user password to a specified password.
01473  * \param context
01474  * \param mailbox
01475  * \param newpass
01476  *
01477  * This does the actual change password work, called by the vm_change_password() function.
01478  *
01479  * \return zero on success, -1 on error.
01480  */
01481 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01482 {
01483    /* This function could be made to generate one from a database, too */
01484    struct ast_vm_user *cur;
01485    int res = -1;
01486    AST_LIST_LOCK(&users);
01487    AST_LIST_TRAVERSE(&users, cur, list) {
01488       if ((!context || !strcasecmp(context, cur->context)) &&
01489          (!strcasecmp(mailbox, cur->mailbox)))
01490             break;
01491    }
01492    if (cur) {
01493       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01494       res = 0;
01495    }
01496    AST_LIST_UNLOCK(&users);
01497    return res;
01498 }
01499 
01500 /*! 
01501  * \brief The handler for the change password option.
01502  * \param vmu The voicemail user to work with.
01503  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01504  * 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.
01505  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01506  */
01507 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01508 {
01509    struct ast_config   *cfg = NULL;
01510    struct ast_variable *var = NULL;
01511    struct ast_category *cat = NULL;
01512    char *category = NULL, *value = NULL, *new = NULL;
01513    const char *tmp = NULL;
01514    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01515    char secretfn[PATH_MAX] = "";
01516    int found = 0;
01517 
01518    if (!change_password_realtime(vmu, newpassword))
01519       return;
01520 
01521    /* check if we should store the secret in the spool directory next to the messages */
01522    switch (vmu->passwordlocation) {
01523    case OPT_PWLOC_SPOOLDIR:
01524       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01525       if (write_password_to_file(secretfn, newpassword) == 0) {
01526          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01527          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01528          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01529          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01530          break;
01531       } else {
01532          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01533       }
01534       /* Fall-through */
01535    case OPT_PWLOC_VOICEMAILCONF:
01536       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01537          while ((category = ast_category_browse(cfg, category))) {
01538             if (!strcasecmp(category, vmu->context)) {
01539                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01540                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01541                   break;
01542                }
01543                value = strstr(tmp, ",");
01544                if (!value) {
01545                   new = alloca(strlen(newpassword)+1);
01546                   sprintf(new, "%s", newpassword);
01547                } else {
01548                   new = alloca((strlen(value) + strlen(newpassword) + 1));
01549                   sprintf(new, "%s%s", newpassword, value);
01550                }
01551                if (!(cat = ast_category_get(cfg, category))) {
01552                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01553                   break;
01554                }
01555                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01556                found = 1;
01557             }
01558          }
01559          /* save the results */
01560          if (found) {
01561             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01562             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01563             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01564             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01565             break;
01566          }
01567       }
01568       /* Fall-through */
01569    case OPT_PWLOC_USERSCONF:
01570       /* check users.conf and update the password stored for the mailbox */
01571       /* if no vmsecret entry exists create one. */
01572       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01573          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01574          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01575             ast_debug(4, "users.conf: %s\n", category);
01576             if (!strcasecmp(category, vmu->mailbox)) {
01577                if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01578                   ast_debug(3, "looks like we need to make vmsecret!\n");
01579                   var = ast_variable_new("vmsecret", newpassword, "");
01580                } else {
01581                   var = NULL;
01582                }
01583                new = alloca(strlen(newpassword) + 1);
01584                sprintf(new, "%s", newpassword);
01585                if (!(cat = ast_category_get(cfg, category))) {
01586                   ast_debug(4, "failed to get category!\n");
01587                   ast_free(var);
01588                   break;
01589                }
01590                if (!var) {
01591                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01592                } else {
01593                   ast_variable_append(cat, var);
01594                }
01595                found = 1;
01596                break;
01597             }
01598          }
01599          /* save the results and clean things up */
01600          if (found) {
01601             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01602             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01603             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01604             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01605          }
01606       }
01607    }
01608 }
01609 
01610 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01611 {
01612    char buf[255];
01613    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01614    ast_debug(1, "External password: %s\n",buf);
01615    if (!ast_safe_system(buf)) {
01616       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01617       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01618       /* Reset the password in memory, too */
01619       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01620    }
01621 }
01622 
01623 /*! 
01624  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01625  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01626  * \param len The length of the path string that was written out.
01627  * \param context
01628  * \param ext 
01629  * \param folder 
01630  * 
01631  * The path is constructed as 
01632  *    VM_SPOOL_DIRcontext/ext/folder
01633  *
01634  * \return zero on success, -1 on error.
01635  */
01636 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01637 {
01638    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01639 }
01640 
01641 /*! 
01642  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01643  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01644  * \param len The length of the path string that was written out.
01645  * \param dir 
01646  * \param num 
01647  * 
01648  * The path is constructed as 
01649  *    VM_SPOOL_DIRcontext/ext/folder
01650  *
01651  * \return zero on success, -1 on error.
01652  */
01653 static int make_file(char *dest, const int len, const char *dir, const int num)
01654 {
01655    return snprintf(dest, len, "%s/msg%04d", dir, num);
01656 }
01657 
01658 /* same as mkstemp, but return a FILE * */
01659 static FILE *vm_mkftemp(char *template)
01660 {
01661    FILE *p = NULL;
01662    int pfd = mkstemp(template);
01663    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01664    if (pfd > -1) {
01665       p = fdopen(pfd, "w+");
01666       if (!p) {
01667          close(pfd);
01668          pfd = -1;
01669       }
01670    }
01671    return p;
01672 }
01673 
01674 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01675  * \param dest    String. base directory.
01676  * \param len     Length of dest.
01677  * \param context String. Ignored if is null or empty string.
01678  * \param ext     String. Ignored if is null or empty string.
01679  * \param folder  String. Ignored if is null or empty string. 
01680  * \return -1 on failure, 0 on success.
01681  */
01682 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01683 {
01684    mode_t   mode = VOICEMAIL_DIR_MODE;
01685    int res;
01686 
01687    make_dir(dest, len, context, ext, folder);
01688    if ((res = ast_mkdir(dest, mode))) {
01689       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01690       return -1;
01691    }
01692    return 0;
01693 }
01694 
01695 static const char * const mailbox_folders[] = {
01696 #ifdef IMAP_STORAGE
01697    imapfolder,
01698 #else
01699    "INBOX",
01700 #endif
01701    "Old",
01702    "Work",
01703    "Family",
01704    "Friends",
01705    "Cust1",
01706    "Cust2",
01707    "Cust3",
01708    "Cust4",
01709    "Cust5",
01710    "Deleted",
01711    "Urgent",
01712 };
01713 
01714 static const char *mbox(struct ast_vm_user *vmu, int id)
01715 {
01716 #ifdef IMAP_STORAGE
01717    if (vmu && id == 0) {
01718       return vmu->imapfolder;
01719    }
01720 #endif
01721    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01722 }
01723 
01724 static int get_folder_by_name(const char *name)
01725 {
01726    size_t i;
01727 
01728    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01729       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01730          return i;
01731       }
01732    }
01733 
01734    return -1;
01735 }
01736 
01737 static void free_user(struct ast_vm_user *vmu)
01738 {
01739    if (ast_test_flag(vmu, VM_ALLOCED)) {
01740 
01741       ast_free(vmu->emailbody);
01742       vmu->emailbody = NULL;
01743 
01744       ast_free(vmu->emailsubject);
01745       vmu->emailsubject = NULL;
01746 
01747       ast_free(vmu);
01748    }
01749 }
01750 
01751 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01752 
01753    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01754    if (!vms->dh_arraysize) {
01755       /* initial allocation */
01756       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01757          return -1;
01758       }
01759       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01760          return -1;
01761       }
01762       vms->dh_arraysize = arraysize;
01763    } else if (vms->dh_arraysize < arraysize) {
01764       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01765          return -1;
01766       }
01767       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01768          return -1;
01769       }
01770       memset(vms->deleted, 0, arraysize * sizeof(int));
01771       memset(vms->heard, 0, arraysize * sizeof(int));
01772       vms->dh_arraysize = arraysize;
01773    }
01774 
01775    return 0;
01776 }
01777 
01778 /* All IMAP-specific functions should go in this block. This
01779  * keeps them from being spread out all over the code */
01780 #ifdef IMAP_STORAGE
01781 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01782 {
01783    char arg[10];
01784    struct vm_state *vms;
01785    unsigned long messageNum;
01786 
01787    /* If greetings aren't stored in IMAP, just delete the file */
01788    if (msgnum < 0 && !imapgreetings) {
01789       ast_filedelete(file, NULL);
01790       return;
01791    }
01792 
01793    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01794       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);
01795       return;
01796    }
01797 
01798    /* find real message number based on msgnum */
01799    /* this may be an index into vms->msgArray based on the msgnum. */
01800    messageNum = vms->msgArray[msgnum];
01801    if (messageNum == 0) {
01802       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01803       return;
01804    }
01805    if (option_debug > 2)
01806       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01807    /* delete message */
01808    snprintf (arg, sizeof(arg), "%lu", messageNum);
01809    ast_mutex_lock(&vms->lock);
01810    mail_setflag (vms->mailstream, arg, "\\DELETED");
01811    mail_expunge(vms->mailstream);
01812    ast_mutex_unlock(&vms->lock);
01813 }
01814 
01815 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01816 {
01817    struct vm_state *vms_p;
01818    char *file, *filename;
01819    char *attachment;
01820    int ret = 0, i;
01821    BODY *body;
01822 
01823    /* This function is only used for retrieval of IMAP greetings
01824     * regular messages are not retrieved this way, nor are greetings
01825     * if they are stored locally*/
01826    if (msgnum > -1 || !imapgreetings) {
01827       return 0;
01828    } else {
01829       file = strrchr(ast_strdupa(dir), '/');
01830       if (file)
01831          *file++ = '\0';
01832       else {
01833          ast_debug (1, "Failed to procure file name from directory passed.\n");
01834          return -1;
01835       }
01836    }
01837 
01838    /* check if someone is accessing this box right now... */
01839    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01840       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01841       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01842       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01843       * that's all we need to do.
01844       */
01845       if (!(vms_p = create_vm_state_from_user(vmu))) {
01846          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01847          return -1;
01848       }
01849    }
01850    
01851    /* Greetings will never have a prepended message */
01852    *vms_p->introfn = '\0';
01853 
01854    ast_mutex_lock(&vms_p->lock);
01855    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01856    if (!vms_p->mailstream) {
01857       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01858       ast_mutex_unlock(&vms_p->lock);
01859       return -1;
01860    }
01861 
01862    /*XXX Yuck, this could probably be done a lot better */
01863    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01864       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01865       /* We have the body, now we extract the file name of the first attachment. */
01866       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01867          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01868       } else {
01869          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01870          ast_mutex_unlock(&vms_p->lock);
01871          return -1;
01872       }
01873       filename = strsep(&attachment, ".");
01874       if (!strcmp(filename, file)) {
01875          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01876          vms_p->msgArray[vms_p->curmsg] = i + 1;
01877          save_body(body, vms_p, "2", attachment, 0);
01878          ast_mutex_unlock(&vms_p->lock);
01879          return 0;
01880       }
01881    }
01882    ast_mutex_unlock(&vms_p->lock);
01883 
01884    return -1;
01885 }
01886 
01887 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01888 {
01889    BODY *body;
01890    char *header_content;
01891    char *attachedfilefmt;
01892    char buf[80];
01893    struct vm_state *vms;
01894    char text_file[PATH_MAX];
01895    FILE *text_file_ptr;
01896    int res = 0;
01897    struct ast_vm_user *vmu;
01898 
01899    if (!(vmu = find_user(NULL, context, mailbox))) {
01900       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01901       return -1;
01902    }
01903    
01904    if (msgnum < 0) {
01905       if (imapgreetings) {
01906          res = imap_retrieve_greeting(dir, msgnum, vmu);
01907          goto exit;
01908       } else {
01909          res = 0;
01910          goto exit;
01911       }
01912    }
01913 
01914    /* Before anything can happen, we need a vm_state so that we can
01915     * actually access the imap server through the vms->mailstream
01916     */
01917    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01918       /* This should not happen. If it does, then I guess we'd
01919        * need to create the vm_state, extract which mailbox to
01920        * open, and then set up the msgArray so that the correct
01921        * IMAP message could be accessed. If I have seen correctly
01922        * though, the vms should be obtainable from the vmstates list
01923        * and should have its msgArray properly set up.
01924        */
01925       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01926       res = -1;
01927       goto exit;
01928    }
01929    
01930    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01931    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01932 
01933    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01934    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01935       res = 0;
01936       goto exit;
01937    }
01938 
01939    if (option_debug > 2)
01940       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01941    if (vms->msgArray[msgnum] == 0) {
01942       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01943       res = -1;
01944       goto exit;
01945    }
01946 
01947    /* This will only work for new messages... */
01948    ast_mutex_lock(&vms->lock);
01949    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01950    ast_mutex_unlock(&vms->lock);
01951    /* empty string means no valid header */
01952    if (ast_strlen_zero(header_content)) {
01953       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01954       res = -1;
01955       goto exit;
01956    }
01957 
01958    ast_mutex_lock(&vms->lock);
01959    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01960    ast_mutex_unlock(&vms->lock);
01961 
01962    /* We have the body, now we extract the file name of the first attachment. */
01963    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01964       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01965    } else {
01966       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01967       res = -1;
01968       goto exit;
01969    }
01970    
01971    /* Find the format of the attached file */
01972 
01973    strsep(&attachedfilefmt, ".");
01974    if (!attachedfilefmt) {
01975       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01976       res = -1;
01977       goto exit;
01978    }
01979    
01980    save_body(body, vms, "2", attachedfilefmt, 0);
01981    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01982       *vms->introfn = '\0';
01983    }
01984 
01985    /* Get info from headers!! */
01986    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01987 
01988    if (!(text_file_ptr = fopen(text_file, "w"))) {
01989       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01990    }
01991 
01992    fprintf(text_file_ptr, "%s\n", "[message]");
01993 
01994    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01995    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01996    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01997    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01998    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01999    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02000    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02001    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02002    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02003    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02004    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02005    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02006    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02007    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02008    fclose(text_file_ptr);
02009 
02010 exit:
02011    free_user(vmu);
02012    return res;
02013 }
02014 
02015 static int folder_int(const char *folder)
02016 {
02017    /*assume a NULL folder means INBOX*/
02018    if (!folder) {
02019       return 0;
02020    }
02021    if (!strcasecmp(folder, imapfolder)) {
02022       return 0;
02023    } else if (!strcasecmp(folder, "Old")) {
02024       return 1;
02025    } else if (!strcasecmp(folder, "Work")) {
02026       return 2;
02027    } else if (!strcasecmp(folder, "Family")) {
02028       return 3;
02029    } else if (!strcasecmp(folder, "Friends")) {
02030       return 4;
02031    } else if (!strcasecmp(folder, "Cust1")) {
02032       return 5;
02033    } else if (!strcasecmp(folder, "Cust2")) {
02034       return 6;
02035    } else if (!strcasecmp(folder, "Cust3")) {
02036       return 7;
02037    } else if (!strcasecmp(folder, "Cust4")) {
02038       return 8;
02039    } else if (!strcasecmp(folder, "Cust5")) {
02040       return 9;
02041    } else if (!strcasecmp(folder, "Urgent")) {
02042       return 11;
02043    } else { /*assume they meant INBOX if folder is not found otherwise*/
02044       return 0;
02045    }
02046 }
02047 
02048 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02049 {
02050    SEARCHPGM *pgm;
02051    SEARCHHEADER *hdr;
02052 
02053    struct ast_vm_user *vmu, vmus;
02054    struct vm_state *vms_p;
02055    int ret = 0;
02056    int fold = folder_int(folder);
02057    int urgent = 0;
02058    
02059    /* If URGENT, then look at INBOX */
02060    if (fold == 11) {
02061       fold = NEW_FOLDER;
02062       urgent = 1;
02063    }
02064 
02065    if (ast_strlen_zero(mailbox))
02066       return 0;
02067 
02068    /* We have to get the user before we can open the stream! */
02069    vmu = find_user(&vmus, context, mailbox);
02070    if (!vmu) {
02071       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02072       return -1;
02073    } else {
02074       /* No IMAP account available */
02075       if (vmu->imapuser[0] == '\0') {
02076          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02077          return -1;
02078       }
02079    }
02080    
02081    /* No IMAP account available */
02082    if (vmu->imapuser[0] == '\0') {
02083       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02084       free_user(vmu);
02085       return -1;
02086    }
02087 
02088    /* check if someone is accessing this box right now... */
02089    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02090    if (!vms_p) {
02091       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02092    }
02093    if (vms_p) {
02094       ast_debug(3, "Returning before search - user is logged in\n");
02095       if (fold == 0) { /* INBOX */
02096          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02097       }
02098       if (fold == 1) { /* Old messages */
02099          return vms_p->oldmessages;
02100       }
02101    }
02102 
02103    /* add one if not there... */
02104    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02105    if (!vms_p) {
02106       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02107    }
02108 
02109    if (!vms_p) {
02110       vms_p = create_vm_state_from_user(vmu);
02111    }
02112    ret = init_mailstream(vms_p, fold);
02113    if (!vms_p->mailstream) {
02114       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02115       return -1;
02116    }
02117    if (ret == 0) {
02118       ast_mutex_lock(&vms_p->lock);
02119       pgm = mail_newsearchpgm ();
02120       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02121       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02122       pgm->header = hdr;
02123       if (fold != OLD_FOLDER) {
02124          pgm->unseen = 1;
02125          pgm->seen = 0;
02126       }
02127       /* In the special case where fold is 1 (old messages) we have to do things a bit
02128        * differently. Old messages are stored in the INBOX but are marked as "seen"
02129        */
02130       else {
02131          pgm->unseen = 0;
02132          pgm->seen = 1;
02133       }
02134       /* look for urgent messages */
02135       if (fold == NEW_FOLDER) {
02136          if (urgent) {
02137             pgm->flagged = 1;
02138             pgm->unflagged = 0;
02139          } else {
02140             pgm->flagged = 0;
02141             pgm->unflagged = 1;
02142          }
02143       }
02144       pgm->undeleted = 1;
02145       pgm->deleted = 0;
02146 
02147       vms_p->vmArrayIndex = 0;
02148       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02149       if (fold == 0 && urgent == 0)
02150          vms_p->newmessages = vms_p->vmArrayIndex;
02151       if (fold == 1)
02152          vms_p->oldmessages = vms_p->vmArrayIndex;
02153       if (fold == 0 && urgent == 1)
02154          vms_p->urgentmessages = vms_p->vmArrayIndex;
02155       /*Freeing the searchpgm also frees the searchhdr*/
02156       mail_free_searchpgm(&pgm);
02157       ast_mutex_unlock(&vms_p->lock);
02158       vms_p->updated = 0;
02159       return vms_p->vmArrayIndex;
02160    } else {
02161       ast_mutex_lock(&vms_p->lock);
02162       mail_ping(vms_p->mailstream);
02163       ast_mutex_unlock(&vms_p->lock);
02164    }
02165    return 0;
02166 }
02167 
02168 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02169 {
02170    /* Check if mailbox is full */
02171    check_quota(vms, vmu->imapfolder);
02172    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02173       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02174       ast_play_and_wait(chan, "vm-mailboxfull");
02175       return -1;
02176    }
02177    
02178    /* Check if we have exceeded maxmsg */
02179    ast_debug(3, "Checking message number quota: mailbox has %d messages, maximum is set to %d, current messages %d\n", msgnum, vmu->maxmsg, inprocess_count(vmu->mailbox, vmu->context, 0));
02180    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02181       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02182       ast_play_and_wait(chan, "vm-mailboxfull");
02183       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02184       return -1;
02185    }
02186 
02187    return 0;
02188 }
02189 
02190 /*!
02191  * \brief Gets the number of messages that exist in a mailbox folder.
02192  * \param context
02193  * \param mailbox
02194  * \param folder
02195  * 
02196  * This method is used when IMAP backend is used.
02197  * \return The number of messages in this mailbox folder (zero or more).
02198  */
02199 static int messagecount(const char *context, const char *mailbox, const char *folder)
02200 {
02201    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02202       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02203    } else {
02204       return __messagecount(context, mailbox, folder);
02205    }
02206 }
02207 
02208 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag)
02209 {
02210    char *myserveremail = serveremail;
02211    char fn[PATH_MAX];
02212    char introfn[PATH_MAX];
02213    char mailbox[256];
02214    char *stringp;
02215    FILE *p = NULL;
02216    char tmp[80] = "/tmp/astmail-XXXXXX";
02217    long len;
02218    void *buf;
02219    int tempcopy = 0;
02220    STRING str;
02221    int ret; /* for better error checking */
02222    char *imap_flags = NIL;
02223    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02224    int box = NEW_FOLDER;
02225 
02226    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02227    if (msgnum < 0) {
02228       if(!imapgreetings) {
02229          return 0;
02230       } else {
02231          box = GREETINGS_FOLDER;
02232       }
02233    }
02234    
02235    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02236       return -1;
02237    }
02238 
02239    /* Set urgent flag for IMAP message */
02240    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02241       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02242       imap_flags = "\\FLAGGED";
02243    }
02244    
02245    /* Attach only the first format */
02246    fmt = ast_strdupa(fmt);
02247    stringp = fmt;
02248    strsep(&stringp, "|");
02249 
02250    if (!ast_strlen_zero(vmu->serveremail))
02251       myserveremail = vmu->serveremail;
02252 
02253    if (msgnum > -1)
02254       make_file(fn, sizeof(fn), dir, msgnum);
02255    else
02256       ast_copy_string (fn, dir, sizeof(fn));
02257 
02258    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02259    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02260       *introfn = '\0';
02261    }
02262    
02263    if (ast_strlen_zero(vmu->email)) {
02264       /* We need the vmu->email to be set when we call make_email_file, but
02265        * if we keep it set, a duplicate e-mail will be created. So at the end
02266        * of this function, we will revert back to an empty string if tempcopy
02267        * is 1.
02268        */
02269       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02270       tempcopy = 1;
02271    }
02272 
02273    if (!strcmp(fmt, "wav49"))
02274       fmt = "WAV";
02275    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02276 
02277    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02278       command hangs. */
02279    if (!(p = vm_mkftemp(tmp))) {
02280       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02281       if (tempcopy)
02282          *(vmu->email) = '\0';
02283       return -1;
02284    }
02285 
02286    if (msgnum < 0 && imapgreetings) {
02287       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02288          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02289          return -1;
02290       }
02291       imap_delete_old_greeting(fn, vms);
02292    }
02293 
02294    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02295       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02296       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02297       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02298    /* read mail file to memory */
02299    len = ftell(p);
02300    rewind(p);
02301    if (!(buf = ast_malloc(len + 1))) {
02302       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02303       fclose(p);
02304       if (tempcopy)
02305          *(vmu->email) = '\0';
02306       return -1;
02307    }
02308    if (fread(buf, len, 1, p) < len) {
02309       if (ferror(p)) {
02310          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02311          return -1;
02312       }
02313    }
02314    ((char *) buf)[len] = '\0';
02315    INIT(&str, mail_string, buf, len);
02316    ret = init_mailstream(vms, box);
02317    if (ret == 0) {
02318       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02319       ast_mutex_lock(&vms->lock);
02320       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02321          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02322       ast_mutex_unlock(&vms->lock);
02323       fclose(p);
02324       unlink(tmp);
02325       ast_free(buf);
02326    } else {
02327       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02328       fclose(p);
02329       unlink(tmp);
02330       ast_free(buf);
02331       return -1;
02332    }
02333    ast_debug(3, "%s stored\n", fn);
02334    
02335    if (tempcopy)
02336       *(vmu->email) = '\0';
02337    inprocess_count(vmu->mailbox, vmu->context, -1);
02338    return 0;
02339 
02340 }
02341 
02342 /*!
02343  * \brief Gets the number of messages that exist in the inbox folder.
02344  * \param mailbox_context
02345  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02346  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02347  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02348  * 
02349  * This method is used when IMAP backend is used.
02350  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02351  *
02352  * \return zero on success, -1 on error.
02353  */
02354 
02355 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02356 {
02357    char tmp[PATH_MAX] = "";
02358    char *mailboxnc;
02359    char *context;
02360    char *mb;
02361    char *cur;
02362    if (newmsgs)
02363       *newmsgs = 0;
02364    if (oldmsgs)
02365       *oldmsgs = 0;
02366    if (urgentmsgs)
02367       *urgentmsgs = 0;
02368 
02369    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02370    /* If no mailbox, return immediately */
02371    if (ast_strlen_zero(mailbox_context))
02372       return 0;
02373    
02374    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02375    context = strchr(tmp, '@');
02376    if (strchr(mailbox_context, ',')) {
02377       int tmpnew, tmpold, tmpurgent;
02378       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02379       mb = tmp;
02380       while ((cur = strsep(&mb, ", "))) {
02381          if (!ast_strlen_zero(cur)) {
02382             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02383                return -1;
02384             else {
02385                if (newmsgs)
02386                   *newmsgs += tmpnew; 
02387                if (oldmsgs)
02388                   *oldmsgs += tmpold;
02389                if (urgentmsgs)
02390                   *urgentmsgs += tmpurgent;
02391             }
02392          }
02393       }
02394       return 0;
02395    }
02396    if (context) {
02397       *context = '\0';
02398       mailboxnc = tmp;
02399       context++;
02400    } else {
02401       context = "default";
02402       mailboxnc = (char *) mailbox_context;
02403    }
02404 
02405    if (newmsgs) {
02406       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02407       if (!vmu) {
02408          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02409          return -1;
02410       }
02411       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02412          return -1;
02413       }
02414    }
02415    if (oldmsgs) {
02416       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02417          return -1;
02418       }
02419    }
02420    if (urgentmsgs) {
02421       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02422          return -1;
02423       }
02424    }
02425    return 0;
02426 }
02427 
02428 /** 
02429  * \brief Determines if the given folder has messages.
02430  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02431  * \param folder the folder to look in
02432  *
02433  * This function is used when the mailbox is stored in an IMAP back end.
02434  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02435  * \return 1 if the folder has one or more messages. zero otherwise.
02436  */
02437 
02438 static int has_voicemail(const char *mailbox, const char *folder)
02439 {
02440    char tmp[256], *tmp2, *box, *context;
02441    ast_copy_string(tmp, mailbox, sizeof(tmp));
02442    tmp2 = tmp;
02443    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02444       while ((box = strsep(&tmp2, ",&"))) {
02445          if (!ast_strlen_zero(box)) {
02446             if (has_voicemail(box, folder)) {
02447                return 1;
02448             }
02449          }
02450       }
02451    }
02452    if ((context = strchr(tmp, '@'))) {
02453       *context++ = '\0';
02454    } else {
02455       context = "default";
02456    }
02457    return __messagecount(context, tmp, folder) ? 1 : 0;
02458 }
02459 
02460 /*!
02461  * \brief Copies a message from one mailbox to another.
02462  * \param chan
02463  * \param vmu
02464  * \param imbox
02465  * \param msgnum
02466  * \param duration
02467  * \param recip
02468  * \param fmt
02469  * \param dir
02470  *
02471  * This works with IMAP storage based mailboxes.
02472  *
02473  * \return zero on success, -1 on error.
02474  */
02475 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)
02476 {
02477    struct vm_state *sendvms = NULL, *destvms = NULL;
02478    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02479    if (msgnum >= recip->maxmsg) {
02480       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02481       return -1;
02482    }
02483    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02484       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02485       return -1;
02486    }
02487    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02488       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02489       return -1;
02490    }
02491    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02492    ast_mutex_lock(&sendvms->lock);
02493    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02494       ast_mutex_unlock(&sendvms->lock);
02495       return 0;
02496    }
02497    ast_mutex_unlock(&sendvms->lock);
02498    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02499    return -1;
02500 }
02501 
02502 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02503 {
02504    char tmp[256], *t = tmp;
02505    size_t left = sizeof(tmp);
02506    
02507    if (box == OLD_FOLDER) {
02508       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02509    } else {
02510       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02511    }
02512 
02513    if (box == NEW_FOLDER) {
02514       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02515    } else {
02516       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02517    }
02518 
02519    /* Build up server information */
02520    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02521 
02522    /* Add authentication user if present */
02523    if (!ast_strlen_zero(authuser))
02524       ast_build_string(&t, &left, "/authuser=%s", authuser);
02525 
02526    /* Add flags if present */
02527    if (!ast_strlen_zero(imapflags))
02528       ast_build_string(&t, &left, "/%s", imapflags);
02529 
02530    /* End with username */
02531 #if 1
02532    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02533 #else
02534    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02535 #endif
02536    if (box == NEW_FOLDER || box == OLD_FOLDER)
02537       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02538    else if (box == GREETINGS_FOLDER)
02539       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02540    else {   /* Other folders such as Friends, Family, etc... */
02541       if (!ast_strlen_zero(imapparentfolder)) {
02542          /* imapparentfolder would typically be set to INBOX */
02543          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02544       } else {
02545          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02546       }
02547    }
02548 }
02549 
02550 static int init_mailstream(struct vm_state *vms, int box)
02551 {
02552    MAILSTREAM *stream = NIL;
02553    long debug;
02554    char tmp[256];
02555    
02556    if (!vms) {
02557       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02558       return -1;
02559    }
02560    if (option_debug > 2)
02561       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02562    if (vms->mailstream == NIL || !vms->mailstream) {
02563       if (option_debug)
02564          ast_log(LOG_DEBUG, "mailstream not set.\n");
02565    } else {
02566       stream = vms->mailstream;
02567    }
02568    /* debug = T;  user wants protocol telemetry? */
02569    debug = NIL;  /* NO protocol telemetry? */
02570 
02571    if (delimiter == '\0') {      /* did not probe the server yet */
02572       char *cp;
02573 #ifdef USE_SYSTEM_IMAP
02574 #include <imap/linkage.c>
02575 #elif defined(USE_SYSTEM_CCLIENT)
02576 #include <c-client/linkage.c>
02577 #else
02578 #include "linkage.c"
02579 #endif
02580       /* Connect to INBOX first to get folders delimiter */
02581       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02582       ast_mutex_lock(&vms->lock);
02583       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02584       ast_mutex_unlock(&vms->lock);
02585       if (stream == NIL) {
02586          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02587          return -1;
02588       }
02589       get_mailbox_delimiter(stream);
02590       /* update delimiter in imapfolder */
02591       for (cp = vms->imapfolder; *cp; cp++)
02592          if (*cp == '/')
02593             *cp = delimiter;
02594    }
02595    /* Now connect to the target folder */
02596    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02597    if (option_debug > 2)
02598       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02599    ast_mutex_lock(&vms->lock);
02600    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02601    ast_mutex_unlock(&vms->lock);
02602    if (vms->mailstream == NIL) {
02603       return -1;
02604    } else {
02605       return 0;
02606    }
02607 }
02608 
02609 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02610 {
02611    SEARCHPGM *pgm;
02612    SEARCHHEADER *hdr;
02613    int ret, urgent = 0;
02614 
02615    /* If Urgent, then look at INBOX */
02616    if (box == 11) {
02617       box = NEW_FOLDER;
02618       urgent = 1;
02619    }
02620 
02621    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02622    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02623    vms->imapversion = vmu->imapversion;
02624    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02625 
02626    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02627       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02628       return -1;
02629    }
02630    
02631    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02632    
02633    /* Check Quota */
02634    if  (box == 0)  {
02635       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02636       check_quota(vms, (char *) mbox(vmu, box));
02637    }
02638 
02639    ast_mutex_lock(&vms->lock);
02640    pgm = mail_newsearchpgm();
02641 
02642    /* Check IMAP folder for Asterisk messages only... */
02643    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02644    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02645    pgm->header = hdr;
02646    pgm->deleted = 0;
02647    pgm->undeleted = 1;
02648 
02649    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02650    if (box == NEW_FOLDER && urgent == 1) {
02651       pgm->unseen = 1;
02652       pgm->seen = 0;
02653       pgm->flagged = 1;
02654       pgm->unflagged = 0;
02655    } else if (box == NEW_FOLDER && urgent == 0) {
02656       pgm->unseen = 1;
02657       pgm->seen = 0;
02658       pgm->flagged = 0;
02659       pgm->unflagged = 1;
02660    } else if (box == OLD_FOLDER) {
02661       pgm->seen = 1;
02662       pgm->unseen = 0;
02663    }
02664 
02665    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02666 
02667    vms->vmArrayIndex = 0;
02668    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02669    vms->lastmsg = vms->vmArrayIndex - 1;
02670    mail_free_searchpgm(&pgm);
02671    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02672     * ensure to allocate enough space to account for all of them. Warn if old messages
02673     * have not been checked first as that is required.
02674     */
02675    if (box == 0 && !vms->dh_arraysize) {
02676       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02677    }
02678    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02679       ast_mutex_unlock(&vms->lock);
02680       return -1;
02681    }
02682 
02683    ast_mutex_unlock(&vms->lock);
02684    return 0;
02685 }
02686 
02687 static void write_file(char *filename, char *buffer, unsigned long len)
02688 {
02689    FILE *output;
02690 
02691    output = fopen (filename, "w");
02692    if (fwrite(buffer, len, 1, output) != 1) {
02693       if (ferror(output)) {
02694          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02695       }
02696    }
02697    fclose (output);
02698 }
02699 
02700 static void update_messages_by_imapuser(const char *user, unsigned long number)
02701 {
02702    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02703 
02704    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02705       return;
02706    }
02707 
02708    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02709    vms->msgArray[vms->vmArrayIndex++] = number;
02710 }
02711 
02712 void mm_searched(MAILSTREAM *stream, unsigned long number)
02713 {
02714    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02715 
02716    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02717       return;
02718 
02719    update_messages_by_imapuser(user, number);
02720 }
02721 
02722 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02723 {
02724    struct ast_variable *var;
02725    struct ast_vm_user *vmu;
02726 
02727    vmu = ast_calloc(1, sizeof *vmu);
02728    if (!vmu)
02729       return NULL;
02730    ast_set_flag(vmu, VM_ALLOCED);
02731    populate_defaults(vmu);
02732 
02733    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02734    if (var) {
02735       apply_options_full(vmu, var);
02736       ast_variables_destroy(var);
02737       return vmu;
02738    } else {
02739       ast_free(vmu);
02740       return NULL;
02741    }
02742 }
02743 
02744 /* Interfaces to C-client */
02745 
02746 void mm_exists(MAILSTREAM * stream, unsigned long number)
02747 {
02748    /* mail_ping will callback here if new mail! */
02749    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02750    if (number == 0) return;
02751    set_update(stream);
02752 }
02753 
02754 
02755 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02756 {
02757    /* mail_ping will callback here if expunged mail! */
02758    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02759    if (number == 0) return;
02760    set_update(stream);
02761 }
02762 
02763 
02764 void mm_flags(MAILSTREAM * stream, unsigned long number)
02765 {
02766    /* mail_ping will callback here if read mail! */
02767    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02768    if (number == 0) return;
02769    set_update(stream);
02770 }
02771 
02772 
02773 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02774 {
02775    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02776    mm_log (string, errflg);
02777 }
02778 
02779 
02780 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02781 {
02782    if (delimiter == '\0') {
02783       delimiter = delim;
02784    }
02785 
02786    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02787    if (attributes & LATT_NOINFERIORS)
02788       ast_debug(5, "no inferiors\n");
02789    if (attributes & LATT_NOSELECT)
02790       ast_debug(5, "no select\n");
02791    if (attributes & LATT_MARKED)
02792       ast_debug(5, "marked\n");
02793    if (attributes & LATT_UNMARKED)
02794       ast_debug(5, "unmarked\n");
02795 }
02796 
02797 
02798 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02799 {
02800    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02801    if (attributes & LATT_NOINFERIORS)
02802       ast_debug(5, "no inferiors\n");
02803    if (attributes & LATT_NOSELECT)
02804       ast_debug(5, "no select\n");
02805    if (attributes & LATT_MARKED)
02806       ast_debug(5, "marked\n");
02807    if (attributes & LATT_UNMARKED)
02808       ast_debug(5, "unmarked\n");
02809 }
02810 
02811 
02812 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02813 {
02814    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02815    if (status->flags & SA_MESSAGES)
02816       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02817    if (status->flags & SA_RECENT)
02818       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02819    if (status->flags & SA_UNSEEN)
02820       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02821    if (status->flags & SA_UIDVALIDITY)
02822       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02823    if (status->flags & SA_UIDNEXT)
02824       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02825    ast_log(AST_LOG_NOTICE, "\n");
02826 }
02827 
02828 
02829 void mm_log(char *string, long errflg)
02830 {
02831    switch ((short) errflg) {
02832       case NIL:
02833          ast_debug(1, "IMAP Info: %s\n", string);
02834          break;
02835       case PARSE:
02836       case WARN:
02837          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02838          break;
02839       case ERROR:
02840          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02841          break;
02842    }
02843 }
02844 
02845 
02846 void mm_dlog(char *string)
02847 {
02848    ast_log(AST_LOG_NOTICE, "%s\n", string);
02849 }
02850 
02851 
02852 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02853 {
02854    struct ast_vm_user *vmu;
02855 
02856    ast_debug(4, "Entering callback mm_login\n");
02857 
02858    ast_copy_string(user, mb->user, MAILTMPLEN);
02859 
02860    /* We should only do this when necessary */
02861    if (!ast_strlen_zero(authpassword)) {
02862       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02863    } else {
02864       AST_LIST_TRAVERSE(&users, vmu, list) {
02865          if (!strcasecmp(mb->user, vmu->imapuser)) {
02866             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02867             break;
02868          }
02869       }
02870       if (!vmu) {
02871          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02872             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02873             free_user(vmu);
02874          }
02875       }
02876    }
02877 }
02878 
02879 
02880 void mm_critical(MAILSTREAM * stream)
02881 {
02882 }
02883 
02884 
02885 void mm_nocritical(MAILSTREAM * stream)
02886 {
02887 }
02888 
02889 
02890 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02891 {
02892    kill (getpid (), SIGSTOP);
02893    return NIL;
02894 }
02895 
02896 
02897 void mm_fatal(char *string)
02898 {
02899    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02900 }
02901 
02902 /* C-client callback to handle quota */
02903 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02904 {
02905    struct vm_state *vms;
02906    char *mailbox = stream->mailbox, *user;
02907    char buf[1024] = "";
02908    unsigned long usage = 0, limit = 0;
02909    
02910    while (pquota) {
02911       usage = pquota->usage;
02912       limit = pquota->limit;
02913       pquota = pquota->next;
02914    }
02915    
02916    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)))) {
02917       ast_log(AST_LOG_ERROR, "No state found.\n");
02918       return;
02919    }
02920 
02921    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02922 
02923    vms->quota_usage = usage;
02924    vms->quota_limit = limit;
02925 }
02926 
02927 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02928 {
02929    char *start, *eol_pnt;
02930    int taglen;
02931 
02932    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02933       return NULL;
02934 
02935    taglen = strlen(tag) + 1;
02936    if (taglen < 1)
02937       return NULL;
02938 
02939    if (!(start = strstr(header, tag)))
02940       return NULL;
02941 
02942    /* Since we can be called multiple times we should clear our buffer */
02943    memset(buf, 0, len);
02944 
02945    ast_copy_string(buf, start+taglen, len);
02946    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02947       *eol_pnt = '\0';
02948    return buf;
02949 }
02950 
02951 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02952 {
02953    char *start, *quote, *eol_pnt;
02954 
02955    if (ast_strlen_zero(mailbox))
02956       return NULL;
02957 
02958    if (!(start = strstr(mailbox, "/user=")))
02959       return NULL;
02960 
02961    ast_copy_string(buf, start+6, len);
02962 
02963    if (!(quote = strchr(buf, '\"'))) {
02964       if (!(eol_pnt = strchr(buf, '/')))
02965          eol_pnt = strchr(buf,'}');
02966       *eol_pnt = '\0';
02967       return buf;
02968    } else {
02969       eol_pnt = strchr(buf+1,'\"');
02970       *eol_pnt = '\0';
02971       return buf+1;
02972    }
02973 }
02974 
02975 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02976 {
02977    struct vm_state *vms_p;
02978 
02979    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02980    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02981       return vms_p;
02982    }
02983    if (option_debug > 4)
02984       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02985    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02986       return NULL;
02987    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02988    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02989    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02990    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02991    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02992    vms_p->imapversion = vmu->imapversion;
02993    if (option_debug > 4)
02994       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
02995    vms_p->updated = 1;
02996    /* set mailbox to INBOX! */
02997    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
02998    init_vm_state(vms_p);
02999    vmstate_insert(vms_p);
03000    return vms_p;
03001 }
03002 
03003 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03004 {
03005    struct vmstate *vlist = NULL;
03006 
03007    if (interactive) {
03008       struct vm_state *vms;
03009       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03010       vms = pthread_getspecific(ts_vmstate.key);
03011       return vms;
03012    }
03013 
03014    AST_LIST_LOCK(&vmstates);
03015    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03016       if (!vlist->vms) {
03017          ast_debug(3, "error: vms is NULL for %s\n", user);
03018          continue;
03019       }
03020       if (vlist->vms->imapversion != imapversion) {
03021          continue;
03022       }
03023       if (!vlist->vms->imapuser) {
03024          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03025          continue;
03026       }
03027 
03028       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03029          AST_LIST_UNLOCK(&vmstates);
03030          return vlist->vms;
03031       }
03032    }
03033    AST_LIST_UNLOCK(&vmstates);
03034 
03035    ast_debug(3, "%s not found in vmstates\n", user);
03036 
03037    return NULL;
03038 }
03039 
03040 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03041 {
03042 
03043    struct vmstate *vlist = NULL;
03044    const char *local_context = S_OR(context, "default");
03045 
03046    if (interactive) {
03047       struct vm_state *vms;
03048       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03049       vms = pthread_getspecific(ts_vmstate.key);
03050       return vms;
03051    }
03052 
03053    AST_LIST_LOCK(&vmstates);
03054    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03055       if (!vlist->vms) {
03056          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03057          continue;
03058       }
03059       if (vlist->vms->imapversion != imapversion) {
03060          continue;
03061       }
03062       if (!vlist->vms->username || !vlist->vms->context) {
03063          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03064          continue;
03065       }
03066 
03067       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);
03068       
03069       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03070          ast_debug(3, "Found it!\n");
03071          AST_LIST_UNLOCK(&vmstates);
03072          return vlist->vms;
03073       }
03074    }
03075    AST_LIST_UNLOCK(&vmstates);
03076 
03077    ast_debug(3, "%s not found in vmstates\n", mailbox);
03078 
03079    return NULL;
03080 }
03081 
03082 static void vmstate_insert(struct vm_state *vms) 
03083 {
03084    struct vmstate *v;
03085    struct vm_state *altvms;
03086 
03087    /* If interactive, it probably already exists, and we should
03088       use the one we already have since it is more up to date.
03089       We can compare the username to find the duplicate */
03090    if (vms->interactive == 1) {
03091       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03092       if (altvms) {  
03093          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03094          vms->newmessages = altvms->newmessages;
03095          vms->oldmessages = altvms->oldmessages;
03096          vms->vmArrayIndex = altvms->vmArrayIndex;
03097          vms->lastmsg = altvms->lastmsg;
03098          vms->curmsg = altvms->curmsg;
03099          /* get a pointer to the persistent store */
03100          vms->persist_vms = altvms;
03101          /* Reuse the mailstream? */
03102 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03103          vms->mailstream = altvms->mailstream;
03104 #else
03105          vms->mailstream = NIL;
03106 #endif
03107       }
03108       return;
03109    }
03110 
03111    if (!(v = ast_calloc(1, sizeof(*v))))
03112       return;
03113    
03114    v->vms = vms;
03115 
03116    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03117 
03118    AST_LIST_LOCK(&vmstates);
03119    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03120    AST_LIST_UNLOCK(&vmstates);
03121 }
03122 
03123 static void vmstate_delete(struct vm_state *vms) 
03124 {
03125    struct vmstate *vc = NULL;
03126    struct vm_state *altvms = NULL;
03127 
03128    /* If interactive, we should copy pertinent info
03129       back to the persistent state (to make update immediate) */
03130    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03131       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03132       altvms->newmessages = vms->newmessages;
03133       altvms->oldmessages = vms->oldmessages;
03134       altvms->updated = 1;
03135       vms->mailstream = mail_close(vms->mailstream);
03136 
03137       /* Interactive states are not stored within the persistent list */
03138       return;
03139    }
03140    
03141    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03142    
03143    AST_LIST_LOCK(&vmstates);
03144    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03145       if (vc->vms == vms) {
03146          AST_LIST_REMOVE_CURRENT(list);
03147          break;
03148       }
03149    }
03150    AST_LIST_TRAVERSE_SAFE_END
03151    AST_LIST_UNLOCK(&vmstates);
03152    
03153    if (vc) {
03154       ast_mutex_destroy(&vc->vms->lock);
03155       ast_free(vc);
03156    }
03157    else
03158       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03159 }
03160 
03161 static void set_update(MAILSTREAM * stream) 
03162 {
03163    struct vm_state *vms;
03164    char *mailbox = stream->mailbox, *user;
03165    char buf[1024] = "";
03166 
03167    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03168       if (user && option_debug > 2)
03169          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03170       return;
03171    }
03172 
03173    ast_debug(3, "User %s mailbox set for update.\n", user);
03174 
03175    vms->updated = 1; /* Set updated flag since mailbox changed */
03176 }
03177 
03178 static void init_vm_state(struct vm_state *vms) 
03179 {
03180    int x;
03181    vms->vmArrayIndex = 0;
03182    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03183       vms->msgArray[x] = 0;
03184    }
03185    ast_mutex_init(&vms->lock);
03186 }
03187 
03188 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03189 {
03190    char *body_content;
03191    char *body_decoded;
03192    char *fn = is_intro ? vms->introfn : vms->fn;
03193    unsigned long len;
03194    unsigned long newlen;
03195    char filename[256];
03196    
03197    if (!body || body == NIL)
03198       return -1;
03199 
03200    ast_mutex_lock(&vms->lock);
03201    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03202    ast_mutex_unlock(&vms->lock);
03203    if (body_content != NIL) {
03204       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03205       /* ast_debug(1,body_content); */
03206       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03207       /* If the body of the file is empty, return an error */
03208       if (!newlen) {
03209          return -1;
03210       }
03211       write_file(filename, (char *) body_decoded, newlen);
03212    } else {
03213       ast_debug(5, "Body of message is NULL.\n");
03214       return -1;
03215    }
03216    return 0;
03217 }
03218 
03219 /*! 
03220  * \brief Get delimiter via mm_list callback 
03221  * \param stream
03222  *
03223  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03224  */
03225 /* MUTEX should already be held */
03226 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03227    char tmp[50];
03228    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03229    mail_list(stream, tmp, "*");
03230 }
03231 
03232 /*! 
03233  * \brief Check Quota for user 
03234  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03235  * \param mailbox the mailbox to check the quota for.
03236  *
03237  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03238  */
03239 static void check_quota(struct vm_state *vms, char *mailbox) {
03240    ast_mutex_lock(&vms->lock);
03241    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03242    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03243    if (vms && vms->mailstream != NULL) {
03244       imap_getquotaroot(vms->mailstream, mailbox);
03245    } else {
03246       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03247    }
03248    ast_mutex_unlock(&vms->lock);
03249 }
03250 
03251 #endif /* IMAP_STORAGE */
03252 
03253 /*! \brief Lock file path
03254     only return failure if ast_lock_path returns 'timeout',
03255    not if the path does not exist or any other reason
03256 */
03257 static int vm_lock_path(const char *path)
03258 {
03259    switch (ast_lock_path(path)) {
03260    case AST_LOCK_TIMEOUT:
03261       return -1;
03262    default:
03263       return 0;
03264    }
03265 }
03266 
03267 
03268 #ifdef ODBC_STORAGE
03269 struct generic_prepare_struct {
03270    char *sql;
03271    int argc;
03272    char **argv;
03273 };
03274 
03275 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03276 {
03277    struct generic_prepare_struct *gps = data;
03278    int res, i;
03279    SQLHSTMT stmt;
03280 
03281    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03282    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03283       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03284       return NULL;
03285    }
03286    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03287    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03288       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03289       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03290       return NULL;
03291    }
03292    for (i = 0; i < gps->argc; i++)
03293       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03294 
03295    return stmt;
03296 }
03297 
03298 /*!
03299  * \brief Retrieves a file from an ODBC data store.
03300  * \param dir the path to the file to be retreived.
03301  * \param msgnum the message number, such as within a mailbox folder.
03302  * 
03303  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03304  * 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.
03305  *
03306  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03307  * The output is the message information file with the name msgnum and the extension .txt
03308  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03309  * 
03310  * \return 0 on success, -1 on error.
03311  */
03312 static int retrieve_file(char *dir, int msgnum)
03313 {
03314    int x = 0;
03315    int res;
03316    int fd = -1;
03317    size_t fdlen = 0;
03318    void *fdm = MAP_FAILED;
03319    SQLSMALLINT colcount = 0;
03320    SQLHSTMT stmt;
03321    char sql[PATH_MAX];
03322    char fmt[80]="";
03323    char *c;
03324    char coltitle[256];
03325    SQLSMALLINT collen;
03326    SQLSMALLINT datatype;
03327    SQLSMALLINT decimaldigits;
03328    SQLSMALLINT nullable;
03329    SQLULEN colsize;
03330    SQLLEN colsize2;
03331    FILE *f = NULL;
03332    char rowdata[80];
03333    char fn[PATH_MAX];
03334    char full_fn[PATH_MAX];
03335    char msgnums[80];
03336    char *argv[] = { dir, msgnums };
03337    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03338 
03339    struct odbc_obj *obj;
03340    obj = ast_odbc_request_obj(odbc_database, 0);
03341    if (obj) {
03342       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03343       c = strchr(fmt, '|');
03344       if (c)
03345          *c = '\0';
03346       if (!strcasecmp(fmt, "wav49"))
03347          strcpy(fmt, "WAV");
03348       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03349       if (msgnum > -1)
03350          make_file(fn, sizeof(fn), dir, msgnum);
03351       else
03352          ast_copy_string(fn, dir, sizeof(fn));
03353 
03354       /* Create the information file */
03355       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03356       
03357       if (!(f = fopen(full_fn, "w+"))) {
03358          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03359          goto yuck;
03360       }
03361       
03362       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03363       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03364       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03365       if (!stmt) {
03366          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03367          ast_odbc_release_obj(obj);
03368          goto yuck;
03369       }
03370       res = SQLFetch(stmt);
03371       if (res == SQL_NO_DATA) {
03372          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03373          ast_odbc_release_obj(obj);
03374          goto yuck;
03375       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03376          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03377          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03378          ast_odbc_release_obj(obj);
03379          goto yuck;
03380       }
03381       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03382       if (fd < 0) {
03383          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03384          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03385          ast_odbc_release_obj(obj);
03386          goto yuck;
03387       }
03388       res = SQLNumResultCols(stmt, &colcount);
03389       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03390          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03391          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03392          ast_odbc_release_obj(obj);
03393          goto yuck;
03394       }
03395       if (f) 
03396          fprintf(f, "[message]\n");
03397       for (x = 0; x < colcount; x++) {
03398          rowdata[0] = '\0';
03399          colsize = 0;
03400          collen = sizeof(coltitle);
03401          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03402                   &datatype, &colsize, &decimaldigits, &nullable);
03403          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03404             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03405             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03406             ast_odbc_release_obj(obj);
03407             goto yuck;
03408          }
03409          if (!strcasecmp(coltitle, "recording")) {
03410             off_t offset;
03411             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03412             fdlen = colsize2;
03413             if (fd > -1) {
03414                char tmp[1]="";
03415                lseek(fd, fdlen - 1, SEEK_SET);
03416                if (write(fd, tmp, 1) != 1) {
03417                   close(fd);
03418                   fd = -1;
03419                   continue;
03420                }
03421                /* Read out in small chunks */
03422                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03423                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03424                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03425                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03426                      ast_odbc_release_obj(obj);
03427                      goto yuck;
03428                   } else {
03429                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03430                      munmap(fdm, CHUNKSIZE);
03431                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03432                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03433                         unlink(full_fn);
03434                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03435                         ast_odbc_release_obj(obj);
03436                         goto yuck;
03437                      }
03438                   }
03439                }
03440                if (truncate(full_fn, fdlen) < 0) {
03441                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03442                }
03443             }
03444          } else {
03445             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03446             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03447                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03448                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03449                ast_odbc_release_obj(obj);
03450                goto yuck;
03451             }
03452             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03453                fprintf(f, "%s=%s\n", coltitle, rowdata);
03454          }
03455       }
03456       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03457       ast_odbc_release_obj(obj);
03458    } else
03459       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03460 yuck:
03461    if (f)
03462       fclose(f);
03463    if (fd > -1)
03464       close(fd);
03465    return x - 1;
03466 }
03467 
03468 /*!
03469  * \brief Determines the highest message number in use for a given user and mailbox folder.
03470  * \param vmu 
03471  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03472  *
03473  * This method is used when mailboxes are stored in an ODBC back end.
03474  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03475  *
03476  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03477 
03478  */
03479 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03480 {
03481    int x = 0;
03482    int res;
03483    SQLHSTMT stmt;
03484    char sql[PATH_MAX];
03485    char rowdata[20];
03486    char *argv[] = { dir };
03487    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03488 
03489    struct odbc_obj *obj;
03490    obj = ast_odbc_request_obj(odbc_database, 0);
03491    if (obj) {
03492       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03493 
03494       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03495       if (!stmt) {
03496          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03497          ast_odbc_release_obj(obj);
03498          goto yuck;
03499       }
03500       res = SQLFetch(stmt);
03501       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03502          if (res == SQL_NO_DATA) {
03503             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03504          } else {
03505             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03506          }
03507 
03508          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03509          ast_odbc_release_obj(obj);
03510          goto yuck;
03511       }
03512       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03513       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03514          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03515          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03516          ast_odbc_release_obj(obj);
03517          goto yuck;
03518       }
03519       if (sscanf(rowdata, "%30d", &x) != 1)
03520          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03521       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03522       ast_odbc_release_obj(obj);
03523       return x;
03524    } else
03525       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03526 yuck:
03527    return x - 1;
03528 }
03529 
03530 /*!
03531  * \brief Determines if the specified message exists.
03532  * \param dir the folder the mailbox folder to look for messages. 
03533  * \param msgnum the message index to query for.
03534  *
03535  * This method is used when mailboxes are stored in an ODBC back end.
03536  *
03537  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03538  */
03539 static int message_exists(char *dir, int msgnum)
03540 {
03541    int x = 0;
03542    int res;
03543    SQLHSTMT stmt;
03544    char sql[PATH_MAX];
03545    char rowdata[20];
03546    char msgnums[20];
03547    char *argv[] = { dir, msgnums };
03548    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03549 
03550    struct odbc_obj *obj;
03551    obj = ast_odbc_request_obj(odbc_database, 0);
03552    if (obj) {
03553       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03554       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03555       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03556       if (!stmt) {
03557          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03558          ast_odbc_release_obj(obj);
03559          goto yuck;
03560       }
03561       res = SQLFetch(stmt);
03562       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03563          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03564          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03565          ast_odbc_release_obj(obj);
03566          goto yuck;
03567       }
03568       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03569       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03570          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03571          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03572          ast_odbc_release_obj(obj);
03573          goto yuck;
03574       }
03575       if (sscanf(rowdata, "%30d", &x) != 1)
03576          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03577       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03578       ast_odbc_release_obj(obj);
03579    } else
03580       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03581 yuck:
03582    return x;
03583 }
03584 
03585 /*!
03586  * \brief returns the number of messages found.
03587  * \param vmu
03588  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03589  *
03590  * This method is used when mailboxes are stored in an ODBC back end.
03591  *
03592  * \return The count of messages being zero or more, less than zero on error.
03593  */
03594 static int count_messages(struct ast_vm_user *vmu, char *dir)
03595 {
03596    int x = 0;
03597    int res;
03598    SQLHSTMT stmt;
03599    char sql[PATH_MAX];
03600    char rowdata[20];
03601    char *argv[] = { dir };
03602    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03603 
03604    struct odbc_obj *obj;
03605    obj = ast_odbc_request_obj(odbc_database, 0);
03606    if (obj) {
03607       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03608       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03609       if (!stmt) {
03610          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03611          ast_odbc_release_obj(obj);
03612          goto yuck;
03613       }
03614       res = SQLFetch(stmt);
03615       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03616          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03617          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03618          ast_odbc_release_obj(obj);
03619          goto yuck;
03620       }
03621       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03622       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03623          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03624          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03625          ast_odbc_release_obj(obj);
03626          goto yuck;
03627       }
03628       if (sscanf(rowdata, "%30d", &x) != 1)
03629          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03630       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03631       ast_odbc_release_obj(obj);
03632       return x;
03633    } else
03634       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03635 yuck:
03636    return x - 1;
03637 
03638 }
03639 
03640 /*!
03641  * \brief Deletes a message from the mailbox folder.
03642  * \param sdir The mailbox folder to work in.
03643  * \param smsg The message index to be deleted.
03644  *
03645  * This method is used when mailboxes are stored in an ODBC back end.
03646  * The specified message is directly deleted from the database 'voicemessages' table.
03647  * 
03648  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03649  */
03650 static void delete_file(const char *sdir, int smsg)
03651 {
03652    SQLHSTMT stmt;
03653    char sql[PATH_MAX];
03654    char msgnums[20];
03655    char *argv[] = { NULL, msgnums };
03656    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03657    struct odbc_obj *obj;
03658 
03659    argv[0] = ast_strdupa(sdir);
03660 
03661    obj = ast_odbc_request_obj(odbc_database, 0);
03662    if (obj) {
03663       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03664       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03665       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03666       if (!stmt)
03667          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03668       else
03669          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03670       ast_odbc_release_obj(obj);
03671    } else
03672       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03673    return;  
03674 }
03675 
03676 /*!
03677  * \brief Copies a voicemail from one mailbox to another.
03678  * \param sdir the folder for which to look for the message to be copied.
03679  * \param smsg the index of the message to be copied.
03680  * \param ddir the destination folder to copy the message into.
03681  * \param dmsg the index to be used for the copied message.
03682  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03683  * \param dmailboxcontext The context for the destination user.
03684  *
03685  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03686  */
03687 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03688 {
03689    SQLHSTMT stmt;
03690    char sql[512];
03691    char msgnums[20];
03692    char msgnumd[20];
03693    struct odbc_obj *obj;
03694    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03695    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03696 
03697    delete_file(ddir, dmsg);
03698    obj = ast_odbc_request_obj(odbc_database, 0);
03699    if (obj) {
03700       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03701       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03702       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, flag, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?", odbc_table, odbc_table);
03703       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03704       if (!stmt)
03705          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03706       else
03707          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03708       ast_odbc_release_obj(obj);
03709    } else
03710       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03711    return;  
03712 }
03713 
03714 struct insert_data {
03715    char *sql;
03716    const char *dir;
03717    const char *msgnums;
03718    void *data;
03719    SQLLEN datalen;
03720    SQLLEN indlen;
03721    const char *context;
03722    const char *macrocontext;
03723    const char *callerid;
03724    const char *origtime;
03725    const char *duration;
03726    const char *mailboxuser;
03727    const char *mailboxcontext;
03728    const char *category;
03729    const char *flag;
03730 };
03731 
03732 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03733 {
03734    struct insert_data *data = vdata;
03735    int res;
03736    SQLHSTMT stmt;
03737 
03738    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03739    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03740       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03741       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03742       return NULL;
03743    }
03744 
03745    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03746    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03747    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03748    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03749    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03750    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03751    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03752    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03753    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03754    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03755    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03756    if (!ast_strlen_zero(data->category)) {
03757       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03758    }
03759    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03760    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03761       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03762       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03763       return NULL;
03764    }
03765 
03766    return stmt;
03767 }
03768 
03769 /*!
03770  * \brief Stores a voicemail into the database.
03771  * \param dir the folder the mailbox folder to store the message.
03772  * \param mailboxuser the user owning the mailbox folder.
03773  * \param mailboxcontext
03774  * \param msgnum the message index for the message to be stored.
03775  *
03776  * This method is used when mailboxes are stored in an ODBC back end.
03777  * The message sound file and information file is looked up on the file system. 
03778  * A SQL query is invoked to store the message into the (MySQL) database.
03779  *
03780  * \return the zero on success -1 on error.
03781  */
03782 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03783 {
03784    int res = 0;
03785    int fd = -1;
03786    void *fdm = MAP_FAILED;
03787    size_t fdlen = -1;
03788    SQLHSTMT stmt;
03789    char sql[PATH_MAX];
03790    char msgnums[20];
03791    char fn[PATH_MAX];
03792    char full_fn[PATH_MAX];
03793    char fmt[80]="";
03794    char *c;
03795    struct ast_config *cfg = NULL;
03796    struct odbc_obj *obj;
03797    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03798       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03799    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03800 
03801    delete_file(dir, msgnum);
03802    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03803       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03804       return -1;
03805    }
03806 
03807    do {
03808       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03809       c = strchr(fmt, '|');
03810       if (c)
03811          *c = '\0';
03812       if (!strcasecmp(fmt, "wav49"))
03813          strcpy(fmt, "WAV");
03814       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03815       if (msgnum > -1)
03816          make_file(fn, sizeof(fn), dir, msgnum);
03817       else
03818          ast_copy_string(fn, dir, sizeof(fn));
03819       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03820       cfg = ast_config_load(full_fn, config_flags);
03821       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03822       fd = open(full_fn, O_RDWR);
03823       if (fd < 0) {
03824          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03825          res = -1;
03826          break;
03827       }
03828       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03829          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03830             idata.context = "";
03831          }
03832          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03833             idata.macrocontext = "";
03834          }
03835          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03836             idata.callerid = "";
03837          }
03838          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03839             idata.origtime = "";
03840          }
03841          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03842             idata.duration = "";
03843          }
03844          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03845             idata.category = "";
03846          }
03847          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03848             idata.flag = "";
03849          }
03850       }
03851       fdlen = lseek(fd, 0, SEEK_END);
03852       lseek(fd, 0, SEEK_SET);
03853       printf("Length is %zd\n", fdlen);
03854       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03855       if (fdm == MAP_FAILED) {
03856          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03857          res = -1;
03858          break;
03859       } 
03860       idata.data = fdm;
03861       idata.datalen = idata.indlen = fdlen;
03862 
03863       if (!ast_strlen_zero(idata.category)) 
03864          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03865       else
03866          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03867 
03868       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03869          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03870       } else {
03871          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03872          res = -1;
03873       }
03874    } while (0);
03875    if (obj) {
03876       ast_odbc_release_obj(obj);
03877    }
03878    if (cfg)
03879       ast_config_destroy(cfg);
03880    if (fdm != MAP_FAILED)
03881       munmap(fdm, fdlen);
03882    if (fd > -1)
03883       close(fd);
03884    return res;
03885 }
03886 
03887 /*!
03888  * \brief Renames a message in a mailbox folder.
03889  * \param sdir The folder of the message to be renamed.
03890  * \param smsg The index of the message to be renamed.
03891  * \param mailboxuser The user to become the owner of the message after it is renamed. Usually this will be the same as the original owner.
03892  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03893  * \param ddir The destination folder for the message to be renamed into
03894  * \param dmsg The destination message for the message to be renamed.
03895  *
03896  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03897  * The is usually used to resequence the messages in the mailbox, such as to delete messag index 0, it would be called successively to slide all the other messages down one index.
03898  * But in theory, because the SQL query performs an update on (dir, msgnum, mailboxuser, mailboxcontext) in the database, it should be possible to have the message relocated to another mailbox or context as well.
03899  */
03900 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03901 {
03902    SQLHSTMT stmt;
03903    char sql[PATH_MAX];
03904    char msgnums[20];
03905    char msgnumd[20];
03906    struct odbc_obj *obj;
03907    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03908    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03909 
03910    delete_file(ddir, dmsg);
03911    obj = ast_odbc_request_obj(odbc_database, 0);
03912    if (obj) {
03913       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03914       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03915       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03916       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03917       if (!stmt)
03918          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03919       else
03920          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03921       ast_odbc_release_obj(obj);
03922    } else
03923       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03924    return;  
03925 }
03926 
03927 /*!
03928  * \brief Removes a voicemail message file.
03929  * \param dir the path to the message file.
03930  * \param msgnum the unique number for the message within the mailbox.
03931  *
03932  * Removes the message content file and the information file.
03933  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03934  * Typical use is to clean up after a RETRIEVE operation. 
03935  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03936  * \return zero on success, -1 on error.
03937  */
03938 static int remove_file(char *dir, int msgnum)
03939 {
03940    char fn[PATH_MAX];
03941    char full_fn[PATH_MAX];
03942    char msgnums[80];
03943    
03944    if (msgnum > -1) {
03945       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03946       make_file(fn, sizeof(fn), dir, msgnum);
03947    } else
03948       ast_copy_string(fn, dir, sizeof(fn));
03949    ast_filedelete(fn, NULL);  
03950    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03951    unlink(full_fn);
03952    return 0;
03953 }
03954 #else
03955 #ifndef IMAP_STORAGE
03956 /*!
03957  * \brief Find all .txt files - even if they are not in sequence from 0000.
03958  * \param vmu
03959  * \param dir
03960  *
03961  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03962  *
03963  * \return the count of messages, zero or more.
03964  */
03965 static int count_messages(struct ast_vm_user *vmu, char *dir)
03966 {
03967 
03968    int vmcount = 0;
03969    DIR *vmdir = NULL;
03970    struct dirent *vment = NULL;
03971 
03972    if (vm_lock_path(dir))
03973       return ERROR_LOCK_PATH;
03974 
03975    if ((vmdir = opendir(dir))) {
03976       while ((vment = readdir(vmdir))) {
03977          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03978             vmcount++;
03979          }
03980       }
03981       closedir(vmdir);
03982    }
03983    ast_unlock_path(dir);
03984    
03985    return vmcount;
03986 }
03987 
03988 /*!
03989  * \brief Renames a message in a mailbox folder.
03990  * \param sfn The path to the mailbox information and data file to be renamed.
03991  * \param dfn The path for where the message data and information files will be renamed to.
03992  *
03993  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03994  */
03995 static void rename_file(char *sfn, char *dfn)
03996 {
03997    char stxt[PATH_MAX];
03998    char dtxt[PATH_MAX];
03999    ast_filerename(sfn, dfn, NULL);
04000    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04001    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04002    if (ast_check_realtime("voicemail_data")) {
04003       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04004    }
04005    rename(stxt, dtxt);
04006 }
04007 
04008 /*! 
04009  * \brief Determines the highest message number in use for a given user and mailbox folder.
04010  * \param vmu 
04011  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04012  *
04013  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04014  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04015  *
04016  * \note Should always be called with a lock already set on dir.
04017  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04018  */
04019 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04020 {
04021    int x;
04022    unsigned char map[MAXMSGLIMIT] = "";
04023    DIR *msgdir;
04024    struct dirent *msgdirent;
04025    int msgdirint;
04026    char extension[4];
04027    int stopcount = 0;
04028 
04029    /* Reading the entire directory into a file map scales better than
04030     * doing a stat repeatedly on a predicted sequence.  I suspect this
04031     * is partially due to stat(2) internally doing a readdir(2) itself to
04032     * find each file. */
04033    if (!(msgdir = opendir(dir))) {
04034       return -1;
04035    }
04036 
04037    while ((msgdirent = readdir(msgdir))) {
04038       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04039          map[msgdirint] = 1;
04040          stopcount++;
04041          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04042       }
04043    }
04044    closedir(msgdir);
04045 
04046    for (x = 0; x < vmu->maxmsg; x++) {
04047       if (map[x] == 1) {
04048          stopcount--;
04049       } else if (map[x] == 0 && !stopcount) {
04050          break;
04051       }
04052    }
04053 
04054    return x - 1;
04055 }
04056 
04057 #endif /* #ifndef IMAP_STORAGE */
04058 #endif /* #else of #ifdef ODBC_STORAGE */
04059 #ifndef IMAP_STORAGE
04060 /*!
04061  * \brief Utility function to copy a file.
04062  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04063  * \param outfile The path for which to copy the file to. The directory permissions must allow the creation (or truncation) of the file, and allow for opening the file in write only mode.
04064  *
04065  * When the compiler option HARDLINK_WHEN_POSSIBLE is set, the copy operation will attempt to use the hard link facility instead of copy the file (to save disk space). If the link operation fails, it falls back to the copy operation.
04066  * The copy operation copies up to 4096 bytes at once.
04067  *
04068  * \return zero on success, -1 on error.
04069  */
04070 static int copy(char *infile, char *outfile)
04071 {
04072    int ifd;
04073    int ofd;
04074    int res;
04075    int len;
04076    char buf[4096];
04077 
04078 #ifdef HARDLINK_WHEN_POSSIBLE
04079    /* Hard link if possible; saves disk space & is faster */
04080    if (link(infile, outfile)) {
04081 #endif
04082       if ((ifd = open(infile, O_RDONLY)) < 0) {
04083          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04084          return -1;
04085       }
04086       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04087          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04088          close(ifd);
04089          return -1;
04090       }
04091       do {
04092          len = read(ifd, buf, sizeof(buf));
04093          if (len < 0) {
04094             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04095             close(ifd);
04096             close(ofd);
04097             unlink(outfile);
04098          }
04099          if (len) {
04100             res = write(ofd, buf, len);
04101             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04102                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04103                close(ifd);
04104                close(ofd);
04105                unlink(outfile);
04106             }
04107          }
04108       } while (len);
04109       close(ifd);
04110       close(ofd);
04111       return 0;
04112 #ifdef HARDLINK_WHEN_POSSIBLE
04113    } else {
04114       /* Hard link succeeded */
04115       return 0;
04116    }
04117 #endif
04118 }
04119 
04120 /*!
04121  * \brief Copies a voicemail information (envelope) file.
04122  * \param frompath
04123  * \param topath 
04124  *
04125  * Every voicemail has the data (.wav) file, and the information file.
04126  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04127  * This is used by the COPY macro when not using IMAP storage.
04128  */
04129 static void copy_plain_file(char *frompath, char *topath)
04130 {
04131    char frompath2[PATH_MAX], topath2[PATH_MAX];
04132    struct ast_variable *tmp,*var = NULL;
04133    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04134    ast_filecopy(frompath, topath, NULL);
04135    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04136    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04137    if (ast_check_realtime("voicemail_data")) {
04138       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04139       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04140       for (tmp = var; tmp; tmp = tmp->next) {
04141          if (!strcasecmp(tmp->name, "origmailbox")) {
04142             origmailbox = tmp->value;
04143          } else if (!strcasecmp(tmp->name, "context")) {
04144             context = tmp->value;
04145          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04146             macrocontext = tmp->value;
04147          } else if (!strcasecmp(tmp->name, "exten")) {
04148             exten = tmp->value;
04149          } else if (!strcasecmp(tmp->name, "priority")) {
04150             priority = tmp->value;
04151          } else if (!strcasecmp(tmp->name, "callerchan")) {
04152             callerchan = tmp->value;
04153          } else if (!strcasecmp(tmp->name, "callerid")) {
04154             callerid = tmp->value;
04155          } else if (!strcasecmp(tmp->name, "origdate")) {
04156             origdate = tmp->value;
04157          } else if (!strcasecmp(tmp->name, "origtime")) {
04158             origtime = tmp->value;
04159          } else if (!strcasecmp(tmp->name, "category")) {
04160             category = tmp->value;
04161          } else if (!strcasecmp(tmp->name, "duration")) {
04162             duration = tmp->value;
04163          }
04164       }
04165       ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, SENTINEL);
04166    }
04167    copy(frompath2, topath2);
04168    ast_variables_destroy(var);
04169 }
04170 #endif
04171 
04172 /*! 
04173  * \brief Removes the voicemail sound and information file.
04174  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04175  *
04176  * This is used by the DELETE macro when voicemails are stored on the file system.
04177  *
04178  * \return zero on success, -1 on error.
04179  */
04180 static int vm_delete(char *file)
04181 {
04182    char *txt;
04183    int txtsize = 0;
04184 
04185    txtsize = (strlen(file) + 5)*sizeof(char);
04186    txt = alloca(txtsize);
04187    /* Sprintf here would safe because we alloca'd exactly the right length,
04188     * but trying to eliminate all sprintf's anyhow
04189     */
04190    if (ast_check_realtime("voicemail_data")) {
04191       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04192    }
04193    snprintf(txt, txtsize, "%s.txt", file);
04194    unlink(txt);
04195    return ast_filedelete(file, NULL);
04196 }
04197 
04198 /*!
04199  * \brief utility used by inchar(), for base_encode()
04200  */
04201 static int inbuf(struct baseio *bio, FILE *fi)
04202 {
04203    int l;
04204 
04205    if (bio->ateof)
04206       return 0;
04207 
04208    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04209       if (ferror(fi))
04210          return -1;
04211 
04212       bio->ateof = 1;
04213       return 0;
04214    }
04215 
04216    bio->iolen = l;
04217    bio->iocp = 0;
04218 
04219    return 1;
04220 }
04221 
04222 /*!
04223  * \brief utility used by base_encode()
04224  */
04225 static int inchar(struct baseio *bio, FILE *fi)
04226 {
04227    if (bio->iocp>=bio->iolen) {
04228       if (!inbuf(bio, fi))
04229          return EOF;
04230    }
04231 
04232    return bio->iobuf[bio->iocp++];
04233 }
04234 
04235 /*!
04236  * \brief utility used by base_encode()
04237  */
04238 static int ochar(struct baseio *bio, int c, FILE *so)
04239 {
04240    if (bio->linelength >= BASELINELEN) {
04241       if (fputs(ENDL, so) == EOF) {
04242          return -1;
04243       }
04244 
04245       bio->linelength = 0;
04246    }
04247 
04248    if (putc(((unsigned char) c), so) == EOF) {
04249       return -1;
04250    }
04251 
04252    bio->linelength++;
04253 
04254    return 1;
04255 }
04256 
04257 /*!
04258  * \brief Performs a base 64 encode algorithm on the contents of a File
04259  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04260  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04261  *
04262  * TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ?
04263  *
04264  * \return zero on success, -1 on error.
04265  */
04266 static int base_encode(char *filename, FILE *so)
04267 {
04268    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04269       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04270       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04271       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04272    int i, hiteof = 0;
04273    FILE *fi;
04274    struct baseio bio;
04275 
04276    memset(&bio, 0, sizeof(bio));
04277    bio.iocp = BASEMAXINLINE;
04278 
04279    if (!(fi = fopen(filename, "rb"))) {
04280       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04281       return -1;
04282    }
04283 
04284    while (!hiteof){
04285       unsigned char igroup[3], ogroup[4];
04286       int c, n;
04287 
04288       memset(igroup, 0, sizeof(igroup));
04289 
04290       for (n = 0; n < 3; n++) {
04291          if ((c = inchar(&bio, fi)) == EOF) {
04292             hiteof = 1;
04293             break;
04294          }
04295 
04296          igroup[n] = (unsigned char) c;
04297       }
04298 
04299       if (n > 0) {
04300          ogroup[0]= dtable[igroup[0] >> 2];
04301          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04302          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04303          ogroup[3]= dtable[igroup[2] & 0x3F];
04304 
04305          if (n < 3) {
04306             ogroup[3] = '=';
04307 
04308             if (n < 2)
04309                ogroup[2] = '=';
04310          }
04311 
04312          for (i = 0; i < 4; i++)
04313             ochar(&bio, ogroup[i], so);
04314       }
04315    }
04316 
04317    fclose(fi);
04318    
04319    if (fputs(ENDL, so) == EOF) {
04320       return 0;
04321    }
04322 
04323    return 1;
04324 }
04325 
04326 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, const char *category, const char *flag)
04327 {
04328    char callerid[256];
04329    char num[12];
04330    char fromdir[256], fromfile[256];
04331    struct ast_config *msg_cfg;
04332    const char *origcallerid, *origtime;
04333    char origcidname[80], origcidnum[80], origdate[80];
04334    int inttime;
04335    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04336 
04337    /* Prepare variables for substitution in email body and subject */
04338    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04339    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04340    snprintf(num, sizeof(num), "%d", msgnum);
04341    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04342    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04343    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04344    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04345       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04346    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04347    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04348    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04349    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04350    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04351 
04352    /* Retrieve info from VM attribute file */
04353    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04354    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04355    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04356       strcat(fromfile, ".txt");
04357    }
04358    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04359       if (option_debug > 0) {
04360          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04361       }
04362       return;
04363    }
04364 
04365    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04366       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04367       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04368       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04369       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04370    }
04371 
04372    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04373       struct timeval tv = { inttime, };
04374       struct ast_tm tm;
04375       ast_localtime(&tv, &tm, NULL);
04376       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04377       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04378    }
04379    ast_config_destroy(msg_cfg);
04380 }
04381 
04382 /*!
04383  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04384  * \param from The string to work with.
04385  * \param buf The buffer into which to write the modified quoted string.
04386  * \param maxlen Always zero, but see \see ast_str
04387  * 
04388  * \return The destination string with quotes wrapped on it (the to field).
04389  */
04390 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04391 {
04392    const char *ptr;
04393 
04394    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04395    ast_str_set(buf, maxlen, "\"");
04396    for (ptr = from; *ptr; ptr++) {
04397       if (*ptr == '"' || *ptr == '\\') {
04398          ast_str_append(buf, maxlen, "\\%c", *ptr);
04399       } else {
04400          ast_str_append(buf, maxlen, "%c", *ptr);
04401       }
04402    }
04403    ast_str_append(buf, maxlen, "\"");
04404 
04405    return ast_str_buffer(*buf);
04406 }
04407 
04408 /*! \brief
04409  * fill in *tm for current time according to the proper timezone, if any.
04410  * \return tm so it can be used as a function argument.
04411  */
04412 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04413 {
04414    const struct vm_zone *z = NULL;
04415    struct timeval t = ast_tvnow();
04416 
04417    /* Does this user have a timezone specified? */
04418    if (!ast_strlen_zero(vmu->zonetag)) {
04419       /* Find the zone in the list */
04420       AST_LIST_LOCK(&zones);
04421       AST_LIST_TRAVERSE(&zones, z, list) {
04422          if (!strcmp(z->name, vmu->zonetag))
04423             break;
04424       }
04425       AST_LIST_UNLOCK(&zones);
04426    }
04427    ast_localtime(&t, tm, z ? z->timezone : NULL);
04428    return tm;
04429 }
04430 
04431 /*!\brief Check if the string would need encoding within the MIME standard, to
04432  * avoid confusing certain mail software that expects messages to be 7-bit
04433  * clean.
04434  */
04435 static int check_mime(const char *str)
04436 {
04437    for (; *str; str++) {
04438       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04439          return 1;
04440       }
04441    }
04442    return 0;
04443 }
04444 
04445 /*!\brief Encode a string according to the MIME rules for encoding strings
04446  * that are not 7-bit clean or contain control characters.
04447  *
04448  * Additionally, if the encoded string would exceed the MIME limit of 76
04449  * characters per line, then the encoding will be broken up into multiple
04450  * sections, separated by a space character, in order to facilitate
04451  * breaking up the associated header across multiple lines.
04452  *
04453  * \param end An expandable buffer for holding the result
04454  * \param maxlen Always zero, but see \see ast_str
04455  * \param start A string to be encoded
04456  * \param preamble The length of the first line already used for this string,
04457  * to ensure that each line maintains a maximum length of 76 chars.
04458  * \param postamble the length of any additional characters appended to the
04459  * line, used to ensure proper field wrapping.
04460  * \retval The encoded string.
04461  */
04462 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04463 {
04464    struct ast_str *tmp = ast_str_alloca(80);
04465    int first_section = 1;
04466 
04467    ast_str_reset(*end);
04468    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04469    for (; *start; start++) {
04470       int need_encoding = 0;
04471       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04472          need_encoding = 1;
04473       }
04474       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04475          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04476          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04477          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04478          /* Start new line */
04479          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04480          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04481          first_section = 0;
04482       }
04483       if (need_encoding && *start == ' ') {
04484          ast_str_append(&tmp, -1, "_");
04485       } else if (need_encoding) {
04486          ast_str_append(&tmp, -1, "=%hhX", *start);
04487       } else {
04488          ast_str_append(&tmp, -1, "%c", *start);
04489       }
04490    }
04491    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04492    return ast_str_buffer(*end);
04493 }
04494 
04495 /*!
04496  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04497  * \param p The output file to generate the email contents into.
04498  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04499  * \param vmu The voicemail user who is sending the voicemail.
04500  * \param msgnum The message index in the mailbox folder.
04501  * \param context 
04502  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04503  * \param fromfolder
04504  * \param cidnum The caller ID number.
04505  * \param cidname The caller ID name.
04506  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04507  * \param attach2 
04508  * \param format The message sound file format. i.e. .wav
04509  * \param duration The time of the message content, in seconds.
04510  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04511  * \param chan
04512  * \param category
04513  * \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
04514  * \param flag
04515  *
04516  * The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email.  That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
04517  */
04518 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)
04519 {
04520    char date[256];
04521    char host[MAXHOSTNAMELEN] = "";
04522    char who[256];
04523    char bound[256];
04524    char dur[256];
04525    struct ast_tm tm;
04526    char enc_cidnum[256] = "", enc_cidname[256] = "";
04527    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04528    char *greeting_attachment; 
04529    char filename[256];
04530 
04531    if (!str1 || !str2) {
04532       ast_free(str1);
04533       ast_free(str2);
04534       return;
04535    }
04536 
04537    if (cidnum) {
04538       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04539    }
04540    if (cidname) {
04541       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04542    }
04543    gethostname(host, sizeof(host) - 1);
04544 
04545    if (strchr(srcemail, '@')) {
04546       ast_copy_string(who, srcemail, sizeof(who));
04547    } else {
04548       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04549    }
04550 
04551    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04552    if (greeting_attachment) {
04553       *greeting_attachment++ = '\0';
04554    }
04555 
04556    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04557    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04558    fprintf(p, "Date: %s" ENDL, date);
04559 
04560    /* Set date format for voicemail mail */
04561    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04562 
04563    if (!ast_strlen_zero(fromstring)) {
04564       struct ast_channel *ast;
04565       if ((ast = ast_dummy_channel_alloc())) {
04566          char *ptr;
04567          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04568          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04569 
04570          if (check_mime(ast_str_buffer(str1))) {
04571             int first_line = 1;
04572             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04573             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04574                *ptr = '\0';
04575                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04576                first_line = 0;
04577                /* Substring is smaller, so this will never grow */
04578                ast_str_set(&str2, 0, "%s", ptr + 1);
04579             }
04580             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04581          } else {
04582             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04583          }
04584          ast = ast_channel_release(ast);
04585       } else {
04586          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04587       }
04588    } else {
04589       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04590    }
04591 
04592    if (check_mime(vmu->fullname)) {
04593       int first_line = 1;
04594       char *ptr;
04595       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04596       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04597          *ptr = '\0';
04598          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04599          first_line = 0;
04600          /* Substring is smaller, so this will never grow */
04601          ast_str_set(&str2, 0, "%s", ptr + 1);
04602       }
04603       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04604    } else {
04605       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04606    }
04607 
04608    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04609       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04610       struct ast_channel *ast;
04611       if ((ast = ast_dummy_channel_alloc())) {
04612          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04613          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04614          if (check_mime(ast_str_buffer(str1))) {
04615             int first_line = 1;
04616             char *ptr;
04617             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04618             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04619                *ptr = '\0';
04620                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04621                first_line = 0;
04622                /* Substring is smaller, so this will never grow */
04623                ast_str_set(&str2, 0, "%s", ptr + 1);
04624             }
04625             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04626          } else {
04627             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04628          }
04629          ast = ast_channel_release(ast);
04630       } else {
04631          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04632       }
04633    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04634       if (ast_strlen_zero(flag)) {
04635          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04636       } else {
04637          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04638       }
04639    } else {
04640       if (ast_strlen_zero(flag)) {
04641          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04642       } else {
04643          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04644       }
04645    }
04646 
04647    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04648       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04649    if (imap) {
04650       /* additional information needed for IMAP searching */
04651       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04652       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04653       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04654       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04655 #ifdef IMAP_STORAGE
04656       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04657 #else
04658       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04659 #endif
04660       /* flag added for Urgent */
04661       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04662       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04663       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04664       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04665       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04666       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04667       if (!ast_strlen_zero(category)) {
04668          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04669       } else {
04670          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04671       }
04672       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04673       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04674       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04675    }
04676    if (!ast_strlen_zero(cidnum)) {
04677       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04678    }
04679    if (!ast_strlen_zero(cidname)) {
04680       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04681    }
04682    fprintf(p, "MIME-Version: 1.0" ENDL);
04683    if (attach_user_voicemail) {
04684       /* Something unique. */
04685       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04686          (int) getpid(), (unsigned int) ast_random());
04687 
04688       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04689       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04690       fprintf(p, "--%s" ENDL, bound);
04691    }
04692    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04693    if (emailbody || vmu->emailbody) {
04694       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04695       struct ast_channel *ast;
04696       if ((ast = ast_dummy_channel_alloc())) {
04697          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04698          ast_str_substitute_variables(&str1, 0, ast, e_body);
04699 #ifdef IMAP_STORAGE
04700             {
04701                /* Convert body to native line terminators for IMAP backend */
04702                char *line = ast_str_buffer(str1), *next;
04703                do {
04704                   /* Terminate line before outputting it to the file */
04705                   if ((next = strchr(line, '\n'))) {
04706                      *next++ = '\0';
04707                   }
04708                   fprintf(p, "%s" ENDL, line);
04709                   line = next;
04710                } while (!ast_strlen_zero(line));
04711             }
04712 #else
04713          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04714 #endif
04715          ast = ast_channel_release(ast);
04716       } else {
04717          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04718       }
04719    } else if (msgnum > -1) {
04720       if (strcmp(vmu->mailbox, mailbox)) {
04721          /* Forwarded type */
04722          struct ast_config *msg_cfg;
04723          const char *v;
04724          int inttime;
04725          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04726          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04727          /* Retrieve info from VM attribute file */
04728          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04729          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04730          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04731             strcat(fromfile, ".txt");
04732          }
04733          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04734             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04735                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04736             }
04737 
04738             /* You might be tempted to do origdate, except that a) it's in the wrong
04739              * format, and b) it's missing for IMAP recordings. */
04740             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04741                struct timeval tv = { inttime, };
04742                struct ast_tm tm;
04743                ast_localtime(&tv, &tm, NULL);
04744                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04745             }
04746             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04747                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04748                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04749                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04750                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04751                date, origcallerid, origdate);
04752             ast_config_destroy(msg_cfg);
04753          } else {
04754             goto plain_message;
04755          }
04756       } else {
04757 plain_message:
04758          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04759             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04760             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04761             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04762             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04763       }
04764    } else {
04765       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04766             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04767    }
04768 
04769    if (imap || attach_user_voicemail) {
04770       if (!ast_strlen_zero(attach2)) {
04771          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04772          ast_debug(5, "creating second attachment filename %s\n", filename);
04773          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04774          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04775          ast_debug(5, "creating attachment filename %s\n", filename);
04776          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04777       } else {
04778          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04779          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04780          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04781       }
04782    }
04783    ast_free(str1);
04784    ast_free(str2);
04785 }
04786 
04787 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)
04788 {
04789    char tmpdir[256], newtmp[256];
04790    char fname[256];
04791    char tmpcmd[256];
04792    int tmpfd = -1;
04793    int soxstatus = 0;
04794 
04795    /* Eww. We want formats to tell us their own MIME type */
04796    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04797 
04798    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04799       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04800       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04801       tmpfd = mkstemp(newtmp);
04802       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04803       ast_debug(3, "newtmp: %s\n", newtmp);
04804       if (tmpfd > -1) {
04805          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04806          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04807             attach = newtmp;
04808             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04809          } else {
04810             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04811                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04812             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04813          }
04814       }
04815    }
04816    fprintf(p, "--%s" ENDL, bound);
04817    if (msgnum > -1)
04818       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04819    else
04820       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04821    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04822    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04823    if (msgnum > -1)
04824       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04825    else
04826       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04827    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04828    base_encode(fname, p);
04829    if (last)
04830       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04831    if (tmpfd > -1) {
04832       if (soxstatus == 0) {
04833          unlink(fname);
04834       }
04835       close(tmpfd);
04836       unlink(newtmp);
04837    }
04838    return 0;
04839 }
04840 
04841 static int sendmail(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, const char *flag)
04842 {
04843    FILE *p = NULL;
04844    char tmp[80] = "/tmp/astmail-XXXXXX";
04845    char tmp2[256];
04846    char *stringp;
04847 
04848    if (vmu && ast_strlen_zero(vmu->email)) {
04849       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04850       return(0);
04851    }
04852 
04853    /* Mail only the first format */
04854    format = ast_strdupa(format);
04855    stringp = format;
04856    strsep(&stringp, "|");
04857 
04858    if (!strcmp(format, "wav49"))
04859       format = "WAV";
04860    ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
04861    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04862       command hangs */
04863    if ((p = vm_mkftemp(tmp)) == NULL) {
04864       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04865       return -1;
04866    } else {
04867       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04868       fclose(p);
04869       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04870       ast_safe_system(tmp2);
04871       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04872    }
04873    return 0;
04874 }
04875 
04876 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag)
04877 {
04878    char enc_cidnum[256], enc_cidname[256];
04879    char date[256];
04880    char host[MAXHOSTNAMELEN] = "";
04881    char who[256];
04882    char dur[PATH_MAX];
04883    char tmp[80] = "/tmp/astmail-XXXXXX";
04884    char tmp2[PATH_MAX];
04885    struct ast_tm tm;
04886    FILE *p;
04887    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04888 
04889    if (!str1 || !str2) {
04890       ast_free(str1);
04891       ast_free(str2);
04892       return -1;
04893    }
04894 
04895    if (cidnum) {
04896       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04897    }
04898    if (cidname) {
04899       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04900    }
04901 
04902    if ((p = vm_mkftemp(tmp)) == NULL) {
04903       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04904       ast_free(str1);
04905       ast_free(str2);
04906       return -1;
04907    }
04908    gethostname(host, sizeof(host)-1);
04909    if (strchr(srcemail, '@')) {
04910       ast_copy_string(who, srcemail, sizeof(who));
04911    } else {
04912       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04913    }
04914    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04915    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04916    fprintf(p, "Date: %s\n", date);
04917 
04918    /* Reformat for custom pager format */
04919    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04920 
04921    if (!ast_strlen_zero(pagerfromstring)) {
04922       struct ast_channel *ast;
04923       if ((ast = ast_dummy_channel_alloc())) {
04924          char *ptr;
04925          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04926          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04927 
04928          if (check_mime(ast_str_buffer(str1))) {
04929             int first_line = 1;
04930             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04931             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04932                *ptr = '\0';
04933                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04934                first_line = 0;
04935                /* Substring is smaller, so this will never grow */
04936                ast_str_set(&str2, 0, "%s", ptr + 1);
04937             }
04938             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04939          } else {
04940             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04941          }
04942          ast = ast_channel_release(ast);
04943       } else {
04944          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04945       }
04946    } else {
04947       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04948    }
04949 
04950    if (check_mime(vmu->fullname)) {
04951       int first_line = 1;
04952       char *ptr;
04953       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04954       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04955          *ptr = '\0';
04956          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04957          first_line = 0;
04958          /* Substring is smaller, so this will never grow */
04959          ast_str_set(&str2, 0, "%s", ptr + 1);
04960       }
04961       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04962    } else {
04963       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04964    }
04965 
04966    if (!ast_strlen_zero(pagersubject)) {
04967       struct ast_channel *ast;
04968       if ((ast = ast_dummy_channel_alloc())) {
04969          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04970          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04971          if (check_mime(ast_str_buffer(str1))) {
04972             int first_line = 1;
04973             char *ptr;
04974             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04975             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04976                *ptr = '\0';
04977                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04978                first_line = 0;
04979                /* Substring is smaller, so this will never grow */
04980                ast_str_set(&str2, 0, "%s", ptr + 1);
04981             }
04982             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04983          } else {
04984             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04985          }
04986          ast = ast_channel_release(ast);
04987       } else {
04988          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04989       }
04990    } else {
04991       if (ast_strlen_zero(flag)) {
04992          fprintf(p, "Subject: New VM\n\n");
04993       } else {
04994          fprintf(p, "Subject: New %s VM\n\n", flag);
04995       }
04996    }
04997 
04998    if (pagerbody) {
04999       struct ast_channel *ast;
05000       if ((ast = ast_dummy_channel_alloc())) {
05001          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05002          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05003          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05004          ast = ast_channel_release(ast);
05005       } else {
05006          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05007       }
05008    } else {
05009       fprintf(p, "New %s long %s msg in box %s\n"
05010             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05011    }
05012 
05013    fclose(p);
05014    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05015    ast_safe_system(tmp2);
05016    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05017    ast_free(str1);
05018    ast_free(str2);
05019    return 0;
05020 }
05021 
05022 /*!
05023  * \brief Gets the current date and time, as formatted string.
05024  * \param s The buffer to hold the output formatted date.
05025  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05026  * 
05027  * The date format string used is "%a %b %e %r UTC %Y".
05028  * 
05029  * \return zero on success, -1 on error.
05030  */
05031 static int get_date(char *s, int len)
05032 {
05033    struct ast_tm tm;
05034    struct timeval t = ast_tvnow();
05035    
05036    ast_localtime(&t, &tm, "UTC");
05037 
05038    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05039 }
05040 
05041 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05042 {
05043    int res;
05044    char fn[PATH_MAX];
05045    char dest[PATH_MAX];
05046 
05047    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05048 
05049    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05050       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05051       return -1;
05052    }
05053 
05054    RETRIEVE(fn, -1, ext, context);
05055    if (ast_fileexists(fn, NULL, NULL) > 0) {
05056       res = ast_stream_and_wait(chan, fn, ecodes);
05057       if (res) {
05058          DISPOSE(fn, -1);
05059          return res;
05060       }
05061    } else {
05062       /* Dispose just in case */
05063       DISPOSE(fn, -1);
05064       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05065       if (res)
05066          return res;
05067       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05068       if (res)
05069          return res;
05070    }
05071    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05072    return res;
05073 }
05074 
05075 static void free_zone(struct vm_zone *z)
05076 {
05077    ast_free(z);
05078 }
05079 
05080 #ifdef ODBC_STORAGE
05081 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05082 {
05083    int x = -1;
05084    int res;
05085    SQLHSTMT stmt = NULL;
05086    char sql[PATH_MAX];
05087    char rowdata[20];
05088    char tmp[PATH_MAX] = "";
05089    struct odbc_obj *obj = NULL;
05090    char *context;
05091    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05092 
05093    if (newmsgs)
05094       *newmsgs = 0;
05095    if (oldmsgs)
05096       *oldmsgs = 0;
05097    if (urgentmsgs)
05098       *urgentmsgs = 0;
05099 
05100    /* If no mailbox, return immediately */
05101    if (ast_strlen_zero(mailbox))
05102       return 0;
05103 
05104    ast_copy_string(tmp, mailbox, sizeof(tmp));
05105 
05106    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05107       int u, n, o;
05108       char *next, *remaining = tmp;
05109       while ((next = strsep(&remaining, " ,"))) {
05110          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05111             return -1;
05112          }
05113          if (urgentmsgs) {
05114             *urgentmsgs += u;
05115          }
05116          if (newmsgs) {
05117             *newmsgs += n;
05118          }
05119          if (oldmsgs) {
05120             *oldmsgs += o;
05121          }
05122       }
05123       return 0;
05124    }
05125 
05126    context = strchr(tmp, '@');
05127    if (context) {
05128       *context = '\0';
05129       context++;
05130    } else
05131       context = "default";
05132 
05133    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05134       do {
05135          if (newmsgs) {
05136             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05137             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05138                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05139                break;
05140             }
05141             res = SQLFetch(stmt);
05142             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05143                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05144                break;
05145             }
05146             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05147             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05148                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05149                break;
05150             }
05151             *newmsgs = atoi(rowdata);
05152             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05153          }
05154 
05155          if (oldmsgs) {
05156             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05157             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05158                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05159                break;
05160             }
05161             res = SQLFetch(stmt);
05162             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05163                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05164                break;
05165             }
05166             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05167             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05168                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05169                break;
05170             }
05171             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05172             *oldmsgs = atoi(rowdata);
05173          }
05174 
05175          if (urgentmsgs) {
05176             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05177             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05178                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05179                break;
05180             }
05181             res = SQLFetch(stmt);
05182             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05183                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05184                break;
05185             }
05186             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05187             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05188                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05189                break;
05190             }
05191             *urgentmsgs = atoi(rowdata);
05192          }
05193 
05194          x = 0;
05195       } while (0);
05196    } else {
05197       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05198    }
05199 
05200    if (stmt) {
05201       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05202    }
05203    if (obj) {
05204       ast_odbc_release_obj(obj);
05205    }
05206    return x;
05207 }
05208 
05209 /*!
05210  * \brief Gets the number of messages that exist in a mailbox folder.
05211  * \param context
05212  * \param mailbox
05213  * \param folder
05214  * 
05215  * This method is used when ODBC backend is used.
05216  * \return The number of messages in this mailbox folder (zero or more).
05217  */
05218 static int messagecount(const char *context, const char *mailbox, const char *folder)
05219 {
05220    struct odbc_obj *obj = NULL;
05221    int nummsgs = 0;
05222    int res;
05223    SQLHSTMT stmt = NULL;
05224    char sql[PATH_MAX];
05225    char rowdata[20];
05226    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05227    if (!folder)
05228       folder = "INBOX";
05229    /* If no mailbox, return immediately */
05230    if (ast_strlen_zero(mailbox))
05231       return 0;
05232 
05233    obj = ast_odbc_request_obj(odbc_database, 0);
05234    if (obj) {
05235       if (!strcmp(folder, "INBOX")) {
05236          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'", odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
05237       } else {
05238          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05239       }
05240       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05241       if (!stmt) {
05242          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05243          goto yuck;
05244       }
05245       res = SQLFetch(stmt);
05246       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05247          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05248          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05249          goto yuck;
05250       }
05251       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05252       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05253          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05254          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05255          goto yuck;
05256       }
05257       nummsgs = atoi(rowdata);
05258       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05259    } else
05260       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05261 
05262 yuck:
05263    if (obj)
05264       ast_odbc_release_obj(obj);
05265    return nummsgs;
05266 }
05267 
05268 /** 
05269  * \brief Determines if the given folder has messages.
05270  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05271  * 
05272  * This function is used when the mailbox is stored in an ODBC back end.
05273  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05274  * \return 1 if the folder has one or more messages. zero otherwise.
05275  */
05276 static int has_voicemail(const char *mailbox, const char *folder)
05277 {
05278    char tmp[256], *tmp2 = tmp, *box, *context;
05279    ast_copy_string(tmp, mailbox, sizeof(tmp));
05280    while ((context = box = strsep(&tmp2, ",&"))) {
05281       strsep(&context, "@");
05282       if (ast_strlen_zero(context))
05283          context = "default";
05284       if (messagecount(context, box, folder))
05285          return 1;
05286    }
05287    return 0;
05288 }
05289 #endif
05290 #ifndef IMAP_STORAGE
05291 /*! 
05292  * \brief Copies a message from one mailbox to another.
05293  * \param chan
05294  * \param vmu
05295  * \param imbox
05296  * \param msgnum
05297  * \param duration
05298  * \param recip
05299  * \param fmt
05300  * \param dir
05301  * \param flag
05302  *
05303  * This is only used by file storage based mailboxes.
05304  *
05305  * \return zero on success, -1 on error.
05306  */
05307 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, const char *flag)
05308 {
05309    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05310    const char *frombox = mbox(vmu, imbox);
05311    int recipmsgnum;
05312    int res = 0;
05313 
05314    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05315 
05316    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05317       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
05318    } else {
05319       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05320    }
05321    
05322    if (!dir)
05323       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05324    else
05325       ast_copy_string(fromdir, dir, sizeof(fromdir));
05326 
05327    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05328    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
05329 
05330    if (vm_lock_path(todir))
05331       return ERROR_LOCK_PATH;
05332 
05333    recipmsgnum = last_message_index(recip, todir) + 1;
05334    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05335       make_file(topath, sizeof(topath), todir, recipmsgnum);
05336 #ifndef ODBC_STORAGE
05337       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05338          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05339       } else {
05340 #endif
05341          /* If we are prepending a message for ODBC, then the message already
05342           * exists in the database, but we want to force copying from the
05343           * filesystem (since only the FS contains the prepend). */
05344          copy_plain_file(frompath, topath);
05345          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05346          vm_delete(topath);
05347 #ifndef ODBC_STORAGE
05348       }
05349 #endif
05350    } else {
05351       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05352       res = -1;
05353    }
05354    ast_unlock_path(todir);
05355    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05356       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05357       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05358       flag);
05359    
05360    return res;
05361 }
05362 #endif
05363 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05364 
05365 static int messagecount(const char *context, const char *mailbox, const char *folder)
05366 {
05367    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05368 }
05369 
05370 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05371 {
05372    DIR *dir;
05373    struct dirent *de;
05374    char fn[256];
05375    int ret = 0;
05376 
05377    /* If no mailbox, return immediately */
05378    if (ast_strlen_zero(mailbox))
05379       return 0;
05380 
05381    if (ast_strlen_zero(folder))
05382       folder = "INBOX";
05383    if (ast_strlen_zero(context))
05384       context = "default";
05385 
05386    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05387 
05388    if (!(dir = opendir(fn)))
05389       return 0;
05390 
05391    while ((de = readdir(dir))) {
05392       if (!strncasecmp(de->d_name, "msg", 3)) {
05393          if (shortcircuit) {
05394             ret = 1;
05395             break;
05396          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05397             ret++;
05398          }
05399       }
05400    }
05401 
05402    closedir(dir);
05403 
05404    return ret;
05405 }
05406 
05407 /** 
05408  * \brief Determines if the given folder has messages.
05409  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05410  * \param folder the folder to look in
05411  *
05412  * This function is used when the mailbox is stored in a filesystem back end.
05413  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05414  * \return 1 if the folder has one or more messages. zero otherwise.
05415  */
05416 static int has_voicemail(const char *mailbox, const char *folder)
05417 {
05418    char tmp[256], *tmp2 = tmp, *box, *context;
05419    ast_copy_string(tmp, mailbox, sizeof(tmp));
05420    if (ast_strlen_zero(folder)) {
05421       folder = "INBOX";
05422    }
05423    while ((box = strsep(&tmp2, ",&"))) {
05424       if ((context = strchr(box, '@')))
05425          *context++ = '\0';
05426       else
05427          context = "default";
05428       if (__has_voicemail(context, box, folder, 1))
05429          return 1;
05430       /* If we are checking INBOX, we should check Urgent as well */
05431       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05432          return 1;
05433       }
05434    }
05435    return 0;
05436 }
05437 
05438 
05439 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05440 {
05441    char tmp[256];
05442    char *context;
05443 
05444    /* If no mailbox, return immediately */
05445    if (ast_strlen_zero(mailbox))
05446       return 0;
05447 
05448    if (newmsgs)
05449       *newmsgs = 0;
05450    if (oldmsgs)
05451       *oldmsgs = 0;
05452    if (urgentmsgs)
05453       *urgentmsgs = 0;
05454 
05455    if (strchr(mailbox, ',')) {
05456       int tmpnew, tmpold, tmpurgent;
05457       char *mb, *cur;
05458 
05459       ast_copy_string(tmp, mailbox, sizeof(tmp));
05460       mb = tmp;
05461       while ((cur = strsep(&mb, ", "))) {
05462          if (!ast_strlen_zero(cur)) {
05463             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05464                return -1;
05465             else {
05466                if (newmsgs)
05467                   *newmsgs += tmpnew; 
05468                if (oldmsgs)
05469                   *oldmsgs += tmpold;
05470                if (urgentmsgs)
05471                   *urgentmsgs += tmpurgent;
05472             }
05473          }
05474       }
05475       return 0;
05476    }
05477 
05478    ast_copy_string(tmp, mailbox, sizeof(tmp));
05479    
05480    if ((context = strchr(tmp, '@')))
05481       *context++ = '\0';
05482    else
05483       context = "default";
05484 
05485    if (newmsgs)
05486       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05487    if (oldmsgs)
05488       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05489    if (urgentmsgs)
05490       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05491 
05492    return 0;
05493 }
05494 
05495 #endif
05496 
05497 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05498 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05499 {
05500    int urgentmsgs = 0;
05501    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05502    if (newmsgs) {
05503       *newmsgs += urgentmsgs;
05504    }
05505    return res;
05506 }
05507 
05508 static void run_externnotify(char *context, char *extension, const char *flag)
05509 {
05510    char arguments[255];
05511    char ext_context[256] = "";
05512    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05513    struct ast_smdi_mwi_message *mwi_msg;
05514 
05515    if (!ast_strlen_zero(context))
05516       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05517    else
05518       ast_copy_string(ext_context, extension, sizeof(ext_context));
05519 
05520    if (smdi_iface) {
05521       if (ast_app_has_voicemail(ext_context, NULL)) 
05522          ast_smdi_mwi_set(smdi_iface, extension);
05523       else
05524          ast_smdi_mwi_unset(smdi_iface, extension);
05525 
05526       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05527          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05528          if (!strncmp(mwi_msg->cause, "INV", 3))
05529             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05530          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05531             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05532          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05533          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05534       } else {
05535          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05536       }
05537    }
05538 
05539    if (!ast_strlen_zero(externnotify)) {
05540       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05541          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05542       } else {
05543          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05544          ast_debug(1, "Executing %s\n", arguments);
05545          ast_safe_system(arguments);
05546       }
05547    }
05548 }
05549 
05550 /*!
05551  * \brief Variables used for saving a voicemail.
05552  *
05553  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05554  */
05555 struct leave_vm_options {
05556    unsigned int flags;
05557    signed char record_gain;
05558    char *exitcontext;
05559 };
05560 
05561 /*!
05562  * \brief Prompts the user and records a voicemail to a mailbox.
05563  * \param chan
05564  * \param ext
05565  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05566  * 
05567  * 
05568  * 
05569  * \return zero on success, -1 on error.
05570  */
05571 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05572 {
05573 #ifdef IMAP_STORAGE
05574    int newmsgs, oldmsgs;
05575 #else
05576    char urgdir[PATH_MAX];
05577 #endif
05578    char txtfile[PATH_MAX];
05579    char tmptxtfile[PATH_MAX];
05580    struct vm_state *vms = NULL;
05581    char callerid[256];
05582    FILE *txt;
05583    char date[256];
05584    int txtdes;
05585    int res = 0;
05586    int msgnum;
05587    int duration = 0;
05588    int ausemacro = 0;
05589    int ousemacro = 0;
05590    int ouseexten = 0;
05591    char tmpdur[16];
05592    char priority[16];
05593    char origtime[16];
05594    char dir[PATH_MAX];
05595    char tmpdir[PATH_MAX];
05596    char fn[PATH_MAX];
05597    char prefile[PATH_MAX] = "";
05598    char tempfile[PATH_MAX] = "";
05599    char ext_context[256] = "";
05600    char fmt[80];
05601    char *context;
05602    char ecodes[17] = "#";
05603    struct ast_str *tmp = ast_str_create(16);
05604    char *tmpptr;
05605    struct ast_vm_user *vmu;
05606    struct ast_vm_user svm;
05607    const char *category = NULL;
05608    const char *code;
05609    const char *alldtmf = "0123456789ABCD*#";
05610    char flag[80];
05611 
05612    if (!tmp) {
05613       return -1;
05614    }
05615 
05616    ast_str_set(&tmp, 0, "%s", ext);
05617    ext = ast_str_buffer(tmp);
05618    if ((context = strchr(ext, '@'))) {
05619       *context++ = '\0';
05620       tmpptr = strchr(context, '&');
05621    } else {
05622       tmpptr = strchr(ext, '&');
05623    }
05624 
05625    if (tmpptr)
05626       *tmpptr++ = '\0';
05627 
05628    ast_channel_lock(chan);
05629    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05630       category = ast_strdupa(category);
05631    }
05632    ast_channel_unlock(chan);
05633 
05634    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05635       ast_copy_string(flag, "Urgent", sizeof(flag));
05636    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05637       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05638    } else {
05639       flag[0] = '\0';
05640    }
05641 
05642    ast_debug(3, "Before find_user\n");
05643    if (!(vmu = find_user(&svm, context, ext))) {
05644       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05645       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05646       ast_free(tmp);
05647       return res;
05648    }
05649    /* Setup pre-file if appropriate */
05650    if (strcmp(vmu->context, "default"))
05651       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05652    else
05653       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05654 
05655    /* Set the path to the prefile. Will be one of 
05656       VM_SPOOL_DIRcontext/ext/busy
05657       VM_SPOOL_DIRcontext/ext/unavail
05658       Depending on the flag set in options.
05659    */
05660    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05661       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05662    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05663       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05664    }
05665    /* Set the path to the tmpfile as
05666       VM_SPOOL_DIR/context/ext/temp
05667       and attempt to create the folder structure.
05668    */
05669    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05670    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05671       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05672       ast_free(tmp);
05673       return -1;
05674    }
05675    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05676    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05677       ast_copy_string(prefile, tempfile, sizeof(prefile));
05678 
05679    DISPOSE(tempfile, -1);
05680    /* It's easier just to try to make it than to check for its existence */
05681 #ifndef IMAP_STORAGE
05682    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05683 #else
05684    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05685    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05686       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05687    }
05688 #endif
05689 
05690    /* Check current or macro-calling context for special extensions */
05691    if (ast_test_flag(vmu, VM_OPERATOR)) {
05692       if (!ast_strlen_zero(vmu->exit)) {
05693          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05694             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05695             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05696             ouseexten = 1;
05697          }
05698       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05699          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05700          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05701          ouseexten = 1;
05702       } else if (!ast_strlen_zero(chan->macrocontext)
05703          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05704             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05705          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05706          ousemacro = 1;
05707       }
05708    }
05709 
05710    if (!ast_strlen_zero(vmu->exit)) {
05711       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05712          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05713          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05714       }
05715    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05716       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05717       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05718    } else if (!ast_strlen_zero(chan->macrocontext)
05719       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05720          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05721       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05722       ausemacro = 1;
05723    }
05724 
05725    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05726       for (code = alldtmf; *code; code++) {
05727          char e[2] = "";
05728          e[0] = *code;
05729          if (strchr(ecodes, e[0]) == NULL
05730             && ast_canmatch_extension(chan, chan->context, e, 1,
05731                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05732             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05733          }
05734       }
05735    }
05736 
05737    /* Play the beginning intro if desired */
05738    if (!ast_strlen_zero(prefile)) {
05739 #ifdef ODBC_STORAGE
05740       int success = 
05741 #endif
05742          RETRIEVE(prefile, -1, ext, context);
05743       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05744          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05745             res = ast_waitstream(chan, ecodes);
05746 #ifdef ODBC_STORAGE
05747          if (success == -1) {
05748             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05749             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05750             store_file(prefile, vmu->mailbox, vmu->context, -1);
05751          }
05752 #endif
05753       } else {
05754          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05755          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05756       }
05757       DISPOSE(prefile, -1);
05758       if (res < 0) {
05759          ast_debug(1, "Hang up during prefile playback\n");
05760          free_user(vmu);
05761          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05762          ast_free(tmp);
05763          return -1;
05764       }
05765    }
05766    if (res == '#') {
05767       /* On a '#' we skip the instructions */
05768       ast_set_flag(options, OPT_SILENT);
05769       res = 0;
05770    }
05771    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05772    if (vmu->maxmsg == 0) {
05773       if (option_debug > 2)
05774          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05775       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05776       goto leave_vm_out;
05777    }
05778    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05779       res = ast_stream_and_wait(chan, INTRO, ecodes);
05780       if (res == '#') {
05781          ast_set_flag(options, OPT_SILENT);
05782          res = 0;
05783       }
05784    }
05785    if (res > 0)
05786       ast_stopstream(chan);
05787    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05788     other than the operator -- an automated attendant or mailbox login for example */
05789    if (res == '*') {
05790       chan->exten[0] = 'a';
05791       chan->exten[1] = '\0';
05792       if (!ast_strlen_zero(vmu->exit)) {
05793          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05794       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05795          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05796       }
05797       chan->priority = 0;
05798       free_user(vmu);
05799       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05800       ast_free(tmp);
05801       return 0;
05802    }
05803 
05804    /* Check for a '0' here */
05805    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05806    transfer:
05807       if (ouseexten || ousemacro) {
05808          chan->exten[0] = 'o';
05809          chan->exten[1] = '\0';
05810          if (!ast_strlen_zero(vmu->exit)) {
05811             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05812          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05813             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05814          }
05815          ast_play_and_wait(chan, "transfer");
05816          chan->priority = 0;
05817          free_user(vmu);
05818          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05819       }
05820       ast_free(tmp);
05821       return OPERATOR_EXIT;
05822    }
05823 
05824    /* Allow all other digits to exit Voicemail and return to the dialplan */
05825    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05826       if (!ast_strlen_zero(options->exitcontext))
05827          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05828       free_user(vmu);
05829       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05830       ast_free(tmp);
05831       return res;
05832    }
05833 
05834    if (res < 0) {
05835       free_user(vmu);
05836       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05837       ast_free(tmp);
05838       return -1;
05839    }
05840    /* The meat of recording the message...  All the announcements and beeps have been played*/
05841    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05842    if (!ast_strlen_zero(fmt)) {
05843       msgnum = 0;
05844 
05845 #ifdef IMAP_STORAGE
05846       /* Is ext a mailbox? */
05847       /* must open stream for this user to get info! */
05848       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05849       if (res < 0) {
05850          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05851          ast_free(tmp);
05852          return -1;
05853       }
05854       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05855       /* It is possible under certain circumstances that inboxcount did not
05856        * create a vm_state when it was needed. This is a catchall which will
05857        * rarely be used.
05858        */
05859          if (!(vms = create_vm_state_from_user(vmu))) {
05860             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05861             ast_free(tmp);
05862             return -1;
05863          }
05864       }
05865       vms->newmessages++;
05866       
05867       /* here is a big difference! We add one to it later */
05868       msgnum = newmsgs + oldmsgs;
05869       ast_debug(3, "Messagecount set to %d\n", msgnum);
05870       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05871       /* set variable for compatibility */
05872       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05873 
05874       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05875          goto leave_vm_out;
05876       }
05877 #else
05878       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05879          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05880          if (!res)
05881             res = ast_waitstream(chan, "");
05882          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05883          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05884          inprocess_count(vmu->mailbox, vmu->context, -1);
05885          goto leave_vm_out;
05886       }
05887 
05888 #endif
05889       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05890       txtdes = mkstemp(tmptxtfile);
05891       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05892       if (txtdes < 0) {
05893          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05894          if (!res)
05895             res = ast_waitstream(chan, "");
05896          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05897          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05898          inprocess_count(vmu->mailbox, vmu->context, -1);
05899          goto leave_vm_out;
05900       }
05901 
05902       /* Now play the beep once we have the message number for our next message. */
05903       if (res >= 0) {
05904          /* Unless we're *really* silent, try to send the beep */
05905          res = ast_stream_and_wait(chan, "beep", "");
05906       }
05907             
05908       /* Store information in real-time storage */
05909       if (ast_check_realtime("voicemail_data")) {
05910          snprintf(priority, sizeof(priority), "%d", chan->priority);
05911          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05912          get_date(date, sizeof(date));
05913          ast_callerid_merge(callerid, sizeof(callerid),
05914             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05915             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05916             "Unknown");
05917          ast_store_realtime("voicemail_data",
05918             "origmailbox", ext,
05919             "context", chan->context,
05920             "macrocontext", chan->macrocontext,
05921             "exten", chan->exten,
05922             "priority", priority,
05923             "callerchan", chan->name,
05924             "callerid", callerid,
05925             "origdate", date,
05926             "origtime", origtime,
05927             "category", S_OR(category, ""),
05928             "filename", tmptxtfile,
05929             SENTINEL);
05930       }
05931 
05932       /* Store information */
05933       txt = fdopen(txtdes, "w+");
05934       if (txt) {
05935          get_date(date, sizeof(date));
05936          ast_callerid_merge(callerid, sizeof(callerid),
05937             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05938             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05939             "Unknown");
05940          fprintf(txt, 
05941             ";\n"
05942             "; Message Information file\n"
05943             ";\n"
05944             "[message]\n"
05945             "origmailbox=%s\n"
05946             "context=%s\n"
05947             "macrocontext=%s\n"
05948             "exten=%s\n"
05949             "rdnis=%s\n"
05950             "priority=%d\n"
05951             "callerchan=%s\n"
05952             "callerid=%s\n"
05953             "origdate=%s\n"
05954             "origtime=%ld\n"
05955             "category=%s\n",
05956             ext,
05957             chan->context,
05958             chan->macrocontext, 
05959             chan->exten,
05960             S_COR(chan->redirecting.from.number.valid,
05961                chan->redirecting.from.number.str, "unknown"),
05962             chan->priority,
05963             chan->name,
05964             callerid,
05965             date, (long) time(NULL),
05966             category ? category : "");
05967       } else {
05968          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05969          inprocess_count(vmu->mailbox, vmu->context, -1);
05970          if (ast_check_realtime("voicemail_data")) {
05971             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05972          }
05973          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05974          goto leave_vm_out;
05975       }
05976       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05977 
05978       if (txt) {
05979          fprintf(txt, "flag=%s\n", flag);
05980          if (duration < vmu->minsecs) {
05981             fclose(txt);
05982             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmu->minsecs);
05983             ast_filedelete(tmptxtfile, NULL);
05984             unlink(tmptxtfile);
05985             if (ast_check_realtime("voicemail_data")) {
05986                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05987             }
05988             inprocess_count(vmu->mailbox, vmu->context, -1);
05989          } else {
05990             fprintf(txt, "duration=%d\n", duration);
05991             fclose(txt);
05992             if (vm_lock_path(dir)) {
05993                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05994                /* Delete files */
05995                ast_filedelete(tmptxtfile, NULL);
05996                unlink(tmptxtfile);
05997                inprocess_count(vmu->mailbox, vmu->context, -1);
05998             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05999                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06000                unlink(tmptxtfile);
06001                ast_unlock_path(dir);
06002                inprocess_count(vmu->mailbox, vmu->context, -1);
06003                if (ast_check_realtime("voicemail_data")) {
06004                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06005                }
06006             } else {
06007 #ifndef IMAP_STORAGE
06008                msgnum = last_message_index(vmu, dir) + 1;
06009 #endif
06010                make_file(fn, sizeof(fn), dir, msgnum);
06011 
06012                /* assign a variable with the name of the voicemail file */ 
06013 #ifndef IMAP_STORAGE
06014                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06015 #else
06016                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06017 #endif
06018 
06019                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06020                ast_filerename(tmptxtfile, fn, NULL);
06021                rename(tmptxtfile, txtfile);
06022                inprocess_count(vmu->mailbox, vmu->context, -1);
06023 
06024                /* Properly set permissions on voicemail text descriptor file.
06025                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06026                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06027                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06028 
06029                ast_unlock_path(dir);
06030                if (ast_check_realtime("voicemail_data")) {
06031                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06032                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06033                }
06034                /* We must store the file first, before copying the message, because
06035                 * ODBC storage does the entire copy with SQL.
06036                 */
06037                if (ast_fileexists(fn, NULL, NULL) > 0) {
06038                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06039                }
06040 
06041                /* Are there to be more recipients of this message? */
06042                while (tmpptr) {
06043                   struct ast_vm_user recipu, *recip;
06044                   char *exten, *cntx;
06045                
06046                   exten = strsep(&tmpptr, "&");
06047                   cntx = strchr(exten, '@');
06048                   if (cntx) {
06049                      *cntx = '\0';
06050                      cntx++;
06051                   }
06052                   if ((recip = find_user(&recipu, cntx, exten))) {
06053                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06054                      free_user(recip);
06055                   }
06056                }
06057 #ifndef IMAP_STORAGE
06058                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06059                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06060                   char sfn[PATH_MAX];
06061                   char dfn[PATH_MAX];
06062                   int x;
06063                   /* It's easier just to try to make it than to check for its existence */
06064                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06065                   x = last_message_index(vmu, urgdir) + 1;
06066                   make_file(sfn, sizeof(sfn), dir, msgnum);
06067                   make_file(dfn, sizeof(dfn), urgdir, x);
06068                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06069                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06070                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06071                   ast_copy_string(fn, dfn, sizeof(fn));
06072                   msgnum = x;
06073                }
06074 #endif
06075                /* Notification needs to happen after the copy, though. */
06076                if (ast_fileexists(fn, NULL, NULL)) {
06077 #ifdef IMAP_STORAGE
06078                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06079                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06080                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06081                      flag);
06082 #else
06083                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06084                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06085                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06086                      flag);
06087 #endif
06088                }
06089 
06090                /* Disposal needs to happen after the optional move and copy */
06091                if (ast_fileexists(fn, NULL, NULL)) {
06092                   DISPOSE(dir, msgnum);
06093                }
06094             }
06095          }
06096       } else {
06097          inprocess_count(vmu->mailbox, vmu->context, -1);
06098       }
06099       if (res == '0') {
06100          goto transfer;
06101       } else if (res > 0 && res != 't')
06102          res = 0;
06103 
06104       if (duration < vmu->minsecs)
06105          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06106          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06107       else
06108          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06109    } else
06110       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06111 leave_vm_out:
06112    free_user(vmu);
06113 
06114 #ifdef IMAP_STORAGE
06115    /* expunge message - use UID Expunge if supported on IMAP server*/
06116    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06117    if (expungeonhangup == 1) {
06118       ast_mutex_lock(&vms->lock);
06119 #ifdef HAVE_IMAP_TK2006
06120       if (LEVELUIDPLUS (vms->mailstream)) {
06121          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06122       } else 
06123 #endif
06124          mail_expunge(vms->mailstream);
06125       ast_mutex_unlock(&vms->lock);
06126    }
06127 #endif
06128 
06129    ast_free(tmp);
06130    return res;
06131 }
06132 
06133 #if !defined(IMAP_STORAGE)
06134 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06135 {
06136     /* we know the actual number of messages, so stop process when number is hit */
06137 
06138     int x, dest;
06139     char sfn[PATH_MAX];
06140     char dfn[PATH_MAX];
06141 
06142     if (vm_lock_path(dir))
06143         return ERROR_LOCK_PATH;
06144 
06145     for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06146         make_file(sfn, sizeof(sfn), dir, x);
06147         if (EXISTS(dir, x, sfn, NULL)) {
06148 
06149             if (x != dest) {
06150                 make_file(dfn, sizeof(dfn), dir, dest);
06151                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06152             }
06153 
06154             dest++;
06155         }
06156     }
06157     ast_unlock_path(dir);
06158 
06159     return dest;
06160 }
06161 #endif
06162 
06163 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06164 {
06165    int d;
06166    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06167    return d;
06168 }
06169 
06170 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06171 {
06172 #ifdef IMAP_STORAGE
06173    /* we must use mbox(x) folder names, and copy the message there */
06174    /* simple. huh? */
06175    char sequence[10];
06176    char mailbox[256];
06177    int res;
06178 
06179    /* get the real IMAP message number for this message */
06180    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06181    
06182    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06183    ast_mutex_lock(&vms->lock);
06184    /* if save to Old folder, put in INBOX as read */
06185    if (box == OLD_FOLDER) {
06186       mail_setflag(vms->mailstream, sequence, "\\Seen");
06187       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06188    } else if (box == NEW_FOLDER) {
06189       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06190       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06191    }
06192    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06193       ast_mutex_unlock(&vms->lock);
06194       return 0;
06195    }
06196    /* Create the folder if it don't exist */
06197    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06198    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06199    if (mail_create(vms->mailstream, mailbox) == NIL) 
06200       ast_debug(5, "Folder exists.\n");
06201    else
06202       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06203    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06204    ast_mutex_unlock(&vms->lock);
06205    return res;
06206 #else
06207    char *dir = vms->curdir;
06208    char *username = vms->username;
06209    char *context = vmu->context;
06210    char sfn[PATH_MAX];
06211    char dfn[PATH_MAX];
06212    char ddir[PATH_MAX];
06213    const char *dbox = mbox(vmu, box);
06214    int x, i;
06215    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06216 
06217    if (vm_lock_path(ddir))
06218       return ERROR_LOCK_PATH;
06219 
06220    x = last_message_index(vmu, ddir) + 1;
06221 
06222    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06223       x--;
06224       for (i = 1; i <= x; i++) {
06225          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06226          make_file(sfn, sizeof(sfn), ddir, i);
06227          make_file(dfn, sizeof(dfn), ddir, i - 1);
06228          if (EXISTS(ddir, i, sfn, NULL)) {
06229             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06230          } else
06231             break;
06232       }
06233    } else {
06234       if (x >= vmu->maxmsg) {
06235          ast_unlock_path(ddir);
06236          return -1;
06237       }
06238    }
06239    make_file(sfn, sizeof(sfn), dir, msg);
06240    make_file(dfn, sizeof(dfn), ddir, x);
06241    if (strcmp(sfn, dfn)) {
06242       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06243    }
06244    ast_unlock_path(ddir);
06245 #endif
06246    return 0;
06247 }
06248 
06249 static int adsi_logo(unsigned char *buf)
06250 {
06251    int bytes = 0;
06252    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06253    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06254    return bytes;
06255 }
06256 
06257 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06258 {
06259    unsigned char buf[256];
06260    int bytes = 0;
06261    int x;
06262    char num[5];
06263 
06264    *useadsi = 0;
06265    bytes += ast_adsi_data_mode(buf + bytes);
06266    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06267 
06268    bytes = 0;
06269    bytes += adsi_logo(buf);
06270    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06271 #ifdef DISPLAY
06272    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06273 #endif
06274    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06275    bytes += ast_adsi_data_mode(buf + bytes);
06276    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06277 
06278    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06279       bytes = 0;
06280       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06281       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06282       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06283       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06284       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06285       return 0;
06286    }
06287 
06288 #ifdef DISPLAY
06289    /* Add a dot */
06290    bytes = 0;
06291    bytes += ast_adsi_logo(buf);
06292    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06293    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06294    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06295    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06296 #endif
06297    bytes = 0;
06298    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06299    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06300    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06301    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06302    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06303    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06304    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06305 
06306 #ifdef DISPLAY
06307    /* Add another dot */
06308    bytes = 0;
06309    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06310    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06311 
06312    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06313    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06314 #endif
06315 
06316    bytes = 0;
06317    /* These buttons we load but don't use yet */
06318    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06319    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06320    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06321    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06322    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06323    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06324    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06325 
06326 #ifdef DISPLAY
06327    /* Add another dot */
06328    bytes = 0;
06329    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06330    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06331    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06332 #endif
06333 
06334    bytes = 0;
06335    for (x = 0; x < 5; x++) {
06336       snprintf(num, sizeof(num), "%d", x);
06337       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06338    }
06339    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06340    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06341 
06342 #ifdef DISPLAY
06343    /* Add another dot */
06344    bytes = 0;
06345    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06346    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06347    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06348 #endif
06349 
06350    if (ast_adsi_end_download(chan)) {
06351       bytes = 0;
06352       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06353       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06354       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06355       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06356       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06357       return 0;
06358    }
06359    bytes = 0;
06360    bytes += ast_adsi_download_disconnect(buf + bytes);
06361    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06362    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06363 
06364    ast_debug(1, "Done downloading scripts...\n");
06365 
06366 #ifdef DISPLAY
06367    /* Add last dot */
06368    bytes = 0;
06369    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06370    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06371 #endif
06372    ast_debug(1, "Restarting session...\n");
06373 
06374    bytes = 0;
06375    /* Load the session now */
06376    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06377       *useadsi = 1;
06378       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06379    } else
06380       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06381 
06382    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06383    return 0;
06384 }
06385 
06386 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06387 {
06388    int x;
06389    if (!ast_adsi_available(chan))
06390       return;
06391    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06392    if (x < 0)
06393       return;
06394    if (!x) {
06395       if (adsi_load_vmail(chan, useadsi)) {
06396          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06397          return;
06398       }
06399    } else
06400       *useadsi = 1;
06401 }
06402 
06403 static void adsi_login(struct ast_channel *chan)
06404 {
06405    unsigned char buf[256];
06406    int bytes = 0;
06407    unsigned char keys[8];
06408    int x;
06409    if (!ast_adsi_available(chan))
06410       return;
06411 
06412    for (x = 0; x < 8; x++)
06413       keys[x] = 0;
06414    /* Set one key for next */
06415    keys[3] = ADSI_KEY_APPS + 3;
06416 
06417    bytes += adsi_logo(buf + bytes);
06418    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06419    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06420    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06421    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06422    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06423    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06424    bytes += ast_adsi_set_keys(buf + bytes, keys);
06425    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06426    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06427 }
06428 
06429 static void adsi_password(struct ast_channel *chan)
06430 {
06431    unsigned char buf[256];
06432    int bytes = 0;
06433    unsigned char keys[8];
06434    int x;
06435    if (!ast_adsi_available(chan))
06436       return;
06437 
06438    for (x = 0; x < 8; x++)
06439       keys[x] = 0;
06440    /* Set one key for next */
06441    keys[3] = ADSI_KEY_APPS + 3;
06442 
06443    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06444    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06445    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06446    bytes += ast_adsi_set_keys(buf + bytes, keys);
06447    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06448    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06449 }
06450 
06451 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06452 {
06453    unsigned char buf[256];
06454    int bytes = 0;
06455    unsigned char keys[8];
06456    int x, y;
06457 
06458    if (!ast_adsi_available(chan))
06459       return;
06460 
06461    for (x = 0; x < 5; x++) {
06462       y = ADSI_KEY_APPS + 12 + start + x;
06463       if (y > ADSI_KEY_APPS + 12 + 4)
06464          y = 0;
06465       keys[x] = ADSI_KEY_SKT | y;
06466    }
06467    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06468    keys[6] = 0;
06469    keys[7] = 0;
06470 
06471    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06472    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06473    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06474    bytes += ast_adsi_set_keys(buf + bytes, keys);
06475    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06476 
06477    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06478 }
06479 
06480 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06481 {
06482    int bytes = 0;
06483    unsigned char buf[256]; 
06484    char buf1[256], buf2[256];
06485    char fn2[PATH_MAX];
06486 
06487    char cid[256] = "";
06488    char *val;
06489    char *name, *num;
06490    char datetime[21] = "";
06491    FILE *f;
06492 
06493    unsigned char keys[8];
06494 
06495    int x;
06496 
06497    if (!ast_adsi_available(chan))
06498       return;
06499 
06500    /* Retrieve important info */
06501    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06502    f = fopen(fn2, "r");
06503    if (f) {
06504       while (!feof(f)) {   
06505          if (!fgets((char *) buf, sizeof(buf), f)) {
06506             continue;
06507          }
06508          if (!feof(f)) {
06509             char *stringp = NULL;
06510             stringp = (char *) buf;
06511             strsep(&stringp, "=");
06512             val = strsep(&stringp, "=");
06513             if (!ast_strlen_zero(val)) {
06514                if (!strcmp((char *) buf, "callerid"))
06515                   ast_copy_string(cid, val, sizeof(cid));
06516                if (!strcmp((char *) buf, "origdate"))
06517                   ast_copy_string(datetime, val, sizeof(datetime));
06518             }
06519          }
06520       }
06521       fclose(f);
06522    }
06523    /* New meaning for keys */
06524    for (x = 0; x < 5; x++)
06525       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06526    keys[6] = 0x0;
06527    keys[7] = 0x0;
06528 
06529    if (!vms->curmsg) {
06530       /* No prev key, provide "Folder" instead */
06531       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06532    }
06533    if (vms->curmsg >= vms->lastmsg) {
06534       /* If last message ... */
06535       if (vms->curmsg) {
06536          /* but not only message, provide "Folder" instead */
06537          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06538          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06539 
06540       } else {
06541          /* Otherwise if only message, leave blank */
06542          keys[3] = 1;
06543       }
06544    }
06545 
06546    if (!ast_strlen_zero(cid)) {
06547       ast_callerid_parse(cid, &name, &num);
06548       if (!name)
06549          name = num;
06550    } else
06551       name = "Unknown Caller";
06552 
06553    /* If deleted, show "undeleted" */
06554 
06555    if (vms->deleted[vms->curmsg])
06556       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06557 
06558    /* Except "Exit" */
06559    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06560    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06561       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06562    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06563 
06564    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06565    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06566    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06567    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06568    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06569    bytes += ast_adsi_set_keys(buf + bytes, keys);
06570    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06571 
06572    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06573 }
06574 
06575 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06576 {
06577    int bytes = 0;
06578    unsigned char buf[256];
06579    unsigned char keys[8];
06580 
06581    int x;
06582 
06583    if (!ast_adsi_available(chan))
06584       return;
06585 
06586    /* New meaning for keys */
06587    for (x = 0; x < 5; x++)
06588       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06589 
06590    keys[6] = 0x0;
06591    keys[7] = 0x0;
06592 
06593    if (!vms->curmsg) {
06594       /* No prev key, provide "Folder" instead */
06595       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06596    }
06597    if (vms->curmsg >= vms->lastmsg) {
06598       /* If last message ... */
06599       if (vms->curmsg) {
06600          /* but not only message, provide "Folder" instead */
06601          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06602       } else {
06603          /* Otherwise if only message, leave blank */
06604          keys[3] = 1;
06605       }
06606    }
06607 
06608    /* If deleted, show "undeleted" */
06609    if (vms->deleted[vms->curmsg]) 
06610       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06611 
06612    /* Except "Exit" */
06613    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06614    bytes += ast_adsi_set_keys(buf + bytes, keys);
06615    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06616 
06617    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06618 }
06619 
06620 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06621 {
06622    unsigned char buf[256] = "";
06623    char buf1[256] = "", buf2[256] = "";
06624    int bytes = 0;
06625    unsigned char keys[8];
06626    int x;
06627 
06628    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06629    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06630    if (!ast_adsi_available(chan))
06631       return;
06632    if (vms->newmessages) {
06633       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06634       if (vms->oldmessages) {
06635          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06636          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06637       } else {
06638          snprintf(buf2, sizeof(buf2), "%s.", newm);
06639       }
06640    } else if (vms->oldmessages) {
06641       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06642       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06643    } else {
06644       strcpy(buf1, "You have no messages.");
06645       buf2[0] = ' ';
06646       buf2[1] = '\0';
06647    }
06648    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06649    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06650    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06651 
06652    for (x = 0; x < 6; x++)
06653       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06654    keys[6] = 0;
06655    keys[7] = 0;
06656 
06657    /* Don't let them listen if there are none */
06658    if (vms->lastmsg < 0)
06659       keys[0] = 1;
06660    bytes += ast_adsi_set_keys(buf + bytes, keys);
06661 
06662    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06663 
06664    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06665 }
06666 
06667 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06668 {
06669    unsigned char buf[256] = "";
06670    char buf1[256] = "", buf2[256] = "";
06671    int bytes = 0;
06672    unsigned char keys[8];
06673    int x;
06674 
06675    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06676 
06677    if (!ast_adsi_available(chan))
06678       return;
06679 
06680    /* Original command keys */
06681    for (x = 0; x < 6; x++)
06682       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06683 
06684    keys[6] = 0;
06685    keys[7] = 0;
06686 
06687    if ((vms->lastmsg + 1) < 1)
06688       keys[0] = 0;
06689 
06690    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06691       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06692 
06693    if (vms->lastmsg + 1)
06694       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06695    else
06696       strcpy(buf2, "no messages.");
06697    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06698    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06699    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06700    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06701    bytes += ast_adsi_set_keys(buf + bytes, keys);
06702 
06703    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06704 
06705    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06706    
06707 }
06708 
06709 /*
06710 static void adsi_clear(struct ast_channel *chan)
06711 {
06712    char buf[256];
06713    int bytes=0;
06714    if (!ast_adsi_available(chan))
06715       return;
06716    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06717    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06718 
06719    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06720 }
06721 */
06722 
06723 static void adsi_goodbye(struct ast_channel *chan)
06724 {
06725    unsigned char buf[256];
06726    int bytes = 0;
06727 
06728    if (!ast_adsi_available(chan))
06729       return;
06730    bytes += adsi_logo(buf + bytes);
06731    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06732    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06733    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06734    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06735 
06736    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06737 }
06738 
06739 /*!\brief get_folder: Folder menu
06740  * Plays "press 1 for INBOX messages" etc.
06741  * Should possibly be internationalized
06742  */
06743 static int get_folder(struct ast_channel *chan, int start)
06744 {
06745    int x;
06746    int d;
06747    char fn[PATH_MAX];
06748    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06749    if (d)
06750       return d;
06751    for (x = start; x < 5; x++) { /* For all folders */
06752       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06753          return d;
06754       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06755       if (d)
06756          return d;
06757       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06758 
06759       /* The inbox folder can have its name changed under certain conditions
06760        * so this checks if the sound file exists for the inbox folder name and
06761        * if it doesn't, plays the default name instead. */
06762       if (x == 0) {
06763          if (ast_fileexists(fn, NULL, NULL)) {
06764             d = vm_play_folder_name(chan, fn);
06765          } else {
06766             ast_verb(1, "failed to find %s\n", fn);
06767             d = vm_play_folder_name(chan, "vm-INBOX");
06768          }
06769       } else {
06770          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06771          d = vm_play_folder_name(chan, fn);
06772       }
06773 
06774       if (d)
06775          return d;
06776       d = ast_waitfordigit(chan, 500);
06777       if (d)
06778          return d;
06779    }
06780 
06781    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06782    if (d)
06783       return d;
06784    d = ast_waitfordigit(chan, 4000);
06785    return d;
06786 }
06787 
06788 /*!
06789  * \brief plays a prompt and waits for a keypress.
06790  * \param chan
06791  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06792  * \param start Does not appear to be used at this time.
06793  *
06794  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06795  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06796  * prompting for the number inputs that correspond to the available folders.
06797  * 
06798  * \return zero on success, or -1 on error.
06799  */
06800 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06801 {
06802    int res = 0;
06803    int loops = 0;
06804 
06805    res = ast_play_and_wait(chan, fn);  /* Folder name */
06806    while (((res < '0') || (res > '9')) &&
06807          (res != '#') && (res >= 0) &&
06808          loops < 4) {
06809       res = get_folder(chan, 0);
06810       loops++;
06811    }
06812    if (loops == 4) { /* give up */
06813       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06814       return '#';
06815    }
06816    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06817    return res;
06818 }
06819 
06820 /*!
06821  * \brief presents the option to prepend to an existing message when forwarding it.
06822  * \param chan
06823  * \param vmu
06824  * \param curdir
06825  * \param curmsg
06826  * \param vm_fmts
06827  * \param context
06828  * \param record_gain
06829  * \param duration
06830  * \param vms
06831  * \param flag 
06832  *
06833  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06834  *
06835  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06836  * \return zero on success, -1 on error.
06837  */
06838 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06839          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06840 {
06841 #ifdef IMAP_STORAGE
06842    int res;
06843 #endif
06844    int cmd = 0;
06845    int retries = 0, prepend_duration = 0, already_recorded = 0;
06846    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06847    char textfile[PATH_MAX];
06848    struct ast_config *msg_cfg;
06849    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06850 #ifndef IMAP_STORAGE
06851    signed char zero_gain = 0;
06852 #endif
06853    const char *duration_str;
06854 
06855    /* Must always populate duration correctly */
06856    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06857    strcpy(textfile, msgfile);
06858    strcpy(backup, msgfile);
06859    strcpy(backup_textfile, msgfile);
06860    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06861    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06862    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06863 
06864    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06865       *duration = atoi(duration_str);
06866    } else {
06867       *duration = 0;
06868    }
06869 
06870    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06871       if (cmd)
06872          retries = 0;
06873       switch (cmd) {
06874       case '1': 
06875 
06876 #ifdef IMAP_STORAGE
06877          /* Record new intro file */
06878          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06879          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06880          res = ast_play_and_wait(chan, INTRO);
06881          res = ast_play_and_wait(chan, "beep");
06882          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, record_gain, vms, flag);
06883          cmd = 't';
06884 #else
06885 
06886          /* prepend a message to the current message, update the metadata and return */
06887 
06888          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06889          strcpy(textfile, msgfile);
06890          strncat(textfile, ".txt", sizeof(textfile) - 1);
06891          *duration = 0;
06892 
06893          /* if we can't read the message metadata, stop now */
06894          if (!msg_cfg) {
06895             cmd = 0;
06896             break;
06897          }
06898          
06899          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06900 #ifndef IMAP_STORAGE
06901          if (already_recorded) {
06902             ast_filecopy(backup, msgfile, NULL);
06903             copy(backup_textfile, textfile);
06904          }
06905          else {
06906             ast_filecopy(msgfile, backup, NULL);
06907             copy(textfile, backup_textfile);
06908          }
06909 #endif
06910          already_recorded = 1;
06911 
06912          if (record_gain)
06913             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06914 
06915          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06916 
06917          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06918             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06919             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06920             ast_filerename(backup, msgfile, NULL);
06921          }
06922 
06923          if (record_gain)
06924             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06925 
06926          
06927          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06928             *duration = atoi(duration_str);
06929 
06930          if (prepend_duration) {
06931             struct ast_category *msg_cat;
06932             /* need enough space for a maximum-length message duration */
06933             char duration_buf[12];
06934 
06935             *duration += prepend_duration;
06936             msg_cat = ast_category_get(msg_cfg, "message");
06937             snprintf(duration_buf, 11, "%ld", *duration);
06938             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06939                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06940             }
06941          }
06942 
06943 #endif
06944          break;
06945       case '2': 
06946          /* NULL out introfile so we know there is no intro! */
06947 #ifdef IMAP_STORAGE
06948          *vms->introfn = '\0';
06949 #endif
06950          cmd = 't';
06951          break;
06952       case '*':
06953          cmd = '*';
06954          break;
06955       default: 
06956          /* If time_out and return to menu, reset already_recorded */
06957          already_recorded = 0;
06958 
06959          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06960             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06961          if (!cmd) {
06962             cmd = ast_play_and_wait(chan, "vm-starmain");
06963             /* "press star to return to the main menu" */
06964          }
06965          if (!cmd) {
06966             cmd = ast_waitfordigit(chan, 6000);
06967          }
06968          if (!cmd) {
06969             retries++;
06970          }
06971          if (retries > 3) {
06972             cmd = '*'; /* Let's cancel this beast */
06973          }
06974          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
06975       }
06976    }
06977 
06978    if (msg_cfg)
06979       ast_config_destroy(msg_cfg);
06980    if (prepend_duration)
06981       *duration = prepend_duration;
06982 
06983    if (already_recorded && cmd == -1) {
06984       /* restore original message if prepention cancelled */
06985       ast_filerename(backup, msgfile, NULL);
06986       rename(backup_textfile, textfile);
06987    }
06988 
06989    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
06990       cmd = 0;
06991    return cmd;
06992 }
06993 
06994 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06995 {
06996    struct ast_event *event;
06997    char *mailbox, *context;
06998 
06999    /* Strip off @default */
07000    context = mailbox = ast_strdupa(box);
07001    strsep(&context, "@");
07002    if (ast_strlen_zero(context))
07003       context = "default";
07004 
07005    if (!(event = ast_event_new(AST_EVENT_MWI,
07006          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07007          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07008          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07009          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07010          AST_EVENT_IE_END))) {
07011       return;
07012    }
07013 
07014    ast_event_queue_and_cache(event);
07015 }
07016 
07017 /*!
07018  * \brief Sends email notification that a user has a new voicemail waiting for them.
07019  * \param chan
07020  * \param vmu
07021  * \param vms
07022  * \param msgnum
07023  * \param duration
07024  * \param fmt
07025  * \param cidnum The Caller ID phone number value.
07026  * \param cidname The Caller ID name value.
07027  * \param flag
07028  *
07029  * \return zero on success, -1 on error.
07030  */
07031 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)
07032 {
07033    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07034    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07035    const char *category;
07036    char *myserveremail = serveremail;
07037 
07038    ast_channel_lock(chan);
07039    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07040       category = ast_strdupa(category);
07041    }
07042    ast_channel_unlock(chan);
07043 
07044 #ifndef IMAP_STORAGE
07045    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07046 #else
07047    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07048 #endif
07049    make_file(fn, sizeof(fn), todir, msgnum);
07050    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07051 
07052    if (!ast_strlen_zero(vmu->attachfmt)) {
07053       if (strstr(fmt, vmu->attachfmt))
07054          fmt = vmu->attachfmt;
07055       else
07056          ast_log(AST_LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'.  Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
07057    }
07058 
07059    /* Attach only the first format */
07060    fmt = ast_strdupa(fmt);
07061    stringp = fmt;
07062    strsep(&stringp, "|");
07063 
07064    if (!ast_strlen_zero(vmu->serveremail))
07065       myserveremail = vmu->serveremail;
07066 
07067    if (!ast_strlen_zero(vmu->email)) {
07068       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07069 
07070       if (attach_user_voicemail)
07071          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07072 
07073       /* XXX possible imap issue, should category be NULL XXX */
07074       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07075 
07076       if (attach_user_voicemail)
07077          DISPOSE(todir, msgnum);
07078    }
07079 
07080    if (!ast_strlen_zero(vmu->pager)) {
07081       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07082    }
07083 
07084    if (ast_test_flag(vmu, VM_DELETE))
07085       DELETE(todir, msgnum, fn, vmu);
07086 
07087    /* Leave voicemail for someone */
07088    if (ast_app_has_voicemail(ext_context, NULL)) 
07089       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07090 
07091    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07092 
07093    ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
07094    run_externnotify(vmu->context, vmu->mailbox, flag);
07095 
07096 #ifdef IMAP_STORAGE
07097    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07098    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07099       vm_imap_delete(NULL, vms->curmsg, vmu);
07100       vms->newmessages--;  /* Fix new message count */
07101    }
07102 #endif
07103 
07104    return 0;
07105 }
07106 
07107 /*!
07108  * \brief Sends a voicemail message to a mailbox recipient.
07109  * \param chan
07110  * \param context
07111  * \param vms
07112  * \param sender
07113  * \param fmt
07114  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07115  *             Will be 0 when called to forward an existing message (option 8)
07116  *             Will be 1 when called to leave a message (option 3->5)
07117  * \param record_gain 
07118  * \param urgent
07119  *
07120  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07121  * 
07122  * When in the leave message mode (is_new_message == 1):
07123  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07124  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07125  *
07126  * When in the forward message mode (is_new_message == 0):
07127  *   - retreives the current message to be forwarded
07128  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07129  *   - determines the target mailbox and folders
07130  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07131  *
07132  * \return zero on success, -1 on error.
07133  */
07134 static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int is_new_message, signed char record_gain, int urgent)
07135 {
07136 #ifdef IMAP_STORAGE
07137    int todircount = 0;
07138    struct vm_state *dstvms;
07139 #endif
07140    char username[70]="";
07141    char fn[PATH_MAX]; /* for playback of name greeting */
07142    char ecodes[16] = "#";
07143    int res = 0, cmd = 0;
07144    struct ast_vm_user *receiver = NULL, *vmtmp;
07145    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07146    char *stringp;
07147    const char *s;
07148    int saved_messages = 0;
07149    int valid_extensions = 0;
07150    char *dir;
07151    int curmsg;
07152    char urgent_str[7] = "";
07153    int prompt_played = 0;
07154 #ifndef IMAP_STORAGE
07155    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07156 #endif
07157    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07158       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07159    }
07160 
07161    if (vms == NULL) return -1;
07162    dir = vms->curdir;
07163    curmsg = vms->curmsg;
07164 
07165    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07166    while (!res && !valid_extensions) {
07167       int use_directory = 0;
07168       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07169          int done = 0;
07170          int retries = 0;
07171          cmd = 0;
07172          while ((cmd >= 0) && !done ){
07173             if (cmd)
07174                retries = 0;
07175             switch (cmd) {
07176             case '1': 
07177                use_directory = 0;
07178                done = 1;
07179                break;
07180             case '2': 
07181                use_directory = 1;
07182                done = 1;
07183                break;
07184             case '*': 
07185                cmd = 't';
07186                done = 1;
07187                break;
07188             default: 
07189                /* Press 1 to enter an extension press 2 to use the directory */
07190                cmd = ast_play_and_wait(chan, "vm-forward");
07191                if (!cmd) {
07192                   cmd = ast_waitfordigit(chan, 3000);
07193                }
07194                if (!cmd) {
07195                   retries++;
07196                }
07197                if (retries > 3) {
07198                   cmd = 't';
07199                   done = 1;
07200                }
07201                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07202             }
07203          }
07204          if (cmd < 0 || cmd == 't')
07205             break;
07206       }
07207       
07208       if (use_directory) {
07209          /* use app_directory */
07210          
07211          char old_context[sizeof(chan->context)];
07212          char old_exten[sizeof(chan->exten)];
07213          int old_priority;
07214          struct ast_app* directory_app;
07215 
07216          directory_app = pbx_findapp("Directory");
07217          if (directory_app) {
07218             char vmcontext[256];
07219             /* make backup copies */
07220             memcpy(old_context, chan->context, sizeof(chan->context));
07221             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07222             old_priority = chan->priority;
07223             
07224             /* call the the Directory, changes the channel */
07225             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07226             res = pbx_exec(chan, directory_app, vmcontext);
07227             
07228             ast_copy_string(username, chan->exten, sizeof(username));
07229             
07230             /* restore the old context, exten, and priority */
07231             memcpy(chan->context, old_context, sizeof(chan->context));
07232             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07233             chan->priority = old_priority;
07234          } else {
07235             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07236             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07237          }
07238       } else {
07239          /* Ask for an extension */
07240          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07241          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07242          prompt_played++;
07243          if (res || prompt_played > 4)
07244             break;
07245          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07246             break;
07247       }
07248       
07249       /* start all over if no username */
07250       if (ast_strlen_zero(username))
07251          continue;
07252       stringp = username;
07253       s = strsep(&stringp, "*");
07254       /* start optimistic */
07255       valid_extensions = 1;
07256       while (s) {
07257          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07258             int oldmsgs;
07259             int newmsgs;
07260             int capacity;
07261             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07262                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07263                /* Shouldn't happen, but allow trying another extension if it does */
07264                res = ast_play_and_wait(chan, "pbx-invalid");
07265                valid_extensions = 0;
07266                break;
07267             }
07268             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07269             if ((newmsgs + oldmsgs) >= capacity) {
07270                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07271                res = ast_play_and_wait(chan, "vm-mailboxfull");
07272                valid_extensions = 0;
07273                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07274                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07275                   free_user(vmtmp);
07276                }
07277                inprocess_count(receiver->mailbox, receiver->context, -1);
07278                break;
07279             }
07280             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07281          } else {
07282             /* XXX Optimization for the future.  When we encounter a single bad extension,
07283              * bailing out on all of the extensions may not be the way to go.  We should
07284              * probably just bail on that single extension, then allow the user to enter
07285              * several more. XXX
07286              */
07287             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07288                free_user(receiver);
07289             }
07290             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07291             /* "I am sorry, that's not a valid extension.  Please try again." */
07292             res = ast_play_and_wait(chan, "pbx-invalid");
07293             valid_extensions = 0;
07294             break;
07295          }
07296 
07297          /* play name if available, else play extension number */
07298          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07299          RETRIEVE(fn, -1, s, receiver->context);
07300          if (ast_fileexists(fn, NULL, NULL) > 0) {
07301             res = ast_stream_and_wait(chan, fn, ecodes);
07302             if (res) {
07303                DISPOSE(fn, -1);
07304                return res;
07305             }
07306          } else {
07307             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07308          }
07309          DISPOSE(fn, -1);
07310 
07311          s = strsep(&stringp, "*");
07312       }
07313       /* break from the loop of reading the extensions */
07314       if (valid_extensions)
07315          break;
07316    }
07317    /* check if we're clear to proceed */
07318    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07319       return res;
07320    if (is_new_message == 1) {
07321       struct leave_vm_options leave_options;
07322       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07323       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07324 
07325       /* Send VoiceMail */
07326       memset(&leave_options, 0, sizeof(leave_options));
07327       leave_options.record_gain = record_gain;
07328       cmd = leave_voicemail(chan, mailbox, &leave_options);
07329    } else {
07330       /* Forward VoiceMail */
07331       long duration = 0;
07332       struct vm_state vmstmp;
07333       int copy_msg_result = 0;
07334       memcpy(&vmstmp, vms, sizeof(vmstmp));
07335 
07336       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07337 
07338       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07339       if (!cmd) {
07340          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07341 #ifdef IMAP_STORAGE
07342             int attach_user_voicemail;
07343             char *myserveremail = serveremail;
07344             
07345             /* get destination mailbox */
07346             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07347             if (!dstvms) {
07348                dstvms = create_vm_state_from_user(vmtmp);
07349             }
07350             if (dstvms) {
07351                init_mailstream(dstvms, 0);
07352                if (!dstvms->mailstream) {
07353                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07354                } else {
07355                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07356                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07357                }
07358             } else {
07359                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07360             }
07361             if (!ast_strlen_zero(vmtmp->serveremail))
07362                myserveremail = vmtmp->serveremail;
07363             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07364             /* NULL category for IMAP storage */
07365             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07366                dstvms->curbox,
07367                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07368                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07369                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07370                NULL, urgent_str);
07371 #else
07372             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07373 #endif
07374             saved_messages++;
07375             AST_LIST_REMOVE_CURRENT(list);
07376             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07377             free_user(vmtmp);
07378             if (res)
07379                break;
07380          }
07381          AST_LIST_TRAVERSE_SAFE_END;
07382          if (saved_messages > 0 && !copy_msg_result) {
07383             /* give confirmation that the message was saved */
07384             /* commented out since we can't forward batches yet
07385             if (saved_messages == 1)
07386                res = ast_play_and_wait(chan, "vm-message");
07387             else
07388                res = ast_play_and_wait(chan, "vm-messages");
07389             if (!res)
07390                res = ast_play_and_wait(chan, "vm-saved"); */
07391 #ifdef IMAP_STORAGE
07392             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07393             if (ast_strlen_zero(vmstmp.introfn))
07394 #endif
07395             res = ast_play_and_wait(chan, "vm-msgsaved");
07396          }
07397 #ifndef IMAP_STORAGE
07398          else {
07399             /* with IMAP, mailbox full warning played by imap_check_limits */
07400             res = ast_play_and_wait(chan, "vm-mailboxfull");
07401          }
07402          /* Restore original message without prepended message if backup exists */
07403          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07404          strcpy(textfile, msgfile);
07405          strcpy(backup, msgfile);
07406          strcpy(backup_textfile, msgfile);
07407          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07408          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07409          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07410          if (ast_fileexists(backup, NULL, NULL) > 0) {
07411             ast_filerename(backup, msgfile, NULL);
07412             rename(backup_textfile, textfile);
07413          }
07414 #endif
07415       }
07416       DISPOSE(dir, curmsg);
07417 #ifndef IMAP_STORAGE
07418       if (cmd) { /* assuming hangup, cleanup backup file */
07419          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07420          strcpy(textfile, msgfile);
07421          strcpy(backup_textfile, msgfile);
07422          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07423          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07424          rename(backup_textfile, textfile);
07425       }
07426 #endif
07427    }
07428 
07429    /* If anything failed above, we still have this list to free */
07430    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07431       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07432       free_user(vmtmp);
07433    }
07434    return res ? res : cmd;
07435 }
07436 
07437 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07438 {
07439    int res;
07440    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07441       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07442    return res;
07443 }
07444 
07445 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07446 {
07447    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07448    return ast_control_streamfile(chan, file, listen_control_forward_key, listen_control_reverse_key, listen_control_stop_key, listen_control_pause_key, listen_control_restart_key, skipms, NULL);
07449 }
07450 
07451 static int play_message_category(struct ast_channel *chan, const char *category)
07452 {
07453    int res = 0;
07454 
07455    if (!ast_strlen_zero(category))
07456       res = ast_play_and_wait(chan, category);
07457 
07458    if (res) {
07459       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07460       res = 0;
07461    }
07462 
07463    return res;
07464 }
07465 
07466 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07467 {
07468    int res = 0;
07469    struct vm_zone *the_zone = NULL;
07470    time_t t;
07471 
07472    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07473       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07474       return 0;
07475    }
07476 
07477    /* Does this user have a timezone specified? */
07478    if (!ast_strlen_zero(vmu->zonetag)) {
07479       /* Find the zone in the list */
07480       struct vm_zone *z;
07481       AST_LIST_LOCK(&zones);
07482       AST_LIST_TRAVERSE(&zones, z, list) {
07483          if (!strcmp(z->name, vmu->zonetag)) {
07484             the_zone = z;
07485             break;
07486          }
07487       }
07488       AST_LIST_UNLOCK(&zones);
07489    }
07490 
07491 /* No internal variable parsing for now, so we'll comment it out for the time being */
07492 #if 0
07493    /* Set the DIFF_* variables */
07494    ast_localtime(&t, &time_now, NULL);
07495    tv_now = ast_tvnow();
07496    ast_localtime(&tv_now, &time_then, NULL);
07497 
07498    /* Day difference */
07499    if (time_now.tm_year == time_then.tm_year)
07500       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07501    else
07502       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07503    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07504 
07505    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07506 #endif
07507    if (the_zone) {
07508       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07509    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07510       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07511    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07512       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07513    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07514       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
07515    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07516       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07517    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07518       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07519    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07520       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07521    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07522       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
07523    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07524       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07525    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07526       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07527    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07528       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' A 'digits/day' dB 'digits/year' Y 'digits/at' k 'hours' M 'minutes'", NULL);
07529    } else {
07530       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07531    }
07532 #if 0
07533    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07534 #endif
07535    return res;
07536 }
07537 
07538 
07539 
07540 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07541 {
07542    int res = 0;
07543    int i;
07544    char *callerid, *name;
07545    char prefile[PATH_MAX] = "";
07546    
07547 
07548    /* If voicemail cid is not enabled, or we didn't get cid or context from
07549     * the attribute file, leave now.
07550     *
07551     * TODO Still need to change this so that if this function is called by the
07552     * message envelope (and someone is explicitly requesting to hear the CID),
07553     * it does not check to see if CID is enabled in the config file.
07554     */
07555    if ((cid == NULL)||(context == NULL))
07556       return res;
07557 
07558    /* Strip off caller ID number from name */
07559    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07560    ast_callerid_parse(cid, &name, &callerid);
07561    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07562       /* Check for internal contexts and only */
07563       /* say extension when the call didn't come from an internal context in the list */
07564       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07565          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07566          if ((strcmp(cidinternalcontexts[i], context) == 0))
07567             break;
07568       }
07569       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07570          if (!res) {
07571             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07572             if (!ast_strlen_zero(prefile)) {
07573             /* See if we can find a recorded name for this person instead of their extension number */
07574                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07575                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07576                   if (!callback)
07577                      res = wait_file2(chan, vms, "vm-from");
07578                   res = ast_stream_and_wait(chan, prefile, "");
07579                } else {
07580                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07581                   /* Say "from extension" as one saying to sound smoother */
07582                   if (!callback)
07583                      res = wait_file2(chan, vms, "vm-from-extension");
07584                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07585                }
07586             }
07587          }
07588       } else if (!res) {
07589          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07590          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07591          if (!callback)
07592             res = wait_file2(chan, vms, "vm-from-phonenumber");
07593          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07594       }
07595    } else {
07596       /* Number unknown */
07597       ast_debug(1, "VM-CID: From an unknown number\n");
07598       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07599       res = wait_file2(chan, vms, "vm-unknown-caller");
07600    }
07601    return res;
07602 }
07603 
07604 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07605 {
07606    int res = 0;
07607    int durationm;
07608    int durations;
07609    /* Verify that we have a duration for the message */
07610    if (duration == NULL)
07611       return res;
07612 
07613    /* Convert from seconds to minutes */
07614    durations = atoi(duration);
07615    durationm = (durations / 60);
07616 
07617    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07618 
07619    if ((!res) && (durationm >= minduration)) {
07620       res = wait_file2(chan, vms, "vm-duration");
07621 
07622       /* POLISH syntax */
07623       if (!strncasecmp(chan->language, "pl", 2)) {
07624          div_t num = div(durationm, 10);
07625 
07626          if (durationm == 1) {
07627             res = ast_play_and_wait(chan, "digits/1z");
07628             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07629          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07630             if (num.rem == 2) {
07631                if (!num.quot) {
07632                   res = ast_play_and_wait(chan, "digits/2-ie");
07633                } else {
07634                   res = say_and_wait(chan, durationm - 2 , chan->language);
07635                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07636                }
07637             } else {
07638                res = say_and_wait(chan, durationm, chan->language);
07639             }
07640             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07641          } else {
07642             res = say_and_wait(chan, durationm, chan->language);
07643             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07644          }
07645       /* DEFAULT syntax */
07646       } else {
07647          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07648          res = wait_file2(chan, vms, "vm-minutes");
07649       }
07650    }
07651    return res;
07652 }
07653 
07654 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07655 {
07656    int res = 0;
07657    char filename[256], *cid;
07658    const char *origtime, *context, *category, *duration, *flag;
07659    struct ast_config *msg_cfg;
07660    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07661 
07662    vms->starting = 0;
07663    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07664    adsi_message(chan, vms);
07665    if (!vms->curmsg) {
07666       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07667    } else if (vms->curmsg == vms->lastmsg) {
07668       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07669    }
07670 
07671    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07672    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07673    msg_cfg = ast_config_load(filename, config_flags);
07674    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07675       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07676       return 0;
07677    }
07678    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07679 
07680    /* Play the word urgent if we are listening to urgent messages */
07681    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07682       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07683    }
07684 
07685    if (!res) {
07686       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07687       /* POLISH syntax */
07688       if (!strncasecmp(chan->language, "pl", 2)) {
07689          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07690             int ten, one;
07691             char nextmsg[256];
07692             ten = (vms->curmsg + 1) / 10;
07693             one = (vms->curmsg + 1) % 10;
07694 
07695             if (vms->curmsg < 20) {
07696                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07697                res = wait_file2(chan, vms, nextmsg);
07698             } else {
07699                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07700                res = wait_file2(chan, vms, nextmsg);
07701                if (one > 0) {
07702                   if (!res) {
07703                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07704                      res = wait_file2(chan, vms, nextmsg);
07705                   }
07706                }
07707             }
07708          }
07709          if (!res)
07710             res = wait_file2(chan, vms, "vm-message");
07711       /* HEBREW syntax */
07712       } else if (!strncasecmp(chan->language, "he", 2)) {
07713          if (!vms->curmsg) {
07714             res = wait_file2(chan, vms, "vm-message");
07715             res = wait_file2(chan, vms, "vm-first");
07716          } else if (vms->curmsg == vms->lastmsg) {
07717             res = wait_file2(chan, vms, "vm-message");
07718             res = wait_file2(chan, vms, "vm-last");
07719          } else {
07720             res = wait_file2(chan, vms, "vm-message");
07721             res = wait_file2(chan, vms, "vm-number");
07722             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07723          }
07724       /* VIETNAMESE syntax */
07725       } else if (!strncasecmp(chan->language, "vi", 2)) {
07726          if (!vms->curmsg) {
07727             res = wait_file2(chan, vms, "vm-message");
07728             res = wait_file2(chan, vms, "vm-first");
07729          } else if (vms->curmsg == vms->lastmsg) {
07730             res = wait_file2(chan, vms, "vm-message");
07731             res = wait_file2(chan, vms, "vm-last");
07732          } else {
07733             res = wait_file2(chan, vms, "vm-message");
07734             res = wait_file2(chan, vms, "vm-number");
07735             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07736          }
07737       } else {
07738          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07739             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07740          } else { /* DEFAULT syntax */
07741             res = wait_file2(chan, vms, "vm-message");
07742          }
07743          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07744             if (!res) {
07745                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07746                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07747             }
07748          }
07749       }
07750    }
07751 
07752    if (!msg_cfg) {
07753       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07754       return 0;
07755    }
07756 
07757    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07758       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07759       DISPOSE(vms->curdir, vms->curmsg);
07760       ast_config_destroy(msg_cfg);
07761       return 0;
07762    }
07763 
07764    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07765    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07766    category = ast_variable_retrieve(msg_cfg, "message", "category");
07767 
07768    context = ast_variable_retrieve(msg_cfg, "message", "context");
07769    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07770       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07771    if (!res) {
07772       res = play_message_category(chan, category);
07773    }
07774    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07775       res = play_message_datetime(chan, vmu, origtime, filename);
07776    }
07777    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07778       res = play_message_callerid(chan, vms, cid, context, 0);
07779    }
07780    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07781       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07782    }
07783    /* Allow pressing '1' to skip envelope / callerid */
07784    if (res == '1') {
07785       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07786       res = 0;
07787    }
07788    ast_config_destroy(msg_cfg);
07789 
07790    if (!res) {
07791       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07792       vms->heard[vms->curmsg] = 1;
07793 #ifdef IMAP_STORAGE
07794       /*IMAP storage stores any prepended message from a forward
07795        * as a separate file from the rest of the message
07796        */
07797       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07798          wait_file(chan, vms, vms->introfn);
07799       }
07800 #endif
07801       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07802          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07803          res = 0;
07804       }
07805       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07806    }
07807    DISPOSE(vms->curdir, vms->curmsg);
07808    return res;
07809 }
07810 
07811 #ifdef IMAP_STORAGE
07812 static int imap_remove_file(char *dir, int msgnum)
07813 {
07814    char fn[PATH_MAX];
07815    char full_fn[PATH_MAX];
07816    char intro[PATH_MAX] = {0,};
07817    
07818    if (msgnum > -1) {
07819       make_file(fn, sizeof(fn), dir, msgnum);
07820       snprintf(intro, sizeof(intro), "%sintro", fn);
07821    } else
07822       ast_copy_string(fn, dir, sizeof(fn));
07823    
07824    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07825       ast_filedelete(fn, NULL);
07826       if (!ast_strlen_zero(intro)) {
07827          ast_filedelete(intro, NULL);
07828       }
07829       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07830       unlink(full_fn);
07831    }
07832    return 0;
07833 }
07834 
07835 
07836 
07837 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07838 {
07839    char *file, *filename;
07840    char *attachment;
07841    char arg[10];
07842    int i;
07843    BODY* body;
07844 
07845    file = strrchr(ast_strdupa(dir), '/');
07846    if (file) {
07847       *file++ = '\0';
07848    } else {
07849       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07850       return -1;
07851    }
07852 
07853    ast_mutex_lock(&vms->lock);
07854    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07855       mail_fetchstructure(vms->mailstream, i + 1, &body);
07856       /* We have the body, now we extract the file name of the first attachment. */
07857       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07858          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07859       } else {
07860          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07861          ast_mutex_unlock(&vms->lock);
07862          return -1;
07863       }
07864       filename = strsep(&attachment, ".");
07865       if (!strcmp(filename, file)) {
07866          sprintf(arg, "%d", i + 1);
07867          mail_setflag(vms->mailstream, arg, "\\DELETED");
07868       }
07869    }
07870    mail_expunge(vms->mailstream);
07871    ast_mutex_unlock(&vms->lock);
07872    return 0;
07873 }
07874 
07875 #elif !defined(IMAP_STORAGE)
07876 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07877 {
07878    int count_msg, last_msg;
07879 
07880    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07881 
07882    /* Rename the member vmbox HERE so that we don't try to return before
07883     * we know what's going on.
07884     */
07885    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07886 
07887    /* Faster to make the directory than to check if it exists. */
07888    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07889 
07890    /* traverses directory using readdir (or select query for ODBC) */
07891    count_msg = count_messages(vmu, vms->curdir);
07892    if (count_msg < 0) {
07893       return count_msg;
07894    } else {
07895       vms->lastmsg = count_msg - 1;
07896    }
07897 
07898    if (vm_allocate_dh(vms, vmu, count_msg)) {
07899       return -1;
07900    }
07901 
07902    /*
07903    The following test is needed in case sequencing gets messed up.
07904    There appears to be more than one way to mess up sequence, so
07905    we will not try to find all of the root causes--just fix it when
07906    detected.
07907    */
07908 
07909    if (vm_lock_path(vms->curdir)) {
07910       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07911       return ERROR_LOCK_PATH;
07912    }
07913 
07914    /* for local storage, checks directory for messages up to maxmsg limit */
07915    last_msg = last_message_index(vmu, vms->curdir);
07916    ast_unlock_path(vms->curdir);
07917 
07918    if (last_msg < -1) {
07919       return last_msg;
07920    } else if (vms->lastmsg != last_msg) {
07921       ast_log(LOG_NOTICE, "Resequencing Mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
07922       resequence_mailbox(vmu, vms->curdir, count_msg);
07923    }
07924 
07925    return 0;
07926 }
07927 #endif
07928 
07929 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07930 {
07931    int x = 0;
07932 
07933 #ifndef IMAP_STORAGE
07934    int last_msg_idx;
07935    int res = 0, nummsg;
07936    char fn2[PATH_MAX];
07937 #endif
07938 
07939    if (vms->lastmsg <= -1) {
07940       goto done;
07941    }
07942 
07943    vms->curmsg = -1;
07944 #ifndef IMAP_STORAGE
07945    /* Get the deleted messages fixed */
07946    if (vm_lock_path(vms->curdir)) {
07947       return ERROR_LOCK_PATH;
07948    }
07949 
07950    /* update count as message may have arrived while we've got mailbox open */
07951    last_msg_idx = last_message_index(vmu, vms->curdir);
07952    if (last_msg_idx != vms->lastmsg) {
07953       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07954    }
07955 
07956    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07957    for (x = 0; x < last_msg_idx + 1; x++) {
07958       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07959          /* Save this message.  It's not in INBOX or hasn't been heard */
07960          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07961          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07962             break;
07963          }
07964          vms->curmsg++;
07965          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07966          if (strcmp(vms->fn, fn2)) {
07967             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07968          }
07969       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07970          /* Move to old folder before deleting */
07971          res = save_to_folder(vmu, vms, x, 1);
07972          if (res == ERROR_LOCK_PATH) {
07973             /* If save failed do not delete the message */
07974             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07975             vms->deleted[x] = 0;
07976             vms->heard[x] = 0;
07977             --x;
07978          }
07979       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07980          /* Move to deleted folder */
07981          res = save_to_folder(vmu, vms, x, 10);
07982          if (res == ERROR_LOCK_PATH) {
07983             /* If save failed do not delete the message */
07984             vms->deleted[x] = 0;
07985             vms->heard[x] = 0;
07986             --x;
07987          }
07988       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07989          /* If realtime storage enabled - we should explicitly delete this message,
07990          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07991          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07992          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07993             DELETE(vms->curdir, x, vms->fn, vmu);
07994          }
07995       }
07996    }
07997 
07998    /* Delete ALL remaining messages */
07999    nummsg = x - 1;
08000    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08001       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08002       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08003          DELETE(vms->curdir, x, vms->fn, vmu);
08004       }
08005    }
08006    ast_unlock_path(vms->curdir);
08007 #else /* defined(IMAP_STORAGE) */
08008    if (vms->deleted) {
08009       /* Since we now expunge after each delete, deleting in reverse order
08010        * ensures that no reordering occurs between each step. */
08011       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
08012          if (vms->deleted[x]) {
08013             ast_debug(3, "IMAP delete of %d\n", x);
08014             DELETE(vms->curdir, x, vms->fn, vmu);
08015          }
08016       }
08017    }
08018 #endif
08019 
08020 done:
08021    if (vms->deleted && vmu->maxmsg) {
08022       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
08023    }
08024    if (vms->heard && vmu->maxmsg) {
08025       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
08026    }
08027 
08028    return 0;
08029 }
08030 
08031 /* In Greek even though we CAN use a syntax like "friends messages"
08032  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08033  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08034  * syntax for the above three categories which is more elegant.
08035  */
08036 
08037 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08038 {
08039    int cmd;
08040    char *buf;
08041 
08042    buf = alloca(strlen(box) + 2);
08043    strcpy(buf, box);
08044    strcat(buf, "s");
08045 
08046    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08047       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08048       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08049    } else {
08050       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08051       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08052    }
08053 }
08054 
08055 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08056 {
08057    int cmd;
08058 
08059    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08060       if (!strcasecmp(box, "vm-INBOX"))
08061          cmd = ast_play_and_wait(chan, "vm-new-e");
08062       else
08063          cmd = ast_play_and_wait(chan, "vm-old-e");
08064       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08065    } else {
08066       cmd = ast_play_and_wait(chan, "vm-messages");
08067       return cmd ? cmd : ast_play_and_wait(chan, box);
08068    }
08069 }
08070 
08071 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08072 {
08073    int cmd;
08074 
08075    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08076       cmd = ast_play_and_wait(chan, "vm-messages");
08077       return cmd ? cmd : ast_play_and_wait(chan, box);
08078    } else {
08079       cmd = ast_play_and_wait(chan, box);
08080       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08081    }
08082 }
08083 
08084 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08085 {
08086    int cmd;
08087 
08088    if (  !strncasecmp(chan->language, "it", 2) ||
08089         !strncasecmp(chan->language, "es", 2) ||
08090         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08091       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08092       return cmd ? cmd : ast_play_and_wait(chan, box);
08093    } else if (!strncasecmp(chan->language, "gr", 2)) {
08094       return vm_play_folder_name_gr(chan, box);
08095    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08096       return ast_play_and_wait(chan, box);
08097    } else if (!strncasecmp(chan->language, "pl", 2)) {
08098       return vm_play_folder_name_pl(chan, box);
08099    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08100       return vm_play_folder_name_ua(chan, box);
08101    } else if (!strncasecmp(chan->language, "vi", 2)) {
08102       return ast_play_and_wait(chan, box);
08103    } else {  /* Default English */
08104       cmd = ast_play_and_wait(chan, box);
08105       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08106    }
08107 }
08108 
08109 /* GREEK SYNTAX
08110    In greek the plural for old/new is
08111    different so we need the following files
08112    We also need vm-denExeteMynhmata because
08113    this syntax is different.
08114 
08115    -> vm-Olds.wav : "Palia"
08116    -> vm-INBOXs.wav : "Nea"
08117    -> vm-denExeteMynhmata : "den exete mynhmata"
08118 */
08119 
08120 
08121 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08122 {
08123    int res = 0;
08124 
08125    if (vms->newmessages) {
08126       res = ast_play_and_wait(chan, "vm-youhave");
08127       if (!res) 
08128          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08129       if (!res) {
08130          if ((vms->newmessages == 1)) {
08131             res = ast_play_and_wait(chan, "vm-INBOX");
08132             if (!res)
08133                res = ast_play_and_wait(chan, "vm-message");
08134          } else {
08135             res = ast_play_and_wait(chan, "vm-INBOXs");
08136             if (!res)
08137                res = ast_play_and_wait(chan, "vm-messages");
08138          }
08139       }
08140    } else if (vms->oldmessages){
08141       res = ast_play_and_wait(chan, "vm-youhave");
08142       if (!res)
08143          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08144       if ((vms->oldmessages == 1)){
08145          res = ast_play_and_wait(chan, "vm-Old");
08146          if (!res)
08147             res = ast_play_and_wait(chan, "vm-message");
08148       } else {
08149          res = ast_play_and_wait(chan, "vm-Olds");
08150          if (!res)
08151             res = ast_play_and_wait(chan, "vm-messages");
08152       }
08153    } else if (!vms->oldmessages && !vms->newmessages) 
08154       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08155    return res;
08156 }
08157 
08158 /* Version of vm_intro() designed to work for many languages.
08159  *
08160  * It is hoped that this function can prevent the proliferation of 
08161  * language-specific vm_intro() functions and in time replace the language-
08162  * specific functions which already exist.  An examination of the language-
08163  * specific functions revealed that they all corrected the same deficiencies
08164  * in vm_intro_en() (which was the default function). Namely:
08165  *
08166  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08167  *     wording of the voicemail greeting hides this problem.  For example,
08168  *     vm-INBOX contains only the word "new".  This means that both of these
08169  *     sequences produce valid utterances:
08170  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08171  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08172  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08173  *     in many languages) the first utterance becomes "you have 1 the new message".
08174  *  2) The function contains hardcoded rules for pluralizing the word "message".
08175  *     These rules are correct for English, but not for many other languages.
08176  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08177  *     required in many languages.
08178  *  4) The gender of the word for "message" is not specified. This is a problem
08179  *     because in many languages the gender of the number in phrases such
08180  *     as "you have one new message" must match the gender of the word
08181  *     meaning "message".
08182  *
08183  * Fixing these problems for each new language has meant duplication of effort.
08184  * This new function solves the problems in the following general ways:
08185  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08186  *     and vm-Old respectively for those languages where it makes sense.
08187  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08188  *     on vm-message.
08189  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08190  *     prefix on vm-new and vm-old (none for English).
08191  *  4) Pass the gender of the language's word for "message" as an agument to
08192  *     this function which is can in turn pass on to the functions which 
08193  *     say numbers and put endings on nounds and adjectives.
08194  *
08195  * All languages require these messages:
08196  *  vm-youhave    "You have..."
08197  *  vm-and     "and"
08198  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08199  *
08200  * To use it for English, you will need these additional sound files:
08201  *  vm-new     "new"
08202  *  vm-message    "message", singular
08203  *  vm-messages      "messages", plural
08204  *
08205  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08206  *
08207  *  vm-newn    "novoye" (singular, neuter)
08208  *  vm-newx    "novikh" (counting plural form, genative plural)
08209  *  vm-message    "sobsheniye" (singular form)
08210  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08211  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08212  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08213  *  digits/2n     "dva" (neuter singular)
08214  */
08215 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08216 {
08217    int res;
08218    int lastnum = 0;
08219 
08220    res = ast_play_and_wait(chan, "vm-youhave");
08221 
08222    if (!res && vms->newmessages) {
08223       lastnum = vms->newmessages;
08224 
08225       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08226          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08227       }
08228 
08229       if (!res && vms->oldmessages) {
08230          res = ast_play_and_wait(chan, "vm-and");
08231       }
08232    }
08233 
08234    if (!res && vms->oldmessages) {
08235       lastnum = vms->oldmessages;
08236 
08237       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08238          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08239       }
08240    }
08241 
08242    if (!res) {
08243       if (lastnum == 0) {
08244          res = ast_play_and_wait(chan, "vm-no");
08245       }
08246       if (!res) {
08247          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08248       }
08249    }
08250 
08251    return res;
08252 }
08253 
08254 /* Default Hebrew syntax */
08255 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08256 {
08257    int res = 0;
08258 
08259    /* Introduce messages they have */
08260    if (!res) {
08261       if ((vms->newmessages) || (vms->oldmessages)) {
08262          res = ast_play_and_wait(chan, "vm-youhave");
08263       }
08264       /*
08265        * The word "shtei" refers to the number 2 in hebrew when performing a count
08266        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08267        * an element, this is one of them.
08268        */
08269       if (vms->newmessages) {
08270          if (!res) {
08271             if (vms->newmessages == 1) {
08272                res = ast_play_and_wait(chan, "vm-INBOX1");
08273             } else {
08274                if (vms->newmessages == 2) {
08275                   res = ast_play_and_wait(chan, "vm-shtei");
08276                } else {
08277                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08278                }
08279                res = ast_play_and_wait(chan, "vm-INBOX");
08280             }
08281          }
08282          if (vms->oldmessages && !res) {
08283             res = ast_play_and_wait(chan, "vm-and");
08284             if (vms->oldmessages == 1) {
08285                res = ast_play_and_wait(chan, "vm-Old1");
08286             } else {
08287                if (vms->oldmessages == 2) {
08288                   res = ast_play_and_wait(chan, "vm-shtei");
08289                } else {
08290                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08291                }
08292                res = ast_play_and_wait(chan, "vm-Old");
08293             }
08294          }
08295       }
08296       if (!res && vms->oldmessages && !vms->newmessages) {
08297          if (!res) {
08298             if (vms->oldmessages == 1) {
08299                res = ast_play_and_wait(chan, "vm-Old1");
08300             } else {
08301                if (vms->oldmessages == 2) {
08302                   res = ast_play_and_wait(chan, "vm-shtei");
08303                } else {
08304                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08305                }
08306                res = ast_play_and_wait(chan, "vm-Old");
08307             }
08308          }
08309       }
08310       if (!res) {
08311          if (!vms->oldmessages && !vms->newmessages) {
08312             if (!res) {
08313                res = ast_play_and_wait(chan, "vm-nomessages");
08314             }
08315          }
08316       }
08317    }
08318    return res;
08319 }
08320    
08321 /* Default English syntax */
08322 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08323 {
08324    int res;
08325 
08326    /* Introduce messages they have */
08327    res = ast_play_and_wait(chan, "vm-youhave");
08328    if (!res) {
08329       if (vms->urgentmessages) {
08330          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08331          if (!res)
08332             res = ast_play_and_wait(chan, "vm-Urgent");
08333          if ((vms->oldmessages || vms->newmessages) && !res) {
08334             res = ast_play_and_wait(chan, "vm-and");
08335          } else if (!res) {
08336             if ((vms->urgentmessages == 1))
08337                res = ast_play_and_wait(chan, "vm-message");
08338             else
08339                res = ast_play_and_wait(chan, "vm-messages");
08340          }
08341       }
08342       if (vms->newmessages) {
08343          res = say_and_wait(chan, vms->newmessages, chan->language);
08344          if (!res)
08345             res = ast_play_and_wait(chan, "vm-INBOX");
08346          if (vms->oldmessages && !res)
08347             res = ast_play_and_wait(chan, "vm-and");
08348          else if (!res) {
08349             if ((vms->newmessages == 1))
08350                res = ast_play_and_wait(chan, "vm-message");
08351             else
08352                res = ast_play_and_wait(chan, "vm-messages");
08353          }
08354             
08355       }
08356       if (!res && vms->oldmessages) {
08357          res = say_and_wait(chan, vms->oldmessages, chan->language);
08358          if (!res)
08359             res = ast_play_and_wait(chan, "vm-Old");
08360          if (!res) {
08361             if (vms->oldmessages == 1)
08362                res = ast_play_and_wait(chan, "vm-message");
08363             else
08364                res = ast_play_and_wait(chan, "vm-messages");
08365          }
08366       }
08367       if (!res) {
08368          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08369             res = ast_play_and_wait(chan, "vm-no");
08370             if (!res)
08371                res = ast_play_and_wait(chan, "vm-messages");
08372          }
08373       }
08374    }
08375    return res;
08376 }
08377 
08378 /* ITALIAN syntax */
08379 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08380 {
08381    /* Introduce messages they have */
08382    int res;
08383    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08384       res = ast_play_and_wait(chan, "vm-no") ||
08385          ast_play_and_wait(chan, "vm-message");
08386    else
08387       res = ast_play_and_wait(chan, "vm-youhave");
08388    if (!res && vms->newmessages) {
08389       res = (vms->newmessages == 1) ?
08390          ast_play_and_wait(chan, "digits/un") ||
08391          ast_play_and_wait(chan, "vm-nuovo") ||
08392          ast_play_and_wait(chan, "vm-message") :
08393          /* 2 or more new messages */
08394          say_and_wait(chan, vms->newmessages, chan->language) ||
08395          ast_play_and_wait(chan, "vm-nuovi") ||
08396          ast_play_and_wait(chan, "vm-messages");
08397       if (!res && vms->oldmessages)
08398          res = ast_play_and_wait(chan, "vm-and");
08399    }
08400    if (!res && vms->oldmessages) {
08401       res = (vms->oldmessages == 1) ?
08402          ast_play_and_wait(chan, "digits/un") ||
08403          ast_play_and_wait(chan, "vm-vecchio") ||
08404          ast_play_and_wait(chan, "vm-message") :
08405          /* 2 or more old messages */
08406          say_and_wait(chan, vms->oldmessages, chan->language) ||
08407          ast_play_and_wait(chan, "vm-vecchi") ||
08408          ast_play_and_wait(chan, "vm-messages");
08409    }
08410    return res;
08411 }
08412 
08413 /* POLISH syntax */
08414 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08415 {
08416    /* Introduce messages they have */
08417    int res;
08418    div_t num;
08419 
08420    if (!vms->oldmessages && !vms->newmessages) {
08421       res = ast_play_and_wait(chan, "vm-no");
08422       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08423       return res;
08424    } else {
08425       res = ast_play_and_wait(chan, "vm-youhave");
08426    }
08427 
08428    if (vms->newmessages) {
08429       num = div(vms->newmessages, 10);
08430       if (vms->newmessages == 1) {
08431          res = ast_play_and_wait(chan, "digits/1-a");
08432          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08433          res = res ? res : ast_play_and_wait(chan, "vm-message");
08434       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08435          if (num.rem == 2) {
08436             if (!num.quot) {
08437                res = ast_play_and_wait(chan, "digits/2-ie");
08438             } else {
08439                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08440                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08441             }
08442          } else {
08443             res = say_and_wait(chan, vms->newmessages, chan->language);
08444          }
08445          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08446          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08447       } else {
08448          res = say_and_wait(chan, vms->newmessages, chan->language);
08449          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08450          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08451       }
08452       if (!res && vms->oldmessages)
08453          res = ast_play_and_wait(chan, "vm-and");
08454    }
08455    if (!res && vms->oldmessages) {
08456       num = div(vms->oldmessages, 10);
08457       if (vms->oldmessages == 1) {
08458          res = ast_play_and_wait(chan, "digits/1-a");
08459          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08460          res = res ? res : ast_play_and_wait(chan, "vm-message");
08461       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08462          if (num.rem == 2) {
08463             if (!num.quot) {
08464                res = ast_play_and_wait(chan, "digits/2-ie");
08465             } else {
08466                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08467                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08468             }
08469          } else {
08470             res = say_and_wait(chan, vms->oldmessages, chan->language);
08471          }
08472          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08473          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08474       } else {
08475          res = say_and_wait(chan, vms->oldmessages, chan->language);
08476          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08477          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08478       }
08479    }
08480 
08481    return res;
08482 }
08483 
08484 /* SWEDISH syntax */
08485 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08486 {
08487    /* Introduce messages they have */
08488    int res;
08489 
08490    res = ast_play_and_wait(chan, "vm-youhave");
08491    if (res)
08492       return res;
08493 
08494    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08495       res = ast_play_and_wait(chan, "vm-no");
08496       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08497       return res;
08498    }
08499 
08500    if (vms->newmessages) {
08501       if ((vms->newmessages == 1)) {
08502          res = ast_play_and_wait(chan, "digits/ett");
08503          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08504          res = res ? res : ast_play_and_wait(chan, "vm-message");
08505       } else {
08506          res = say_and_wait(chan, vms->newmessages, chan->language);
08507          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08508          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08509       }
08510       if (!res && vms->oldmessages)
08511          res = ast_play_and_wait(chan, "vm-and");
08512    }
08513    if (!res && vms->oldmessages) {
08514       if (vms->oldmessages == 1) {
08515          res = ast_play_and_wait(chan, "digits/ett");
08516          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08517          res = res ? res : ast_play_and_wait(chan, "vm-message");
08518       } else {
08519          res = say_and_wait(chan, vms->oldmessages, chan->language);
08520          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08521          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08522       }
08523    }
08524 
08525    return res;
08526 }
08527 
08528 /* NORWEGIAN syntax */
08529 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08530 {
08531    /* Introduce messages they have */
08532    int res;
08533 
08534    res = ast_play_and_wait(chan, "vm-youhave");
08535    if (res)
08536       return res;
08537 
08538    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08539       res = ast_play_and_wait(chan, "vm-no");
08540       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08541       return res;
08542    }
08543 
08544    if (vms->newmessages) {
08545       if ((vms->newmessages == 1)) {
08546          res = ast_play_and_wait(chan, "digits/1");
08547          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08548          res = res ? res : ast_play_and_wait(chan, "vm-message");
08549       } else {
08550          res = say_and_wait(chan, vms->newmessages, chan->language);
08551          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08552          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08553       }
08554       if (!res && vms->oldmessages)
08555          res = ast_play_and_wait(chan, "vm-and");
08556    }
08557    if (!res && vms->oldmessages) {
08558       if (vms->oldmessages == 1) {
08559          res = ast_play_and_wait(chan, "digits/1");
08560          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08561          res = res ? res : ast_play_and_wait(chan, "vm-message");
08562       } else {
08563          res = say_and_wait(chan, vms->oldmessages, chan->language);
08564          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08565          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08566       }
08567    }
08568 
08569    return res;
08570 }
08571 
08572 /* GERMAN syntax */
08573 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08574 {
08575    /* Introduce messages they have */
08576    int res;
08577    res = ast_play_and_wait(chan, "vm-youhave");
08578    if (!res) {
08579       if (vms->newmessages) {
08580          if ((vms->newmessages == 1))
08581             res = ast_play_and_wait(chan, "digits/1F");
08582          else
08583             res = say_and_wait(chan, vms->newmessages, chan->language);
08584          if (!res)
08585             res = ast_play_and_wait(chan, "vm-INBOX");
08586          if (vms->oldmessages && !res)
08587             res = ast_play_and_wait(chan, "vm-and");
08588          else if (!res) {
08589             if ((vms->newmessages == 1))
08590                res = ast_play_and_wait(chan, "vm-message");
08591             else
08592                res = ast_play_and_wait(chan, "vm-messages");
08593          }
08594             
08595       }
08596       if (!res && vms->oldmessages) {
08597          if (vms->oldmessages == 1)
08598             res = ast_play_and_wait(chan, "digits/1F");
08599          else
08600             res = say_and_wait(chan, vms->oldmessages, chan->language);
08601          if (!res)
08602             res = ast_play_and_wait(chan, "vm-Old");
08603          if (!res) {
08604             if (vms->oldmessages == 1)
08605                res = ast_play_and_wait(chan, "vm-message");
08606             else
08607                res = ast_play_and_wait(chan, "vm-messages");
08608          }
08609       }
08610       if (!res) {
08611          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08612             res = ast_play_and_wait(chan, "vm-no");
08613             if (!res)
08614                res = ast_play_and_wait(chan, "vm-messages");
08615          }
08616       }
08617    }
08618    return res;
08619 }
08620 
08621 /* SPANISH syntax */
08622 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08623 {
08624    /* Introduce messages they have */
08625    int res;
08626    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08627       res = ast_play_and_wait(chan, "vm-youhaveno");
08628       if (!res)
08629          res = ast_play_and_wait(chan, "vm-messages");
08630    } else {
08631       res = ast_play_and_wait(chan, "vm-youhave");
08632    }
08633    if (!res) {
08634       if (vms->newmessages) {
08635          if (!res) {
08636             if ((vms->newmessages == 1)) {
08637                res = ast_play_and_wait(chan, "digits/1M");
08638                if (!res)
08639                   res = ast_play_and_wait(chan, "vm-message");
08640                if (!res)
08641                   res = ast_play_and_wait(chan, "vm-INBOXs");
08642             } else {
08643                res = say_and_wait(chan, vms->newmessages, chan->language);
08644                if (!res)
08645                   res = ast_play_and_wait(chan, "vm-messages");
08646                if (!res)
08647                   res = ast_play_and_wait(chan, "vm-INBOX");
08648             }
08649          }
08650          if (vms->oldmessages && !res)
08651             res = ast_play_and_wait(chan, "vm-and");
08652       }
08653       if (vms->oldmessages) {
08654          if (!res) {
08655             if (vms->oldmessages == 1) {
08656                res = ast_play_and_wait(chan, "digits/1M");
08657                if (!res)
08658                   res = ast_play_and_wait(chan, "vm-message");
08659                if (!res)
08660                   res = ast_play_and_wait(chan, "vm-Olds");
08661             } else {
08662                res = say_and_wait(chan, vms->oldmessages, chan->language);
08663                if (!res)
08664                   res = ast_play_and_wait(chan, "vm-messages");
08665                if (!res)
08666                   res = ast_play_and_wait(chan, "vm-Old");
08667             }
08668          }
08669       }
08670    }
08671 return res;
08672 }
08673 
08674 /* BRAZILIAN PORTUGUESE syntax */
08675 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08676    /* Introduce messages they have */
08677    int res;
08678    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08679       res = ast_play_and_wait(chan, "vm-nomessages");
08680       return res;
08681    } else {
08682       res = ast_play_and_wait(chan, "vm-youhave");
08683    }
08684    if (vms->newmessages) {
08685       if (!res)
08686          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08687       if ((vms->newmessages == 1)) {
08688          if (!res)
08689             res = ast_play_and_wait(chan, "vm-message");
08690          if (!res)
08691             res = ast_play_and_wait(chan, "vm-INBOXs");
08692       } else {
08693          if (!res)
08694             res = ast_play_and_wait(chan, "vm-messages");
08695          if (!res)
08696             res = ast_play_and_wait(chan, "vm-INBOX");
08697       }
08698       if (vms->oldmessages && !res)
08699          res = ast_play_and_wait(chan, "vm-and");
08700    }
08701    if (vms->oldmessages) {
08702       if (!res)
08703          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08704       if (vms->oldmessages == 1) {
08705          if (!res)
08706             res = ast_play_and_wait(chan, "vm-message");
08707          if (!res)
08708             res = ast_play_and_wait(chan, "vm-Olds");
08709       } else {
08710          if (!res)
08711             res = ast_play_and_wait(chan, "vm-messages");
08712          if (!res)
08713             res = ast_play_and_wait(chan, "vm-Old");
08714       }
08715    }
08716    return res;
08717 }
08718 
08719 /* FRENCH syntax */
08720 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08721 {
08722    /* Introduce messages they have */
08723    int res;
08724    res = ast_play_and_wait(chan, "vm-youhave");
08725    if (!res) {
08726       if (vms->newmessages) {
08727          res = say_and_wait(chan, vms->newmessages, chan->language);
08728          if (!res)
08729             res = ast_play_and_wait(chan, "vm-INBOX");
08730          if (vms->oldmessages && !res)
08731             res = ast_play_and_wait(chan, "vm-and");
08732          else if (!res) {
08733             if ((vms->newmessages == 1))
08734                res = ast_play_and_wait(chan, "vm-message");
08735             else
08736                res = ast_play_and_wait(chan, "vm-messages");
08737          }
08738             
08739       }
08740       if (!res && vms->oldmessages) {
08741          res = say_and_wait(chan, vms->oldmessages, chan->language);
08742          if (!res)
08743             res = ast_play_and_wait(chan, "vm-Old");
08744          if (!res) {
08745             if (vms->oldmessages == 1)
08746                res = ast_play_and_wait(chan, "vm-message");
08747             else
08748                res = ast_play_and_wait(chan, "vm-messages");
08749          }
08750       }
08751       if (!res) {
08752          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08753             res = ast_play_and_wait(chan, "vm-no");
08754             if (!res)
08755                res = ast_play_and_wait(chan, "vm-messages");
08756          }
08757       }
08758    }
08759    return res;
08760 }
08761 
08762 /* DUTCH syntax */
08763 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08764 {
08765    /* Introduce messages they have */
08766    int res;
08767    res = ast_play_and_wait(chan, "vm-youhave");
08768    if (!res) {
08769       if (vms->newmessages) {
08770          res = say_and_wait(chan, vms->newmessages, chan->language);
08771          if (!res) {
08772             if (vms->newmessages == 1)
08773                res = ast_play_and_wait(chan, "vm-INBOXs");
08774             else
08775                res = ast_play_and_wait(chan, "vm-INBOX");
08776          }
08777          if (vms->oldmessages && !res)
08778             res = ast_play_and_wait(chan, "vm-and");
08779          else if (!res) {
08780             if ((vms->newmessages == 1))
08781                res = ast_play_and_wait(chan, "vm-message");
08782             else
08783                res = ast_play_and_wait(chan, "vm-messages");
08784          }
08785             
08786       }
08787       if (!res && vms->oldmessages) {
08788          res = say_and_wait(chan, vms->oldmessages, chan->language);
08789          if (!res) {
08790             if (vms->oldmessages == 1)
08791                res = ast_play_and_wait(chan, "vm-Olds");
08792             else
08793                res = ast_play_and_wait(chan, "vm-Old");
08794          }
08795          if (!res) {
08796             if (vms->oldmessages == 1)
08797                res = ast_play_and_wait(chan, "vm-message");
08798             else
08799                res = ast_play_and_wait(chan, "vm-messages");
08800          }
08801       }
08802       if (!res) {
08803          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08804             res = ast_play_and_wait(chan, "vm-no");
08805             if (!res)
08806                res = ast_play_and_wait(chan, "vm-messages");
08807          }
08808       }
08809    }
08810    return res;
08811 }
08812 
08813 /* PORTUGUESE syntax */
08814 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08815 {
08816    /* Introduce messages they have */
08817    int res;
08818    res = ast_play_and_wait(chan, "vm-youhave");
08819    if (!res) {
08820       if (vms->newmessages) {
08821          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08822          if (!res) {
08823             if ((vms->newmessages == 1)) {
08824                res = ast_play_and_wait(chan, "vm-message");
08825                if (!res)
08826                   res = ast_play_and_wait(chan, "vm-INBOXs");
08827             } else {
08828                res = ast_play_and_wait(chan, "vm-messages");
08829                if (!res)
08830                   res = ast_play_and_wait(chan, "vm-INBOX");
08831             }
08832          }
08833          if (vms->oldmessages && !res)
08834             res = ast_play_and_wait(chan, "vm-and");
08835       }
08836       if (!res && vms->oldmessages) {
08837          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08838          if (!res) {
08839             if (vms->oldmessages == 1) {
08840                res = ast_play_and_wait(chan, "vm-message");
08841                if (!res)
08842                   res = ast_play_and_wait(chan, "vm-Olds");
08843             } else {
08844                res = ast_play_and_wait(chan, "vm-messages");
08845                if (!res)
08846                   res = ast_play_and_wait(chan, "vm-Old");
08847             }
08848          }
08849       }
08850       if (!res) {
08851          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08852             res = ast_play_and_wait(chan, "vm-no");
08853             if (!res)
08854                res = ast_play_and_wait(chan, "vm-messages");
08855          }
08856       }
08857    }
08858    return res;
08859 }
08860 
08861 
08862 /* CZECH syntax */
08863 /* in czech there must be declension of word new and message
08864  * czech        : english        : czech      : english
08865  * --------------------------------------------------------
08866  * vm-youhave   : you have 
08867  * vm-novou     : one new        : vm-zpravu  : message
08868  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08869  * vm-novych    : 5-infinite new : vm-zprav   : messages
08870  * vm-starou   : one old
08871  * vm-stare     : 2-4 old 
08872  * vm-starych   : 5-infinite old
08873  * jednu        : one   - falling 4. 
08874  * vm-no        : no  ( no messages )
08875  */
08876 
08877 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08878 {
08879    int res;
08880    res = ast_play_and_wait(chan, "vm-youhave");
08881    if (!res) {
08882       if (vms->newmessages) {
08883          if (vms->newmessages == 1) {
08884             res = ast_play_and_wait(chan, "digits/jednu");
08885          } else {
08886             res = say_and_wait(chan, vms->newmessages, chan->language);
08887          }
08888          if (!res) {
08889             if ((vms->newmessages == 1))
08890                res = ast_play_and_wait(chan, "vm-novou");
08891             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08892                res = ast_play_and_wait(chan, "vm-nove");
08893             if (vms->newmessages > 4)
08894                res = ast_play_and_wait(chan, "vm-novych");
08895          }
08896          if (vms->oldmessages && !res)
08897             res = ast_play_and_wait(chan, "vm-and");
08898          else if (!res) {
08899             if ((vms->newmessages == 1))
08900                res = ast_play_and_wait(chan, "vm-zpravu");
08901             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08902                res = ast_play_and_wait(chan, "vm-zpravy");
08903             if (vms->newmessages > 4)
08904                res = ast_play_and_wait(chan, "vm-zprav");
08905          }
08906       }
08907       if (!res && vms->oldmessages) {
08908          res = say_and_wait(chan, vms->oldmessages, chan->language);
08909          if (!res) {
08910             if ((vms->oldmessages == 1))
08911                res = ast_play_and_wait(chan, "vm-starou");
08912             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08913                res = ast_play_and_wait(chan, "vm-stare");
08914             if (vms->oldmessages > 4)
08915                res = ast_play_and_wait(chan, "vm-starych");
08916          }
08917          if (!res) {
08918             if ((vms->oldmessages == 1))
08919                res = ast_play_and_wait(chan, "vm-zpravu");
08920             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08921                res = ast_play_and_wait(chan, "vm-zpravy");
08922             if (vms->oldmessages > 4)
08923                res = ast_play_and_wait(chan, "vm-zprav");
08924          }
08925       }
08926       if (!res) {
08927          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08928             res = ast_play_and_wait(chan, "vm-no");
08929             if (!res)
08930                res = ast_play_and_wait(chan, "vm-zpravy");
08931          }
08932       }
08933    }
08934    return res;
08935 }
08936 
08937 /* CHINESE (Taiwan) syntax */
08938 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08939 {
08940    int res;
08941    /* Introduce messages they have */
08942    res = ast_play_and_wait(chan, "vm-you");
08943 
08944    if (!res && vms->newmessages) {
08945       res = ast_play_and_wait(chan, "vm-have");
08946       if (!res)
08947          res = say_and_wait(chan, vms->newmessages, chan->language);
08948       if (!res)
08949          res = ast_play_and_wait(chan, "vm-tong");
08950       if (!res)
08951          res = ast_play_and_wait(chan, "vm-INBOX");
08952       if (vms->oldmessages && !res)
08953          res = ast_play_and_wait(chan, "vm-and");
08954       else if (!res) 
08955          res = ast_play_and_wait(chan, "vm-messages");
08956    }
08957    if (!res && vms->oldmessages) {
08958       res = ast_play_and_wait(chan, "vm-have");
08959       if (!res)
08960          res = say_and_wait(chan, vms->oldmessages, chan->language);
08961       if (!res)
08962          res = ast_play_and_wait(chan, "vm-tong");
08963       if (!res)
08964          res = ast_play_and_wait(chan, "vm-Old");
08965       if (!res)
08966          res = ast_play_and_wait(chan, "vm-messages");
08967    }
08968    if (!res && !vms->oldmessages && !vms->newmessages) {
08969       res = ast_play_and_wait(chan, "vm-haveno");
08970       if (!res)
08971          res = ast_play_and_wait(chan, "vm-messages");
08972    }
08973    return res;
08974 }
08975 
08976 /* Vietnamese syntax */
08977 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08978 {
08979    int res;
08980 
08981    /* Introduce messages they have */
08982    res = ast_play_and_wait(chan, "vm-youhave");
08983    if (!res) {
08984       if (vms->newmessages) {
08985          res = say_and_wait(chan, vms->newmessages, chan->language);
08986          if (!res)
08987             res = ast_play_and_wait(chan, "vm-INBOX");
08988          if (vms->oldmessages && !res)
08989             res = ast_play_and_wait(chan, "vm-and");
08990       }
08991       if (!res && vms->oldmessages) {
08992          res = say_and_wait(chan, vms->oldmessages, chan->language);
08993          if (!res)
08994             res = ast_play_and_wait(chan, "vm-Old");        
08995       }
08996       if (!res) {
08997          if (!vms->oldmessages && !vms->newmessages) {
08998             res = ast_play_and_wait(chan, "vm-no");
08999             if (!res)
09000                res = ast_play_and_wait(chan, "vm-message");
09001          }
09002       }
09003    }
09004    return res;
09005 }
09006 
09007 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09008 {
09009    char prefile[256];
09010    
09011    /* Notify the user that the temp greeting is set and give them the option to remove it */
09012    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09013    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09014       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09015       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09016          ast_play_and_wait(chan, "vm-tempgreetactive");
09017       }
09018       DISPOSE(prefile, -1);
09019    }
09020 
09021    /* Play voicemail intro - syntax is different for different languages */
09022    if (0) {
09023       return 0;
09024    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09025       return vm_intro_cs(chan, vms);
09026    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09027       static int deprecation_warning = 0;
09028       if (deprecation_warning++ % 10 == 0) {
09029          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09030       }
09031       return vm_intro_cs(chan, vms);
09032    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09033       return vm_intro_de(chan, vms);
09034    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09035       return vm_intro_es(chan, vms);
09036    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09037       return vm_intro_fr(chan, vms);
09038    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09039       return vm_intro_gr(chan, vms);
09040    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09041       return vm_intro_he(chan, vms);
09042    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09043       return vm_intro_it(chan, vms);
09044    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09045       return vm_intro_nl(chan, vms);
09046    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09047       return vm_intro_no(chan, vms);
09048    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09049       return vm_intro_pl(chan, vms);
09050    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09051       return vm_intro_pt_BR(chan, vms);
09052    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09053       return vm_intro_pt(chan, vms);
09054    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09055       return vm_intro_multilang(chan, vms, "n");
09056    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09057       return vm_intro_se(chan, vms);
09058    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09059       return vm_intro_multilang(chan, vms, "n");
09060    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09061       return vm_intro_vi(chan, vms);
09062    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09063       return vm_intro_zh(chan, vms);
09064    } else {                                             /* Default to ENGLISH */
09065       return vm_intro_en(chan, vms);
09066    }
09067 }
09068 
09069 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09070 {
09071    int res = 0;
09072    /* Play instructions and wait for new command */
09073    while (!res) {
09074       if (vms->starting) {
09075          if (vms->lastmsg > -1) {
09076             if (skipadvanced)
09077                res = ast_play_and_wait(chan, "vm-onefor-full");
09078             else
09079                res = ast_play_and_wait(chan, "vm-onefor");
09080             if (!res)
09081                res = vm_play_folder_name(chan, vms->vmbox);
09082          }
09083          if (!res) {
09084             if (skipadvanced)
09085                res = ast_play_and_wait(chan, "vm-opts-full");
09086             else
09087                res = ast_play_and_wait(chan, "vm-opts");
09088          }
09089       } else {
09090          /* Added for additional help */
09091          if (skipadvanced) {
09092             res = ast_play_and_wait(chan, "vm-onefor-full");
09093             if (!res)
09094                res = vm_play_folder_name(chan, vms->vmbox);
09095             res = ast_play_and_wait(chan, "vm-opts-full");
09096          }
09097          /* Logic:
09098           * If the current message is not the first OR
09099           * if we're listening to the first new message and there are
09100           * also urgent messages, then prompt for navigation to the
09101           * previous message
09102           */
09103          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09104             res = ast_play_and_wait(chan, "vm-prev");
09105          }
09106          if (!res && !skipadvanced)
09107             res = ast_play_and_wait(chan, "vm-advopts");
09108          if (!res)
09109             res = ast_play_and_wait(chan, "vm-repeat");
09110          /* Logic:
09111           * If we're not listening to the last message OR
09112           * we're listening to the last urgent message and there are
09113           * also new non-urgent messages, then prompt for navigation
09114           * to the next message
09115           */
09116          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09117             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09118             res = ast_play_and_wait(chan, "vm-next");
09119          }
09120          if (!res) {
09121             if (!vms->deleted[vms->curmsg])
09122                res = ast_play_and_wait(chan, "vm-delete");
09123             else
09124                res = ast_play_and_wait(chan, "vm-undelete");
09125             if (!res)
09126                res = ast_play_and_wait(chan, "vm-toforward");
09127             if (!res)
09128                res = ast_play_and_wait(chan, "vm-savemessage");
09129          }
09130       }
09131       if (!res) {
09132          res = ast_play_and_wait(chan, "vm-helpexit");
09133       }
09134       if (!res)
09135          res = ast_waitfordigit(chan, 6000);
09136       if (!res) {
09137          vms->repeats++;
09138          if (vms->repeats > 2) {
09139             res = 't';
09140          }
09141       }
09142    }
09143    return res;
09144 }
09145 
09146 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09147 {
09148    int res = 0;
09149    /* Play instructions and wait for new command */
09150    while (!res) {
09151       if (vms->lastmsg > -1) {
09152          res = ast_play_and_wait(chan, "vm-listen");
09153          if (!res)
09154             res = vm_play_folder_name(chan, vms->vmbox);
09155          if (!res)
09156             res = ast_play_and_wait(chan, "press");
09157          if (!res)
09158             res = ast_play_and_wait(chan, "digits/1");
09159       }
09160       if (!res)
09161          res = ast_play_and_wait(chan, "vm-opts");
09162       if (!res) {
09163          vms->starting = 0;
09164          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09165       }
09166    }
09167    return res;
09168 }
09169 
09170 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09171 {
09172    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09173       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09174    } else {             /* Default to ENGLISH */
09175       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09176    }
09177 }
09178 
09179 
09180 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09181 {
09182    int cmd = 0;
09183    int duration = 0;
09184    int tries = 0;
09185    char newpassword[80] = "";
09186    char newpassword2[80] = "";
09187    char prefile[PATH_MAX] = "";
09188    unsigned char buf[256];
09189    int bytes = 0;
09190 
09191    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09192    if (ast_adsi_available(chan)) {
09193       bytes += adsi_logo(buf + bytes);
09194       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09195       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09196       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09197       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09198       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09199    }
09200 
09201    /* First, have the user change their password 
09202       so they won't get here again */
09203    for (;;) {
09204       newpassword[1] = '\0';
09205       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09206       if (cmd == '#')
09207          newpassword[0] = '\0';
09208       if (cmd < 0 || cmd == 't' || cmd == '#')
09209          return cmd;
09210       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09211       if (cmd < 0 || cmd == 't' || cmd == '#')
09212          return cmd;
09213       cmd = check_password(vmu, newpassword); /* perform password validation */
09214       if (cmd != 0) {
09215          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09216          cmd = ast_play_and_wait(chan, vm_invalid_password);
09217       } else {
09218          newpassword2[1] = '\0';
09219          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09220          if (cmd == '#')
09221             newpassword2[0] = '\0';
09222          if (cmd < 0 || cmd == 't' || cmd == '#')
09223             return cmd;
09224          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09225          if (cmd < 0 || cmd == 't' || cmd == '#')
09226             return cmd;
09227          if (!strcmp(newpassword, newpassword2))
09228             break;
09229          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09230          cmd = ast_play_and_wait(chan, vm_mismatch);
09231       }
09232       if (++tries == 3)
09233          return -1;
09234       if (cmd != 0) {
09235          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09236       }
09237    }
09238    if (pwdchange & PWDCHANGE_INTERNAL)
09239       vm_change_password(vmu, newpassword);
09240    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09241       vm_change_password_shell(vmu, newpassword);
09242 
09243    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09244    cmd = ast_play_and_wait(chan, vm_passchanged);
09245 
09246    /* If forcename is set, have the user record their name */  
09247    if (ast_test_flag(vmu, VM_FORCENAME)) {
09248       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09249       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09250          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09251          if (cmd < 0 || cmd == 't' || cmd == '#')
09252             return cmd;
09253       }
09254    }
09255 
09256    /* If forcegreetings is set, have the user record their greetings */
09257    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09258       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09259       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09260          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09261          if (cmd < 0 || cmd == 't' || cmd == '#')
09262             return cmd;
09263       }
09264 
09265       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09266       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09267          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09268          if (cmd < 0 || cmd == 't' || cmd == '#')
09269             return cmd;
09270       }
09271    }
09272 
09273    return cmd;
09274 }
09275 
09276 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09277 {
09278    int cmd = 0;
09279    int retries = 0;
09280    int duration = 0;
09281    char newpassword[80] = "";
09282    char newpassword2[80] = "";
09283    char prefile[PATH_MAX] = "";
09284    unsigned char buf[256];
09285    int bytes = 0;
09286 
09287    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09288    if (ast_adsi_available(chan)) {
09289       bytes += adsi_logo(buf + bytes);
09290       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09291       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09292       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09293       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09294       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09295    }
09296    while ((cmd >= 0) && (cmd != 't')) {
09297       if (cmd)
09298          retries = 0;
09299       switch (cmd) {
09300       case '1': /* Record your unavailable message */
09301          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09302          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09303          break;
09304       case '2':  /* Record your busy message */
09305          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09306          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09307          break;
09308       case '3': /* Record greeting */
09309          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09310          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09311          break;
09312       case '4':  /* manage the temporary greeting */
09313          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09314          break;
09315       case '5': /* change password */
09316          if (vmu->password[0] == '-') {
09317             cmd = ast_play_and_wait(chan, "vm-no");
09318             break;
09319          }
09320          newpassword[1] = '\0';
09321          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09322          if (cmd == '#')
09323             newpassword[0] = '\0';
09324          else {
09325             if (cmd < 0)
09326                break;
09327             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09328                break;
09329             }
09330          }
09331          cmd = check_password(vmu, newpassword); /* perform password validation */
09332          if (cmd != 0) {
09333             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09334             cmd = ast_play_and_wait(chan, vm_invalid_password);
09335             if (!cmd) {
09336                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09337             }
09338             break;
09339          }
09340          newpassword2[1] = '\0';
09341          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09342          if (cmd == '#')
09343             newpassword2[0] = '\0';
09344          else {
09345             if (cmd < 0)
09346                break;
09347 
09348             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09349                break;
09350             }
09351          }
09352          if (strcmp(newpassword, newpassword2)) {
09353             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09354             cmd = ast_play_and_wait(chan, vm_mismatch);
09355             if (!cmd) {
09356                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09357             }
09358             break;
09359          }
09360 
09361          if (pwdchange & PWDCHANGE_INTERNAL) {
09362             vm_change_password(vmu, newpassword);
09363          }
09364          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09365             vm_change_password_shell(vmu, newpassword);
09366          }
09367 
09368          ast_debug(1, "User %s set password to %s of length %d\n",
09369             vms->username, newpassword, (int) strlen(newpassword));
09370          cmd = ast_play_and_wait(chan, vm_passchanged);
09371          break;
09372       case '*': 
09373          cmd = 't';
09374          break;
09375       default: 
09376          cmd = 0;
09377          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09378          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09379          if (ast_fileexists(prefile, NULL, NULL)) {
09380             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09381          }
09382          DISPOSE(prefile, -1);
09383          if (!cmd) {
09384             cmd = ast_play_and_wait(chan, "vm-options");
09385          }
09386          if (!cmd) {
09387             cmd = ast_waitfordigit(chan, 6000);
09388          }
09389          if (!cmd) {
09390             retries++;
09391          }
09392          if (retries > 3) {
09393             cmd = 't';
09394          }
09395          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09396       }
09397    }
09398    if (cmd == 't')
09399       cmd = 0;
09400    return cmd;
09401 }
09402 
09403 /*!
09404  * \brief The handler for 'record a temporary greeting'. 
09405  * \param chan
09406  * \param vmu
09407  * \param vms
09408  * \param fmtc
09409  * \param record_gain
09410  *
09411  * This is option 4 from the mailbox options menu.
09412  * This function manages the following promptings:
09413  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09414  * 2: remove (delete) the temporary greeting.
09415  * *: return to the main menu.
09416  *
09417  * \return zero on success, -1 on error.
09418  */
09419 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09420 {
09421    int cmd = 0;
09422    int retries = 0;
09423    int duration = 0;
09424    char prefile[PATH_MAX] = "";
09425    unsigned char buf[256];
09426    int bytes = 0;
09427 
09428    if (ast_adsi_available(chan)) {
09429       bytes += adsi_logo(buf + bytes);
09430       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09431       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09432       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09433       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09434       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09435    }
09436 
09437    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09438    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09439    while ((cmd >= 0) && (cmd != 't')) {
09440       if (cmd)
09441          retries = 0;
09442       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09443       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09444          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09445          cmd = 't';  
09446       } else {
09447          switch (cmd) {
09448          case '1':
09449             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
09450             break;
09451          case '2':
09452             DELETE(prefile, -1, prefile, vmu);
09453             ast_play_and_wait(chan, "vm-tempremoved");
09454             cmd = 't';  
09455             break;
09456          case '*': 
09457             cmd = 't';
09458             break;
09459          default:
09460             cmd = ast_play_and_wait(chan,
09461                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09462                   "vm-tempgreeting2" : "vm-tempgreeting");
09463             if (!cmd) {
09464                cmd = ast_waitfordigit(chan, 6000);
09465             }
09466             if (!cmd) {
09467                retries++;
09468             }
09469             if (retries > 3) {
09470                cmd = 't';
09471             }
09472             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09473          }
09474       }
09475       DISPOSE(prefile, -1);
09476    }
09477    if (cmd == 't')
09478       cmd = 0;
09479    return cmd;
09480 }
09481 
09482 /*!
09483  * \brief Greek syntax for 'You have N messages' greeting.
09484  * \param chan
09485  * \param vms
09486  * \param vmu
09487  *
09488  * \return zero on success, -1 on error.
09489  */   
09490 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09491 {
09492    int cmd = 0;
09493 
09494    if (vms->lastmsg > -1) {
09495       cmd = play_message(chan, vmu, vms);
09496    } else {
09497       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09498       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09499          if (!cmd) {
09500             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09501             cmd = ast_play_and_wait(chan, vms->fn);
09502          }
09503          if (!cmd)
09504             cmd = ast_play_and_wait(chan, "vm-messages");
09505       } else {
09506          if (!cmd)
09507             cmd = ast_play_and_wait(chan, "vm-messages");
09508          if (!cmd) {
09509             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09510             cmd = ast_play_and_wait(chan, vms->fn);
09511          }
09512       }
09513    } 
09514    return cmd;
09515 }
09516 
09517 /* Hebrew Syntax */
09518 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09519 {
09520    int cmd = 0;
09521 
09522    if (vms->lastmsg > -1) {
09523       cmd = play_message(chan, vmu, vms);
09524    } else {
09525       if (!strcasecmp(vms->fn, "INBOX")) {
09526          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09527       } else {
09528          cmd = ast_play_and_wait(chan, "vm-nomessages");
09529       }
09530    }
09531    return cmd;
09532 }
09533 
09534 /*! 
09535  * \brief Default English syntax for 'You have N messages' greeting.
09536  * \param chan
09537  * \param vms
09538  * \param vmu
09539  *
09540  * \return zero on success, -1 on error.
09541  */
09542 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09543 {
09544    int cmd = 0;
09545 
09546    if (vms->lastmsg > -1) {
09547       cmd = play_message(chan, vmu, vms);
09548    } else {
09549       cmd = ast_play_and_wait(chan, "vm-youhave");
09550       if (!cmd) 
09551          cmd = ast_play_and_wait(chan, "vm-no");
09552       if (!cmd) {
09553          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09554          cmd = ast_play_and_wait(chan, vms->fn);
09555       }
09556       if (!cmd)
09557          cmd = ast_play_and_wait(chan, "vm-messages");
09558    }
09559    return cmd;
09560 }
09561 
09562 /*! 
09563  *\brief Italian syntax for 'You have N messages' greeting.
09564  * \param chan
09565  * \param vms
09566  * \param vmu
09567  *
09568  * \return zero on success, -1 on error.
09569  */
09570 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09571 {
09572    int cmd;
09573 
09574    if (vms->lastmsg > -1) {
09575       cmd = play_message(chan, vmu, vms);
09576    } else {
09577       cmd = ast_play_and_wait(chan, "vm-no");
09578       if (!cmd)
09579          cmd = ast_play_and_wait(chan, "vm-message");
09580       if (!cmd) {
09581          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09582          cmd = ast_play_and_wait(chan, vms->fn);
09583       }
09584    }
09585    return cmd;
09586 }
09587 
09588 /*! 
09589  * \brief Spanish syntax for 'You have N messages' greeting.
09590  * \param chan
09591  * \param vms
09592  * \param vmu
09593  *
09594  * \return zero on success, -1 on error.
09595  */
09596 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09597 {
09598    int cmd;
09599 
09600    if (vms->lastmsg > -1) {
09601       cmd = play_message(chan, vmu, vms);
09602    } else {
09603       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09604       if (!cmd)
09605          cmd = ast_play_and_wait(chan, "vm-messages");
09606       if (!cmd) {
09607          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09608          cmd = ast_play_and_wait(chan, vms->fn);
09609       }
09610    }
09611    return cmd;
09612 }
09613 
09614 /*! 
09615  * \brief Portuguese syntax for 'You have N messages' greeting.
09616  * \param chan
09617  * \param vms
09618  * \param vmu
09619  *
09620  * \return zero on success, -1 on error.
09621  */
09622 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09623 {
09624    int cmd;
09625 
09626    if (vms->lastmsg > -1) {
09627       cmd = play_message(chan, vmu, vms);
09628    } else {
09629       cmd = ast_play_and_wait(chan, "vm-no");
09630       if (!cmd) {
09631          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09632          cmd = ast_play_and_wait(chan, vms->fn);
09633       }
09634       if (!cmd)
09635          cmd = ast_play_and_wait(chan, "vm-messages");
09636    }
09637    return cmd;
09638 }
09639 
09640 /*! 
09641  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09642  * \param chan
09643  * \param vms
09644  * \param vmu
09645  *
09646  * \return zero on success, -1 on error.
09647  */
09648 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09649 {
09650    int cmd;
09651 
09652    if (vms->lastmsg > -1) {
09653       cmd = play_message(chan, vmu, vms);
09654    } else {
09655       cmd = ast_play_and_wait(chan, "vm-you");
09656       if (!cmd) 
09657          cmd = ast_play_and_wait(chan, "vm-haveno");
09658       if (!cmd)
09659          cmd = ast_play_and_wait(chan, "vm-messages");
09660       if (!cmd) {
09661          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09662          cmd = ast_play_and_wait(chan, vms->fn);
09663       }
09664    }
09665    return cmd;
09666 }
09667 
09668 /*! 
09669  * \brief Vietnamese syntax for 'You have N messages' greeting.
09670  * \param chan
09671  * \param vms
09672  * \param vmu
09673  *
09674  * \return zero on success, -1 on error.
09675  */
09676 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09677 {
09678    int cmd = 0;
09679 
09680    if (vms->lastmsg > -1) {
09681       cmd = play_message(chan, vmu, vms);
09682    } else {
09683       cmd = ast_play_and_wait(chan, "vm-no");
09684       if (!cmd) {
09685          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09686          cmd = ast_play_and_wait(chan, vms->fn);
09687       }
09688    }
09689    return cmd;
09690 }
09691 
09692 /*!
09693  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09694  * \param chan The channel for the current user. We read the language property from this.
09695  * \param vms passed into the language-specific vm_browse_messages function.
09696  * \param vmu passed into the language-specific vm_browse_messages function.
09697  * 
09698  * The method to be invoked is determined by the value of language code property in the user's channel.
09699  * The default (when unable to match) is to use english.
09700  *
09701  * \return zero on success, -1 on error.
09702  */
09703 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09704 {
09705    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09706       return vm_browse_messages_es(chan, vms, vmu);
09707    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09708       return vm_browse_messages_gr(chan, vms, vmu);
09709    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09710       return vm_browse_messages_he(chan, vms, vmu);
09711    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09712       return vm_browse_messages_it(chan, vms, vmu);
09713    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09714       return vm_browse_messages_pt(chan, vms, vmu);
09715    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09716       return vm_browse_messages_vi(chan, vms, vmu);
09717    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09718       return vm_browse_messages_zh(chan, vms, vmu);
09719    } else {                                             /* Default to English syntax */
09720       return vm_browse_messages_en(chan, vms, vmu);
09721    }
09722 }
09723 
09724 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09725          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09726          int skipuser, int max_logins, int silent)
09727 {
09728    int useadsi = 0, valid = 0, logretries = 0;
09729    char password[AST_MAX_EXTENSION]="", *passptr;
09730    struct ast_vm_user vmus, *vmu = NULL;
09731 
09732    /* If ADSI is supported, setup login screen */
09733    adsi_begin(chan, &useadsi);
09734    if (!skipuser && useadsi)
09735       adsi_login(chan);
09736    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09737    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09738       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09739       return -1;
09740    }
09741 
09742    /* Authenticate them and get their mailbox/password */
09743 
09744    while (!valid && (logretries < max_logins)) {
09745       /* Prompt for, and read in the username */
09746       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09747          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09748          return -1;
09749       }
09750       if (ast_strlen_zero(mailbox)) {
09751          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09752             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09753          } else {
09754             ast_verb(3, "Username not entered\n"); 
09755             return -1;
09756          }
09757       } else if (mailbox[0] == '*') {
09758          /* user entered '*' */
09759          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09760          if (ast_exists_extension(chan, chan->context, "a", 1,
09761             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09762             return -1;
09763          }
09764          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09765          mailbox[0] = '\0';
09766       }
09767 
09768       if (useadsi)
09769          adsi_password(chan);
09770 
09771       if (!ast_strlen_zero(prefix)) {
09772          char fullusername[80] = "";
09773          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09774          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09775          ast_copy_string(mailbox, fullusername, mailbox_size);
09776       }
09777 
09778       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09779       vmu = find_user(&vmus, context, mailbox);
09780       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09781          /* saved password is blank, so don't bother asking */
09782          password[0] = '\0';
09783       } else {
09784          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09785          if (ast_streamfile(chan, vm_password, chan->language)) {
09786             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09787             return -1;
09788          }
09789          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09790             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09791             return -1;
09792          } else if (password[0] == '*') {
09793             /* user entered '*' */
09794             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09795             if (ast_exists_extension(chan, chan->context, "a", 1,
09796                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09797                mailbox[0] = '*';
09798                return -1;
09799             }
09800             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09801             mailbox[0] = '\0';
09802             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09803             vmu = NULL;
09804          }
09805       }
09806 
09807       if (vmu) {
09808          passptr = vmu->password;
09809          if (passptr[0] == '-') passptr++;
09810       }
09811       if (vmu && !strcmp(passptr, password))
09812          valid++;
09813       else {
09814          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09815          if (!ast_strlen_zero(prefix))
09816             mailbox[0] = '\0';
09817       }
09818       logretries++;
09819       if (!valid) {
09820          if (skipuser || logretries >= max_logins) {
09821             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09822             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09823                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09824                return -1;
09825             }
09826          } else {
09827             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09828             if (useadsi)
09829                adsi_login(chan);
09830             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09831                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09832                return -1;
09833             }
09834          }
09835          if (ast_waitstream(chan, "")) /* Channel is hung up */
09836             return -1;
09837       }
09838    }
09839    if (!valid && (logretries >= max_logins)) {
09840       ast_stopstream(chan);
09841       ast_play_and_wait(chan, "vm-goodbye");
09842       return -1;
09843    }
09844    if (vmu && !skipuser) {
09845       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09846    }
09847    return 0;
09848 }
09849 
09850 static int vm_execmain(struct ast_channel *chan, const char *data)
09851 {
09852    /* XXX This is, admittedly, some pretty horrendous code.  For some
09853       reason it just seemed a lot easier to do with GOTO's.  I feel
09854       like I'm back in my GWBASIC days. XXX */
09855    int res = -1;
09856    int cmd = 0;
09857    int valid = 0;
09858    char prefixstr[80] ="";
09859    char ext_context[256]="";
09860    int box;
09861    int useadsi = 0;
09862    int skipuser = 0;
09863    struct vm_state vms;
09864    struct ast_vm_user *vmu = NULL, vmus;
09865    char *context = NULL;
09866    int silentexit = 0;
09867    struct ast_flags flags = { 0 };
09868    signed char record_gain = 0;
09869    int play_auto = 0;
09870    int play_folder = 0;
09871    int in_urgent = 0;
09872 #ifdef IMAP_STORAGE
09873    int deleted = 0;
09874 #endif
09875 
09876    /* Add the vm_state to the active list and keep it active */
09877    memset(&vms, 0, sizeof(vms));
09878 
09879    vms.lastmsg = -1;
09880 
09881    memset(&vmus, 0, sizeof(vmus));
09882 
09883    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09884    if (chan->_state != AST_STATE_UP) {
09885       ast_debug(1, "Before ast_answer\n");
09886       ast_answer(chan);
09887    }
09888 
09889    if (!ast_strlen_zero(data)) {
09890       char *opts[OPT_ARG_ARRAY_SIZE];
09891       char *parse;
09892       AST_DECLARE_APP_ARGS(args,
09893          AST_APP_ARG(argv0);
09894          AST_APP_ARG(argv1);
09895       );
09896 
09897       parse = ast_strdupa(data);
09898 
09899       AST_STANDARD_APP_ARGS(args, parse);
09900 
09901       if (args.argc == 2) {
09902          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09903             return -1;
09904          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09905             int gain;
09906             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09907                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09908                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09909                   return -1;
09910                } else {
09911                   record_gain = (signed char) gain;
09912                }
09913             } else {
09914                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09915             }
09916          }
09917          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09918             play_auto = 1;
09919             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09920                /* See if it is a folder name first */
09921                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09922                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09923                      play_folder = -1;
09924                   }
09925                } else {
09926                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09927                }
09928             } else {
09929                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09930             }
09931             if (play_folder > 9 || play_folder < 0) {
09932                ast_log(AST_LOG_WARNING,
09933                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09934                   opts[OPT_ARG_PLAYFOLDER]);
09935                play_folder = 0;
09936             }
09937          }
09938       } else {
09939          /* old style options parsing */
09940          while (*(args.argv0)) {
09941             if (*(args.argv0) == 's')
09942                ast_set_flag(&flags, OPT_SILENT);
09943             else if (*(args.argv0) == 'p')
09944                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09945             else 
09946                break;
09947             (args.argv0)++;
09948          }
09949 
09950       }
09951 
09952       valid = ast_test_flag(&flags, OPT_SILENT);
09953 
09954       if ((context = strchr(args.argv0, '@')))
09955          *context++ = '\0';
09956 
09957       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09958          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09959       else
09960          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09961 
09962       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09963          skipuser++;
09964       else
09965          valid = 0;
09966    }
09967 
09968    if (!valid)
09969       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09970 
09971    ast_debug(1, "After vm_authenticate\n");
09972 
09973    if (vms.username[0] == '*') {
09974       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09975 
09976       /* user entered '*' */
09977       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09978          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
09979          res = 0; /* prevent hangup */
09980          goto out;
09981       }
09982    }
09983 
09984    if (!res) {
09985       valid = 1;
09986       if (!skipuser)
09987          vmu = &vmus;
09988    } else {
09989       res = 0;
09990    }
09991 
09992    /* If ADSI is supported, setup login screen */
09993    adsi_begin(chan, &useadsi);
09994 
09995    ast_test_suite_assert(valid);
09996    if (!valid) {
09997       goto out;
09998    }
09999    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10000 
10001 #ifdef IMAP_STORAGE
10002    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10003    pthread_setspecific(ts_vmstate.key, &vms);
10004 
10005    vms.interactive = 1;
10006    vms.updated = 1;
10007    if (vmu)
10008       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10009    vmstate_insert(&vms);
10010    init_vm_state(&vms);
10011 #endif
10012    /* Avoid allocating a buffer of 0 bytes, because some platforms really don't like that. */
10013    if (!(vms.deleted = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
10014       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
10015       cmd = ast_play_and_wait(chan, "an-error-has-occured");
10016       return -1;
10017    }
10018    if (!(vms.heard = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
10019       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
10020       cmd = ast_play_and_wait(chan, "an-error-has-occured");
10021       return -1;
10022    }
10023    
10024    /* Set language from config to override channel language */
10025    if (!ast_strlen_zero(vmu->language))
10026       ast_string_field_set(chan, language, vmu->language);
10027 
10028    /* Retrieve urgent, old and new message counts */
10029    ast_debug(1, "Before open_mailbox\n");
10030    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10031    if (res < 0)
10032       goto out;
10033    vms.oldmessages = vms.lastmsg + 1;
10034    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10035    /* check INBOX */
10036    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10037    if (res < 0)
10038       goto out;
10039    vms.newmessages = vms.lastmsg + 1;
10040    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10041    /* Start in Urgent */
10042    in_urgent = 1;
10043    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10044    if (res < 0)
10045       goto out;
10046    vms.urgentmessages = vms.lastmsg + 1;
10047    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10048 
10049    /* Select proper mailbox FIRST!! */
10050    if (play_auto) {
10051       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10052       if (vms.urgentmessages) {
10053          in_urgent = 1;
10054          res = open_mailbox(&vms, vmu, 11);
10055       } else {
10056          in_urgent = 0;
10057          res = open_mailbox(&vms, vmu, play_folder);
10058       }
10059       if (res < 0)
10060          goto out;
10061 
10062       /* If there are no new messages, inform the user and hangup */
10063       if (vms.lastmsg == -1) {
10064          in_urgent = 0;
10065          cmd = vm_browse_messages(chan, &vms, vmu);
10066          res = 0;
10067          goto out;
10068       }
10069    } else {
10070       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10071          /* If we only have old messages start here */
10072          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10073          in_urgent = 0;
10074          play_folder = 1;
10075          if (res < 0)
10076             goto out;
10077       } else if (!vms.urgentmessages && vms.newmessages) {
10078          /* If we have new messages but none are urgent */
10079          in_urgent = 0;
10080          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10081          if (res < 0)
10082             goto out;
10083       }
10084    }
10085 
10086    if (useadsi)
10087       adsi_status(chan, &vms);
10088    res = 0;
10089 
10090    /* Check to see if this is a new user */
10091    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10092       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10093       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10094          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10095       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10096       if ((cmd == 't') || (cmd == '#')) {
10097          /* Timeout */
10098          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10099          res = 0;
10100          goto out;
10101       } else if (cmd < 0) {
10102          /* Hangup */
10103          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10104          res = -1;
10105          goto out;
10106       }
10107    }
10108 #ifdef IMAP_STORAGE
10109       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10110       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10111          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10112          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10113       }
10114       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10115       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10116          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10117          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10118       }
10119 #endif
10120 
10121    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10122    if (play_auto) {
10123       cmd = '1';
10124    } else {
10125       cmd = vm_intro(chan, vmu, &vms);
10126    }
10127    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10128 
10129    vms.repeats = 0;
10130    vms.starting = 1;
10131    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10132       /* Run main menu */
10133       switch (cmd) {
10134       case '1': /* First message */
10135          vms.curmsg = 0;
10136          /* Fall through */
10137       case '5': /* Play current message */
10138          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10139          cmd = vm_browse_messages(chan, &vms, vmu);
10140          break;
10141       case '2': /* Change folders */
10142          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10143          if (useadsi)
10144             adsi_folders(chan, 0, "Change to folder...");
10145 
10146          cmd = get_folder2(chan, "vm-changeto", 0);
10147          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10148          if (cmd == '#') {
10149             cmd = 0;
10150          } else if (cmd > 0) {
10151             cmd = cmd - '0';
10152             res = close_mailbox(&vms, vmu);
10153             if (res == ERROR_LOCK_PATH)
10154                goto out;
10155             /* If folder is not urgent, set in_urgent to zero! */
10156             if (cmd != 11) in_urgent = 0;
10157             res = open_mailbox(&vms, vmu, cmd);
10158             if (res < 0)
10159                goto out;
10160             play_folder = cmd;
10161             cmd = 0;
10162          }
10163          if (useadsi)
10164             adsi_status2(chan, &vms);
10165 
10166          if (!cmd) {
10167             cmd = vm_play_folder_name(chan, vms.vmbox);
10168          }
10169 
10170          vms.starting = 1;
10171          break;
10172       case '3': /* Advanced options */
10173          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10174          cmd = 0;
10175          vms.repeats = 0;
10176          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10177             switch (cmd) {
10178             case '1': /* Reply */
10179                if (vms.lastmsg > -1 && !vms.starting) {
10180                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10181                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10182                      res = cmd;
10183                      goto out;
10184                   }
10185                } else {
10186                   cmd = ast_play_and_wait(chan, "vm-sorry");
10187                }
10188                cmd = 't';
10189                break;
10190             case '2': /* Callback */
10191                if (!vms.starting)
10192                   ast_verb(3, "Callback Requested\n");
10193                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10194                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10195                   if (cmd == 9) {
10196                      silentexit = 1;
10197                      goto out;
10198                   } else if (cmd == ERROR_LOCK_PATH) {
10199                      res = cmd;
10200                      goto out;
10201                   }
10202                } else {
10203                   cmd = ast_play_and_wait(chan, "vm-sorry");
10204                }
10205                cmd = 't';
10206                break;
10207             case '3': /* Envelope */
10208                if (vms.lastmsg > -1 && !vms.starting) {
10209                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10210                   if (cmd == ERROR_LOCK_PATH) {
10211                      res = cmd;
10212                      goto out;
10213                   }
10214                } else {
10215                   cmd = ast_play_and_wait(chan, "vm-sorry");
10216                }
10217                cmd = 't';
10218                break;
10219             case '4': /* Dialout */
10220                if (!ast_strlen_zero(vmu->dialout)) {
10221                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10222                   if (cmd == 9) {
10223                      silentexit = 1;
10224                      goto out;
10225                   }
10226                } else {
10227                   cmd = ast_play_and_wait(chan, "vm-sorry");
10228                }
10229                cmd = 't';
10230                break;
10231 
10232             case '5': /* Leave VoiceMail */
10233                if (ast_test_flag(vmu, VM_SVMAIL)) {
10234                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10235                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10236                      res = cmd;
10237                      goto out;
10238                   }
10239                } else {
10240                   cmd = ast_play_and_wait(chan, "vm-sorry");
10241                }
10242                cmd = 't';
10243                break;
10244 
10245             case '*': /* Return to main menu */
10246                cmd = 't';
10247                break;
10248 
10249             default:
10250                cmd = 0;
10251                if (!vms.starting) {
10252                   cmd = ast_play_and_wait(chan, "vm-toreply");
10253                }
10254                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10255                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10256                }
10257                if (!cmd && !vms.starting) {
10258                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10259                }
10260                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10261                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10262                }
10263                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10264                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10265                }
10266                if (!cmd) {
10267                   cmd = ast_play_and_wait(chan, "vm-starmain");
10268                }
10269                if (!cmd) {
10270                   cmd = ast_waitfordigit(chan, 6000);
10271                }
10272                if (!cmd) {
10273                   vms.repeats++;
10274                }
10275                if (vms.repeats > 3) {
10276                   cmd = 't';
10277                }
10278                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10279             }
10280          }
10281          if (cmd == 't') {
10282             cmd = 0;
10283             vms.repeats = 0;
10284          }
10285          break;
10286       case '4': /* Go to the previous message */
10287          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10288          if (vms.curmsg > 0) {
10289             vms.curmsg--;
10290             cmd = play_message(chan, vmu, &vms);
10291          } else {
10292             /* Check if we were listening to new
10293                messages.  If so, go to Urgent messages
10294                instead of saying "no more messages"
10295             */
10296             if (in_urgent == 0 && vms.urgentmessages > 0) {
10297                /* Check for Urgent messages */
10298                in_urgent = 1;
10299                res = close_mailbox(&vms, vmu);
10300                if (res == ERROR_LOCK_PATH)
10301                   goto out;
10302                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10303                if (res < 0)
10304                   goto out;
10305                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10306                vms.curmsg = vms.lastmsg;
10307                if (vms.lastmsg < 0) {
10308                   cmd = ast_play_and_wait(chan, "vm-nomore");
10309                }
10310             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10311                vms.curmsg = vms.lastmsg;
10312                cmd = play_message(chan, vmu, &vms);
10313             } else {
10314                cmd = ast_play_and_wait(chan, "vm-nomore");
10315             }
10316          }
10317          break;
10318       case '6': /* Go to the next message */
10319          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10320          if (vms.curmsg < vms.lastmsg) {
10321             vms.curmsg++;
10322             cmd = play_message(chan, vmu, &vms);
10323          } else {
10324             if (in_urgent && vms.newmessages > 0) {
10325                /* Check if we were listening to urgent
10326                 * messages.  If so, go to regular new messages
10327                 * instead of saying "no more messages"
10328                 */
10329                in_urgent = 0;
10330                res = close_mailbox(&vms, vmu);
10331                if (res == ERROR_LOCK_PATH)
10332                   goto out;
10333                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10334                if (res < 0)
10335                   goto out;
10336                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10337                vms.curmsg = -1;
10338                if (vms.lastmsg < 0) {
10339                   cmd = ast_play_and_wait(chan, "vm-nomore");
10340                }
10341             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10342                vms.curmsg = 0;
10343                cmd = play_message(chan, vmu, &vms);
10344             } else {
10345                cmd = ast_play_and_wait(chan, "vm-nomore");
10346             }
10347          }
10348          break;
10349       case '7': /* Delete the current message */
10350          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10351             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10352             if (useadsi)
10353                adsi_delete(chan, &vms);
10354             if (vms.deleted[vms.curmsg]) {
10355                if (play_folder == 0) {
10356                   if (in_urgent) {
10357                      vms.urgentmessages--;
10358                   } else {
10359                      vms.newmessages--;
10360                   }
10361                }
10362                else if (play_folder == 1)
10363                   vms.oldmessages--;
10364                cmd = ast_play_and_wait(chan, "vm-deleted");
10365             } else {
10366                if (play_folder == 0) {
10367                   if (in_urgent) {
10368                      vms.urgentmessages++;
10369                   } else {
10370                      vms.newmessages++;
10371                   }
10372                }
10373                else if (play_folder == 1)
10374                   vms.oldmessages++;
10375                cmd = ast_play_and_wait(chan, "vm-undeleted");
10376             }
10377             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10378                if (vms.curmsg < vms.lastmsg) {
10379                   vms.curmsg++;
10380                   cmd = play_message(chan, vmu, &vms);
10381                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10382                   vms.curmsg = 0;
10383                   cmd = play_message(chan, vmu, &vms);
10384                } else {
10385                   /* Check if we were listening to urgent
10386                      messages.  If so, go to regular new messages
10387                      instead of saying "no more messages"
10388                   */
10389                   if (in_urgent == 1) {
10390                      /* Check for new messages */
10391                      in_urgent = 0;
10392                      res = close_mailbox(&vms, vmu);
10393                      if (res == ERROR_LOCK_PATH)
10394                         goto out;
10395                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10396                      if (res < 0)
10397                         goto out;
10398                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10399                      vms.curmsg = -1;
10400                      if (vms.lastmsg < 0) {
10401                         cmd = ast_play_and_wait(chan, "vm-nomore");
10402                      }
10403                   } else {
10404                      cmd = ast_play_and_wait(chan, "vm-nomore");
10405                   }
10406                }
10407             }
10408          } else /* Delete not valid if we haven't selected a message */
10409             cmd = 0;
10410 #ifdef IMAP_STORAGE
10411          deleted = 1;
10412 #endif
10413          break;
10414    
10415       case '8': /* Forward the current message */
10416          if (vms.lastmsg > -1) {
10417             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10418             if (cmd == ERROR_LOCK_PATH) {
10419                res = cmd;
10420                goto out;
10421             }
10422          } else {
10423             /* Check if we were listening to urgent
10424                messages.  If so, go to regular new messages
10425                instead of saying "no more messages"
10426             */
10427             if (in_urgent == 1 && vms.newmessages > 0) {
10428                /* Check for new messages */
10429                in_urgent = 0;
10430                res = close_mailbox(&vms, vmu);
10431                if (res == ERROR_LOCK_PATH)
10432                   goto out;
10433                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10434                if (res < 0)
10435                   goto out;
10436                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10437                vms.curmsg = -1;
10438                if (vms.lastmsg < 0) {
10439                   cmd = ast_play_and_wait(chan, "vm-nomore");
10440                }
10441             } else {
10442                cmd = ast_play_and_wait(chan, "vm-nomore");
10443             }
10444          }
10445          break;
10446       case '9': /* Save message to folder */
10447          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10448          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10449             /* No message selected */
10450             cmd = 0;
10451             break;
10452          }
10453          if (useadsi)
10454             adsi_folders(chan, 1, "Save to folder...");
10455          cmd = get_folder2(chan, "vm-savefolder", 1);
10456          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10457          box = 0; /* Shut up compiler */
10458          if (cmd == '#') {
10459             cmd = 0;
10460             break;
10461          } else if (cmd > 0) {
10462             box = cmd = cmd - '0';
10463             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10464             if (cmd == ERROR_LOCK_PATH) {
10465                res = cmd;
10466                goto out;
10467 #ifndef IMAP_STORAGE
10468             } else if (!cmd) {
10469                vms.deleted[vms.curmsg] = 1;
10470 #endif
10471             } else {
10472                vms.deleted[vms.curmsg] = 0;
10473                vms.heard[vms.curmsg] = 0;
10474             }
10475          }
10476          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10477          if (useadsi)
10478             adsi_message(chan, &vms);
10479          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10480          if (!cmd) {
10481             cmd = ast_play_and_wait(chan, "vm-message");
10482             if (!cmd) 
10483                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10484             if (!cmd)
10485                cmd = ast_play_and_wait(chan, "vm-savedto");
10486             if (!cmd)
10487                cmd = vm_play_folder_name(chan, vms.fn);
10488          } else {
10489             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10490          }
10491          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10492             if (vms.curmsg < vms.lastmsg) {
10493                vms.curmsg++;
10494                cmd = play_message(chan, vmu, &vms);
10495             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10496                vms.curmsg = 0;
10497                cmd = play_message(chan, vmu, &vms);
10498             } else {
10499                /* Check if we were listening to urgent
10500                   messages.  If so, go to regular new messages
10501                   instead of saying "no more messages"
10502                */
10503                if (in_urgent == 1 && vms.newmessages > 0) {
10504                   /* Check for new messages */
10505                   in_urgent = 0;
10506                   res = close_mailbox(&vms, vmu);
10507                   if (res == ERROR_LOCK_PATH)
10508                      goto out;
10509                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10510                   if (res < 0)
10511                      goto out;
10512                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10513                   vms.curmsg = -1;
10514                   if (vms.lastmsg < 0) {
10515                      cmd = ast_play_and_wait(chan, "vm-nomore");
10516                   }
10517                } else {
10518                   cmd = ast_play_and_wait(chan, "vm-nomore");
10519                }
10520             }
10521          }
10522          break;
10523       case '*': /* Help */
10524          if (!vms.starting) {
10525             cmd = ast_play_and_wait(chan, "vm-onefor");
10526             if (!strncasecmp(chan->language, "he", 2)) {
10527                cmd = ast_play_and_wait(chan, "vm-for");
10528             }
10529             if (!cmd)
10530                cmd = vm_play_folder_name(chan, vms.vmbox);
10531             if (!cmd)
10532                cmd = ast_play_and_wait(chan, "vm-opts");
10533             if (!cmd)
10534                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10535          } else
10536             cmd = 0;
10537          break;
10538       case '0': /* Mailbox options */
10539          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10540          if (useadsi)
10541             adsi_status(chan, &vms);
10542          break;
10543       default: /* Nothing */
10544          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10545          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10546          break;
10547       }
10548    }
10549    if ((cmd == 't') || (cmd == '#')) {
10550       /* Timeout */
10551       res = 0;
10552    } else {
10553       /* Hangup */
10554       res = -1;
10555    }
10556 
10557 out:
10558    if (res > -1) {
10559       ast_stopstream(chan);
10560       adsi_goodbye(chan);
10561       if (valid && res != OPERATOR_EXIT) {
10562          if (silentexit)
10563             res = ast_play_and_wait(chan, "vm-dialout");
10564          else 
10565             res = ast_play_and_wait(chan, "vm-goodbye");
10566       }
10567       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10568          res = 0;
10569       }
10570       if (useadsi)
10571          ast_adsi_unload_session(chan);
10572    }
10573    if (vmu)
10574       close_mailbox(&vms, vmu);
10575    if (valid) {
10576       int new = 0, old = 0, urgent = 0;
10577       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10578       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10579       /* Urgent flag not passwd to externnotify here */
10580       run_externnotify(vmu->context, vmu->mailbox, NULL);
10581       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10582       queue_mwi_event(ext_context, urgent, new, old);
10583    }
10584 #ifdef IMAP_STORAGE
10585    /* expunge message - use UID Expunge if supported on IMAP server*/
10586    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10587    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10588       ast_mutex_lock(&vms.lock);
10589 #ifdef HAVE_IMAP_TK2006
10590       if (LEVELUIDPLUS (vms.mailstream)) {
10591          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10592       } else 
10593 #endif
10594          mail_expunge(vms.mailstream);
10595       ast_mutex_unlock(&vms.lock);
10596    }
10597    /*  before we delete the state, we should copy pertinent info
10598     *  back to the persistent model */
10599    if (vmu) {
10600       vmstate_delete(&vms);
10601    }
10602 #endif
10603    if (vmu)
10604       free_user(vmu);
10605    if (vms.deleted)
10606       ast_free(vms.deleted);
10607    if (vms.heard)
10608       ast_free(vms.heard);
10609 
10610 #ifdef IMAP_STORAGE
10611    pthread_setspecific(ts_vmstate.key, NULL);
10612 #endif
10613    return res;
10614 }
10615 
10616 static int vm_exec(struct ast_channel *chan, const char *data)
10617 {
10618    int res = 0;
10619    char *tmp;
10620    struct leave_vm_options leave_options;
10621    struct ast_flags flags = { 0 };
10622    char *opts[OPT_ARG_ARRAY_SIZE];
10623    AST_DECLARE_APP_ARGS(args,
10624       AST_APP_ARG(argv0);
10625       AST_APP_ARG(argv1);
10626    );
10627    
10628    memset(&leave_options, 0, sizeof(leave_options));
10629 
10630    if (chan->_state != AST_STATE_UP)
10631       ast_answer(chan);
10632 
10633    if (!ast_strlen_zero(data)) {
10634       tmp = ast_strdupa(data);
10635       AST_STANDARD_APP_ARGS(args, tmp);
10636       if (args.argc == 2) {
10637          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10638             return -1;
10639          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10640          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10641             int gain;
10642 
10643             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10644                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10645                return -1;
10646             } else {
10647                leave_options.record_gain = (signed char) gain;
10648             }
10649          }
10650          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10651             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10652                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10653          }
10654       }
10655    } else {
10656       char temp[256];
10657       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10658       if (res < 0)
10659          return res;
10660       if (ast_strlen_zero(temp))
10661          return 0;
10662       args.argv0 = ast_strdupa(temp);
10663    }
10664 
10665    res = leave_voicemail(chan, args.argv0, &leave_options);
10666    if (res == 't') {
10667       ast_play_and_wait(chan, "vm-goodbye");
10668       res = 0;
10669    }
10670 
10671    if (res == OPERATOR_EXIT) {
10672       res = 0;
10673    }
10674 
10675    if (res == ERROR_LOCK_PATH) {
10676       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10677       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10678       res = 0;
10679    }
10680 
10681    return res;
10682 }
10683 
10684 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10685 {
10686    struct ast_vm_user *vmu;
10687 
10688    if (!ast_strlen_zero(box) && box[0] == '*') {
10689       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10690             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10691             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10692             "\n\tand will be ignored.\n", box, context);
10693       return NULL;
10694    }
10695 
10696    AST_LIST_TRAVERSE(&users, vmu, list) {
10697       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10698          if (strcasecmp(vmu->context, context)) {
10699             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10700                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10701                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10702                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10703          }
10704          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10705          return NULL;
10706       }
10707       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10708          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10709          return NULL;
10710       }
10711    }
10712    
10713    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10714       return NULL;
10715    
10716    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10717    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10718 
10719    AST_LIST_INSERT_TAIL(&users, vmu, list);
10720    
10721    return vmu;
10722 }
10723 
10724 static int append_mailbox(const char *context, const char *box, const char *data)
10725 {
10726    /* Assumes lock is already held */
10727    char *tmp;
10728    char *stringp;
10729    char *s;
10730    struct ast_vm_user *vmu;
10731    char *mailbox_full;
10732    int new = 0, old = 0, urgent = 0;
10733    char secretfn[PATH_MAX] = "";
10734 
10735    tmp = ast_strdupa(data);
10736 
10737    if (!(vmu = find_or_create(context, box)))
10738       return -1;
10739 
10740    populate_defaults(vmu);
10741 
10742    stringp = tmp;
10743    if ((s = strsep(&stringp, ","))) {
10744       if (!ast_strlen_zero(s) && s[0] == '*') {
10745          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10746             "\n\tmust be reset in voicemail.conf.\n", box);
10747       }
10748       /* assign password regardless of validity to prevent NULL password from being assigned */
10749       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10750    }
10751    if (stringp && (s = strsep(&stringp, ","))) {
10752       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10753    }
10754    if (stringp && (s = strsep(&stringp, ","))) {
10755       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10756    }
10757    if (stringp && (s = strsep(&stringp, ","))) {
10758       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10759    }
10760    if (stringp && (s = strsep(&stringp, ","))) {
10761       apply_options(vmu, s);
10762    }
10763 
10764    switch (vmu->passwordlocation) {
10765    case OPT_PWLOC_SPOOLDIR:
10766       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10767       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10768    }
10769 
10770    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10771    strcpy(mailbox_full, box);
10772    strcat(mailbox_full, "@");
10773    strcat(mailbox_full, context);
10774 
10775    inboxcount2(mailbox_full, &urgent, &new, &old);
10776    queue_mwi_event(mailbox_full, urgent, new, old);
10777 
10778    return 0;
10779 }
10780 
10781 AST_TEST_DEFINE(test_voicemail_vmuser)
10782 {
10783    int res = 0;
10784    struct ast_vm_user *vmu;
10785    /* language parameter seems to only be used for display in manager action */
10786    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10787       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10788       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10789       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10790       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10791       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10792       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10793       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10794       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10795 #ifdef IMAP_STORAGE
10796    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10797       "imapfolder=INBOX|imapvmshareid=6000";
10798 #endif
10799 
10800    switch (cmd) {
10801    case TEST_INIT:
10802       info->name = "vmuser";
10803       info->category = "/apps/app_voicemail/";
10804       info->summary = "Vmuser unit test";
10805       info->description =
10806          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10807       return AST_TEST_NOT_RUN;
10808    case TEST_EXECUTE:
10809       break;
10810    }
10811 
10812    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10813       return AST_TEST_NOT_RUN;
10814    }
10815    ast_set_flag(vmu, VM_ALLOCED);
10816    populate_defaults(vmu);
10817 
10818    apply_options(vmu, options_string);
10819 
10820    if (!ast_test_flag(vmu, VM_ATTACH)) {
10821       ast_test_status_update(test, "Parse failure for attach option\n");
10822       res = 1;
10823    }
10824    if (strcasecmp(vmu->attachfmt, "wav49")) {
10825       ast_test_status_update(test, "Parse failure for attachftm option\n");
10826       res = 1;
10827    }
10828    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10829       ast_test_status_update(test, "Parse failure for serveremail option\n");
10830       res = 1;
10831    }
10832    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10833       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10834       res = 1;
10835    }
10836    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10837       ast_test_status_update(test, "Parse failure for emailbody option\n");
10838       res = 1;
10839    }
10840    if (strcasecmp(vmu->zonetag, "central")) {
10841       ast_test_status_update(test, "Parse failure for tz option\n");
10842       res = 1;
10843    }
10844    if (!ast_test_flag(vmu, VM_DELETE)) {
10845       ast_test_status_update(test, "Parse failure for delete option\n");
10846       res = 1;
10847    }
10848    if (!ast_test_flag(vmu, VM_SAYCID)) {
10849       ast_test_status_update(test, "Parse failure for saycid option\n");
10850       res = 1;
10851    }
10852    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10853       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10854       res = 1;
10855    }
10856    if (!ast_test_flag(vmu, VM_REVIEW)) {
10857       ast_test_status_update(test, "Parse failure for review option\n");
10858       res = 1;
10859    }
10860    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10861       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10862       res = 1;
10863    }
10864    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10865       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10866       res = 1;
10867    }
10868    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10869       ast_test_status_update(test, "Parse failure for operator option\n");
10870       res = 1;
10871    }
10872    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10873       ast_test_status_update(test, "Parse failure for envelope option\n");
10874       res = 1;
10875    }
10876    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10877       ast_test_status_update(test, "Parse failure for moveheard option\n");
10878       res = 1;
10879    }
10880    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10881       ast_test_status_update(test, "Parse failure for sayduration option\n");
10882       res = 1;
10883    }
10884    if (vmu->saydurationm != 5) {
10885       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10886       res = 1;
10887    }
10888    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10889       ast_test_status_update(test, "Parse failure for forcename option\n");
10890       res = 1;
10891    }
10892    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10893       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10894       res = 1;
10895    }
10896    if (strcasecmp(vmu->callback, "somecontext")) {
10897       ast_test_status_update(test, "Parse failure for callbacks option\n");
10898       res = 1;
10899    }
10900    if (strcasecmp(vmu->dialout, "somecontext2")) {
10901       ast_test_status_update(test, "Parse failure for dialout option\n");
10902       res = 1;
10903    }
10904    if (strcasecmp(vmu->exit, "somecontext3")) {
10905       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10906       res = 1;
10907    }
10908    if (vmu->minsecs != 10) {
10909       ast_test_status_update(test, "Parse failure for minsecs option\n");
10910       res = 1;
10911    }
10912    if (vmu->maxsecs != 100) {
10913       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10914       res = 1;
10915    }
10916    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10917       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10918       res = 1;
10919    }
10920    if (vmu->maxdeletedmsg != 50) {
10921       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10922       res = 1;
10923    }
10924    if (vmu->volgain != 1.3) {
10925       ast_test_status_update(test, "Parse failure for volgain option\n");
10926       res = 1;
10927    }
10928    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10929       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10930       res = 1;
10931    }
10932 #ifdef IMAP_STORAGE
10933    apply_options(vmu, option_string2);
10934 
10935    if (strcasecmp(vmu->imapuser, "imapuser")) {
10936       ast_test_status_update(test, "Parse failure for imapuser option\n");
10937       res = 1;
10938    }
10939    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10940       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10941       res = 1;
10942    }
10943    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10944       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10945       res = 1;
10946    }
10947    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10948       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10949       res = 1;
10950    }
10951 #endif
10952 
10953    free_user(vmu);
10954    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10955 }
10956 
10957 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10958 {
10959    struct ast_vm_user svm;
10960    char *context, *box;
10961    AST_DECLARE_APP_ARGS(args,
10962       AST_APP_ARG(mbox);
10963       AST_APP_ARG(options);
10964    );
10965    static int dep_warning = 0;
10966 
10967    if (ast_strlen_zero(data)) {
10968       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10969       return -1;
10970    }
10971 
10972    if (!dep_warning) {
10973       dep_warning = 1;
10974       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10975    }
10976 
10977    box = ast_strdupa(data);
10978 
10979    AST_STANDARD_APP_ARGS(args, box);
10980 
10981    if (args.options) {
10982    }
10983 
10984    if ((context = strchr(args.mbox, '@'))) {
10985       *context = '\0';
10986       context++;
10987    }
10988 
10989    if (find_user(&svm, context, args.mbox)) {
10990       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10991    } else
10992       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10993 
10994    return 0;
10995 }
10996 
10997 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10998 {
10999    struct ast_vm_user svm;
11000    AST_DECLARE_APP_ARGS(arg,
11001       AST_APP_ARG(mbox);
11002       AST_APP_ARG(context);
11003    );
11004 
11005    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11006 
11007    if (ast_strlen_zero(arg.mbox)) {
11008       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11009       return -1;
11010    }
11011 
11012    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11013    return 0;
11014 }
11015 
11016 static struct ast_custom_function mailbox_exists_acf = {
11017    .name = "MAILBOX_EXISTS",
11018    .read = acf_mailbox_exists,
11019 };
11020 
11021 static int vmauthenticate(struct ast_channel *chan, const char *data)
11022 {
11023    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11024    struct ast_vm_user vmus;
11025    char *options = NULL;
11026    int silent = 0, skipuser = 0;
11027    int res = -1;
11028    
11029    if (data) {
11030       s = ast_strdupa(data);
11031       user = strsep(&s, ",");
11032       options = strsep(&s, ",");
11033       if (user) {
11034          s = user;
11035          user = strsep(&s, "@");
11036          context = strsep(&s, "");
11037          if (!ast_strlen_zero(user))
11038             skipuser++;
11039          ast_copy_string(mailbox, user, sizeof(mailbox));
11040       }
11041    }
11042 
11043    if (options) {
11044       silent = (strchr(options, 's')) != NULL;
11045    }
11046 
11047    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11048       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11049       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11050       ast_play_and_wait(chan, "auth-thankyou");
11051       res = 0;
11052    } else if (mailbox[0] == '*') {
11053       /* user entered '*' */
11054       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11055          res = 0; /* prevent hangup */
11056       }
11057    }
11058 
11059    return res;
11060 }
11061 
11062 static char *show_users_realtime(int fd, const char *context)
11063 {
11064    struct ast_config *cfg;
11065    const char *cat = NULL;
11066 
11067    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11068       "context", context, SENTINEL))) {
11069       return CLI_FAILURE;
11070    }
11071 
11072    ast_cli(fd,
11073       "\n"
11074       "=============================================================\n"
11075       "=== Configured Voicemail Users ==============================\n"
11076       "=============================================================\n"
11077       "===\n");
11078 
11079    while ((cat = ast_category_browse(cfg, cat))) {
11080       struct ast_variable *var = NULL;
11081       ast_cli(fd,
11082          "=== Mailbox ...\n"
11083          "===\n");
11084       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11085          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11086       ast_cli(fd,
11087          "===\n"
11088          "=== ---------------------------------------------------------\n"
11089          "===\n");
11090    }
11091 
11092    ast_cli(fd,
11093       "=============================================================\n"
11094       "\n");
11095 
11096    ast_config_destroy(cfg);
11097 
11098    return CLI_SUCCESS;
11099 }
11100 
11101 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11102 {
11103    int which = 0;
11104    int wordlen;
11105    struct ast_vm_user *vmu;
11106    const char *context = "";
11107 
11108    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11109    if (pos > 4)
11110       return NULL;
11111    if (pos == 3)
11112       return (state == 0) ? ast_strdup("for") : NULL;
11113    wordlen = strlen(word);
11114    AST_LIST_TRAVERSE(&users, vmu, list) {
11115       if (!strncasecmp(word, vmu->context, wordlen)) {
11116          if (context && strcmp(context, vmu->context) && ++which > state)
11117             return ast_strdup(vmu->context);
11118          /* ignore repeated contexts ? */
11119          context = vmu->context;
11120       }
11121    }
11122    return NULL;
11123 }
11124 
11125 /*! \brief Show a list of voicemail users in the CLI */
11126 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11127 {
11128    struct ast_vm_user *vmu;
11129 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11130    const char *context = NULL;
11131    int users_counter = 0;
11132 
11133    switch (cmd) {
11134    case CLI_INIT:
11135       e->command = "voicemail show users";
11136       e->usage =
11137          "Usage: voicemail show users [for <context>]\n"
11138          "       Lists all mailboxes currently set up\n";
11139       return NULL;
11140    case CLI_GENERATE:
11141       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11142    }  
11143 
11144    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11145       return CLI_SHOWUSAGE;
11146    if (a->argc == 5) {
11147       if (strcmp(a->argv[3],"for"))
11148          return CLI_SHOWUSAGE;
11149       context = a->argv[4];
11150    }
11151 
11152    if (ast_check_realtime("voicemail")) {
11153       if (!context) {
11154          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11155          return CLI_SHOWUSAGE;
11156       }
11157       return show_users_realtime(a->fd, context);
11158    }
11159 
11160    AST_LIST_LOCK(&users);
11161    if (AST_LIST_EMPTY(&users)) {
11162       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11163       AST_LIST_UNLOCK(&users);
11164       return CLI_FAILURE;
11165    }
11166    if (a->argc == 3)
11167       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11168    else {
11169       int count = 0;
11170       AST_LIST_TRAVERSE(&users, vmu, list) {
11171          if (!strcmp(context, vmu->context))
11172             count++;
11173       }
11174       if (count) {
11175          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11176       } else {
11177          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11178          AST_LIST_UNLOCK(&users);
11179          return CLI_FAILURE;
11180       }
11181    }
11182    AST_LIST_TRAVERSE(&users, vmu, list) {
11183       int newmsgs = 0, oldmsgs = 0;
11184       char count[12], tmp[256] = "";
11185 
11186       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
11187          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11188          inboxcount(tmp, &newmsgs, &oldmsgs);
11189          snprintf(count, sizeof(count), "%d", newmsgs);
11190          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11191          users_counter++;
11192       }
11193    }
11194    AST_LIST_UNLOCK(&users);
11195    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11196    return CLI_SUCCESS;
11197 }
11198 
11199 /*! \brief Show a list of voicemail zones in the CLI */
11200 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11201 {
11202    struct vm_zone *zone;
11203 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11204    char *res = CLI_SUCCESS;
11205 
11206    switch (cmd) {
11207    case CLI_INIT:
11208       e->command = "voicemail show zones";
11209       e->usage =
11210          "Usage: voicemail show zones\n"
11211          "       Lists zone message formats\n";
11212       return NULL;
11213    case CLI_GENERATE:
11214       return NULL;
11215    }
11216 
11217    if (a->argc != 3)
11218       return CLI_SHOWUSAGE;
11219 
11220    AST_LIST_LOCK(&zones);
11221    if (!AST_LIST_EMPTY(&zones)) {
11222       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11223       AST_LIST_TRAVERSE(&zones, zone, list) {
11224          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11225       }
11226    } else {
11227       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11228       res = CLI_FAILURE;
11229    }
11230    AST_LIST_UNLOCK(&zones);
11231 
11232    return res;
11233 }
11234 
11235 /*! \brief Reload voicemail configuration from the CLI */
11236 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11237 {
11238    switch (cmd) {
11239    case CLI_INIT:
11240       e->command = "voicemail reload";
11241       e->usage =
11242          "Usage: voicemail reload\n"
11243          "       Reload voicemail configuration\n";
11244       return NULL;
11245    case CLI_GENERATE:
11246       return NULL;
11247    }
11248 
11249    if (a->argc != 2)
11250       return CLI_SHOWUSAGE;
11251 
11252    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11253    load_config(1);
11254    
11255    return CLI_SUCCESS;
11256 }
11257 
11258 static struct ast_cli_entry cli_voicemail[] = {
11259    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11260    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11261    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11262 };
11263 
11264 #ifdef IMAP_STORAGE
11265    #define DATA_EXPORT_VM_USERS(USER)              \
11266       USER(ast_vm_user, context, AST_DATA_STRING)        \
11267       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11268       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11269       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11270       USER(ast_vm_user, email, AST_DATA_STRING)       \
11271       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11272       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11273       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11274       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11275       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11276       USER(ast_vm_user, language, AST_DATA_STRING)       \
11277       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11278       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11279       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11280       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11281       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11282       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11283       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11284       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11285       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11286       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11287       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11288       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11289       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11290       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11291       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11292 #else
11293    #define DATA_EXPORT_VM_USERS(USER)              \
11294       USER(ast_vm_user, context, AST_DATA_STRING)        \
11295       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11296       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11297       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11298       USER(ast_vm_user, email, AST_DATA_STRING)       \
11299       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11300       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11301       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11302       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11303       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11304       USER(ast_vm_user, language, AST_DATA_STRING)       \
11305       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11306       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11307       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11308       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11309       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11310       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11311       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11312       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11313       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11314       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11315       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11316       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11317 #endif
11318 
11319 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11320 
11321 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11322    ZONE(vm_zone, name, AST_DATA_STRING)      \
11323    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11324    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11325 
11326 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11327 
11328 /*!
11329  * \internal
11330  * \brief Add voicemail user to the data_root.
11331  * \param[in] search The search tree.
11332  * \param[in] data_root The main result node.
11333  * \param[in] user The voicemail user.
11334  */
11335 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11336     struct ast_data *data_root, struct ast_vm_user *user)
11337 {
11338    struct ast_data *data_user, *data_zone;
11339    struct ast_data *data_state;
11340    struct vm_zone *zone = NULL;
11341    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11342    char ext_context[256] = "";
11343 
11344    data_user = ast_data_add_node(data_root, "user");
11345    if (!data_user) {
11346       return -1;
11347    }
11348 
11349    ast_data_add_structure(ast_vm_user, data_user, user);
11350 
11351    AST_LIST_LOCK(&zones);
11352    AST_LIST_TRAVERSE(&zones, zone, list) {
11353       if (!strcmp(zone->name, user->zonetag)) {
11354          break;
11355       }
11356    }
11357    AST_LIST_UNLOCK(&zones);
11358 
11359    /* state */
11360    data_state = ast_data_add_node(data_user, "state");
11361    if (!data_state) {
11362       return -1;
11363    }
11364    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11365    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11366    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11367    ast_data_add_int(data_state, "newmsg", newmsg);
11368    ast_data_add_int(data_state, "oldmsg", oldmsg);
11369 
11370    if (zone) {
11371       data_zone = ast_data_add_node(data_user, "zone");
11372       ast_data_add_structure(vm_zone, data_zone, zone);
11373    }
11374 
11375    if (!ast_data_search_match(search, data_user)) {
11376       ast_data_remove_node(data_root, data_user);
11377    }
11378 
11379    return 0;
11380 }
11381 
11382 static int vm_users_data_provider_get(const struct ast_data_search *search,
11383    struct ast_data *data_root)
11384 {
11385    struct ast_vm_user *user;
11386 
11387    AST_LIST_LOCK(&users);
11388    AST_LIST_TRAVERSE(&users, user, list) {
11389       vm_users_data_provider_get_helper(search, data_root, user);
11390    }
11391    AST_LIST_UNLOCK(&users);
11392 
11393    return 0;
11394 }
11395 
11396 static const struct ast_data_handler vm_users_data_provider = {
11397    .version = AST_DATA_HANDLER_VERSION,
11398    .get = vm_users_data_provider_get
11399 };
11400 
11401 static const struct ast_data_entry vm_data_providers[] = {
11402    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11403 };
11404 
11405 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11406 {
11407    int new = 0, old = 0, urgent = 0;
11408 
11409    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11410 
11411    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11412       mwi_sub->old_urgent = urgent;
11413       mwi_sub->old_new = new;
11414       mwi_sub->old_old = old;
11415       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11416       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11417    }
11418 }
11419 
11420 static void poll_subscribed_mailboxes(void)
11421 {
11422    struct mwi_sub *mwi_sub;
11423 
11424    AST_RWLIST_RDLOCK(&mwi_subs);
11425    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11426       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11427          poll_subscribed_mailbox(mwi_sub);
11428       }
11429    }
11430    AST_RWLIST_UNLOCK(&mwi_subs);
11431 }
11432 
11433 static void *mb_poll_thread(void *data)
11434 {
11435    while (poll_thread_run) {
11436       struct timespec ts = { 0, };
11437       struct timeval wait;
11438 
11439       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11440       ts.tv_sec = wait.tv_sec;
11441       ts.tv_nsec = wait.tv_usec * 1000;
11442 
11443       ast_mutex_lock(&poll_lock);
11444       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11445       ast_mutex_unlock(&poll_lock);
11446 
11447       if (!poll_thread_run)
11448          break;
11449 
11450       poll_subscribed_mailboxes();
11451    }
11452 
11453    return NULL;
11454 }
11455 
11456 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11457 {
11458    ast_free(mwi_sub);
11459 }
11460 
11461 static int handle_unsubscribe(void *datap)
11462 {
11463    struct mwi_sub *mwi_sub;
11464    uint32_t *uniqueid = datap;
11465    
11466    AST_RWLIST_WRLOCK(&mwi_subs);
11467    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11468       if (mwi_sub->uniqueid == *uniqueid) {
11469          AST_LIST_REMOVE_CURRENT(entry);
11470          break;
11471       }
11472    }
11473    AST_RWLIST_TRAVERSE_SAFE_END
11474    AST_RWLIST_UNLOCK(&mwi_subs);
11475 
11476    if (mwi_sub)
11477       mwi_sub_destroy(mwi_sub);
11478 
11479    ast_free(uniqueid);  
11480    return 0;
11481 }
11482 
11483 static int handle_subscribe(void *datap)
11484 {
11485    unsigned int len;
11486    struct mwi_sub *mwi_sub;
11487    struct mwi_sub_task *p = datap;
11488 
11489    len = sizeof(*mwi_sub);
11490    if (!ast_strlen_zero(p->mailbox))
11491       len += strlen(p->mailbox);
11492 
11493    if (!ast_strlen_zero(p->context))
11494       len += strlen(p->context) + 1; /* Allow for seperator */
11495 
11496    if (!(mwi_sub = ast_calloc(1, len)))
11497       return -1;
11498 
11499    mwi_sub->uniqueid = p->uniqueid;
11500    if (!ast_strlen_zero(p->mailbox))
11501       strcpy(mwi_sub->mailbox, p->mailbox);
11502 
11503    if (!ast_strlen_zero(p->context)) {
11504       strcat(mwi_sub->mailbox, "@");
11505       strcat(mwi_sub->mailbox, p->context);
11506    }
11507 
11508    AST_RWLIST_WRLOCK(&mwi_subs);
11509    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11510    AST_RWLIST_UNLOCK(&mwi_subs);
11511    ast_free((void *) p->mailbox);
11512    ast_free((void *) p->context);
11513    ast_free(p);
11514    poll_subscribed_mailbox(mwi_sub);
11515    return 0;
11516 }
11517 
11518 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11519 {
11520    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11521    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11522       return;
11523 
11524    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11525       return;
11526 
11527    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11528    *uniqueid = u;
11529    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11530       ast_free(uniqueid);
11531    }
11532 }
11533 
11534 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11535 {
11536    struct mwi_sub_task *mwist;
11537    
11538    if (ast_event_get_type(event) != AST_EVENT_SUB)
11539       return;
11540 
11541    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11542       return;
11543 
11544    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11545       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11546       return;
11547    }
11548    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11549    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11550    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11551    
11552    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11553       ast_free(mwist);
11554    }
11555 }
11556 
11557 static void start_poll_thread(void)
11558 {
11559    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11560       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11561       AST_EVENT_IE_END);
11562 
11563    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11564       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11565       AST_EVENT_IE_END);
11566 
11567    if (mwi_sub_sub)
11568       ast_event_report_subs(mwi_sub_sub);
11569 
11570    poll_thread_run = 1;
11571 
11572    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11573 }
11574 
11575 static void stop_poll_thread(void)
11576 {
11577    poll_thread_run = 0;
11578 
11579    if (mwi_sub_sub) {
11580       ast_event_unsubscribe(mwi_sub_sub);
11581       mwi_sub_sub = NULL;
11582    }
11583 
11584    if (mwi_unsub_sub) {
11585       ast_event_unsubscribe(mwi_unsub_sub);
11586       mwi_unsub_sub = NULL;
11587    }
11588 
11589    ast_mutex_lock(&poll_lock);
11590    ast_cond_signal(&poll_cond);
11591    ast_mutex_unlock(&poll_lock);
11592 
11593    pthread_join(poll_thread, NULL);
11594 
11595    poll_thread = AST_PTHREADT_NULL;
11596 }
11597 
11598 /*! \brief Manager list voicemail users command */
11599 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11600 {
11601    struct ast_vm_user *vmu = NULL;
11602    const char *id = astman_get_header(m, "ActionID");
11603    char actionid[128] = "";
11604 
11605    if (!ast_strlen_zero(id))
11606       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11607 
11608    AST_LIST_LOCK(&users);
11609 
11610    if (AST_LIST_EMPTY(&users)) {
11611       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11612       AST_LIST_UNLOCK(&users);
11613       return RESULT_SUCCESS;
11614    }
11615    
11616    astman_send_ack(s, m, "Voicemail user list will follow");
11617    
11618    AST_LIST_TRAVERSE(&users, vmu, list) {
11619       char dirname[256];
11620 
11621 #ifdef IMAP_STORAGE
11622       int new, old;
11623       inboxcount(vmu->mailbox, &new, &old);
11624 #endif
11625       
11626       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11627       astman_append(s,
11628          "%s"
11629          "Event: VoicemailUserEntry\r\n"
11630          "VMContext: %s\r\n"
11631          "VoiceMailbox: %s\r\n"
11632          "Fullname: %s\r\n"
11633          "Email: %s\r\n"
11634          "Pager: %s\r\n"
11635          "ServerEmail: %s\r\n"
11636          "MailCommand: %s\r\n"
11637          "Language: %s\r\n"
11638          "TimeZone: %s\r\n"
11639          "Callback: %s\r\n"
11640          "Dialout: %s\r\n"
11641          "UniqueID: %s\r\n"
11642          "ExitContext: %s\r\n"
11643          "SayDurationMinimum: %d\r\n"
11644          "SayEnvelope: %s\r\n"
11645          "SayCID: %s\r\n"
11646          "AttachMessage: %s\r\n"
11647          "AttachmentFormat: %s\r\n"
11648          "DeleteMessage: %s\r\n"
11649          "VolumeGain: %.2f\r\n"
11650          "CanReview: %s\r\n"
11651          "CallOperator: %s\r\n"
11652          "MaxMessageCount: %d\r\n"
11653          "MaxMessageLength: %d\r\n"
11654          "NewMessageCount: %d\r\n"
11655 #ifdef IMAP_STORAGE
11656          "OldMessageCount: %d\r\n"
11657          "IMAPUser: %s\r\n"
11658 #endif
11659          "\r\n",
11660          actionid,
11661          vmu->context,
11662          vmu->mailbox,
11663          vmu->fullname,
11664          vmu->email,
11665          vmu->pager,
11666          vmu->serveremail,
11667          vmu->mailcmd,
11668          vmu->language,
11669          vmu->zonetag,
11670          vmu->callback,
11671          vmu->dialout,
11672          vmu->uniqueid,
11673          vmu->exit,
11674          vmu->saydurationm,
11675          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11676          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11677          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11678          vmu->attachfmt,
11679          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11680          vmu->volgain,
11681          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11682          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11683          vmu->maxmsg,
11684          vmu->maxsecs,
11685 #ifdef IMAP_STORAGE
11686          new, old, vmu->imapuser
11687 #else
11688          count_messages(vmu, dirname)
11689 #endif
11690          );
11691    }     
11692    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11693 
11694    AST_LIST_UNLOCK(&users);
11695 
11696    return RESULT_SUCCESS;
11697 }
11698 
11699 /*! \brief Free the users structure. */
11700 static void free_vm_users(void) 
11701 {
11702    struct ast_vm_user *current;
11703    AST_LIST_LOCK(&users);
11704    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11705       ast_set_flag(current, VM_ALLOCED);
11706       free_user(current);
11707    }
11708    AST_LIST_UNLOCK(&users);
11709 }
11710 
11711 /*! \brief Free the zones structure. */
11712 static void free_vm_zones(void)
11713 {
11714    struct vm_zone *zcur;
11715    AST_LIST_LOCK(&zones);
11716    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11717       free_zone(zcur);
11718    AST_LIST_UNLOCK(&zones);
11719 }
11720 
11721 static const char *substitute_escapes(const char *value)
11722 {
11723    char *current;
11724 
11725    /* Add 16 for fudge factor */
11726    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11727 
11728    ast_str_reset(str);
11729    
11730    /* Substitute strings \r, \n, and \t into the appropriate characters */
11731    for (current = (char *) value; *current; current++) {
11732       if (*current == '\\') {
11733          current++;
11734          if (!*current) {
11735             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11736             break;
11737          }
11738          switch (*current) {
11739          case '\\':
11740             ast_str_append(&str, 0, "\\");
11741             break;
11742          case 'r':
11743             ast_str_append(&str, 0, "\r");
11744             break;
11745          case 'n':
11746 #ifdef IMAP_STORAGE
11747             if (!str->used || str->str[str->used - 1] != '\r') {
11748                ast_str_append(&str, 0, "\r");
11749             }
11750 #endif
11751             ast_str_append(&str, 0, "\n");
11752             break;
11753          case 't':
11754             ast_str_append(&str, 0, "\t");
11755             break;
11756          default:
11757             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11758             break;
11759          }
11760       } else {
11761          ast_str_append(&str, 0, "%c", *current);
11762       }
11763    }
11764 
11765    return ast_str_buffer(str);
11766 }
11767 
11768 static int load_config(int reload)
11769 {
11770    struct ast_vm_user *current;
11771    struct ast_config *cfg, *ucfg;
11772    char *cat;
11773    struct ast_variable *var;
11774    const char *val;
11775    char *q, *stringp, *tmp;
11776    int x;
11777    int tmpadsi[4];
11778    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11779    char secretfn[PATH_MAX] = "";
11780 
11781    ast_unload_realtime("voicemail");
11782    ast_unload_realtime("voicemail_data");
11783 
11784    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11785       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11786          return 0;
11787       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11788          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11789          ucfg = NULL;
11790       }
11791       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11792       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11793          ast_config_destroy(ucfg);
11794          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11795          return 0;
11796       }
11797    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11798       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11799       return 0;
11800    } else {
11801       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11802       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11803          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11804          ucfg = NULL;
11805       }
11806    }
11807 #ifdef IMAP_STORAGE
11808    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11809 #endif
11810    /* set audio control prompts */
11811    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11812    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11813    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11814    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11815    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11816 
11817    /* Free all the users structure */  
11818    free_vm_users();
11819 
11820    /* Free all the zones structure */
11821    free_vm_zones();
11822 
11823    AST_LIST_LOCK(&users);  
11824 
11825    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11826    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11827 
11828    if (cfg) {
11829       /* General settings */
11830 
11831       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11832          val = "default";
11833       ast_copy_string(userscontext, val, sizeof(userscontext));
11834       /* Attach voice message to mail message ? */
11835       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11836          val = "yes";
11837       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11838 
11839       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11840          val = "no";
11841       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11842 
11843       volgain = 0.0;
11844       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11845          sscanf(val, "%30lf", &volgain);
11846 
11847 #ifdef ODBC_STORAGE
11848       strcpy(odbc_database, "asterisk");
11849       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11850          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11851       }
11852       strcpy(odbc_table, "voicemessages");
11853       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11854          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11855       }
11856 #endif      
11857       /* Mail command */
11858       strcpy(mailcmd, SENDMAIL);
11859       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11860          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11861 
11862       maxsilence = 0;
11863       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11864          maxsilence = atoi(val);
11865          if (maxsilence > 0)
11866             maxsilence *= 1000;
11867       }
11868       
11869       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11870          maxmsg = MAXMSG;
11871       } else {
11872          maxmsg = atoi(val);
11873          if (maxmsg < 0) {
11874             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11875             maxmsg = MAXMSG;
11876          } else if (maxmsg > MAXMSGLIMIT) {
11877             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11878             maxmsg = MAXMSGLIMIT;
11879          }
11880       }
11881 
11882       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11883          maxdeletedmsg = 0;
11884       } else {
11885          if (sscanf(val, "%30d", &x) == 1)
11886             maxdeletedmsg = x;
11887          else if (ast_true(val))
11888             maxdeletedmsg = MAXMSG;
11889          else
11890             maxdeletedmsg = 0;
11891 
11892          if (maxdeletedmsg < 0) {
11893             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11894             maxdeletedmsg = MAXMSG;
11895          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11896             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11897             maxdeletedmsg = MAXMSGLIMIT;
11898          }
11899       }
11900 
11901       /* Load date format config for voicemail mail */
11902       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11903          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11904       }
11905 
11906       /* Load date format config for voicemail pager mail */
11907       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11908          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11909       }
11910 
11911       /* External password changing command */
11912       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11913          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11914          pwdchange = PWDCHANGE_EXTERNAL;
11915       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11916          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11917          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11918       }
11919  
11920       /* External password validation command */
11921       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11922          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11923          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11924       }
11925 
11926 #ifdef IMAP_STORAGE
11927       /* IMAP server address */
11928       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11929          ast_copy_string(imapserver, val, sizeof(imapserver));
11930       } else {
11931          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11932       }
11933       /* IMAP server port */
11934       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11935          ast_copy_string(imapport, val, sizeof(imapport));
11936       } else {
11937          ast_copy_string(imapport, "143", sizeof(imapport));
11938       }
11939       /* IMAP server flags */
11940       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11941          ast_copy_string(imapflags, val, sizeof(imapflags));
11942       }
11943       /* IMAP server master username */
11944       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11945          ast_copy_string(authuser, val, sizeof(authuser));
11946       }
11947       /* IMAP server master password */
11948       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11949          ast_copy_string(authpassword, val, sizeof(authpassword));
11950       }
11951       /* Expunge on exit */
11952       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11953          if (ast_false(val))
11954             expungeonhangup = 0;
11955          else
11956             expungeonhangup = 1;
11957       } else {
11958          expungeonhangup = 1;
11959       }
11960       /* IMAP voicemail folder */
11961       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11962          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11963       } else {
11964          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
11965       }
11966       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11967          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11968       }
11969       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11970          imapgreetings = ast_true(val);
11971       } else {
11972          imapgreetings = 0;
11973       }
11974       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11975          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11976       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
11977          /* Also support greetingsfolder as documented in voicemail.conf.sample */
11978          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11979       } else {
11980          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
11981       }
11982 
11983       /* There is some very unorthodox casting done here. This is due
11984        * to the way c-client handles the argument passed in. It expects a 
11985        * void pointer and casts the pointer directly to a long without
11986        * first dereferencing it. */
11987       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
11988          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
11989       } else {
11990          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
11991       }
11992 
11993       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
11994          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
11995       } else {
11996          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
11997       }
11998 
11999       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12000          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12001       } else {
12002          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12003       }
12004 
12005       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12006          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12007       } else {
12008          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12009       }
12010 
12011       /* Increment configuration version */
12012       imapversion++;
12013 #endif
12014       /* External voicemail notify application */
12015       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12016          ast_copy_string(externnotify, val, sizeof(externnotify));
12017          ast_debug(1, "found externnotify: %s\n", externnotify);
12018       } else {
12019          externnotify[0] = '\0';
12020       }
12021 
12022       /* SMDI voicemail notification */
12023       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12024          ast_debug(1, "Enabled SMDI voicemail notification\n");
12025          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12026             smdi_iface = ast_smdi_interface_find(val);
12027          } else {
12028             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12029             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12030          }
12031          if (!smdi_iface) {
12032             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12033          } 
12034       }
12035 
12036       /* Silence treshold */
12037       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12038       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12039          silencethreshold = atoi(val);
12040       
12041       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12042          val = ASTERISK_USERNAME;
12043       ast_copy_string(serveremail, val, sizeof(serveremail));
12044       
12045       vmmaxsecs = 0;
12046       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12047          if (sscanf(val, "%30d", &x) == 1) {
12048             vmmaxsecs = x;
12049          } else {
12050             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12051          }
12052       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12053          static int maxmessage_deprecate = 0;
12054          if (maxmessage_deprecate == 0) {
12055             maxmessage_deprecate = 1;
12056             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12057          }
12058          if (sscanf(val, "%30d", &x) == 1) {
12059             vmmaxsecs = x;
12060          } else {
12061             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12062          }
12063       }
12064 
12065       vmminsecs = 0;
12066       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12067          if (sscanf(val, "%30d", &x) == 1) {
12068             vmminsecs = x;
12069             if (maxsilence / 1000 >= vmminsecs) {
12070                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12071             }
12072          } else {
12073             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12074          }
12075       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12076          static int maxmessage_deprecate = 0;
12077          if (maxmessage_deprecate == 0) {
12078             maxmessage_deprecate = 1;
12079             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12080          }
12081          if (sscanf(val, "%30d", &x) == 1) {
12082             vmminsecs = x;
12083             if (maxsilence / 1000 >= vmminsecs) {
12084                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12085             }
12086          } else {
12087             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12088          }
12089       }
12090 
12091       val = ast_variable_retrieve(cfg, "general", "format");
12092       if (!val) {
12093          val = "wav";   
12094       } else {
12095          tmp = ast_strdupa(val);
12096          val = ast_format_str_reduce(tmp);
12097          if (!val) {
12098             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12099             val = "wav";
12100          }
12101       }
12102       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12103 
12104       skipms = 3000;
12105       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12106          if (sscanf(val, "%30d", &x) == 1) {
12107             maxgreet = x;
12108          } else {
12109             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12110          }
12111       }
12112 
12113       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12114          if (sscanf(val, "%30d", &x) == 1) {
12115             skipms = x;
12116          } else {
12117             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12118          }
12119       }
12120 
12121       maxlogins = 3;
12122       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12123          if (sscanf(val, "%30d", &x) == 1) {
12124             maxlogins = x;
12125          } else {
12126             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12127          }
12128       }
12129 
12130       minpassword = MINPASSWORD;
12131       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12132          if (sscanf(val, "%30d", &x) == 1) {
12133             minpassword = x;
12134          } else {
12135             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12136          }
12137       }
12138 
12139       /* Force new user to record name ? */
12140       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12141          val = "no";
12142       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12143 
12144       /* Force new user to record greetings ? */
12145       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12146          val = "no";
12147       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12148 
12149       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12150          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12151          stringp = ast_strdupa(val);
12152          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12153             if (!ast_strlen_zero(stringp)) {
12154                q = strsep(&stringp, ",");
12155                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12156                   q++;
12157                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12158                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12159             } else {
12160                cidinternalcontexts[x][0] = '\0';
12161             }
12162          }
12163       }
12164       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12165          ast_debug(1, "VM Review Option disabled globally\n");
12166          val = "no";
12167       }
12168       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12169 
12170       /* Temporary greeting reminder */
12171       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12172          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12173          val = "no";
12174       } else {
12175          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12176       }
12177       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12178       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12179          ast_debug(1, "VM next message wrap disabled globally\n");
12180          val = "no";
12181       }
12182       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12183 
12184       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12185          ast_debug(1, "VM Operator break disabled globally\n");
12186          val = "no";
12187       }
12188       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12189 
12190       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12191          ast_debug(1, "VM CID Info before msg disabled globally\n");
12192          val = "no";
12193       } 
12194       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12195 
12196       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12197          ast_debug(1, "Send Voicemail msg disabled globally\n");
12198          val = "no";
12199       }
12200       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12201    
12202       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12203          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12204          val = "yes";
12205       }
12206       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12207 
12208       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12209          ast_debug(1, "Move Heard enabled globally\n");
12210          val = "yes";
12211       }
12212       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12213 
12214       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12215          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12216          val = "no";
12217       }
12218       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12219 
12220       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12221          ast_debug(1, "Duration info before msg enabled globally\n");
12222          val = "yes";
12223       }
12224       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12225 
12226       saydurationminfo = 2;
12227       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12228          if (sscanf(val, "%30d", &x) == 1) {
12229             saydurationminfo = x;
12230          } else {
12231             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12232          }
12233       }
12234 
12235       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12236          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12237          val = "no";
12238       }
12239       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12240 
12241       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12242          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12243          ast_debug(1, "found dialout context: %s\n", dialcontext);
12244       } else {
12245          dialcontext[0] = '\0';  
12246       }
12247       
12248       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12249          ast_copy_string(callcontext, val, sizeof(callcontext));
12250          ast_debug(1, "found callback context: %s\n", callcontext);
12251       } else {
12252          callcontext[0] = '\0';
12253       }
12254 
12255       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12256          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12257          ast_debug(1, "found operator context: %s\n", exitcontext);
12258       } else {
12259          exitcontext[0] = '\0';
12260       }
12261       
12262       /* load password sounds configuration */
12263       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12264          ast_copy_string(vm_password, val, sizeof(vm_password));
12265       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12266          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12267       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12268          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12269       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12270          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12271       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12272          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12273       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12274          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12275       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12276          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12277       }
12278       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12279          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12280       }
12281       /* load configurable audio prompts */
12282       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12283          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12284       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12285          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12286       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12287          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12288       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12289          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12290       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12291          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12292 
12293       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12294          val = "no";
12295       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12296 
12297       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12298          val = "voicemail.conf";
12299       }
12300       if (!(strcmp(val, "spooldir"))) {
12301          passwordlocation = OPT_PWLOC_SPOOLDIR;
12302       } else {
12303          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12304       }
12305 
12306       poll_freq = DEFAULT_POLL_FREQ;
12307       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12308          if (sscanf(val, "%30u", &poll_freq) != 1) {
12309             poll_freq = DEFAULT_POLL_FREQ;
12310             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12311          }
12312       }
12313 
12314       poll_mailboxes = 0;
12315       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12316          poll_mailboxes = ast_true(val);
12317 
12318       if (ucfg) { 
12319          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12320             if (!strcasecmp(cat, "general")) {
12321                continue;
12322             }
12323             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12324                continue;
12325             if ((current = find_or_create(userscontext, cat))) {
12326                populate_defaults(current);
12327                apply_options_full(current, ast_variable_browse(ucfg, cat));
12328                ast_copy_string(current->context, userscontext, sizeof(current->context));
12329                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12330                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12331                }
12332 
12333                switch (current->passwordlocation) {
12334                case OPT_PWLOC_SPOOLDIR:
12335                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12336                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12337                }
12338             }
12339          }
12340          ast_config_destroy(ucfg);
12341       }
12342       cat = ast_category_browse(cfg, NULL);
12343       while (cat) {
12344          if (strcasecmp(cat, "general")) {
12345             var = ast_variable_browse(cfg, cat);
12346             if (strcasecmp(cat, "zonemessages")) {
12347                /* Process mailboxes in this context */
12348                while (var) {
12349                   append_mailbox(cat, var->name, var->value);
12350                   var = var->next;
12351                }
12352             } else {
12353                /* Timezones in this context */
12354                while (var) {
12355                   struct vm_zone *z;
12356                   if ((z = ast_malloc(sizeof(*z)))) {
12357                      char *msg_format, *tzone;
12358                      msg_format = ast_strdupa(var->value);
12359                      tzone = strsep(&msg_format, "|,");
12360                      if (msg_format) {
12361                         ast_copy_string(z->name, var->name, sizeof(z->name));
12362                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12363                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12364                         AST_LIST_LOCK(&zones);
12365                         AST_LIST_INSERT_HEAD(&zones, z, list);
12366                         AST_LIST_UNLOCK(&zones);
12367                      } else {
12368                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12369                         ast_free(z);
12370                      }
12371                   } else {
12372                      AST_LIST_UNLOCK(&users);
12373                      ast_config_destroy(cfg);
12374                      return -1;
12375                   }
12376                   var = var->next;
12377                }
12378             }
12379          }
12380          cat = ast_category_browse(cfg, cat);
12381       }
12382       memset(fromstring, 0, sizeof(fromstring));
12383       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12384       strcpy(charset, "ISO-8859-1");
12385       if (emailbody) {
12386          ast_free(emailbody);
12387          emailbody = NULL;
12388       }
12389       if (emailsubject) {
12390          ast_free(emailsubject);
12391          emailsubject = NULL;
12392       }
12393       if (pagerbody) {
12394          ast_free(pagerbody);
12395          pagerbody = NULL;
12396       }
12397       if (pagersubject) {
12398          ast_free(pagersubject);
12399          pagersubject = NULL;
12400       }
12401       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12402          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12403       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12404          ast_copy_string(fromstring, val, sizeof(fromstring));
12405       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12406          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12407       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12408          ast_copy_string(charset, val, sizeof(charset));
12409       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12410          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12411          for (x = 0; x < 4; x++) {
12412             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12413          }
12414       }
12415       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12416          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12417          for (x = 0; x < 4; x++) {
12418             memcpy(&adsisec[x], &tmpadsi[x], 1);
12419          }
12420       }
12421       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12422          if (atoi(val)) {
12423             adsiver = atoi(val);
12424          }
12425       }
12426       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12427          ast_copy_string(zonetag, val, sizeof(zonetag));
12428       }
12429       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12430          ast_copy_string(locale, val, sizeof(locale));
12431       }
12432       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12433          emailsubject = ast_strdup(substitute_escapes(val));
12434       }
12435       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12436          emailbody = ast_strdup(substitute_escapes(val));
12437       }
12438       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12439          pagersubject = ast_strdup(substitute_escapes(val));
12440       }
12441       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12442          pagerbody = ast_strdup(substitute_escapes(val));
12443       }
12444       AST_LIST_UNLOCK(&users);
12445       ast_config_destroy(cfg);
12446 
12447       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12448          start_poll_thread();
12449       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12450          stop_poll_thread();;
12451 
12452       return 0;
12453    } else {
12454       AST_LIST_UNLOCK(&users);
12455       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12456       if (ucfg)
12457          ast_config_destroy(ucfg);
12458       return 0;
12459    }
12460 }
12461 
12462 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12463 {
12464    int res = -1;
12465    char dir[PATH_MAX];
12466    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12467    ast_debug(2, "About to try retrieving name file %s\n", dir);
12468    RETRIEVE(dir, -1, mailbox, context);
12469    if (ast_fileexists(dir, NULL, NULL)) {
12470       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12471    }
12472    DISPOSE(dir, -1);
12473    return res;
12474 }
12475 
12476 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12477    struct ast_config *pwconf;
12478    struct ast_flags config_flags = { 0 };
12479 
12480    pwconf = ast_config_load(secretfn, config_flags);
12481    if (pwconf) {
12482       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12483       if (val) {
12484          ast_copy_string(password, val, passwordlen);
12485          return;
12486       }
12487    }
12488    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12489 }
12490 
12491 static int write_password_to_file(const char *secretfn, const char *password) {
12492    struct ast_config *conf;
12493    struct ast_category *cat;
12494    struct ast_variable *var;
12495 
12496    if (!(conf=ast_config_new())) {
12497       ast_log(LOG_ERROR, "Error creating new config structure\n");
12498       return -1;
12499    }
12500    if (!(cat=ast_category_new("general","",1))) {
12501       ast_log(LOG_ERROR, "Error creating new category structure\n");
12502       return -1;
12503    }
12504    if (!(var=ast_variable_new("password",password,""))) {
12505       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12506       return -1;
12507    }
12508    ast_category_append(conf,cat);
12509    ast_variable_append(cat,var);
12510    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12511       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12512       return -1;
12513    }
12514    return 0;
12515 }
12516 
12517 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12518 {
12519    char *context;
12520    char *args_copy;
12521    int res;
12522 
12523    if (ast_strlen_zero(data)) {
12524       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12525       return -1;
12526    }
12527 
12528    args_copy = ast_strdupa(data);
12529    if ((context = strchr(args_copy, '@'))) {
12530       *context++ = '\0';
12531    } else {
12532       context = "default";
12533    }
12534 
12535    if ((res = sayname(chan, args_copy, context) < 0)) {
12536       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12537       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12538       if (!res) {
12539          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12540       }
12541    }
12542 
12543    return res;
12544 }
12545 
12546 #ifdef TEST_FRAMEWORK
12547 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12548 {
12549    return 0;
12550 }
12551 
12552 static struct ast_frame *fake_read(struct ast_channel *ast)
12553 {
12554    return &ast_null_frame;
12555 }
12556 
12557 AST_TEST_DEFINE(test_voicemail_vmsayname)
12558 {
12559    char dir[PATH_MAX];
12560    char dir2[PATH_MAX];
12561    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12562    static const char TEST_EXTENSION[] = "1234";
12563 
12564    struct ast_channel *test_channel1 = NULL;
12565    int res = -1;
12566 
12567    static const struct ast_channel_tech fake_tech = {
12568       .write = fake_write,
12569       .read = fake_read,
12570    };
12571 
12572    switch (cmd) {
12573    case TEST_INIT:
12574       info->name = "vmsayname_exec";
12575       info->category = "/apps/app_voicemail/";
12576       info->summary = "Vmsayname unit test";
12577       info->description =
12578          "This tests passing various parameters to vmsayname";
12579       return AST_TEST_NOT_RUN;
12580    case TEST_EXECUTE:
12581       break;
12582    }
12583 
12584    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12585         NULL, NULL, 0, 0, "TestChannel1"))) {
12586       goto exit_vmsayname_test;
12587    }
12588 
12589    /* normally this is done in the channel driver */
12590    test_channel1->nativeformats = AST_FORMAT_GSM;
12591    test_channel1->writeformat = AST_FORMAT_GSM;
12592    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12593    test_channel1->readformat = AST_FORMAT_GSM;
12594    test_channel1->rawreadformat = AST_FORMAT_GSM;
12595    test_channel1->tech = &fake_tech;
12596 
12597    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12598    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12599    if (!(res = vmsayname_exec(test_channel1, dir))) {
12600       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12601       if (ast_fileexists(dir, NULL, NULL)) {
12602          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12603          res = -1;
12604          goto exit_vmsayname_test;
12605       } else {
12606          /* no greeting already exists as expected, let's create one to fully test sayname */
12607          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12608             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12609             goto exit_vmsayname_test;
12610          }
12611          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12612          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12613          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12614          if ((res = symlink(dir, dir2))) {
12615             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12616             goto exit_vmsayname_test;
12617          }
12618          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12619          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12620          res = vmsayname_exec(test_channel1, dir);
12621 
12622          /* TODO: there may be a better way to do this */
12623          unlink(dir2);
12624          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12625          rmdir(dir2);
12626          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12627          rmdir(dir2);
12628       }
12629    }
12630 
12631 exit_vmsayname_test:
12632 
12633    if (test_channel1) {
12634       ast_hangup(test_channel1);
12635    }
12636 
12637    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12638 }
12639 
12640 AST_TEST_DEFINE(test_voicemail_msgcount)
12641 {
12642    int i, j, res = AST_TEST_PASS, syserr;
12643    struct ast_vm_user *vmu;
12644    struct vm_state vms;
12645 #ifdef IMAP_STORAGE
12646    struct ast_channel *chan = NULL;
12647 #endif
12648    struct {
12649       char dir[256];
12650       char file[256];
12651       char txtfile[256];
12652    } tmp[3];
12653    char syscmd[256];
12654    const char origweasels[] = "tt-weasels";
12655    const char testcontext[] = "test";
12656    const char testmailbox[] = "00000000";
12657    const char testspec[] = "00000000@test";
12658    FILE *txt;
12659    int new, old, urgent;
12660    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12661    const int folder2mbox[3] = { 1, 11, 0 };
12662    const int expected_results[3][12] = {
12663       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12664       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12665       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12666       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12667    };
12668 
12669    switch (cmd) {
12670    case TEST_INIT:
12671       info->name = "test_voicemail_msgcount";
12672       info->category = "/apps/app_voicemail/";
12673       info->summary = "Test Voicemail status checks";
12674       info->description =
12675          "Verify that message counts are correct when retrieved through the public API";
12676       return AST_TEST_NOT_RUN;
12677    case TEST_EXECUTE:
12678       break;
12679    }
12680 
12681    /* Make sure the original path was completely empty */
12682    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12683    if ((syserr = ast_safe_system(syscmd))) {
12684       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12685          syserr > 0 ? strerror(syserr) : "unable to fork()");
12686       return AST_TEST_FAIL;
12687    }
12688 
12689 #ifdef IMAP_STORAGE
12690    if (!(chan = ast_dummy_channel_alloc())) {
12691       ast_test_status_update(test, "Unable to create dummy channel\n");
12692       return AST_TEST_FAIL;
12693    }
12694 #endif
12695 
12696    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12697       !(vmu = find_or_create(testcontext, testmailbox))) {
12698       ast_test_status_update(test, "Cannot create vmu structure\n");
12699       ast_unreplace_sigchld();
12700       return AST_TEST_FAIL;
12701    }
12702 
12703    populate_defaults(vmu);
12704    memset(&vms, 0, sizeof(vms));
12705 
12706    /* Create temporary voicemail */
12707    for (i = 0; i < 3; i++) {
12708       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12709       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12710       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12711 
12712       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12713          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12714             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12715          if ((syserr = ast_safe_system(syscmd))) {
12716             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12717                syserr > 0 ? strerror(syserr) : "unable to fork()");
12718             ast_unreplace_sigchld();
12719             return AST_TEST_FAIL;
12720          }
12721       }
12722 
12723       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12724          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12725          fclose(txt);
12726       } else {
12727          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12728          res = AST_TEST_FAIL;
12729          break;
12730       }
12731       open_mailbox(&vms, vmu, folder2mbox[i]);
12732       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12733 
12734       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12735       for (j = 0; j < 3; j++) {
12736          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12737          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12738             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12739                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12740             res = AST_TEST_FAIL;
12741          }
12742       }
12743 
12744       new = old = urgent = 0;
12745       if (ast_app_inboxcount(testspec, &new, &old)) {
12746          ast_test_status_update(test, "inboxcount returned failure\n");
12747          res = AST_TEST_FAIL;
12748       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12749          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12750             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12751          res = AST_TEST_FAIL;
12752       }
12753 
12754       new = old = urgent = 0;
12755       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12756          ast_test_status_update(test, "inboxcount2 returned failure\n");
12757          res = AST_TEST_FAIL;
12758       } else if (old != expected_results[i][6 + 0] ||
12759             urgent != expected_results[i][6 + 1] ||
12760                new != expected_results[i][6 + 2]    ) {
12761          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12762             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12763          res = AST_TEST_FAIL;
12764       }
12765 
12766       new = old = urgent = 0;
12767       for (j = 0; j < 3; j++) {
12768          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12769             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12770                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12771             res = AST_TEST_FAIL;
12772          }
12773       }
12774    }
12775 
12776    for (i = 0; i < 3; i++) {
12777       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12778        * server, in which case, the rm below will not affect the
12779        * voicemails. */
12780       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12781       DISPOSE(tmp[i].dir, 0);
12782    }
12783 
12784    if (vms.deleted) {
12785       ast_free(vms.deleted);
12786    }
12787    if (vms.heard) {
12788       ast_free(vms.heard);
12789    }
12790 
12791 #ifdef IMAP_STORAGE
12792    chan = ast_channel_release(chan);
12793 #endif
12794 
12795    /* And remove test directory */
12796    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12797    if ((syserr = ast_safe_system(syscmd))) {
12798       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12799          syserr > 0 ? strerror(syserr) : "unable to fork()");
12800    }
12801 
12802    return res;
12803 }
12804 
12805 AST_TEST_DEFINE(test_voicemail_notify_endl)
12806 {
12807    int res = AST_TEST_PASS;
12808    char testcontext[] = "test";
12809    char testmailbox[] = "00000000";
12810    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12811    char attach[256], attach2[256];
12812    char buf[256] = ""; /* No line should actually be longer than 80 */
12813    struct ast_channel *chan = NULL;
12814    struct ast_vm_user *vmu, vmus = {
12815       .flags = 0,
12816    };
12817    FILE *file;
12818    struct {
12819       char *name;
12820       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12821       void *location;
12822       union {
12823          int intval;
12824          char *strval;
12825       } u;
12826    } test_items[] = {
12827       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12828       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12829       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12830       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12831       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12832       { "attach2", STRPTR, attach2, .u.strval = "" },
12833       { "attach", STRPTR, attach, .u.strval = "" },
12834    };
12835    int which;
12836 
12837    switch (cmd) {
12838    case TEST_INIT:
12839       info->name = "test_voicemail_notify_endl";
12840       info->category = "/apps/app_voicemail/";
12841       info->summary = "Test Voicemail notification end-of-line";
12842       info->description =
12843          "Verify that notification emails use a consistent end-of-line character";
12844       return AST_TEST_NOT_RUN;
12845    case TEST_EXECUTE:
12846       break;
12847    }
12848 
12849    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12850    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12851 
12852    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12853       !(vmu = find_or_create(testcontext, testmailbox))) {
12854       ast_test_status_update(test, "Cannot create vmu structure\n");
12855       return AST_TEST_NOT_RUN;
12856    }
12857 
12858    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12859       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12860       return AST_TEST_NOT_RUN;
12861    }
12862 
12863    populate_defaults(vmu);
12864    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12865 #ifdef IMAP_STORAGE
12866    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12867 #endif
12868 
12869    file = tmpfile();
12870    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12871       /* Kill previous test, if any */
12872       rewind(file);
12873       if (ftruncate(fileno(file), 0)) {
12874          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12875          res = AST_TEST_FAIL;
12876          break;
12877       }
12878 
12879       /* Make each change, in order, to the test mailbox */
12880       if (test_items[which].type == INT) {
12881          *((int *) test_items[which].location) = test_items[which].u.intval;
12882       } else if (test_items[which].type == FLAGVAL) {
12883          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12884             ast_clear_flag(vmu, test_items[which].u.intval);
12885          } else {
12886             ast_set_flag(vmu, test_items[which].u.intval);
12887          }
12888       } else if (test_items[which].type == STATIC) {
12889          strcpy(test_items[which].location, test_items[which].u.strval);
12890       } else if (test_items[which].type == STRPTR) {
12891          test_items[which].location = test_items[which].u.strval;
12892       }
12893 
12894       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12895       rewind(file);
12896       while (fgets(buf, sizeof(buf), file)) {
12897          if (
12898 #ifdef IMAP_STORAGE
12899          buf[strlen(buf) - 2] != '\r'
12900 #else
12901          buf[strlen(buf) - 2] == '\r'
12902 #endif
12903          || buf[strlen(buf) - 1] != '\n') {
12904             res = AST_TEST_FAIL;
12905          }
12906       }
12907    }
12908    fclose(file);
12909    return res;
12910 }
12911 #endif /* defined(TEST_FRAMEWORK) */
12912 
12913 static int reload(void)
12914 {
12915    return load_config(1);
12916 }
12917 
12918 static int unload_module(void)
12919 {
12920    int res;
12921 
12922    res = ast_unregister_application(app);
12923    res |= ast_unregister_application(app2);
12924    res |= ast_unregister_application(app3);
12925    res |= ast_unregister_application(app4);
12926    res |= ast_unregister_application(sayname_app);
12927    res |= ast_custom_function_unregister(&mailbox_exists_acf);
12928    res |= ast_manager_unregister("VoicemailUsersList");
12929    res |= ast_data_unregister(NULL);
12930 #ifdef TEST_FRAMEWORK
12931    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
12932    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
12933    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
12934    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
12935 #endif
12936    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12937    ast_uninstall_vm_functions();
12938    ao2_ref(inprocess_container, -1);
12939 
12940    if (poll_thread != AST_PTHREADT_NULL)
12941       stop_poll_thread();
12942 
12943    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
12944    ast_unload_realtime("voicemail");
12945    ast_unload_realtime("voicemail_data");
12946 
12947    free_vm_users();
12948    free_vm_zones();
12949    return res;
12950 }
12951 
12952 static int load_module(void)
12953 {
12954    int res;
12955    my_umask = umask(0);
12956    umask(my_umask);
12957 
12958    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
12959       return AST_MODULE_LOAD_DECLINE;
12960    }
12961 
12962    /* compute the location of the voicemail spool directory */
12963    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
12964    
12965    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
12966       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
12967    }
12968 
12969    if ((res = load_config(0)))
12970       return res;
12971 
12972    res = ast_register_application_xml(app, vm_exec);
12973    res |= ast_register_application_xml(app2, vm_execmain);
12974    res |= ast_register_application_xml(app3, vm_box_exists);
12975    res |= ast_register_application_xml(app4, vmauthenticate);
12976    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
12977    res |= ast_custom_function_register(&mailbox_exists_acf);
12978    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
12979 #ifdef TEST_FRAMEWORK
12980    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
12981    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
12982    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
12983    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
12984 #endif
12985 
12986    if (res)
12987       return res;
12988 
12989    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
12990    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
12991 
12992    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
12993    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
12994    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
12995 
12996    return res;
12997 }
12998 
12999 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13000 {
13001    int cmd = 0;
13002    char destination[80] = "";
13003    int retries = 0;
13004 
13005    if (!num) {
13006       ast_verb(3, "Destination number will be entered manually\n");
13007       while (retries < 3 && cmd != 't') {
13008          destination[1] = '\0';
13009          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13010          if (!cmd)
13011             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13012          if (!cmd)
13013             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13014          if (!cmd) {
13015             cmd = ast_waitfordigit(chan, 6000);
13016             if (cmd)
13017                destination[0] = cmd;
13018          }
13019          if (!cmd) {
13020             retries++;
13021          } else {
13022 
13023             if (cmd < 0)
13024                return 0;
13025             if (cmd == '*') {
13026                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13027                return 0;
13028             }
13029             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13030                retries++;
13031             else
13032                cmd = 't';
13033          }
13034          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13035       }
13036       if (retries >= 3) {
13037          return 0;
13038       }
13039       
13040    } else {
13041       if (option_verbose > 2)
13042          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13043       ast_copy_string(destination, num, sizeof(destination));
13044    }
13045 
13046    if (!ast_strlen_zero(destination)) {
13047       if (destination[strlen(destination) -1 ] == '*')
13048          return 0; 
13049       if (option_verbose > 2)
13050          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13051       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13052       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13053       chan->priority = 0;
13054       return 9;
13055    }
13056    return 0;
13057 }
13058 
13059 /*!
13060  * \brief The advanced options within a message.
13061  * \param chan
13062  * \param vmu 
13063  * \param vms
13064  * \param msg
13065  * \param option
13066  * \param record_gain
13067  *
13068  * Provides handling for the play message envelope, call the person back, or reply to message. 
13069  *
13070  * \return zero on success, -1 on error.
13071  */
13072 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)
13073 {
13074    int res = 0;
13075    char filename[PATH_MAX];
13076    struct ast_config *msg_cfg = NULL;
13077    const char *origtime, *context;
13078    char *name, *num;
13079    int retries = 0;
13080    char *cid;
13081    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13082 
13083    vms->starting = 0; 
13084 
13085    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13086 
13087    /* Retrieve info from VM attribute file */
13088    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13089    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13090    msg_cfg = ast_config_load(filename, config_flags);
13091    DISPOSE(vms->curdir, vms->curmsg);
13092    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13093       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13094       return 0;
13095    }
13096 
13097    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13098       ast_config_destroy(msg_cfg);
13099       return 0;
13100    }
13101 
13102    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13103 
13104    context = ast_variable_retrieve(msg_cfg, "message", "context");
13105    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13106       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13107    switch (option) {
13108    case 3: /* Play message envelope */
13109       if (!res)
13110          res = play_message_datetime(chan, vmu, origtime, filename);
13111       if (!res)
13112          res = play_message_callerid(chan, vms, cid, context, 0);
13113 
13114       res = 't';
13115       break;
13116 
13117    case 2:  /* Call back */
13118 
13119       if (ast_strlen_zero(cid))
13120          break;
13121 
13122       ast_callerid_parse(cid, &name, &num);
13123       while ((res > -1) && (res != 't')) {
13124          switch (res) {
13125          case '1':
13126             if (num) {
13127                /* Dial the CID number */
13128                res = dialout(chan, vmu, num, vmu->callback);
13129                if (res) {
13130                   ast_config_destroy(msg_cfg);
13131                   return 9;
13132                }
13133             } else {
13134                res = '2';
13135             }
13136             break;
13137 
13138          case '2':
13139             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13140             if (!ast_strlen_zero(vmu->dialout)) {
13141                res = dialout(chan, vmu, NULL, vmu->dialout);
13142                if (res) {
13143                   ast_config_destroy(msg_cfg);
13144                   return 9;
13145                }
13146             } else {
13147                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13148                res = ast_play_and_wait(chan, "vm-sorry");
13149             }
13150             ast_config_destroy(msg_cfg);
13151             return res;
13152          case '*':
13153             res = 't';
13154             break;
13155          case '3':
13156          case '4':
13157          case '5':
13158          case '6':
13159          case '7':
13160          case '8':
13161          case '9':
13162          case '0':
13163 
13164             res = ast_play_and_wait(chan, "vm-sorry");
13165             retries++;
13166             break;
13167          default:
13168             if (num) {
13169                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13170                res = ast_play_and_wait(chan, "vm-num-i-have");
13171                if (!res)
13172                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13173                if (!res)
13174                   res = ast_play_and_wait(chan, "vm-tocallnum");
13175                /* Only prompt for a caller-specified number if there is a dialout context specified */
13176                if (!ast_strlen_zero(vmu->dialout)) {
13177                   if (!res)
13178                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13179                }
13180             } else {
13181                res = ast_play_and_wait(chan, "vm-nonumber");
13182                if (!ast_strlen_zero(vmu->dialout)) {
13183                   if (!res)
13184                      res = ast_play_and_wait(chan, "vm-toenternumber");
13185                }
13186             }
13187             if (!res) {
13188                res = ast_play_and_wait(chan, "vm-star-cancel");
13189             }
13190             if (!res) {
13191                res = ast_waitfordigit(chan, 6000);
13192             }
13193             if (!res) {
13194                retries++;
13195                if (retries > 3) {
13196                   res = 't';
13197                }
13198             }
13199             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13200             break; 
13201             
13202          }
13203          if (res == 't')
13204             res = 0;
13205          else if (res == '*')
13206             res = -1;
13207       }
13208       break;
13209       
13210    case 1:  /* Reply */
13211       /* Send reply directly to sender */
13212       if (ast_strlen_zero(cid))
13213          break;
13214 
13215       ast_callerid_parse(cid, &name, &num);
13216       if (!num) {
13217          ast_verb(3, "No CID number available, no reply sent\n");
13218          if (!res)
13219             res = ast_play_and_wait(chan, "vm-nonumber");
13220          ast_config_destroy(msg_cfg);
13221          return res;
13222       } else {
13223          struct ast_vm_user vmu2;
13224          if (find_user(&vmu2, vmu->context, num)) {
13225             struct leave_vm_options leave_options;
13226             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13227             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13228 
13229             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13230             
13231             memset(&leave_options, 0, sizeof(leave_options));
13232             leave_options.record_gain = record_gain;
13233             res = leave_voicemail(chan, mailbox, &leave_options);
13234             if (!res)
13235                res = 't';
13236             ast_config_destroy(msg_cfg);
13237             return res;
13238          } else {
13239             /* Sender has no mailbox, can't reply */
13240             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13241             ast_play_and_wait(chan, "vm-nobox");
13242             res = 't';
13243             ast_config_destroy(msg_cfg);
13244             return res;
13245          }
13246       } 
13247       res = 0;
13248 
13249       break;
13250    }
13251 
13252 #ifndef IMAP_STORAGE
13253    ast_config_destroy(msg_cfg);
13254 
13255    if (!res) {
13256       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13257       vms->heard[msg] = 1;
13258       res = wait_file(chan, vms, vms->fn);
13259    }
13260 #endif
13261    return res;
13262 }
13263 
13264 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13265          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
13266          signed char record_gain, struct vm_state *vms, char *flag)
13267 {
13268    /* Record message & let caller review or re-record it, or set options if applicable */
13269    int res = 0;
13270    int cmd = 0;
13271    int max_attempts = 3;
13272    int attempts = 0;
13273    int recorded = 0;
13274    int msg_exists = 0;
13275    signed char zero_gain = 0;
13276    char tempfile[PATH_MAX];
13277    char *acceptdtmf = "#";
13278    char *canceldtmf = "";
13279    int canceleddtmf = 0;
13280 
13281    /* Note that urgent and private are for flagging messages as such in the future */
13282 
13283    /* barf if no pointer passed to store duration in */
13284    if (duration == NULL) {
13285       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13286       return -1;
13287    }
13288 
13289    if (!outsidecaller)
13290       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13291    else
13292       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13293 
13294    cmd = '3';  /* Want to start by recording */
13295 
13296    while ((cmd >= 0) && (cmd != 't')) {
13297       switch (cmd) {
13298       case '1':
13299          if (!msg_exists) {
13300             /* In this case, 1 is to record a message */
13301             cmd = '3';
13302             break;
13303          } else {
13304             /* Otherwise 1 is to save the existing message */
13305             ast_verb(3, "Saving message as is\n");
13306             if (!outsidecaller) 
13307                ast_filerename(tempfile, recordfile, NULL);
13308             ast_stream_and_wait(chan, "vm-msgsaved", "");
13309             if (!outsidecaller) {
13310                /* Saves to IMAP server only if imapgreeting=yes */
13311                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13312                DISPOSE(recordfile, -1);
13313             }
13314             cmd = 't';
13315             return res;
13316          }
13317       case '2':
13318          /* Review */
13319          ast_verb(3, "Reviewing the message\n");
13320          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13321          break;
13322       case '3':
13323          msg_exists = 0;
13324          /* Record */
13325          if (recorded == 1) 
13326             ast_verb(3, "Re-recording the message\n");
13327          else  
13328             ast_verb(3, "Recording the message\n");
13329          
13330          if (recorded && outsidecaller) {
13331             cmd = ast_play_and_wait(chan, INTRO);
13332             cmd = ast_play_and_wait(chan, "beep");
13333          }
13334          recorded = 1;
13335          /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
13336          if (record_gain)
13337             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13338          if (ast_test_flag(vmu, VM_OPERATOR))
13339             canceldtmf = "0";
13340          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13341          if (strchr(canceldtmf, cmd)) {
13342          /* need this flag here to distinguish between pressing '0' during message recording or after */
13343             canceleddtmf = 1;
13344          }
13345          if (record_gain)
13346             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13347          if (cmd == -1) {
13348             /* User has hung up, no options to give */
13349             if (!outsidecaller) {
13350                /* user was recording a greeting and they hung up, so let's delete the recording. */
13351                ast_filedelete(tempfile, NULL);
13352             }     
13353             return cmd;
13354          }
13355          if (cmd == '0') {
13356             break;
13357          } else if (cmd == '*') {
13358             break;
13359 #if 0
13360          } else if (vmu->review && (*duration < 5)) {
13361             /* Message is too short */
13362             ast_verb(3, "Message too short\n");
13363             cmd = ast_play_and_wait(chan, "vm-tooshort");
13364             cmd = ast_filedelete(tempfile, NULL);
13365             break;
13366          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
13367             /* Message is all silence */
13368             ast_verb(3, "Nothing recorded\n");
13369             cmd = ast_filedelete(tempfile, NULL);
13370             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13371             if (!cmd)
13372                cmd = ast_play_and_wait(chan, "vm-speakup");
13373             break;
13374 #endif
13375          } else {
13376             /* If all is well, a message exists */
13377             msg_exists = 1;
13378             cmd = 0;
13379          }
13380          break;
13381       case '4':
13382          if (outsidecaller) {  /* only mark vm messages */
13383             /* Mark Urgent */
13384             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13385                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13386                res = ast_play_and_wait(chan, "vm-marked-urgent");
13387                strcpy(flag, "Urgent");
13388             } else if (flag) {
13389                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13390                res = ast_play_and_wait(chan, "vm-urgent-removed");
13391                strcpy(flag, "");
13392             } else {
13393                ast_play_and_wait(chan, "vm-sorry");
13394             }
13395             cmd = 0;
13396          } else {
13397             cmd = ast_play_and_wait(chan, "vm-sorry");
13398          }
13399          break;
13400       case '5':
13401       case '6':
13402       case '7':
13403       case '8':
13404       case '9':
13405       case '*':
13406       case '#':
13407          cmd = ast_play_and_wait(chan, "vm-sorry");
13408          break;
13409 #if 0 
13410 /*  XXX Commented out for the moment because of the dangers of deleting
13411     a message while recording (can put the message numbers out of sync) */
13412       case '*':
13413          /* Cancel recording, delete message, offer to take another message*/
13414          cmd = ast_play_and_wait(chan, "vm-deleted");
13415          cmd = ast_filedelete(tempfile, NULL);
13416          if (outsidecaller) {
13417             res = vm_exec(chan, NULL);
13418             return res;
13419          }
13420          else
13421             return 1;
13422 #endif
13423       case '0':
13424          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13425             cmd = ast_play_and_wait(chan, "vm-sorry");
13426             break;
13427          }
13428          if (msg_exists || recorded) {
13429             cmd = ast_play_and_wait(chan, "vm-saveoper");
13430             if (!cmd)
13431                cmd = ast_waitfordigit(chan, 3000);
13432             if (cmd == '1') {
13433                ast_filerename(tempfile, recordfile, NULL);
13434                ast_play_and_wait(chan, "vm-msgsaved");
13435                cmd = '0';
13436             } else if (cmd == '4') {
13437                if (flag) {
13438                   ast_play_and_wait(chan, "vm-marked-urgent");
13439                   strcpy(flag, "Urgent");
13440                }
13441                ast_play_and_wait(chan, "vm-msgsaved");
13442                cmd = '0';
13443             } else {
13444                ast_play_and_wait(chan, "vm-deleted");
13445                DELETE(tempfile, -1, tempfile, vmu);
13446                cmd = '0';
13447             }
13448          }
13449          return cmd;
13450       default:
13451          /* If the caller is an ouside caller, and the review option is enabled,
13452             allow them to review the message, but let the owner of the box review
13453             their OGM's */
13454          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13455             return cmd;
13456          if (msg_exists) {
13457             cmd = ast_play_and_wait(chan, "vm-review");
13458             if (!cmd && outsidecaller) {
13459                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13460                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13461                } else if (flag) {
13462                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13463                }
13464             }
13465          } else {
13466             cmd = ast_play_and_wait(chan, "vm-torerecord");
13467             if (!cmd)
13468                cmd = ast_waitfordigit(chan, 600);
13469          }
13470          
13471          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13472             cmd = ast_play_and_wait(chan, "vm-reachoper");
13473             if (!cmd)
13474                cmd = ast_waitfordigit(chan, 600);
13475          }
13476 #if 0
13477          if (!cmd)
13478             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13479 #endif
13480          if (!cmd)
13481             cmd = ast_waitfordigit(chan, 6000);
13482          if (!cmd) {
13483             attempts++;
13484          }
13485          if (attempts > max_attempts) {
13486             cmd = 't';
13487          }
13488       }
13489    }
13490    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13491       /* Hang up or timeout, so delete the recording. */
13492       ast_filedelete(tempfile, NULL);
13493    }
13494 
13495    if (cmd != 't' && outsidecaller)
13496       ast_play_and_wait(chan, "vm-goodbye");
13497 
13498    return cmd;
13499 }
13500 
13501 /* This is a workaround so that menuselect displays a proper description
13502  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13503  */
13504 
13505 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13506       .load = load_module,
13507       .unload = unload_module,
13508       .reload = reload,
13509       .nonoptreq = "res_adsi,res_smdi",
13510       );