Tue Aug 20 16:34:23 2013

Asterisk developer's documentation


app_voicemail_imapstorage.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       <support_level>core</support_level>
00053    </member>
00054    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00055       <depend>generic_odbc</depend>
00056       <depend>ltdl</depend>
00057       <conflict>IMAP_STORAGE</conflict>
00058       <conflict>FILE_STORAGE</conflict>
00059       <defaultenabled>no</defaultenabled>
00060       <support_level>core</support_level>
00061    </member>
00062    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00063       <depend>imap_tk</depend>
00064       <conflict>ODBC_STORAGE</conflict>
00065       <conflict>FILE_STORAGE</conflict>
00066       <use>openssl</use>
00067       <defaultenabled>no</defaultenabled>
00068       <support_level>core</support_level>
00069    </member>
00070 </category>
00071 ***/
00072 
00073 #include "asterisk.h"
00074 
00075 #ifdef IMAP_STORAGE
00076 #include <ctype.h>
00077 #include <signal.h>
00078 #include <pwd.h>
00079 #ifdef USE_SYSTEM_IMAP
00080 #include <imap/c-client.h>
00081 #include <imap/imap4r1.h>
00082 #include <imap/linkage.h>
00083 #elif defined (USE_SYSTEM_CCLIENT)
00084 #include <c-client/c-client.h>
00085 #include <c-client/imap4r1.h>
00086 #include <c-client/linkage.h>
00087 #else
00088 #include "c-client.h"
00089 #include "imap4r1.h"
00090 #include "linkage.h"
00091 #endif
00092 #endif
00093 
00094 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376262 $")
00095 
00096 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00097 #include <sys/time.h>
00098 #include <sys/stat.h>
00099 #include <sys/mman.h>
00100 #include <time.h>
00101 #include <dirent.h>
00102 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00103 #include <sys/wait.h>
00104 #endif
00105 
00106 #include "asterisk/logger.h"
00107 #include "asterisk/lock.h"
00108 #include "asterisk/file.h"
00109 #include "asterisk/channel.h"
00110 #include "asterisk/pbx.h"
00111 #include "asterisk/config.h"
00112 #include "asterisk/say.h"
00113 #include "asterisk/module.h"
00114 #include "asterisk/adsi.h"
00115 #include "asterisk/app.h"
00116 #include "asterisk/manager.h"
00117 #include "asterisk/dsp.h"
00118 #include "asterisk/localtime.h"
00119 #include "asterisk/cli.h"
00120 #include "asterisk/utils.h"
00121 #include "asterisk/stringfields.h"
00122 #include "asterisk/smdi.h"
00123 #include "asterisk/astobj2.h"
00124 #include "asterisk/event.h"
00125 #include "asterisk/taskprocessor.h"
00126 #include "asterisk/test.h"
00127 
00128 #ifdef ODBC_STORAGE
00129 #include "asterisk/res_odbc.h"
00130 #endif
00131 
00132 #ifdef IMAP_STORAGE
00133 #include "asterisk/threadstorage.h"
00134 #endif
00135 
00136 /*** DOCUMENTATION
00137    <application name="VoiceMail" language="en_US">
00138       <synopsis>
00139          Leave a Voicemail message.
00140       </synopsis>
00141       <syntax>
00142          <parameter name="mailboxs" argsep="&amp;" required="true">
00143             <argument name="mailbox1" argsep="@" required="true">
00144                <argument name="mailbox" required="true" />
00145                <argument name="context" />
00146             </argument>
00147             <argument name="mailbox2" argsep="@" multiple="true">
00148                <argument name="mailbox" required="true" />
00149                <argument name="context" />
00150             </argument>
00151          </parameter>
00152          <parameter name="options">
00153             <optionlist>
00154                <option name="b">
00155                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00156                </option>
00157                <option name="d">
00158                   <argument name="c" />
00159                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00160                   if played during the greeting. Context defaults to the current context.</para>
00161                </option>
00162                <option name="g">
00163                   <argument name="#" required="true" />
00164                   <para>Use the specified amount of gain when recording the voicemail
00165                   message. The units are whole-number decibels (dB). Only works on supported
00166                   technologies, which is DAHDI only.</para>
00167                </option>
00168                <option name="s">
00169                   <para>Skip the playback of instructions for leaving a message to the
00170                   calling party.</para>
00171                </option>
00172                <option name="u">
00173                   <para>Play the <literal>unavailable</literal> greeting.</para>
00174                </option>
00175                <option name="U">
00176                   <para>Mark message as <literal>URGENT</literal>.</para>
00177                </option>
00178                <option name="P">
00179                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00180                </option>
00181             </optionlist>
00182          </parameter>
00183       </syntax>
00184       <description>
00185          <para>This application allows the calling party to leave a message for the specified
00186          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00187          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00188          exist.</para>
00189          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00190          <enumlist>
00191             <enum name="0">
00192                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00193             </enum>
00194             <enum name="*">
00195                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00196             </enum>
00197          </enumlist>
00198          <para>This application will set the following channel variable upon completion:</para>
00199          <variablelist>
00200             <variable name="VMSTATUS">
00201                <para>This indicates the status of the execution of the VoiceMail application.</para>
00202                <value name="SUCCESS" />
00203                <value name="USEREXIT" />
00204                <value name="FAILED" />
00205             </variable>
00206          </variablelist>
00207       </description>
00208       <see-also>
00209          <ref type="application">VoiceMailMain</ref>
00210       </see-also>
00211    </application>
00212    <application name="VoiceMailMain" language="en_US">
00213       <synopsis>
00214          Check Voicemail messages.
00215       </synopsis>
00216       <syntax>
00217          <parameter name="mailbox" required="true" argsep="@">
00218             <argument name="mailbox" />
00219             <argument name="context" />
00220          </parameter>
00221          <parameter name="options">
00222             <optionlist>
00223                <option name="p">
00224                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00225                   the mailbox that is entered by the caller.</para>
00226                </option>
00227                <option name="g">
00228                   <argument name="#" required="true" />
00229                   <para>Use the specified amount of gain when recording a voicemail message.
00230                   The units are whole-number decibels (dB).</para>
00231                </option>
00232                <option name="s">
00233                   <para>Skip checking the passcode for the mailbox.</para>
00234                </option>
00235                <option name="a">
00236                   <argument name="folder" required="true" />
00237                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00238                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00239                   <enumlist>
00240                      <enum name="0"><para>INBOX</para></enum>
00241                      <enum name="1"><para>Old</para></enum>
00242                      <enum name="2"><para>Work</para></enum>
00243                      <enum name="3"><para>Family</para></enum>
00244                      <enum name="4"><para>Friends</para></enum>
00245                      <enum name="5"><para>Cust1</para></enum>
00246                      <enum name="6"><para>Cust2</para></enum>
00247                      <enum name="7"><para>Cust3</para></enum>
00248                      <enum name="8"><para>Cust4</para></enum>
00249                      <enum name="9"><para>Cust5</para></enum>
00250                   </enumlist>
00251                </option>
00252             </optionlist>
00253          </parameter>
00254       </syntax>
00255       <description>
00256          <para>This application allows the calling party to check voicemail messages. A specific
00257          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00258          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00259          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00260          <literal>default</literal> context will be used.</para>
00261          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00262          or Password, and the extension exists:</para>
00263          <enumlist>
00264             <enum name="*">
00265                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00266             </enum>
00267          </enumlist>
00268       </description>
00269       <see-also>
00270          <ref type="application">VoiceMail</ref>
00271       </see-also>
00272    </application>
00273    <application name="MailboxExists" language="en_US">
00274       <synopsis>
00275          Check to see if Voicemail mailbox exists.
00276       </synopsis>
00277       <syntax>
00278          <parameter name="mailbox" required="true" argsep="@">
00279             <argument name="mailbox" required="true" />
00280             <argument name="context" />
00281          </parameter>
00282          <parameter name="options">
00283             <para>None options.</para>
00284          </parameter>
00285       </syntax>
00286       <description>
00287          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00288          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00289          will be used.</para>
00290          <para>This application will set the following channel variable upon completion:</para>
00291          <variablelist>
00292             <variable name="VMBOXEXISTSSTATUS">
00293                <para>This will contain the status of the execution of the MailboxExists application.
00294                Possible values include:</para>
00295                <value name="SUCCESS" />
00296                <value name="FAILED" />
00297             </variable>
00298          </variablelist>
00299       </description>
00300    </application>
00301    <application name="VMAuthenticate" language="en_US">
00302       <synopsis>
00303          Authenticate with Voicemail passwords.
00304       </synopsis>
00305       <syntax>
00306          <parameter name="mailbox" required="true" argsep="@">
00307             <argument name="mailbox" />
00308             <argument name="context" />
00309          </parameter>
00310          <parameter name="options">
00311             <optionlist>
00312                <option name="s">
00313                   <para>Skip playing the initial prompts.</para>
00314                </option>
00315             </optionlist>
00316          </parameter>
00317       </syntax>
00318       <description>
00319          <para>This application behaves the same way as the Authenticate application, but the passwords
00320          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00321          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00322          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00323          mailbox.</para>
00324          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00325          or Password, and the extension exists:</para>
00326          <enumlist>
00327             <enum name="*">
00328                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00329             </enum>
00330          </enumlist>
00331       </description>
00332    </application>
00333    <application name="VMSayName" language="en_US">
00334       <synopsis>
00335          Play the name of a voicemail user
00336       </synopsis>
00337       <syntax>
00338          <parameter name="mailbox" required="true" argsep="@">
00339             <argument name="mailbox" />
00340             <argument name="context" />
00341          </parameter>
00342       </syntax>
00343       <description>
00344          <para>This application will say the recorded name of the voicemail user specified as the
00345          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00346       </description>
00347    </application>
00348    <function name="MAILBOX_EXISTS" language="en_US">
00349       <synopsis>
00350          Tell if a mailbox is configured.
00351       </synopsis>
00352       <syntax argsep="@">
00353          <parameter name="mailbox" required="true" />
00354          <parameter name="context" />
00355       </syntax>
00356       <description>
00357          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00358          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00359          context.</para>
00360       </description>
00361    </function>
00362    <manager name="VoicemailUsersList" language="en_US">
00363       <synopsis>
00364          List All Voicemail User Information.
00365       </synopsis>
00366       <syntax>
00367          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00368       </syntax>
00369       <description>
00370       </description>
00371    </manager>
00372  ***/
00373 
00374 #ifdef IMAP_STORAGE
00375 static char imapserver[48];
00376 static char imapport[8];
00377 static char imapflags[128];
00378 static char imapfolder[64];
00379 static char imapparentfolder[64] = "\0";
00380 static char greetingfolder[64];
00381 static char authuser[32];
00382 static char authpassword[42];
00383 static int imapversion = 1;
00384 
00385 static int expungeonhangup = 1;
00386 static int imapgreetings = 0;
00387 static char delimiter = '\0';
00388 
00389 struct vm_state;
00390 struct ast_vm_user;
00391 
00392 AST_THREADSTORAGE(ts_vmstate);
00393 
00394 /* Forward declarations for IMAP */
00395 static int init_mailstream(struct vm_state *vms, int box);
00396 static void write_file(char *filename, char *buffer, unsigned long len);
00397 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00398 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00399 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00400 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00401 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00402 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00403 static void vmstate_insert(struct vm_state *vms);
00404 static void vmstate_delete(struct vm_state *vms);
00405 static void set_update(MAILSTREAM * stream);
00406 static void init_vm_state(struct vm_state *vms);
00407 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00408 static void get_mailbox_delimiter(MAILSTREAM *stream);
00409 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00410 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00411 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);
00412 static void update_messages_by_imapuser(const char *user, unsigned long number);
00413 static int vm_delete(char *file);
00414 
00415 static int imap_remove_file (char *dir, int msgnum);
00416 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00417 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00418 static void check_quota(struct vm_state *vms, char *mailbox);
00419 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00420 struct vmstate {
00421    struct vm_state *vms;
00422    AST_LIST_ENTRY(vmstate) list;
00423 };
00424 
00425 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00426 
00427 #endif
00428 
00429 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00430 
00431 #define COMMAND_TIMEOUT 5000
00432 /* Don't modify these here; set your umask at runtime instead */
00433 #define  VOICEMAIL_DIR_MODE   0777
00434 #define  VOICEMAIL_FILE_MODE  0666
00435 #define  CHUNKSIZE   65536
00436 
00437 #define VOICEMAIL_CONFIG "voicemail.conf"
00438 #define ASTERISK_USERNAME "asterisk"
00439 
00440 /* Define fast-forward, pause, restart, and reverse keys
00441  * while listening to a voicemail message - these are
00442  * strings, not characters */
00443 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00444 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00445 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00446 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00447 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00448 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00449 
00450 /* Default mail command to mail voicemail. Change it with the
00451  * mailcmd= command in voicemail.conf */
00452 #define SENDMAIL "/usr/sbin/sendmail -t"
00453 
00454 #define INTRO "vm-intro"
00455 
00456 #define MAXMSG 100
00457 #define MAXMSGLIMIT 9999
00458 
00459 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00460 
00461 #define BASELINELEN 72
00462 #define BASEMAXINLINE 256
00463 #ifdef IMAP_STORAGE
00464 #define ENDL "\r\n"
00465 #else
00466 #define ENDL "\n"
00467 #endif
00468 
00469 #define MAX_DATETIME_FORMAT   512
00470 #define MAX_NUM_CID_CONTEXTS 10
00471 
00472 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00473 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00474 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00475 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00476 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00477 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00478 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00479 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00480 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00481 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00482 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00483 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00484 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00485 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00486 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00487 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00488 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00489 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00490 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00491 #define ERROR_LOCK_PATH  -100
00492 #define OPERATOR_EXIT     300
00493 
00494 
00495 enum vm_box {
00496    NEW_FOLDER,
00497    OLD_FOLDER,
00498    WORK_FOLDER,
00499    FAMILY_FOLDER,
00500    FRIENDS_FOLDER,
00501    GREETINGS_FOLDER
00502 };
00503 
00504 enum vm_option_flags {
00505    OPT_SILENT =           (1 << 0),
00506    OPT_BUSY_GREETING =    (1 << 1),
00507    OPT_UNAVAIL_GREETING = (1 << 2),
00508    OPT_RECORDGAIN =       (1 << 3),
00509    OPT_PREPEND_MAILBOX =  (1 << 4),
00510    OPT_AUTOPLAY =         (1 << 6),
00511    OPT_DTMFEXIT =         (1 << 7),
00512    OPT_MESSAGE_Urgent =   (1 << 8),
00513    OPT_MESSAGE_PRIORITY = (1 << 9)
00514 };
00515 
00516 enum vm_option_args {
00517    OPT_ARG_RECORDGAIN = 0,
00518    OPT_ARG_PLAYFOLDER = 1,
00519    OPT_ARG_DTMFEXIT   = 2,
00520    /* This *must* be the last value in this enum! */
00521    OPT_ARG_ARRAY_SIZE = 3,
00522 };
00523 
00524 enum vm_passwordlocation {
00525    OPT_PWLOC_VOICEMAILCONF = 0,
00526    OPT_PWLOC_SPOOLDIR      = 1,
00527    OPT_PWLOC_USERSCONF     = 2,
00528 };
00529 
00530 AST_APP_OPTIONS(vm_app_options, {
00531    AST_APP_OPTION('s', OPT_SILENT),
00532    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00533    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00534    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00535    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00536    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00537    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00538    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00539    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00540 });
00541 
00542 static int load_config(int reload);
00543 #ifdef TEST_FRAMEWORK
00544 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00545 #endif
00546 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00547 
00548 /*! \page vmlang Voicemail Language Syntaxes Supported
00549 
00550    \par Syntaxes supported, not really language codes.
00551    \arg \b en    - English
00552    \arg \b de    - German
00553    \arg \b es    - Spanish
00554    \arg \b fr    - French
00555    \arg \b it    - Italian
00556    \arg \b nl    - Dutch
00557    \arg \b pt    - Portuguese
00558    \arg \b pt_BR - Portuguese (Brazil)
00559    \arg \b gr    - Greek
00560    \arg \b no    - Norwegian
00561    \arg \b se    - Swedish
00562    \arg \b tw    - Chinese (Taiwan)
00563    \arg \b ua - Ukrainian
00564 
00565 German requires the following additional soundfile:
00566 \arg \b 1F  einE (feminine)
00567 
00568 Spanish requires the following additional soundfile:
00569 \arg \b 1M      un (masculine)
00570 
00571 Dutch, Portuguese & Spanish require the following additional soundfiles:
00572 \arg \b vm-INBOXs singular of 'new'
00573 \arg \b vm-Olds      singular of 'old/heard/read'
00574 
00575 NB these are plural:
00576 \arg \b vm-INBOX  nieuwe (nl)
00577 \arg \b vm-Old    oude (nl)
00578 
00579 Polish uses:
00580 \arg \b vm-new-a  'new', feminine singular accusative
00581 \arg \b vm-new-e  'new', feminine plural accusative
00582 \arg \b vm-new-ych   'new', feminine plural genitive
00583 \arg \b vm-old-a  'old', feminine singular accusative
00584 \arg \b vm-old-e  'old', feminine plural accusative
00585 \arg \b vm-old-ych   'old', feminine plural genitive
00586 \arg \b digits/1-a   'one', not always same as 'digits/1'
00587 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00588 
00589 Swedish uses:
00590 \arg \b vm-nytt      singular of 'new'
00591 \arg \b vm-nya    plural of 'new'
00592 \arg \b vm-gammalt   singular of 'old'
00593 \arg \b vm-gamla  plural of 'old'
00594 \arg \b digits/ett   'one', not always same as 'digits/1'
00595 
00596 Norwegian uses:
00597 \arg \b vm-ny     singular of 'new'
00598 \arg \b vm-nye    plural of 'new'
00599 \arg \b vm-gammel singular of 'old'
00600 \arg \b vm-gamle  plural of 'old'
00601 
00602 Dutch also uses:
00603 \arg \b nl-om     'at'?
00604 
00605 Spanish also uses:
00606 \arg \b vm-youhaveno
00607 
00608 Italian requires the following additional soundfile:
00609 
00610 For vm_intro_it:
00611 \arg \b vm-nuovo  new
00612 \arg \b vm-nuovi  new plural
00613 \arg \b vm-vecchio   old
00614 \arg \b vm-vecchi old plural
00615 
00616 Chinese (Taiwan) requires the following additional soundfile:
00617 \arg \b vm-tong      A class-word for call (tong1)
00618 \arg \b vm-ri     A class-word for day (ri4)
00619 \arg \b vm-you    You (ni3)
00620 \arg \b vm-haveno   Have no (mei2 you3)
00621 \arg \b vm-have     Have (you3)
00622 \arg \b vm-listen   To listen (yao4 ting1)
00623 
00624 
00625 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00626 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00627 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00628 
00629 */
00630 
00631 struct baseio {
00632    int iocp;
00633    int iolen;
00634    int linelength;
00635    int ateof;
00636    unsigned char iobuf[BASEMAXINLINE];
00637 };
00638 
00639 /*! Structure for linked list of users 
00640  * Use ast_vm_user_destroy() to free one of these structures. */
00641 struct ast_vm_user {
00642    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00643    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00644    char password[80];               /*!< Secret pin code, numbers only */
00645    char fullname[80];               /*!< Full name, for directory app */
00646    char email[80];                  /*!< E-mail address */
00647    char *emailsubject;              /*!< E-mail subject */
00648    char *emailbody;                 /*!< E-mail body */
00649    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00650    char serveremail[80];            /*!< From: Mail address */
00651    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00652    char zonetag[80];                /*!< Time zone */
00653    char locale[20];                 /*!< The locale (for presentation of date/time) */
00654    char callback[80];
00655    char dialout[80];
00656    char uniqueid[80];               /*!< Unique integer identifier */
00657    char exit[80];
00658    char attachfmt[20];              /*!< Attachment format */
00659    unsigned int flags;              /*!< VM_ flags */ 
00660    int saydurationm;
00661    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00662    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00663    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00664    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00665    int passwordlocation;            /*!< Storage location of the password */
00666 #ifdef IMAP_STORAGE
00667    char imapuser[80];               /*!< IMAP server login */
00668    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00669    char imapfolder[64];             /*!< IMAP voicemail folder */
00670    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00671    int imapversion;                 /*!< If configuration changes, use the new values */
00672 #endif
00673    double volgain;                  /*!< Volume gain for voicemails sent via email */
00674    AST_LIST_ENTRY(ast_vm_user) list;
00675 };
00676 
00677 /*! Voicemail time zones */
00678 struct vm_zone {
00679    AST_LIST_ENTRY(vm_zone) list;
00680    char name[80];
00681    char timezone[80];
00682    char msg_format[512];
00683 };
00684 
00685 #define VMSTATE_MAX_MSG_ARRAY 256
00686 
00687 /*! Voicemail mailbox state */
00688 struct vm_state {
00689    char curbox[80];
00690    char username[80];
00691    char context[80];
00692    char curdir[PATH_MAX];
00693    char vmbox[PATH_MAX];
00694    char fn[PATH_MAX];
00695    char intro[PATH_MAX];
00696    int *deleted;
00697    int *heard;
00698    int dh_arraysize; /* used for deleted / heard allocation */
00699    int curmsg;
00700    int lastmsg;
00701    int newmessages;
00702    int oldmessages;
00703    int urgentmessages;
00704    int starting;
00705    int repeats;
00706 #ifdef IMAP_STORAGE
00707    ast_mutex_t lock;
00708    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00709    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00710    MAILSTREAM *mailstream;
00711    int vmArrayIndex;
00712    char imapuser[80];                   /*!< IMAP server login */
00713    char imapfolder[64];                 /*!< IMAP voicemail folder */
00714    int imapversion;
00715    int interactive;
00716    char introfn[PATH_MAX];              /*!< Name of prepended file */
00717    unsigned int quota_limit;
00718    unsigned int quota_usage;
00719    struct vm_state *persist_vms;
00720 #endif
00721 };
00722 
00723 #ifdef ODBC_STORAGE
00724 static char odbc_database[80];
00725 static char odbc_table[80];
00726 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00727 #define DISPOSE(a,b) remove_file(a,b)
00728 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00729 #define EXISTS(a,b,c,d) (message_exists(a,b))
00730 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00731 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00732 #define DELETE(a,b,c,d) (delete_file(a,b))
00733 #else
00734 #ifdef IMAP_STORAGE
00735 #define DISPOSE(a,b) (imap_remove_file(a,b))
00736 #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))
00737 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00738 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00739 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00740 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00741 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00742 #else
00743 #define RETRIEVE(a,b,c,d)
00744 #define DISPOSE(a,b)
00745 #define STORE(a,b,c,d,e,f,g,h,i,j)
00746 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00747 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00748 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00749 #define DELETE(a,b,c,d) (vm_delete(c))
00750 #endif
00751 #endif
00752 
00753 static char VM_SPOOL_DIR[PATH_MAX];
00754 
00755 static char ext_pass_cmd[128];
00756 static char ext_pass_check_cmd[128];
00757 
00758 static int my_umask;
00759 
00760 #define PWDCHANGE_INTERNAL (1 << 1)
00761 #define PWDCHANGE_EXTERNAL (1 << 2)
00762 static int pwdchange = PWDCHANGE_INTERNAL;
00763 
00764 #ifdef ODBC_STORAGE
00765 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00766 #else
00767 # ifdef IMAP_STORAGE
00768 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00769 # else
00770 # define tdesc "Comedian Mail (Voicemail System)"
00771 # endif
00772 #endif
00773 
00774 static char userscontext[AST_MAX_EXTENSION] = "default";
00775 
00776 static char *addesc = "Comedian Mail";
00777 
00778 /* Leave a message */
00779 static char *app = "VoiceMail";
00780 
00781 /* Check mail, control, etc */
00782 static char *app2 = "VoiceMailMain";
00783 
00784 static char *app3 = "MailboxExists";
00785 static char *app4 = "VMAuthenticate";
00786 
00787 static char *sayname_app = "VMSayName";
00788 
00789 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00790 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00791 static char zonetag[80];
00792 static char locale[20];
00793 static int maxsilence;
00794 static int maxmsg;
00795 static int maxdeletedmsg;
00796 static int silencethreshold = 128;
00797 static char serveremail[80];
00798 static char mailcmd[160];  /* Configurable mail cmd */
00799 static char externnotify[160]; 
00800 static struct ast_smdi_interface *smdi_iface = NULL;
00801 static char vmfmts[80];
00802 static double volgain;
00803 static int vmminsecs;
00804 static int vmmaxsecs;
00805 static int maxgreet;
00806 static int skipms;
00807 static int maxlogins;
00808 static int minpassword;
00809 static int passwordlocation;
00810 
00811 /*! Poll mailboxes for changes since there is something external to
00812  *  app_voicemail that may change them. */
00813 static unsigned int poll_mailboxes;
00814 
00815 /*! Polling frequency */
00816 static unsigned int poll_freq;
00817 /*! By default, poll every 30 seconds */
00818 #define DEFAULT_POLL_FREQ 30
00819 
00820 AST_MUTEX_DEFINE_STATIC(poll_lock);
00821 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00822 static pthread_t poll_thread = AST_PTHREADT_NULL;
00823 static unsigned char poll_thread_run;
00824 
00825 /*! Subscription to ... MWI event subscriptions */
00826 static struct ast_event_sub *mwi_sub_sub;
00827 /*! Subscription to ... MWI event un-subscriptions */
00828 static struct ast_event_sub *mwi_unsub_sub;
00829 
00830 /*!
00831  * \brief An MWI subscription
00832  *
00833  * This is so we can keep track of which mailboxes are subscribed to.
00834  * This way, we know which mailboxes to poll when the pollmailboxes
00835  * option is being used.
00836  */
00837 struct mwi_sub {
00838    AST_RWLIST_ENTRY(mwi_sub) entry;
00839    int old_urgent;
00840    int old_new;
00841    int old_old;
00842    uint32_t uniqueid;
00843    char mailbox[1];
00844 };
00845 
00846 struct mwi_sub_task {
00847    const char *mailbox;
00848    const char *context;
00849    uint32_t uniqueid;
00850 };
00851 
00852 static struct ast_taskprocessor *mwi_subscription_tps;
00853 
00854 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00855 
00856 /* custom audio control prompts for voicemail playback */
00857 static char listen_control_forward_key[12];
00858 static char listen_control_reverse_key[12];
00859 static char listen_control_pause_key[12];
00860 static char listen_control_restart_key[12];
00861 static char listen_control_stop_key[12];
00862 
00863 /* custom password sounds */
00864 static char vm_password[80] = "vm-password";
00865 static char vm_newpassword[80] = "vm-newpassword";
00866 static char vm_passchanged[80] = "vm-passchanged";
00867 static char vm_reenterpassword[80] = "vm-reenterpassword";
00868 static char vm_mismatch[80] = "vm-mismatch";
00869 static char vm_invalid_password[80] = "vm-invalid-password";
00870 static char vm_pls_try_again[80] = "vm-pls-try-again";
00871 
00872 /*
00873  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00874  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00875  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00876  * app.c's __ast_play_and_record function
00877  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00878  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00879  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00880  * more effort than either of the other two.
00881  */
00882 static char vm_prepend_timeout[80] = "vm-then-pound";
00883 
00884 static struct ast_flags globalflags = {0};
00885 
00886 static int saydurationminfo;
00887 
00888 static char dialcontext[AST_MAX_CONTEXT] = "";
00889 static char callcontext[AST_MAX_CONTEXT] = "";
00890 static char exitcontext[AST_MAX_CONTEXT] = "";
00891 
00892 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00893 
00894 
00895 static char *emailbody = NULL;
00896 static char *emailsubject = NULL;
00897 static char *pagerbody = NULL;
00898 static char *pagersubject = NULL;
00899 static char fromstring[100];
00900 static char pagerfromstring[100];
00901 static char charset[32] = "ISO-8859-1";
00902 
00903 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00904 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00905 static int adsiver = 1;
00906 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00907 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00908 
00909 /* Forward declarations - generic */
00910 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00911 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);
00912 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00913 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00914          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00915          signed char record_gain, struct vm_state *vms, char *flag);
00916 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00917 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00918 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);
00919 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);
00920 static void apply_options(struct ast_vm_user *vmu, const char *options);
00921 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);
00922 static int is_valid_dtmf(const char *key);
00923 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00924 static int write_password_to_file(const char *secretfn, const char *password);
00925 static const char *substitute_escapes(const char *value);
00926 static void free_user(struct ast_vm_user *vmu);
00927 
00928 struct ao2_container *inprocess_container;
00929 
00930 struct inprocess {
00931    int count;
00932    char *context;
00933    char mailbox[0];
00934 };
00935 
00936 static int inprocess_hash_fn(const void *obj, const int flags)
00937 {
00938    const struct inprocess *i = obj;
00939    return atoi(i->mailbox);
00940 }
00941 
00942 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00943 {
00944    struct inprocess *i = obj, *j = arg;
00945    if (strcmp(i->mailbox, j->mailbox)) {
00946       return 0;
00947    }
00948    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00949 }
00950 
00951 static int inprocess_count(const char *context, const char *mailbox, int delta)
00952 {
00953    struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00954    arg->context = arg->mailbox + strlen(mailbox) + 1;
00955    strcpy(arg->mailbox, mailbox); /* SAFE */
00956    strcpy(arg->context, context); /* SAFE */
00957    ao2_lock(inprocess_container);
00958    if ((i = ao2_find(inprocess_container, arg, 0))) {
00959       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00960       ao2_unlock(inprocess_container);
00961       ao2_ref(i, -1);
00962       return ret;
00963    }
00964    if (delta < 0) {
00965       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00966    }
00967    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00968       ao2_unlock(inprocess_container);
00969       return 0;
00970    }
00971    i->context = i->mailbox + strlen(mailbox) + 1;
00972    strcpy(i->mailbox, mailbox); /* SAFE */
00973    strcpy(i->context, context); /* SAFE */
00974    i->count = delta;
00975    ao2_link(inprocess_container, i);
00976    ao2_unlock(inprocess_container);
00977    ao2_ref(i, -1);
00978    return 0;
00979 }
00980 
00981 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00982 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00983 #endif
00984 
00985 /*!
00986  * \brief Strips control and non 7-bit clean characters from input string.
00987  *
00988  * \note To map control and none 7-bit characters to a 7-bit clean characters
00989  *  please use ast_str_encode_mine().
00990  */
00991 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00992 {
00993    char *bufptr = buf;
00994    for (; *input; input++) {
00995       if (*input < 32) {
00996          continue;
00997       }
00998       *bufptr++ = *input;
00999       if (bufptr == buf + buflen - 1) {
01000          break;
01001       }
01002    }
01003    *bufptr = '\0';
01004    return buf;
01005 }
01006 
01007 
01008 /*!
01009  * \brief Sets default voicemail system options to a voicemail user.
01010  *
01011  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01012  * - all the globalflags
01013  * - the saydurationminfo
01014  * - the callcontext
01015  * - the dialcontext
01016  * - the exitcontext
01017  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01018  * - volume gain
01019  * - emailsubject, emailbody set to NULL
01020  */
01021 static void populate_defaults(struct ast_vm_user *vmu)
01022 {
01023    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01024    vmu->passwordlocation = passwordlocation;
01025    if (saydurationminfo) {
01026       vmu->saydurationm = saydurationminfo;
01027    }
01028    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01029    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01030    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01031    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01032    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01033    if (vmminsecs) {
01034       vmu->minsecs = vmminsecs;
01035    }
01036    if (vmmaxsecs) {
01037       vmu->maxsecs = vmmaxsecs;
01038    }
01039    if (maxmsg) {
01040       vmu->maxmsg = maxmsg;
01041    }
01042    if (maxdeletedmsg) {
01043       vmu->maxdeletedmsg = maxdeletedmsg;
01044    }
01045    vmu->volgain = volgain;
01046    ast_free(vmu->emailsubject);
01047    vmu->emailsubject = NULL;
01048    ast_free(vmu->emailbody);
01049    vmu->emailbody = NULL;
01050 #ifdef IMAP_STORAGE
01051    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01052 #endif
01053 }
01054 
01055 /*!
01056  * \brief Sets a a specific property value.
01057  * \param vmu The voicemail user object to work with.
01058  * \param var The name of the property to be set.
01059  * \param value The value to be set to the property.
01060  * 
01061  * The property name must be one of the understood properties. See the source for details.
01062  */
01063 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01064 {
01065    int x;
01066    if (!strcasecmp(var, "attach")) {
01067       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01068    } else if (!strcasecmp(var, "attachfmt")) {
01069       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01070    } else if (!strcasecmp(var, "serveremail")) {
01071       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01072    } else if (!strcasecmp(var, "emailbody")) {
01073       vmu->emailbody = ast_strdup(substitute_escapes(value));
01074    } else if (!strcasecmp(var, "emailsubject")) {
01075       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01076    } else if (!strcasecmp(var, "language")) {
01077       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01078    } else if (!strcasecmp(var, "tz")) {
01079       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01080    } else if (!strcasecmp(var, "locale")) {
01081       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01082 #ifdef IMAP_STORAGE
01083    } else if (!strcasecmp(var, "imapuser")) {
01084       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01085       vmu->imapversion = imapversion;
01086    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01087       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01088       vmu->imapversion = imapversion;
01089    } else if (!strcasecmp(var, "imapfolder")) {
01090       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01091    } else if (!strcasecmp(var, "imapvmshareid")) {
01092       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01093       vmu->imapversion = imapversion;
01094 #endif
01095    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01096       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01097    } else if (!strcasecmp(var, "saycid")){
01098       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01099    } else if (!strcasecmp(var, "sendvoicemail")){
01100       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01101    } else if (!strcasecmp(var, "review")){
01102       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01103    } else if (!strcasecmp(var, "tempgreetwarn")){
01104       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01105    } else if (!strcasecmp(var, "messagewrap")){
01106       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01107    } else if (!strcasecmp(var, "operator")) {
01108       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01109    } else if (!strcasecmp(var, "envelope")){
01110       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01111    } else if (!strcasecmp(var, "moveheard")){
01112       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01113    } else if (!strcasecmp(var, "sayduration")){
01114       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01115    } else if (!strcasecmp(var, "saydurationm")){
01116       if (sscanf(value, "%30d", &x) == 1) {
01117          vmu->saydurationm = x;
01118       } else {
01119          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01120       }
01121    } else if (!strcasecmp(var, "forcename")){
01122       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01123    } else if (!strcasecmp(var, "forcegreetings")){
01124       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01125    } else if (!strcasecmp(var, "callback")) {
01126       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01127    } else if (!strcasecmp(var, "dialout")) {
01128       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01129    } else if (!strcasecmp(var, "exitcontext")) {
01130       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01131    } else if (!strcasecmp(var, "minsecs")) {
01132       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01133          vmu->minsecs = x;
01134       } else {
01135          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01136          vmu->minsecs = vmminsecs;
01137       }
01138    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01139       vmu->maxsecs = atoi(value);
01140       if (vmu->maxsecs <= 0) {
01141          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01142          vmu->maxsecs = vmmaxsecs;
01143       } else {
01144          vmu->maxsecs = atoi(value);
01145       }
01146       if (!strcasecmp(var, "maxmessage"))
01147          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01148    } else if (!strcasecmp(var, "maxmsg")) {
01149       vmu->maxmsg = atoi(value);
01150       /* Accept maxmsg=0 (Greetings only voicemail) */
01151       if (vmu->maxmsg < 0) {
01152          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01153          vmu->maxmsg = MAXMSG;
01154       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01155          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01156          vmu->maxmsg = MAXMSGLIMIT;
01157       }
01158    } else if (!strcasecmp(var, "nextaftercmd")) {
01159       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01160    } else if (!strcasecmp(var, "backupdeleted")) {
01161       if (sscanf(value, "%30d", &x) == 1)
01162          vmu->maxdeletedmsg = x;
01163       else if (ast_true(value))
01164          vmu->maxdeletedmsg = MAXMSG;
01165       else
01166          vmu->maxdeletedmsg = 0;
01167 
01168       if (vmu->maxdeletedmsg < 0) {
01169          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01170          vmu->maxdeletedmsg = MAXMSG;
01171       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01172          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01173          vmu->maxdeletedmsg = MAXMSGLIMIT;
01174       }
01175    } else if (!strcasecmp(var, "volgain")) {
01176       sscanf(value, "%30lf", &vmu->volgain);
01177    } else if (!strcasecmp(var, "passwordlocation")) {
01178       if (!strcasecmp(value, "spooldir")) {
01179          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01180       } else {
01181          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01182       }
01183    } else if (!strcasecmp(var, "options")) {
01184       apply_options(vmu, value);
01185    }
01186 }
01187 
01188 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01189 {
01190    int fds[2], pid = 0;
01191 
01192    memset(buf, 0, len);
01193 
01194    if (pipe(fds)) {
01195       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01196    } else {
01197       /* good to go*/
01198       pid = ast_safe_fork(0);
01199 
01200       if (pid < 0) {
01201          /* ok maybe not */
01202          close(fds[0]);
01203          close(fds[1]);
01204          snprintf(buf, len, "FAILURE: Fork failed");
01205       } else if (pid) {
01206          /* parent */
01207          close(fds[1]);
01208          if (read(fds[0], buf, len) < 0) {
01209             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01210          }
01211          close(fds[0]);
01212       } else {
01213          /*  child */
01214          AST_DECLARE_APP_ARGS(arg,
01215             AST_APP_ARG(v)[20];
01216          );
01217          char *mycmd = ast_strdupa(command);
01218 
01219          close(fds[0]);
01220          dup2(fds[1], STDOUT_FILENO);
01221          close(fds[1]);
01222          ast_close_fds_above_n(STDOUT_FILENO);
01223 
01224          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01225 
01226          execv(arg.v[0], arg.v); 
01227          printf("FAILURE: %s", strerror(errno));
01228          _exit(0);
01229       }
01230    }
01231    return buf;
01232 }
01233 
01234 /*!
01235  * \brief Check that password meets minimum required length
01236  * \param vmu The voicemail user to change the password for.
01237  * \param password The password string to check
01238  *
01239  * \return zero on ok, 1 on not ok.
01240  */
01241 static int check_password(struct ast_vm_user *vmu, char *password)
01242 {
01243    /* check minimum length */
01244    if (strlen(password) < minpassword)
01245       return 1;
01246    /* check that password does not contain '*' character */
01247    if (!ast_strlen_zero(password) && password[0] == '*')
01248       return 1;
01249    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01250       char cmd[255], buf[255];
01251 
01252       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01253 
01254       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01255       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01256          ast_debug(5, "Result: %s\n", buf);
01257          if (!strncasecmp(buf, "VALID", 5)) {
01258             ast_debug(3, "Passed password check: '%s'\n", buf);
01259             return 0;
01260          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01261             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01262             return 0;
01263          } else {
01264             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01265             return 1;
01266          }
01267       }
01268    }
01269    return 0;
01270 }
01271 
01272 /*! 
01273  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01274  * \param vmu The voicemail user to change the password for.
01275  * \param password The new value to be set to the password for this user.
01276  * 
01277  * This only works if there is a realtime engine configured.
01278  * This is called from the (top level) vm_change_password.
01279  *
01280  * \return zero on success, -1 on error.
01281  */
01282 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01283 {
01284    int res = -1;
01285    if (!strcmp(vmu->password, password)) {
01286       /* No change (but an update would return 0 rows updated, so we opt out here) */
01287       return 0;
01288    }
01289 
01290    if (strlen(password) > 10) {
01291       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01292    }
01293    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01294       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01295       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01296       res = 0;
01297    }
01298    return res;
01299 }
01300 
01301 /*!
01302  * \brief Destructively Parse options and apply.
01303  */
01304 static void apply_options(struct ast_vm_user *vmu, const char *options)
01305 {  
01306    char *stringp;
01307    char *s;
01308    char *var, *value;
01309    stringp = ast_strdupa(options);
01310    while ((s = strsep(&stringp, "|"))) {
01311       value = s;
01312       if ((var = strsep(&value, "=")) && value) {
01313          apply_option(vmu, var, value);
01314       }
01315    }  
01316 }
01317 
01318 /*!
01319  * \brief Loads the options specific to a voicemail user.
01320  * 
01321  * This is called when a vm_user structure is being set up, such as from load_options.
01322  */
01323 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01324 {
01325    for (; var; var = var->next) {
01326       if (!strcasecmp(var->name, "vmsecret")) {
01327          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01328       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01329          if (ast_strlen_zero(retval->password)) {
01330             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01331                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01332                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01333             } else {
01334                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01335             }
01336          }
01337       } else if (!strcasecmp(var->name, "uniqueid")) {
01338          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01339       } else if (!strcasecmp(var->name, "pager")) {
01340          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01341       } else if (!strcasecmp(var->name, "email")) {
01342          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01343       } else if (!strcasecmp(var->name, "fullname")) {
01344          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01345       } else if (!strcasecmp(var->name, "context")) {
01346          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01347       } else if (!strcasecmp(var->name, "emailsubject")) {
01348          ast_free(retval->emailsubject);
01349          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01350       } else if (!strcasecmp(var->name, "emailbody")) {
01351          ast_free(retval->emailbody);
01352          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01353 #ifdef IMAP_STORAGE
01354       } else if (!strcasecmp(var->name, "imapuser")) {
01355          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01356          retval->imapversion = imapversion;
01357       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01358          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01359          retval->imapversion = imapversion;
01360       } else if (!strcasecmp(var->name, "imapfolder")) {
01361          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01362       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01363          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01364          retval->imapversion = imapversion;
01365 #endif
01366       } else
01367          apply_option(retval, var->name, var->value);
01368    }
01369 }
01370 
01371 /*!
01372  * \brief Determines if a DTMF key entered is valid.
01373  * \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.
01374  *
01375  * Tests the character entered against the set of valid DTMF characters. 
01376  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01377  */
01378 static int is_valid_dtmf(const char *key)
01379 {
01380    int i;
01381    char *local_key = ast_strdupa(key);
01382 
01383    for (i = 0; i < strlen(key); ++i) {
01384       if (!strchr(VALID_DTMF, *local_key)) {
01385          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01386          return 0;
01387       }
01388       local_key++;
01389    }
01390    return 1;
01391 }
01392 
01393 /*!
01394  * \brief Finds a voicemail user from the realtime engine.
01395  * \param ivm
01396  * \param context
01397  * \param mailbox
01398  *
01399  * 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.
01400  *
01401  * \return The ast_vm_user structure for the user that was found.
01402  */
01403 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01404 {
01405    struct ast_variable *var;
01406    struct ast_vm_user *retval;
01407 
01408    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01409       if (ivm) {
01410          memset(retval, 0, sizeof(*retval));
01411       }
01412       populate_defaults(retval);
01413       if (!ivm) {
01414          ast_set_flag(retval, VM_ALLOCED);
01415       }
01416       if (mailbox) {
01417          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01418       }
01419       if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
01420          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01421       } else {
01422          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01423       }
01424       if (var) {
01425          apply_options_full(retval, var);
01426          ast_variables_destroy(var);
01427       } else { 
01428          if (!ivm) 
01429             free_user(retval);
01430          retval = NULL;
01431       }  
01432    } 
01433    return retval;
01434 }
01435 
01436 /*!
01437  * \brief Finds a voicemail user from the users file or the realtime engine.
01438  * \param ivm
01439  * \param context
01440  * \param mailbox
01441  * 
01442  * \return The ast_vm_user structure for the user that was found.
01443  */
01444 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01445 {
01446    /* This function could be made to generate one from a database, too */
01447    struct ast_vm_user *vmu = NULL, *cur;
01448    AST_LIST_LOCK(&users);
01449 
01450    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01451       context = "default";
01452 
01453    AST_LIST_TRAVERSE(&users, cur, list) {
01454 #ifdef IMAP_STORAGE
01455       if (cur->imapversion != imapversion) {
01456          continue;
01457       }
01458 #endif
01459       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01460          break;
01461       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01462          break;
01463    }
01464    if (cur) {
01465       /* Make a copy, so that on a reload, we have no race */
01466       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01467          *vmu = *cur;
01468          if (!ivm) {
01469             vmu->emailbody = ast_strdup(cur->emailbody);
01470             vmu->emailsubject = ast_strdup(cur->emailsubject);
01471          }
01472          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01473          AST_LIST_NEXT(vmu, list) = NULL;
01474       }
01475    } else
01476       vmu = find_user_realtime(ivm, context, mailbox);
01477    AST_LIST_UNLOCK(&users);
01478    return vmu;
01479 }
01480 
01481 /*!
01482  * \brief Resets a user password to a specified password.
01483  * \param context
01484  * \param mailbox
01485  * \param newpass
01486  *
01487  * This does the actual change password work, called by the vm_change_password() function.
01488  *
01489  * \return zero on success, -1 on error.
01490  */
01491 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01492 {
01493    /* This function could be made to generate one from a database, too */
01494    struct ast_vm_user *cur;
01495    int res = -1;
01496    AST_LIST_LOCK(&users);
01497    AST_LIST_TRAVERSE(&users, cur, list) {
01498       if ((!context || !strcasecmp(context, cur->context)) &&
01499          (!strcasecmp(mailbox, cur->mailbox)))
01500             break;
01501    }
01502    if (cur) {
01503       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01504       res = 0;
01505    }
01506    AST_LIST_UNLOCK(&users);
01507    return res;
01508 }
01509 
01510 /*! 
01511  * \brief The handler for the change password option.
01512  * \param vmu The voicemail user to work with.
01513  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01514  * 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.
01515  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01516  */
01517 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01518 {
01519    struct ast_config   *cfg = NULL;
01520    struct ast_variable *var = NULL;
01521    struct ast_category *cat = NULL;
01522    char *category = NULL, *value = NULL, *new = NULL;
01523    const char *tmp = NULL;
01524    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01525    char secretfn[PATH_MAX] = "";
01526    int found = 0;
01527 
01528    if (!change_password_realtime(vmu, newpassword))
01529       return;
01530 
01531    /* check if we should store the secret in the spool directory next to the messages */
01532    switch (vmu->passwordlocation) {
01533    case OPT_PWLOC_SPOOLDIR:
01534       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01535       if (write_password_to_file(secretfn, newpassword) == 0) {
01536          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01537          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01538          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01539          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01540          break;
01541       } else {
01542          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01543       }
01544       /* Fall-through */
01545    case OPT_PWLOC_VOICEMAILCONF:
01546       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01547          while ((category = ast_category_browse(cfg, category))) {
01548             if (!strcasecmp(category, vmu->context)) {
01549                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01550                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01551                   break;
01552                }
01553                value = strstr(tmp, ",");
01554                if (!value) {
01555                   new = ast_alloca(strlen(newpassword)+1);
01556                   sprintf(new, "%s", newpassword);
01557                } else {
01558                   new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
01559                   sprintf(new, "%s%s", newpassword, value);
01560                }
01561                if (!(cat = ast_category_get(cfg, category))) {
01562                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01563                   break;
01564                }
01565                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01566                found = 1;
01567             }
01568          }
01569          /* save the results */
01570          if (found) {
01571             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01572             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01573             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01574             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01575             break;
01576          }
01577       }
01578       /* Fall-through */
01579    case OPT_PWLOC_USERSCONF:
01580       /* check users.conf and update the password stored for the mailbox */
01581       /* if no vmsecret entry exists create one. */
01582       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01583          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01584          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01585             ast_debug(4, "users.conf: %s\n", category);
01586             if (!strcasecmp(category, vmu->mailbox)) {
01587                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01588                   ast_debug(3, "looks like we need to make vmsecret!\n");
01589                   var = ast_variable_new("vmsecret", newpassword, "");
01590                } else {
01591                   var = NULL;
01592                }
01593                new = ast_alloca(strlen(newpassword) + 1);
01594                sprintf(new, "%s", newpassword);
01595                if (!(cat = ast_category_get(cfg, category))) {
01596                   ast_debug(4, "failed to get category!\n");
01597                   ast_free(var);
01598                   break;
01599                }
01600                if (!var) {
01601                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01602                } else {
01603                   ast_variable_append(cat, var);
01604                }
01605                found = 1;
01606                break;
01607             }
01608          }
01609          /* save the results and clean things up */
01610          if (found) {
01611             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01612             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01613             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01614             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01615          }
01616       }
01617    }
01618 }
01619 
01620 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01621 {
01622    char buf[255];
01623    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01624    ast_debug(1, "External password: %s\n",buf);
01625    if (!ast_safe_system(buf)) {
01626       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01627       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01628       /* Reset the password in memory, too */
01629       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01630    }
01631 }
01632 
01633 /*! 
01634  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01635  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01636  * \param len The length of the path string that was written out.
01637  * \param context
01638  * \param ext 
01639  * \param folder 
01640  * 
01641  * The path is constructed as 
01642  *    VM_SPOOL_DIRcontext/ext/folder
01643  *
01644  * \return zero on success, -1 on error.
01645  */
01646 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01647 {
01648    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01649 }
01650 
01651 /*! 
01652  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01653  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01654  * \param len The length of the path string that was written out.
01655  * \param dir 
01656  * \param num 
01657  * 
01658  * The path is constructed as 
01659  *    VM_SPOOL_DIRcontext/ext/folder
01660  *
01661  * \return zero on success, -1 on error.
01662  */
01663 static int make_file(char *dest, const int len, const char *dir, const int num)
01664 {
01665    return snprintf(dest, len, "%s/msg%04d", dir, num);
01666 }
01667 
01668 /* same as mkstemp, but return a FILE * */
01669 static FILE *vm_mkftemp(char *template)
01670 {
01671    FILE *p = NULL;
01672    int pfd = mkstemp(template);
01673    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01674    if (pfd > -1) {
01675       p = fdopen(pfd, "w+");
01676       if (!p) {
01677          close(pfd);
01678          pfd = -1;
01679       }
01680    }
01681    return p;
01682 }
01683 
01684 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01685  * \param dest    String. base directory.
01686  * \param len     Length of dest.
01687  * \param context String. Ignored if is null or empty string.
01688  * \param ext     String. Ignored if is null or empty string.
01689  * \param folder  String. Ignored if is null or empty string. 
01690  * \return -1 on failure, 0 on success.
01691  */
01692 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01693 {
01694    mode_t   mode = VOICEMAIL_DIR_MODE;
01695    int res;
01696 
01697    make_dir(dest, len, context, ext, folder);
01698    if ((res = ast_mkdir(dest, mode))) {
01699       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01700       return -1;
01701    }
01702    return 0;
01703 }
01704 
01705 static const char * const mailbox_folders[] = {
01706 #ifdef IMAP_STORAGE
01707    imapfolder,
01708 #else
01709    "INBOX",
01710 #endif
01711    "Old",
01712    "Work",
01713    "Family",
01714    "Friends",
01715    "Cust1",
01716    "Cust2",
01717    "Cust3",
01718    "Cust4",
01719    "Cust5",
01720    "Deleted",
01721    "Urgent",
01722 };
01723 
01724 static const char *mbox(struct ast_vm_user *vmu, int id)
01725 {
01726 #ifdef IMAP_STORAGE
01727    if (vmu && id == 0) {
01728       return vmu->imapfolder;
01729    }
01730 #endif
01731    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01732 }
01733 
01734 static int get_folder_by_name(const char *name)
01735 {
01736    size_t i;
01737 
01738    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01739       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01740          return i;
01741       }
01742    }
01743 
01744    return -1;
01745 }
01746 
01747 static void free_user(struct ast_vm_user *vmu)
01748 {
01749    if (ast_test_flag(vmu, VM_ALLOCED)) {
01750 
01751       ast_free(vmu->emailbody);
01752       vmu->emailbody = NULL;
01753 
01754       ast_free(vmu->emailsubject);
01755       vmu->emailsubject = NULL;
01756 
01757       ast_free(vmu);
01758    }
01759 }
01760 
01761 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01762 
01763    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01764 
01765    /* remove old allocation */
01766    if (vms->deleted) {
01767       ast_free(vms->deleted);
01768       vms->deleted = NULL;
01769    }
01770    if (vms->heard) {
01771       ast_free(vms->heard);
01772       vms->heard = NULL;
01773    }
01774    vms->dh_arraysize = 0;
01775 
01776    if (arraysize > 0) {
01777       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01778          return -1;
01779       }
01780       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01781          ast_free(vms->deleted);
01782          vms->deleted = NULL;
01783          return -1;
01784       }
01785       vms->dh_arraysize = arraysize;
01786    }
01787 
01788    return 0;
01789 }
01790 
01791 /* All IMAP-specific functions should go in this block. This
01792  * keeps them from being spread out all over the code */
01793 #ifdef IMAP_STORAGE
01794 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01795 {
01796    char arg[10];
01797    struct vm_state *vms;
01798    unsigned long messageNum;
01799 
01800    /* If greetings aren't stored in IMAP, just delete the file */
01801    if (msgnum < 0 && !imapgreetings) {
01802       ast_filedelete(file, NULL);
01803       return;
01804    }
01805 
01806    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01807       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);
01808       return;
01809    }
01810 
01811    if (msgnum < 0) {
01812       imap_delete_old_greeting(file, vms);
01813       return;
01814    }
01815 
01816    /* find real message number based on msgnum */
01817    /* this may be an index into vms->msgArray based on the msgnum. */
01818    messageNum = vms->msgArray[msgnum];
01819    if (messageNum == 0) {
01820       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01821       return;
01822    }
01823    if (option_debug > 2)
01824       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01825    /* delete message */
01826    snprintf (arg, sizeof(arg), "%lu", messageNum);
01827    ast_mutex_lock(&vms->lock);
01828    mail_setflag (vms->mailstream, arg, "\\DELETED");
01829    mail_expunge(vms->mailstream);
01830    ast_mutex_unlock(&vms->lock);
01831 }
01832 
01833 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01834 {
01835    struct vm_state *vms_p;
01836    char *file, *filename;
01837    char *attachment;
01838    int i;
01839    BODY *body;
01840 
01841    /* This function is only used for retrieval of IMAP greetings
01842     * regular messages are not retrieved this way, nor are greetings
01843     * if they are stored locally*/
01844    if (msgnum > -1 || !imapgreetings) {
01845       return 0;
01846    } else {
01847       file = strrchr(ast_strdupa(dir), '/');
01848       if (file)
01849          *file++ = '\0';
01850       else {
01851          ast_debug (1, "Failed to procure file name from directory passed.\n");
01852          return -1;
01853       }
01854    }
01855 
01856    /* check if someone is accessing this box right now... */
01857    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01858       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01859       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01860       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01861       * that's all we need to do.
01862       */
01863       if (!(vms_p = create_vm_state_from_user(vmu))) {
01864          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01865          return -1;
01866       }
01867    }
01868 
01869    /* Greetings will never have a prepended message */
01870    *vms_p->introfn = '\0';
01871 
01872    ast_mutex_lock(&vms_p->lock);
01873    init_mailstream(vms_p, GREETINGS_FOLDER);
01874    if (!vms_p->mailstream) {
01875       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01876       ast_mutex_unlock(&vms_p->lock);
01877       return -1;
01878    }
01879 
01880    /*XXX Yuck, this could probably be done a lot better */
01881    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01882       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01883       /* We have the body, now we extract the file name of the first attachment. */
01884       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01885          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01886       } else {
01887          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01888          ast_mutex_unlock(&vms_p->lock);
01889          return -1;
01890       }
01891       filename = strsep(&attachment, ".");
01892       if (!strcmp(filename, file)) {
01893          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01894          vms_p->msgArray[vms_p->curmsg] = i + 1;
01895          save_body(body, vms_p, "2", attachment, 0);
01896          ast_mutex_unlock(&vms_p->lock);
01897          return 0;
01898       }
01899    }
01900    ast_mutex_unlock(&vms_p->lock);
01901 
01902    return -1;
01903 }
01904 
01905 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01906 {
01907    BODY *body;
01908    char *header_content;
01909    char *attachedfilefmt;
01910    char buf[80];
01911    struct vm_state *vms;
01912    char text_file[PATH_MAX];
01913    FILE *text_file_ptr;
01914    int res = 0;
01915    struct ast_vm_user *vmu;
01916 
01917    if (!(vmu = find_user(NULL, context, mailbox))) {
01918       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01919       return -1;
01920    }
01921    
01922    if (msgnum < 0) {
01923       if (imapgreetings) {
01924          res = imap_retrieve_greeting(dir, msgnum, vmu);
01925          goto exit;
01926       } else {
01927          res = 0;
01928          goto exit;
01929       }
01930    }
01931 
01932    /* Before anything can happen, we need a vm_state so that we can
01933     * actually access the imap server through the vms->mailstream
01934     */
01935    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01936       /* This should not happen. If it does, then I guess we'd
01937        * need to create the vm_state, extract which mailbox to
01938        * open, and then set up the msgArray so that the correct
01939        * IMAP message could be accessed. If I have seen correctly
01940        * though, the vms should be obtainable from the vmstates list
01941        * and should have its msgArray properly set up.
01942        */
01943       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01944       res = -1;
01945       goto exit;
01946    }
01947    
01948    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01949    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01950 
01951    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01952    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01953       res = 0;
01954       goto exit;
01955    }
01956 
01957    if (option_debug > 2)
01958       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01959    if (vms->msgArray[msgnum] == 0) {
01960       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01961       res = -1;
01962       goto exit;
01963    }
01964 
01965    /* This will only work for new messages... */
01966    ast_mutex_lock(&vms->lock);
01967    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01968    ast_mutex_unlock(&vms->lock);
01969    /* empty string means no valid header */
01970    if (ast_strlen_zero(header_content)) {
01971       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01972       res = -1;
01973       goto exit;
01974    }
01975 
01976    ast_mutex_lock(&vms->lock);
01977    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01978    ast_mutex_unlock(&vms->lock);
01979 
01980    /* We have the body, now we extract the file name of the first attachment. */
01981    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01982       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01983    } else {
01984       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01985       res = -1;
01986       goto exit;
01987    }
01988    
01989    /* Find the format of the attached file */
01990 
01991    strsep(&attachedfilefmt, ".");
01992    if (!attachedfilefmt) {
01993       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01994       res = -1;
01995       goto exit;
01996    }
01997    
01998    save_body(body, vms, "2", attachedfilefmt, 0);
01999    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
02000       *vms->introfn = '\0';
02001    }
02002 
02003    /* Get info from headers!! */
02004    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
02005 
02006    if (!(text_file_ptr = fopen(text_file, "w"))) {
02007       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
02008    }
02009 
02010    fprintf(text_file_ptr, "%s\n", "[message]");
02011 
02012    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
02013    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
02014    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
02015    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02016    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
02017    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02018    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02019    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02020    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02021    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02022    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02023    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02024    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02025    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02026    fclose(text_file_ptr);
02027 
02028 exit:
02029    free_user(vmu);
02030    return res;
02031 }
02032 
02033 static int folder_int(const char *folder)
02034 {
02035    /*assume a NULL folder means INBOX*/
02036    if (!folder) {
02037       return 0;
02038    }
02039    if (!strcasecmp(folder, imapfolder)) {
02040       return 0;
02041    } else if (!strcasecmp(folder, "Old")) {
02042       return 1;
02043    } else if (!strcasecmp(folder, "Work")) {
02044       return 2;
02045    } else if (!strcasecmp(folder, "Family")) {
02046       return 3;
02047    } else if (!strcasecmp(folder, "Friends")) {
02048       return 4;
02049    } else if (!strcasecmp(folder, "Cust1")) {
02050       return 5;
02051    } else if (!strcasecmp(folder, "Cust2")) {
02052       return 6;
02053    } else if (!strcasecmp(folder, "Cust3")) {
02054       return 7;
02055    } else if (!strcasecmp(folder, "Cust4")) {
02056       return 8;
02057    } else if (!strcasecmp(folder, "Cust5")) {
02058       return 9;
02059    } else if (!strcasecmp(folder, "Urgent")) {
02060       return 11;
02061    } else { /*assume they meant INBOX if folder is not found otherwise*/
02062       return 0;
02063    }
02064 }
02065 
02066 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02067 {
02068    SEARCHPGM *pgm;
02069    SEARCHHEADER *hdr;
02070 
02071    struct ast_vm_user *vmu, vmus;
02072    struct vm_state *vms_p;
02073    int ret = 0;
02074    int fold = folder_int(folder);
02075    int urgent = 0;
02076    
02077    /* If URGENT, then look at INBOX */
02078    if (fold == 11) {
02079       fold = NEW_FOLDER;
02080       urgent = 1;
02081    }
02082 
02083    if (ast_strlen_zero(mailbox))
02084       return 0;
02085 
02086    /* We have to get the user before we can open the stream! */
02087    vmu = find_user(&vmus, context, mailbox);
02088    if (!vmu) {
02089       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02090       return -1;
02091    } else {
02092       /* No IMAP account available */
02093       if (vmu->imapuser[0] == '\0') {
02094          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02095          return -1;
02096       }
02097    }
02098    
02099    /* No IMAP account available */
02100    if (vmu->imapuser[0] == '\0') {
02101       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02102       free_user(vmu);
02103       return -1;
02104    }
02105 
02106    /* check if someone is accessing this box right now... */
02107    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02108    if (!vms_p) {
02109       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02110    }
02111    if (vms_p) {
02112       ast_debug(3, "Returning before search - user is logged in\n");
02113       if (fold == 0) { /* INBOX */
02114          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02115       }
02116       if (fold == 1) { /* Old messages */
02117          return vms_p->oldmessages;
02118       }
02119    }
02120 
02121    /* add one if not there... */
02122    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02123    if (!vms_p) {
02124       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02125    }
02126 
02127    if (!vms_p) {
02128       vms_p = create_vm_state_from_user(vmu);
02129    }
02130    ret = init_mailstream(vms_p, fold);
02131    if (!vms_p->mailstream) {
02132       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02133       return -1;
02134    }
02135    if (ret == 0) {
02136       ast_mutex_lock(&vms_p->lock);
02137       pgm = mail_newsearchpgm ();
02138       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02139       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02140       pgm->header = hdr;
02141       if (fold != OLD_FOLDER) {
02142          pgm->unseen = 1;
02143          pgm->seen = 0;
02144       }
02145       /* In the special case where fold is 1 (old messages) we have to do things a bit
02146        * differently. Old messages are stored in the INBOX but are marked as "seen"
02147        */
02148       else {
02149          pgm->unseen = 0;
02150          pgm->seen = 1;
02151       }
02152       /* look for urgent messages */
02153       if (fold == NEW_FOLDER) {
02154          if (urgent) {
02155             pgm->flagged = 1;
02156             pgm->unflagged = 0;
02157          } else {
02158             pgm->flagged = 0;
02159             pgm->unflagged = 1;
02160          }
02161       }
02162       pgm->undeleted = 1;
02163       pgm->deleted = 0;
02164 
02165       vms_p->vmArrayIndex = 0;
02166       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02167       if (fold == 0 && urgent == 0)
02168          vms_p->newmessages = vms_p->vmArrayIndex;
02169       if (fold == 1)
02170          vms_p->oldmessages = vms_p->vmArrayIndex;
02171       if (fold == 0 && urgent == 1)
02172          vms_p->urgentmessages = vms_p->vmArrayIndex;
02173       /*Freeing the searchpgm also frees the searchhdr*/
02174       mail_free_searchpgm(&pgm);
02175       ast_mutex_unlock(&vms_p->lock);
02176       vms_p->updated = 0;
02177       return vms_p->vmArrayIndex;
02178    } else {
02179       ast_mutex_lock(&vms_p->lock);
02180       mail_ping(vms_p->mailstream);
02181       ast_mutex_unlock(&vms_p->lock);
02182    }
02183    return 0;
02184 }
02185 
02186 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02187 {
02188    /* Check if mailbox is full */
02189    check_quota(vms, vmu->imapfolder);
02190    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02191       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02192       ast_play_and_wait(chan, "vm-mailboxfull");
02193       return -1;
02194    }
02195    
02196    /* Check if we have exceeded maxmsg */
02197    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));
02198    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02199       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02200       ast_play_and_wait(chan, "vm-mailboxfull");
02201       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02202       return -1;
02203    }
02204 
02205    return 0;
02206 }
02207 
02208 /*!
02209  * \brief Gets the number of messages that exist in a mailbox folder.
02210  * \param context
02211  * \param mailbox
02212  * \param folder
02213  * 
02214  * This method is used when IMAP backend is used.
02215  * \return The number of messages in this mailbox folder (zero or more).
02216  */
02217 static int messagecount(const char *context, const char *mailbox, const char *folder)
02218 {
02219    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02220       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02221    } else {
02222       return __messagecount(context, mailbox, folder);
02223    }
02224 }
02225 
02226 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)
02227 {
02228    char *myserveremail = serveremail;
02229    char fn[PATH_MAX];
02230    char introfn[PATH_MAX];
02231    char mailbox[256];
02232    char *stringp;
02233    FILE *p = NULL;
02234    char tmp[80] = "/tmp/astmail-XXXXXX";
02235    long len;
02236    void *buf;
02237    int tempcopy = 0;
02238    STRING str;
02239    int ret; /* for better error checking */
02240    char *imap_flags = NIL;
02241    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02242    int box = NEW_FOLDER;
02243 
02244    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02245    if (msgnum < 0) {
02246       if(!imapgreetings) {
02247          return 0;
02248       } else {
02249          box = GREETINGS_FOLDER;
02250       }
02251    }
02252    
02253    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02254       return -1;
02255    }
02256 
02257    /* Set urgent flag for IMAP message */
02258    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02259       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02260       imap_flags = "\\FLAGGED";
02261    }
02262    
02263    /* Attach only the first format */
02264    fmt = ast_strdupa(fmt);
02265    stringp = fmt;
02266    strsep(&stringp, "|");
02267 
02268    if (!ast_strlen_zero(vmu->serveremail))
02269       myserveremail = vmu->serveremail;
02270 
02271    if (msgnum > -1)
02272       make_file(fn, sizeof(fn), dir, msgnum);
02273    else
02274       ast_copy_string (fn, dir, sizeof(fn));
02275 
02276    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02277    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02278       *introfn = '\0';
02279    }
02280    
02281    if (ast_strlen_zero(vmu->email)) {
02282       /* We need the vmu->email to be set when we call make_email_file, but
02283        * if we keep it set, a duplicate e-mail will be created. So at the end
02284        * of this function, we will revert back to an empty string if tempcopy
02285        * is 1.
02286        */
02287       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02288       tempcopy = 1;
02289    }
02290 
02291    if (!strcmp(fmt, "wav49"))
02292       fmt = "WAV";
02293    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02294 
02295    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02296       command hangs. */
02297    if (!(p = vm_mkftemp(tmp))) {
02298       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02299       if (tempcopy)
02300          *(vmu->email) = '\0';
02301       return -1;
02302    }
02303 
02304    if (msgnum < 0 && imapgreetings) {
02305       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02306          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02307          return -1;
02308       }
02309       imap_delete_old_greeting(fn, vms);
02310    }
02311 
02312    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02313       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02314       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02315       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02316    /* read mail file to memory */
02317    len = ftell(p);
02318    rewind(p);
02319    if (!(buf = ast_malloc(len + 1))) {
02320       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02321       fclose(p);
02322       if (tempcopy)
02323          *(vmu->email) = '\0';
02324       return -1;
02325    }
02326    if (fread(buf, len, 1, p) < len) {
02327       if (ferror(p)) {
02328          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02329          return -1;
02330       }
02331    }
02332    ((char *) buf)[len] = '\0';
02333    INIT(&str, mail_string, buf, len);
02334    ret = init_mailstream(vms, box);
02335    if (ret == 0) {
02336       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02337       ast_mutex_lock(&vms->lock);
02338       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02339          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02340       ast_mutex_unlock(&vms->lock);
02341       fclose(p);
02342       unlink(tmp);
02343       ast_free(buf);
02344    } else {
02345       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02346       fclose(p);
02347       unlink(tmp);
02348       ast_free(buf);
02349       return -1;
02350    }
02351    ast_debug(3, "%s stored\n", fn);
02352    
02353    if (tempcopy)
02354       *(vmu->email) = '\0';
02355    inprocess_count(vmu->mailbox, vmu->context, -1);
02356    return 0;
02357 
02358 }
02359 
02360 /*!
02361  * \brief Gets the number of messages that exist in the inbox folder.
02362  * \param mailbox_context
02363  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02364  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02365  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02366  * 
02367  * This method is used when IMAP backend is used.
02368  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02369  *
02370  * \return zero on success, -1 on error.
02371  */
02372 
02373 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02374 {
02375    char tmp[PATH_MAX] = "";
02376    char *mailboxnc;
02377    char *context;
02378    char *mb;
02379    char *cur;
02380    if (newmsgs)
02381       *newmsgs = 0;
02382    if (oldmsgs)
02383       *oldmsgs = 0;
02384    if (urgentmsgs)
02385       *urgentmsgs = 0;
02386 
02387    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02388    /* If no mailbox, return immediately */
02389    if (ast_strlen_zero(mailbox_context))
02390       return 0;
02391    
02392    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02393    context = strchr(tmp, '@');
02394    if (strchr(mailbox_context, ',')) {
02395       int tmpnew, tmpold, tmpurgent;
02396       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02397       mb = tmp;
02398       while ((cur = strsep(&mb, ", "))) {
02399          if (!ast_strlen_zero(cur)) {
02400             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02401                return -1;
02402             else {
02403                if (newmsgs)
02404                   *newmsgs += tmpnew; 
02405                if (oldmsgs)
02406                   *oldmsgs += tmpold;
02407                if (urgentmsgs)
02408                   *urgentmsgs += tmpurgent;
02409             }
02410          }
02411       }
02412       return 0;
02413    }
02414    if (context) {
02415       *context = '\0';
02416       mailboxnc = tmp;
02417       context++;
02418    } else {
02419       context = "default";
02420       mailboxnc = (char *) mailbox_context;
02421    }
02422 
02423    if (newmsgs) {
02424       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02425       if (!vmu) {
02426          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02427          return -1;
02428       }
02429       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02430          free_user(vmu);
02431          return -1;
02432       }
02433       free_user(vmu);
02434    }
02435    if (oldmsgs) {
02436       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02437          return -1;
02438       }
02439    }
02440    if (urgentmsgs) {
02441       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02442          return -1;
02443       }
02444    }
02445    return 0;
02446 }
02447 
02448 /** 
02449  * \brief Determines if the given folder has messages.
02450  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02451  * \param folder the folder to look in
02452  *
02453  * This function is used when the mailbox is stored in an IMAP back end.
02454  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02455  * \return 1 if the folder has one or more messages. zero otherwise.
02456  */
02457 
02458 static int has_voicemail(const char *mailbox, const char *folder)
02459 {
02460    char tmp[256], *tmp2, *box, *context;
02461    ast_copy_string(tmp, mailbox, sizeof(tmp));
02462    tmp2 = tmp;
02463    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02464       while ((box = strsep(&tmp2, ",&"))) {
02465          if (!ast_strlen_zero(box)) {
02466             if (has_voicemail(box, folder)) {
02467                return 1;
02468             }
02469          }
02470       }
02471    }
02472    if ((context = strchr(tmp, '@'))) {
02473       *context++ = '\0';
02474    } else {
02475       context = "default";
02476    }
02477    return __messagecount(context, tmp, folder) ? 1 : 0;
02478 }
02479 
02480 /*!
02481  * \brief Copies a message from one mailbox to another.
02482  * \param chan
02483  * \param vmu
02484  * \param imbox
02485  * \param msgnum
02486  * \param duration
02487  * \param recip
02488  * \param fmt
02489  * \param dir
02490  *
02491  * This works with IMAP storage based mailboxes.
02492  *
02493  * \return zero on success, -1 on error.
02494  */
02495 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)
02496 {
02497    struct vm_state *sendvms = NULL, *destvms = NULL;
02498    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02499    if (msgnum >= recip->maxmsg) {
02500       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02501       return -1;
02502    }
02503    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02504       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02505       return -1;
02506    }
02507    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02508       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02509       return -1;
02510    }
02511    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02512    ast_mutex_lock(&sendvms->lock);
02513    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02514       ast_mutex_unlock(&sendvms->lock);
02515       return 0;
02516    }
02517    ast_mutex_unlock(&sendvms->lock);
02518    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02519    return -1;
02520 }
02521 
02522 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02523 {
02524    char tmp[256], *t = tmp;
02525    size_t left = sizeof(tmp);
02526    
02527    if (box == OLD_FOLDER) {
02528       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02529    } else {
02530       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02531    }
02532 
02533    if (box == NEW_FOLDER) {
02534       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02535    } else {
02536       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02537    }
02538 
02539    /* Build up server information */
02540    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02541 
02542    /* Add authentication user if present */
02543    if (!ast_strlen_zero(authuser))
02544       ast_build_string(&t, &left, "/authuser=%s", authuser);
02545 
02546    /* Add flags if present */
02547    if (!ast_strlen_zero(imapflags))
02548       ast_build_string(&t, &left, "/%s", imapflags);
02549 
02550    /* End with username */
02551 #if 1
02552    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02553 #else
02554    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02555 #endif
02556    if (box == NEW_FOLDER || box == OLD_FOLDER)
02557       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02558    else if (box == GREETINGS_FOLDER)
02559       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02560    else {   /* Other folders such as Friends, Family, etc... */
02561       if (!ast_strlen_zero(imapparentfolder)) {
02562          /* imapparentfolder would typically be set to INBOX */
02563          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02564       } else {
02565          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02566       }
02567    }
02568 }
02569 
02570 static int init_mailstream(struct vm_state *vms, int box)
02571 {
02572    MAILSTREAM *stream = NIL;
02573    long debug;
02574    char tmp[256];
02575    
02576    if (!vms) {
02577       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02578       return -1;
02579    }
02580    if (option_debug > 2)
02581       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02582    if (vms->mailstream == NIL || !vms->mailstream) {
02583       if (option_debug)
02584          ast_log(LOG_DEBUG, "mailstream not set.\n");
02585    } else {
02586       stream = vms->mailstream;
02587    }
02588    /* debug = T;  user wants protocol telemetry? */
02589    debug = NIL;  /* NO protocol telemetry? */
02590 
02591    if (delimiter == '\0') {      /* did not probe the server yet */
02592       char *cp;
02593 #ifdef USE_SYSTEM_IMAP
02594 #include <imap/linkage.c>
02595 #elif defined(USE_SYSTEM_CCLIENT)
02596 #include <c-client/linkage.c>
02597 #else
02598 #include "linkage.c"
02599 #endif
02600       /* Connect to INBOX first to get folders delimiter */
02601       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02602       ast_mutex_lock(&vms->lock);
02603       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02604       ast_mutex_unlock(&vms->lock);
02605       if (stream == NIL) {
02606          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02607          return -1;
02608       }
02609       get_mailbox_delimiter(stream);
02610       /* update delimiter in imapfolder */
02611       for (cp = vms->imapfolder; *cp; cp++)
02612          if (*cp == '/')
02613             *cp = delimiter;
02614    }
02615    /* Now connect to the target folder */
02616    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02617    if (option_debug > 2)
02618       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02619    ast_mutex_lock(&vms->lock);
02620    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02621    ast_mutex_unlock(&vms->lock);
02622    if (vms->mailstream == NIL) {
02623       return -1;
02624    } else {
02625       return 0;
02626    }
02627 }
02628 
02629 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02630 {
02631    SEARCHPGM *pgm;
02632    SEARCHHEADER *hdr;
02633    int ret, urgent = 0;
02634 
02635    /* If Urgent, then look at INBOX */
02636    if (box == 11) {
02637       box = NEW_FOLDER;
02638       urgent = 1;
02639    }
02640 
02641    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02642    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02643    vms->imapversion = vmu->imapversion;
02644    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02645 
02646    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02647       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02648       return -1;
02649    }
02650    
02651    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02652    
02653    /* Check Quota */
02654    if  (box == 0)  {
02655       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02656       check_quota(vms, (char *) mbox(vmu, box));
02657    }
02658 
02659    ast_mutex_lock(&vms->lock);
02660    pgm = mail_newsearchpgm();
02661 
02662    /* Check IMAP folder for Asterisk messages only... */
02663    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02664    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02665    pgm->header = hdr;
02666    pgm->deleted = 0;
02667    pgm->undeleted = 1;
02668 
02669    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02670    if (box == NEW_FOLDER && urgent == 1) {
02671       pgm->unseen = 1;
02672       pgm->seen = 0;
02673       pgm->flagged = 1;
02674       pgm->unflagged = 0;
02675    } else if (box == NEW_FOLDER && urgent == 0) {
02676       pgm->unseen = 1;
02677       pgm->seen = 0;
02678       pgm->flagged = 0;
02679       pgm->unflagged = 1;
02680    } else if (box == OLD_FOLDER) {
02681       pgm->seen = 1;
02682       pgm->unseen = 0;
02683    }
02684 
02685    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02686 
02687    vms->vmArrayIndex = 0;
02688    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02689    vms->lastmsg = vms->vmArrayIndex - 1;
02690    mail_free_searchpgm(&pgm);
02691    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02692     * ensure to allocate enough space to account for all of them. Warn if old messages
02693     * have not been checked first as that is required.
02694     */
02695    if (box == 0 && !vms->dh_arraysize) {
02696       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02697    }
02698    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02699       ast_mutex_unlock(&vms->lock);
02700       return -1;
02701    }
02702 
02703    ast_mutex_unlock(&vms->lock);
02704    return 0;
02705 }
02706 
02707 static void write_file(char *filename, char *buffer, unsigned long len)
02708 {
02709    FILE *output;
02710 
02711    output = fopen (filename, "w");
02712    if (fwrite(buffer, len, 1, output) != 1) {
02713       if (ferror(output)) {
02714          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02715       }
02716    }
02717    fclose (output);
02718 }
02719 
02720 static void update_messages_by_imapuser(const char *user, unsigned long number)
02721 {
02722    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02723 
02724    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02725       return;
02726    }
02727 
02728    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02729    vms->msgArray[vms->vmArrayIndex++] = number;
02730 }
02731 
02732 void mm_searched(MAILSTREAM *stream, unsigned long number)
02733 {
02734    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02735 
02736    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02737       return;
02738 
02739    update_messages_by_imapuser(user, number);
02740 }
02741 
02742 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02743 {
02744    struct ast_variable *var;
02745    struct ast_vm_user *vmu;
02746 
02747    vmu = ast_calloc(1, sizeof *vmu);
02748    if (!vmu)
02749       return NULL;
02750 
02751    populate_defaults(vmu);
02752    ast_set_flag(vmu, VM_ALLOCED);
02753 
02754    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02755    if (var) {
02756       apply_options_full(vmu, var);
02757       ast_variables_destroy(var);
02758       return vmu;
02759    } else {
02760       ast_free(vmu);
02761       return NULL;
02762    }
02763 }
02764 
02765 /* Interfaces to C-client */
02766 
02767 void mm_exists(MAILSTREAM * stream, unsigned long number)
02768 {
02769    /* mail_ping will callback here if new mail! */
02770    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02771    if (number == 0) return;
02772    set_update(stream);
02773 }
02774 
02775 
02776 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02777 {
02778    /* mail_ping will callback here if expunged mail! */
02779    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02780    if (number == 0) return;
02781    set_update(stream);
02782 }
02783 
02784 
02785 void mm_flags(MAILSTREAM * stream, unsigned long number)
02786 {
02787    /* mail_ping will callback here if read mail! */
02788    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02789    if (number == 0) return;
02790    set_update(stream);
02791 }
02792 
02793 
02794 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02795 {
02796    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02797    mm_log (string, errflg);
02798 }
02799 
02800 
02801 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02802 {
02803    if (delimiter == '\0') {
02804       delimiter = delim;
02805    }
02806 
02807    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02808    if (attributes & LATT_NOINFERIORS)
02809       ast_debug(5, "no inferiors\n");
02810    if (attributes & LATT_NOSELECT)
02811       ast_debug(5, "no select\n");
02812    if (attributes & LATT_MARKED)
02813       ast_debug(5, "marked\n");
02814    if (attributes & LATT_UNMARKED)
02815       ast_debug(5, "unmarked\n");
02816 }
02817 
02818 
02819 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02820 {
02821    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02822    if (attributes & LATT_NOINFERIORS)
02823       ast_debug(5, "no inferiors\n");
02824    if (attributes & LATT_NOSELECT)
02825       ast_debug(5, "no select\n");
02826    if (attributes & LATT_MARKED)
02827       ast_debug(5, "marked\n");
02828    if (attributes & LATT_UNMARKED)
02829       ast_debug(5, "unmarked\n");
02830 }
02831 
02832 
02833 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02834 {
02835    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02836    if (status->flags & SA_MESSAGES)
02837       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02838    if (status->flags & SA_RECENT)
02839       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02840    if (status->flags & SA_UNSEEN)
02841       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02842    if (status->flags & SA_UIDVALIDITY)
02843       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02844    if (status->flags & SA_UIDNEXT)
02845       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02846    ast_log(AST_LOG_NOTICE, "\n");
02847 }
02848 
02849 
02850 void mm_log(char *string, long errflg)
02851 {
02852    switch ((short) errflg) {
02853       case NIL:
02854          ast_debug(1, "IMAP Info: %s\n", string);
02855          break;
02856       case PARSE:
02857       case WARN:
02858          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02859          break;
02860       case ERROR:
02861          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02862          break;
02863    }
02864 }
02865 
02866 
02867 void mm_dlog(char *string)
02868 {
02869    ast_log(AST_LOG_NOTICE, "%s\n", string);
02870 }
02871 
02872 
02873 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02874 {
02875    struct ast_vm_user *vmu;
02876 
02877    ast_debug(4, "Entering callback mm_login\n");
02878 
02879    ast_copy_string(user, mb->user, MAILTMPLEN);
02880 
02881    /* We should only do this when necessary */
02882    if (!ast_strlen_zero(authpassword)) {
02883       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02884    } else {
02885       AST_LIST_TRAVERSE(&users, vmu, list) {
02886          if (!strcasecmp(mb->user, vmu->imapuser)) {
02887             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02888             break;
02889          }
02890       }
02891       if (!vmu) {
02892          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02893             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02894             free_user(vmu);
02895          }
02896       }
02897    }
02898 }
02899 
02900 
02901 void mm_critical(MAILSTREAM * stream)
02902 {
02903 }
02904 
02905 
02906 void mm_nocritical(MAILSTREAM * stream)
02907 {
02908 }
02909 
02910 
02911 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02912 {
02913    kill (getpid (), SIGSTOP);
02914    return NIL;
02915 }
02916 
02917 
02918 void mm_fatal(char *string)
02919 {
02920    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02921 }
02922 
02923 /* C-client callback to handle quota */
02924 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02925 {
02926    struct vm_state *vms;
02927    char *mailbox = stream->mailbox, *user;
02928    char buf[1024] = "";
02929    unsigned long usage = 0, limit = 0;
02930    
02931    while (pquota) {
02932       usage = pquota->usage;
02933       limit = pquota->limit;
02934       pquota = pquota->next;
02935    }
02936    
02937    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)))) {
02938       ast_log(AST_LOG_ERROR, "No state found.\n");
02939       return;
02940    }
02941 
02942    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02943 
02944    vms->quota_usage = usage;
02945    vms->quota_limit = limit;
02946 }
02947 
02948 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02949 {
02950    char *start, *eol_pnt;
02951    int taglen;
02952 
02953    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02954       return NULL;
02955 
02956    taglen = strlen(tag) + 1;
02957    if (taglen < 1)
02958       return NULL;
02959 
02960    if (!(start = strstr(header, tag)))
02961       return NULL;
02962 
02963    /* Since we can be called multiple times we should clear our buffer */
02964    memset(buf, 0, len);
02965 
02966    ast_copy_string(buf, start+taglen, len);
02967    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02968       *eol_pnt = '\0';
02969    return buf;
02970 }
02971 
02972 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02973 {
02974    char *start, *quote, *eol_pnt;
02975 
02976    if (ast_strlen_zero(mailbox))
02977       return NULL;
02978 
02979    if (!(start = strstr(mailbox, "/user=")))
02980       return NULL;
02981 
02982    ast_copy_string(buf, start+6, len);
02983 
02984    if (!(quote = strchr(buf, '\"'))) {
02985       if (!(eol_pnt = strchr(buf, '/')))
02986          eol_pnt = strchr(buf,'}');
02987       *eol_pnt = '\0';
02988       return buf;
02989    } else {
02990       eol_pnt = strchr(buf+1,'\"');
02991       *eol_pnt = '\0';
02992       return buf+1;
02993    }
02994 }
02995 
02996 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02997 {
02998    struct vm_state *vms_p;
02999 
03000    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03001    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
03002       return vms_p;
03003    }
03004    if (option_debug > 4)
03005       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
03006    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
03007       return NULL;
03008    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
03009    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
03010    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
03011    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
03012    vms_p->mailstream = NIL; /* save for access from interactive entry point */
03013    vms_p->imapversion = vmu->imapversion;
03014    if (option_debug > 4)
03015       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
03016    vms_p->updated = 1;
03017    /* set mailbox to INBOX! */
03018    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03019    init_vm_state(vms_p);
03020    vmstate_insert(vms_p);
03021    return vms_p;
03022 }
03023 
03024 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03025 {
03026    struct vmstate *vlist = NULL;
03027 
03028    if (interactive) {
03029       struct vm_state *vms;
03030       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03031       vms = pthread_getspecific(ts_vmstate.key);
03032       return vms;
03033    }
03034 
03035    AST_LIST_LOCK(&vmstates);
03036    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03037       if (!vlist->vms) {
03038          ast_debug(3, "error: vms is NULL for %s\n", user);
03039          continue;
03040       }
03041       if (vlist->vms->imapversion != imapversion) {
03042          continue;
03043       }
03044       if (!vlist->vms->imapuser) {
03045          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03046          continue;
03047       }
03048 
03049       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03050          AST_LIST_UNLOCK(&vmstates);
03051          return vlist->vms;
03052       }
03053    }
03054    AST_LIST_UNLOCK(&vmstates);
03055 
03056    ast_debug(3, "%s not found in vmstates\n", user);
03057 
03058    return NULL;
03059 }
03060 
03061 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03062 {
03063 
03064    struct vmstate *vlist = NULL;
03065    const char *local_context = S_OR(context, "default");
03066 
03067    if (interactive) {
03068       struct vm_state *vms;
03069       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03070       vms = pthread_getspecific(ts_vmstate.key);
03071       return vms;
03072    }
03073 
03074    AST_LIST_LOCK(&vmstates);
03075    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03076       if (!vlist->vms) {
03077          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03078          continue;
03079       }
03080       if (vlist->vms->imapversion != imapversion) {
03081          continue;
03082       }
03083       if (!vlist->vms->username || !vlist->vms->context) {
03084          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03085          continue;
03086       }
03087 
03088       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);
03089       
03090       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03091          ast_debug(3, "Found it!\n");
03092          AST_LIST_UNLOCK(&vmstates);
03093          return vlist->vms;
03094       }
03095    }
03096    AST_LIST_UNLOCK(&vmstates);
03097 
03098    ast_debug(3, "%s not found in vmstates\n", mailbox);
03099 
03100    return NULL;
03101 }
03102 
03103 static void vmstate_insert(struct vm_state *vms) 
03104 {
03105    struct vmstate *v;
03106    struct vm_state *altvms;
03107 
03108    /* If interactive, it probably already exists, and we should
03109       use the one we already have since it is more up to date.
03110       We can compare the username to find the duplicate */
03111    if (vms->interactive == 1) {
03112       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03113       if (altvms) {  
03114          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03115          vms->newmessages = altvms->newmessages;
03116          vms->oldmessages = altvms->oldmessages;
03117          vms->vmArrayIndex = altvms->vmArrayIndex;
03118          vms->lastmsg = altvms->lastmsg;
03119          vms->curmsg = altvms->curmsg;
03120          /* get a pointer to the persistent store */
03121          vms->persist_vms = altvms;
03122          /* Reuse the mailstream? */
03123 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03124          vms->mailstream = altvms->mailstream;
03125 #else
03126          vms->mailstream = NIL;
03127 #endif
03128       }
03129       return;
03130    }
03131 
03132    if (!(v = ast_calloc(1, sizeof(*v))))
03133       return;
03134    
03135    v->vms = vms;
03136 
03137    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03138 
03139    AST_LIST_LOCK(&vmstates);
03140    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03141    AST_LIST_UNLOCK(&vmstates);
03142 }
03143 
03144 static void vmstate_delete(struct vm_state *vms) 
03145 {
03146    struct vmstate *vc = NULL;
03147    struct vm_state *altvms = NULL;
03148 
03149    /* If interactive, we should copy pertinent info
03150       back to the persistent state (to make update immediate) */
03151    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03152       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03153       altvms->newmessages = vms->newmessages;
03154       altvms->oldmessages = vms->oldmessages;
03155       altvms->updated = 1;
03156       vms->mailstream = mail_close(vms->mailstream);
03157 
03158       /* Interactive states are not stored within the persistent list */
03159       return;
03160    }
03161    
03162    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03163    
03164    AST_LIST_LOCK(&vmstates);
03165    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03166       if (vc->vms == vms) {
03167          AST_LIST_REMOVE_CURRENT(list);
03168          break;
03169       }
03170    }
03171    AST_LIST_TRAVERSE_SAFE_END
03172    AST_LIST_UNLOCK(&vmstates);
03173    
03174    if (vc) {
03175       ast_mutex_destroy(&vc->vms->lock);
03176       ast_free(vc);
03177    }
03178    else
03179       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03180 }
03181 
03182 static void set_update(MAILSTREAM * stream) 
03183 {
03184    struct vm_state *vms;
03185    char *mailbox = stream->mailbox, *user;
03186    char buf[1024] = "";
03187 
03188    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03189       if (user && option_debug > 2)
03190          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03191       return;
03192    }
03193 
03194    ast_debug(3, "User %s mailbox set for update.\n", user);
03195 
03196    vms->updated = 1; /* Set updated flag since mailbox changed */
03197 }
03198 
03199 static void init_vm_state(struct vm_state *vms) 
03200 {
03201    int x;
03202    vms->vmArrayIndex = 0;
03203    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03204       vms->msgArray[x] = 0;
03205    }
03206    ast_mutex_init(&vms->lock);
03207 }
03208 
03209 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03210 {
03211    char *body_content;
03212    char *body_decoded;
03213    char *fn = is_intro ? vms->introfn : vms->fn;
03214    unsigned long len;
03215    unsigned long newlen;
03216    char filename[256];
03217    
03218    if (!body || body == NIL)
03219       return -1;
03220 
03221    ast_mutex_lock(&vms->lock);
03222    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03223    ast_mutex_unlock(&vms->lock);
03224    if (body_content != NIL) {
03225       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03226       /* ast_debug(1,body_content); */
03227       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03228       /* If the body of the file is empty, return an error */
03229       if (!newlen) {
03230          return -1;
03231       }
03232       write_file(filename, (char *) body_decoded, newlen);
03233    } else {
03234       ast_debug(5, "Body of message is NULL.\n");
03235       return -1;
03236    }
03237    return 0;
03238 }
03239 
03240 /*! 
03241  * \brief Get delimiter via mm_list callback 
03242  * \param stream
03243  *
03244  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03245  */
03246 /* MUTEX should already be held */
03247 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03248    char tmp[50];
03249    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03250    mail_list(stream, tmp, "*");
03251 }
03252 
03253 /*! 
03254  * \brief Check Quota for user 
03255  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03256  * \param mailbox the mailbox to check the quota for.
03257  *
03258  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03259  */
03260 static void check_quota(struct vm_state *vms, char *mailbox) {
03261    ast_mutex_lock(&vms->lock);
03262    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03263    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03264    if (vms && vms->mailstream != NULL) {
03265       imap_getquotaroot(vms->mailstream, mailbox);
03266    } else {
03267       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03268    }
03269    ast_mutex_unlock(&vms->lock);
03270 }
03271 
03272 #endif /* IMAP_STORAGE */
03273 
03274 /*! \brief Lock file path
03275  * only return failure if ast_lock_path returns 'timeout',
03276  * not if the path does not exist or any other reason
03277  */
03278 static int vm_lock_path(const char *path)
03279 {
03280    switch (ast_lock_path(path)) {
03281    case AST_LOCK_TIMEOUT:
03282       return -1;
03283    default:
03284       return 0;
03285    }
03286 }
03287 
03288 
03289 #ifdef ODBC_STORAGE
03290 struct generic_prepare_struct {
03291    char *sql;
03292    int argc;
03293    char **argv;
03294 };
03295 
03296 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03297 {
03298    struct generic_prepare_struct *gps = data;
03299    int res, i;
03300    SQLHSTMT stmt;
03301 
03302    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03303    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03304       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03305       return NULL;
03306    }
03307    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03308    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03309       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03310       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03311       return NULL;
03312    }
03313    for (i = 0; i < gps->argc; i++)
03314       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03315 
03316    return stmt;
03317 }
03318 
03319 /*!
03320  * \brief Retrieves a file from an ODBC data store.
03321  * \param dir the path to the file to be retreived.
03322  * \param msgnum the message number, such as within a mailbox folder.
03323  * 
03324  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03325  * 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.
03326  *
03327  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03328  * The output is the message information file with the name msgnum and the extension .txt
03329  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03330  * 
03331  * \return 0 on success, -1 on error.
03332  */
03333 static int retrieve_file(char *dir, int msgnum)
03334 {
03335    int x = 0;
03336    int res;
03337    int fd = -1;
03338    size_t fdlen = 0;
03339    void *fdm = MAP_FAILED;
03340    SQLSMALLINT colcount = 0;
03341    SQLHSTMT stmt;
03342    char sql[PATH_MAX];
03343    char fmt[80]="";
03344    char *c;
03345    char coltitle[256];
03346    SQLSMALLINT collen;
03347    SQLSMALLINT datatype;
03348    SQLSMALLINT decimaldigits;
03349    SQLSMALLINT nullable;
03350    SQLULEN colsize;
03351    SQLLEN colsize2;
03352    FILE *f = NULL;
03353    char rowdata[80];
03354    char fn[PATH_MAX];
03355    char full_fn[PATH_MAX];
03356    char msgnums[80];
03357    char *argv[] = { dir, msgnums };
03358    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03359 
03360    struct odbc_obj *obj;
03361    obj = ast_odbc_request_obj(odbc_database, 0);
03362    if (obj) {
03363       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03364       c = strchr(fmt, '|');
03365       if (c)
03366          *c = '\0';
03367       if (!strcasecmp(fmt, "wav49"))
03368          strcpy(fmt, "WAV");
03369       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03370       if (msgnum > -1)
03371          make_file(fn, sizeof(fn), dir, msgnum);
03372       else
03373          ast_copy_string(fn, dir, sizeof(fn));
03374 
03375       /* Create the information file */
03376       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03377       
03378       if (!(f = fopen(full_fn, "w+"))) {
03379          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03380          goto yuck;
03381       }
03382       
03383       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03384       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03385       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03386       if (!stmt) {
03387          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03388          ast_odbc_release_obj(obj);
03389          goto yuck;
03390       }
03391       res = SQLFetch(stmt);
03392       if (res == SQL_NO_DATA) {
03393          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03394          ast_odbc_release_obj(obj);
03395          goto yuck;
03396       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03397          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03398          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03399          ast_odbc_release_obj(obj);
03400          goto yuck;
03401       }
03402       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03403       if (fd < 0) {
03404          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03405          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03406          ast_odbc_release_obj(obj);
03407          goto yuck;
03408       }
03409       res = SQLNumResultCols(stmt, &colcount);
03410       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03411          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03412          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03413          ast_odbc_release_obj(obj);
03414          goto yuck;
03415       }
03416       if (f) 
03417          fprintf(f, "[message]\n");
03418       for (x = 0; x < colcount; x++) {
03419          rowdata[0] = '\0';
03420          colsize = 0;
03421          collen = sizeof(coltitle);
03422          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03423                   &datatype, &colsize, &decimaldigits, &nullable);
03424          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03425             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03426             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03427             ast_odbc_release_obj(obj);
03428             goto yuck;
03429          }
03430          if (!strcasecmp(coltitle, "recording")) {
03431             off_t offset;
03432             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03433             fdlen = colsize2;
03434             if (fd > -1) {
03435                char tmp[1]="";
03436                lseek(fd, fdlen - 1, SEEK_SET);
03437                if (write(fd, tmp, 1) != 1) {
03438                   close(fd);
03439                   fd = -1;
03440                   continue;
03441                }
03442                /* Read out in small chunks */
03443                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03444                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03445                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03446                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03447                      ast_odbc_release_obj(obj);
03448                      goto yuck;
03449                   } else {
03450                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03451                      munmap(fdm, CHUNKSIZE);
03452                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03453                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03454                         unlink(full_fn);
03455                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03456                         ast_odbc_release_obj(obj);
03457                         goto yuck;
03458                      }
03459                   }
03460                }
03461                if (truncate(full_fn, fdlen) < 0) {
03462                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03463                }
03464             }
03465          } else {
03466             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03467             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03468                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03469                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03470                ast_odbc_release_obj(obj);
03471                goto yuck;
03472             }
03473             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03474                fprintf(f, "%s=%s\n", coltitle, rowdata);
03475          }
03476       }
03477       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03478       ast_odbc_release_obj(obj);
03479    } else
03480       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03481 yuck:
03482    if (f)
03483       fclose(f);
03484    if (fd > -1)
03485       close(fd);
03486    return x - 1;
03487 }
03488 
03489 /*!
03490  * \brief Determines the highest message number in use for a given user and mailbox folder.
03491  * \param vmu 
03492  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03493  *
03494  * This method is used when mailboxes are stored in an ODBC back end.
03495  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03496  *
03497  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03498 
03499  */
03500 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03501 {
03502    int x = 0;
03503    int res;
03504    SQLHSTMT stmt;
03505    char sql[PATH_MAX];
03506    char rowdata[20];
03507    char *argv[] = { dir };
03508    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03509 
03510    struct odbc_obj *obj;
03511    obj = ast_odbc_request_obj(odbc_database, 0);
03512    if (obj) {
03513       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03514 
03515       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03516       if (!stmt) {
03517          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03518          ast_odbc_release_obj(obj);
03519          goto yuck;
03520       }
03521       res = SQLFetch(stmt);
03522       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03523          if (res == SQL_NO_DATA) {
03524             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03525          } else {
03526             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03527          }
03528 
03529          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03530          ast_odbc_release_obj(obj);
03531          goto yuck;
03532       }
03533       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03534       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03535          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03536          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03537          ast_odbc_release_obj(obj);
03538          goto yuck;
03539       }
03540       if (sscanf(rowdata, "%30d", &x) != 1)
03541          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03542       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03543       ast_odbc_release_obj(obj);
03544       return x;
03545    } else
03546       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03547 yuck:
03548    return x - 1;
03549 }
03550 
03551 /*!
03552  * \brief Determines if the specified message exists.
03553  * \param dir the folder the mailbox folder to look for messages. 
03554  * \param msgnum the message index to query for.
03555  *
03556  * This method is used when mailboxes are stored in an ODBC back end.
03557  *
03558  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03559  */
03560 static int message_exists(char *dir, int msgnum)
03561 {
03562    int x = 0;
03563    int res;
03564    SQLHSTMT stmt;
03565    char sql[PATH_MAX];
03566    char rowdata[20];
03567    char msgnums[20];
03568    char *argv[] = { dir, msgnums };
03569    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03570 
03571    struct odbc_obj *obj;
03572    obj = ast_odbc_request_obj(odbc_database, 0);
03573    if (obj) {
03574       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03575       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03576       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03577       if (!stmt) {
03578          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03579          ast_odbc_release_obj(obj);
03580          goto yuck;
03581       }
03582       res = SQLFetch(stmt);
03583       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03584          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03585          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03586          ast_odbc_release_obj(obj);
03587          goto yuck;
03588       }
03589       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03590       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03591          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03592          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03593          ast_odbc_release_obj(obj);
03594          goto yuck;
03595       }
03596       if (sscanf(rowdata, "%30d", &x) != 1)
03597          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03598       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03599       ast_odbc_release_obj(obj);
03600    } else
03601       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03602 yuck:
03603    return x;
03604 }
03605 
03606 /*!
03607  * \brief returns the number of messages found.
03608  * \param vmu
03609  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03610  *
03611  * This method is used when mailboxes are stored in an ODBC back end.
03612  *
03613  * \return The count of messages being zero or more, less than zero on error.
03614  */
03615 static int count_messages(struct ast_vm_user *vmu, char *dir)
03616 {
03617    int x = 0;
03618    int res;
03619    SQLHSTMT stmt;
03620    char sql[PATH_MAX];
03621    char rowdata[20];
03622    char *argv[] = { dir };
03623    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03624 
03625    struct odbc_obj *obj;
03626    obj = ast_odbc_request_obj(odbc_database, 0);
03627    if (obj) {
03628       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03629       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03630       if (!stmt) {
03631          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03632          ast_odbc_release_obj(obj);
03633          goto yuck;
03634       }
03635       res = SQLFetch(stmt);
03636       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03637          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03638          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03639          ast_odbc_release_obj(obj);
03640          goto yuck;
03641       }
03642       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03643       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03644          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03645          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03646          ast_odbc_release_obj(obj);
03647          goto yuck;
03648       }
03649       if (sscanf(rowdata, "%30d", &x) != 1)
03650          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03651       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03652       ast_odbc_release_obj(obj);
03653       return x;
03654    } else
03655       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03656 yuck:
03657    return x - 1;
03658 
03659 }
03660 
03661 /*!
03662  * \brief Deletes a message from the mailbox folder.
03663  * \param sdir The mailbox folder to work in.
03664  * \param smsg The message index to be deleted.
03665  *
03666  * This method is used when mailboxes are stored in an ODBC back end.
03667  * The specified message is directly deleted from the database 'voicemessages' table.
03668  * 
03669  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03670  */
03671 static void delete_file(const char *sdir, int smsg)
03672 {
03673    SQLHSTMT stmt;
03674    char sql[PATH_MAX];
03675    char msgnums[20];
03676    char *argv[] = { NULL, msgnums };
03677    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03678    struct odbc_obj *obj;
03679 
03680    argv[0] = ast_strdupa(sdir);
03681 
03682    obj = ast_odbc_request_obj(odbc_database, 0);
03683    if (obj) {
03684       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03685       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03686       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03687       if (!stmt)
03688          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03689       else
03690          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03691       ast_odbc_release_obj(obj);
03692    } else
03693       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03694    return;  
03695 }
03696 
03697 /*!
03698  * \brief Copies a voicemail from one mailbox to another.
03699  * \param sdir the folder for which to look for the message to be copied.
03700  * \param smsg the index of the message to be copied.
03701  * \param ddir the destination folder to copy the message into.
03702  * \param dmsg the index to be used for the copied message.
03703  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03704  * \param dmailboxcontext The context for the destination user.
03705  *
03706  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03707  */
03708 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03709 {
03710    SQLHSTMT stmt;
03711    char sql[512];
03712    char msgnums[20];
03713    char msgnumd[20];
03714    struct odbc_obj *obj;
03715    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03716    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03717 
03718    delete_file(ddir, dmsg);
03719    obj = ast_odbc_request_obj(odbc_database, 0);
03720    if (obj) {
03721       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03722       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03723       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);
03724       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03725       if (!stmt)
03726          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03727       else
03728          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03729       ast_odbc_release_obj(obj);
03730    } else
03731       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03732    return;  
03733 }
03734 
03735 struct insert_data {
03736    char *sql;
03737    const char *dir;
03738    const char *msgnums;
03739    void *data;
03740    SQLLEN datalen;
03741    SQLLEN indlen;
03742    const char *context;
03743    const char *macrocontext;
03744    const char *callerid;
03745    const char *origtime;
03746    const char *duration;
03747    const char *mailboxuser;
03748    const char *mailboxcontext;
03749    const char *category;
03750    const char *flag;
03751 };
03752 
03753 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03754 {
03755    struct insert_data *data = vdata;
03756    int res;
03757    SQLHSTMT stmt;
03758 
03759    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03760    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03761       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03762       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03763       return NULL;
03764    }
03765 
03766    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03767    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03768    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03769    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03770    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03771    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03772    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03773    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03774    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03775    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03776    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03777    if (!ast_strlen_zero(data->category)) {
03778       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03779    }
03780    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03781    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03782       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03783       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03784       return NULL;
03785    }
03786 
03787    return stmt;
03788 }
03789 
03790 /*!
03791  * \brief Stores a voicemail into the database.
03792  * \param dir the folder the mailbox folder to store the message.
03793  * \param mailboxuser the user owning the mailbox folder.
03794  * \param mailboxcontext
03795  * \param msgnum the message index for the message to be stored.
03796  *
03797  * This method is used when mailboxes are stored in an ODBC back end.
03798  * The message sound file and information file is looked up on the file system. 
03799  * A SQL query is invoked to store the message into the (MySQL) database.
03800  *
03801  * \return the zero on success -1 on error.
03802  */
03803 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03804 {
03805    int res = 0;
03806    int fd = -1;
03807    void *fdm = MAP_FAILED;
03808    off_t fdlen = -1;
03809    SQLHSTMT stmt;
03810    char sql[PATH_MAX];
03811    char msgnums[20];
03812    char fn[PATH_MAX];
03813    char full_fn[PATH_MAX];
03814    char fmt[80]="";
03815    char *c;
03816    struct ast_config *cfg = NULL;
03817    struct odbc_obj *obj;
03818    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03819       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03820    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03821 
03822    delete_file(dir, msgnum);
03823    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03824       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03825       return -1;
03826    }
03827 
03828    do {
03829       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03830       c = strchr(fmt, '|');
03831       if (c)
03832          *c = '\0';
03833       if (!strcasecmp(fmt, "wav49"))
03834          strcpy(fmt, "WAV");
03835       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03836       if (msgnum > -1)
03837          make_file(fn, sizeof(fn), dir, msgnum);
03838       else
03839          ast_copy_string(fn, dir, sizeof(fn));
03840       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03841       cfg = ast_config_load(full_fn, config_flags);
03842       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03843       fd = open(full_fn, O_RDWR);
03844       if (fd < 0) {
03845          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03846          res = -1;
03847          break;
03848       }
03849       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03850          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03851             idata.context = "";
03852          }
03853          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03854             idata.macrocontext = "";
03855          }
03856          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03857             idata.callerid = "";
03858          }
03859          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03860             idata.origtime = "";
03861          }
03862          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03863             idata.duration = "";
03864          }
03865          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03866             idata.category = "";
03867          }
03868          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03869             idata.flag = "";
03870          }
03871       }
03872       fdlen = lseek(fd, 0, SEEK_END);
03873       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
03874          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
03875          res = -1;
03876          break;
03877       }
03878       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03879       if (fdm == MAP_FAILED) {
03880          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
03881          res = -1;
03882          break;
03883       } 
03884       idata.data = fdm;
03885       idata.datalen = idata.indlen = fdlen;
03886 
03887       if (!ast_strlen_zero(idata.category)) 
03888          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03889       else
03890          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03891 
03892       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03893          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03894       } else {
03895          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03896          res = -1;
03897       }
03898    } while (0);
03899    if (obj) {
03900       ast_odbc_release_obj(obj);
03901    }
03902    if (cfg)
03903       ast_config_destroy(cfg);
03904    if (fdm != MAP_FAILED)
03905       munmap(fdm, fdlen);
03906    if (fd > -1)
03907       close(fd);
03908    return res;
03909 }
03910 
03911 /*!
03912  * \brief Renames a message in a mailbox folder.
03913  * \param sdir The folder of the message to be renamed.
03914  * \param smsg The index of the message to be renamed.
03915  * \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.
03916  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03917  * \param ddir The destination folder for the message to be renamed into
03918  * \param dmsg The destination message for the message to be renamed.
03919  *
03920  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03921  * 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.
03922  * 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.
03923  */
03924 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03925 {
03926    SQLHSTMT stmt;
03927    char sql[PATH_MAX];
03928    char msgnums[20];
03929    char msgnumd[20];
03930    struct odbc_obj *obj;
03931    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03932    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03933 
03934    delete_file(ddir, dmsg);
03935    obj = ast_odbc_request_obj(odbc_database, 0);
03936    if (obj) {
03937       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03938       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03939       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03940       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03941       if (!stmt)
03942          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03943       else
03944          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03945       ast_odbc_release_obj(obj);
03946    } else
03947       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03948    return;  
03949 }
03950 
03951 /*!
03952  * \brief Removes a voicemail message file.
03953  * \param dir the path to the message file.
03954  * \param msgnum the unique number for the message within the mailbox.
03955  *
03956  * Removes the message content file and the information file.
03957  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03958  * Typical use is to clean up after a RETRIEVE operation. 
03959  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03960  * \return zero on success, -1 on error.
03961  */
03962 static int remove_file(char *dir, int msgnum)
03963 {
03964    char fn[PATH_MAX];
03965    char full_fn[PATH_MAX];
03966    char msgnums[80];
03967    
03968    if (msgnum > -1) {
03969       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03970       make_file(fn, sizeof(fn), dir, msgnum);
03971    } else
03972       ast_copy_string(fn, dir, sizeof(fn));
03973    ast_filedelete(fn, NULL);  
03974    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03975    unlink(full_fn);
03976    return 0;
03977 }
03978 #else
03979 #ifndef IMAP_STORAGE
03980 /*!
03981  * \brief Find all .txt files - even if they are not in sequence from 0000.
03982  * \param vmu
03983  * \param dir
03984  *
03985  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03986  *
03987  * \return the count of messages, zero or more.
03988  */
03989 static int count_messages(struct ast_vm_user *vmu, char *dir)
03990 {
03991 
03992    int vmcount = 0;
03993    DIR *vmdir = NULL;
03994    struct dirent *vment = NULL;
03995 
03996    if (vm_lock_path(dir))
03997       return ERROR_LOCK_PATH;
03998 
03999    if ((vmdir = opendir(dir))) {
04000       while ((vment = readdir(vmdir))) {
04001          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
04002             vmcount++;
04003          }
04004       }
04005       closedir(vmdir);
04006    }
04007    ast_unlock_path(dir);
04008    
04009    return vmcount;
04010 }
04011 
04012 /*!
04013  * \brief Renames a message in a mailbox folder.
04014  * \param sfn The path to the mailbox information and data file to be renamed.
04015  * \param dfn The path for where the message data and information files will be renamed to.
04016  *
04017  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04018  */
04019 static void rename_file(char *sfn, char *dfn)
04020 {
04021    char stxt[PATH_MAX];
04022    char dtxt[PATH_MAX];
04023    ast_filerename(sfn, dfn, NULL);
04024    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04025    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04026    if (ast_check_realtime("voicemail_data")) {
04027       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04028    }
04029    rename(stxt, dtxt);
04030 }
04031 
04032 /*! 
04033  * \brief Determines the highest message number in use for a given user and mailbox folder.
04034  * \param vmu 
04035  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04036  *
04037  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04038  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04039  *
04040  * \note Should always be called with a lock already set on dir.
04041  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04042  */
04043 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04044 {
04045    int x;
04046    unsigned char map[MAXMSGLIMIT] = "";
04047    DIR *msgdir;
04048    struct dirent *msgdirent;
04049    int msgdirint;
04050    char extension[4];
04051    int stopcount = 0;
04052 
04053    /* Reading the entire directory into a file map scales better than
04054     * doing a stat repeatedly on a predicted sequence.  I suspect this
04055     * is partially due to stat(2) internally doing a readdir(2) itself to
04056     * find each file. */
04057    if (!(msgdir = opendir(dir))) {
04058       return -1;
04059    }
04060 
04061    while ((msgdirent = readdir(msgdir))) {
04062       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04063          map[msgdirint] = 1;
04064          stopcount++;
04065          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04066       }
04067    }
04068    closedir(msgdir);
04069 
04070    for (x = 0; x < vmu->maxmsg; x++) {
04071       if (map[x] == 1) {
04072          stopcount--;
04073       } else if (map[x] == 0 && !stopcount) {
04074          break;
04075       }
04076    }
04077 
04078    return x - 1;
04079 }
04080 
04081 #endif /* #ifndef IMAP_STORAGE */
04082 #endif /* #else of #ifdef ODBC_STORAGE */
04083 #ifndef IMAP_STORAGE
04084 /*!
04085  * \brief Utility function to copy a file.
04086  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04087  * \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.
04088  *
04089  * 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.
04090  * The copy operation copies up to 4096 bytes at once.
04091  *
04092  * \return zero on success, -1 on error.
04093  */
04094 static int copy(char *infile, char *outfile)
04095 {
04096    int ifd;
04097    int ofd;
04098    int res;
04099    int len;
04100    char buf[4096];
04101 
04102 #ifdef HARDLINK_WHEN_POSSIBLE
04103    /* Hard link if possible; saves disk space & is faster */
04104    if (link(infile, outfile)) {
04105 #endif
04106       if ((ifd = open(infile, O_RDONLY)) < 0) {
04107          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04108          return -1;
04109       }
04110       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04111          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04112          close(ifd);
04113          return -1;
04114       }
04115       do {
04116          len = read(ifd, buf, sizeof(buf));
04117          if (len < 0) {
04118             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04119             close(ifd);
04120             close(ofd);
04121             unlink(outfile);
04122          } else if (len) {
04123             res = write(ofd, buf, len);
04124             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04125                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04126                close(ifd);
04127                close(ofd);
04128                unlink(outfile);
04129             }
04130          }
04131       } while (len);
04132       close(ifd);
04133       close(ofd);
04134       return 0;
04135 #ifdef HARDLINK_WHEN_POSSIBLE
04136    } else {
04137       /* Hard link succeeded */
04138       return 0;
04139    }
04140 #endif
04141 }
04142 
04143 /*!
04144  * \brief Copies a voicemail information (envelope) file.
04145  * \param frompath
04146  * \param topath 
04147  *
04148  * Every voicemail has the data (.wav) file, and the information file.
04149  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04150  * This is used by the COPY macro when not using IMAP storage.
04151  */
04152 static void copy_plain_file(char *frompath, char *topath)
04153 {
04154    char frompath2[PATH_MAX], topath2[PATH_MAX];
04155    struct ast_variable *tmp,*var = NULL;
04156    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04157    ast_filecopy(frompath, topath, NULL);
04158    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04159    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04160    if (ast_check_realtime("voicemail_data")) {
04161       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04162       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04163       for (tmp = var; tmp; tmp = tmp->next) {
04164          if (!strcasecmp(tmp->name, "origmailbox")) {
04165             origmailbox = tmp->value;
04166          } else if (!strcasecmp(tmp->name, "context")) {
04167             context = tmp->value;
04168          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04169             macrocontext = tmp->value;
04170          } else if (!strcasecmp(tmp->name, "exten")) {
04171             exten = tmp->value;
04172          } else if (!strcasecmp(tmp->name, "priority")) {
04173             priority = tmp->value;
04174          } else if (!strcasecmp(tmp->name, "callerchan")) {
04175             callerchan = tmp->value;
04176          } else if (!strcasecmp(tmp->name, "callerid")) {
04177             callerid = tmp->value;
04178          } else if (!strcasecmp(tmp->name, "origdate")) {
04179             origdate = tmp->value;
04180          } else if (!strcasecmp(tmp->name, "origtime")) {
04181             origtime = tmp->value;
04182          } else if (!strcasecmp(tmp->name, "category")) {
04183             category = tmp->value;
04184          } else if (!strcasecmp(tmp->name, "duration")) {
04185             duration = tmp->value;
04186          }
04187       }
04188       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);
04189    }
04190    copy(frompath2, topath2);
04191    ast_variables_destroy(var);
04192 }
04193 #endif
04194 
04195 /*! 
04196  * \brief Removes the voicemail sound and information file.
04197  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04198  *
04199  * This is used by the DELETE macro when voicemails are stored on the file system.
04200  *
04201  * \return zero on success, -1 on error.
04202  */
04203 static int vm_delete(char *file)
04204 {
04205    char *txt;
04206    int txtsize = 0;
04207 
04208    txtsize = (strlen(file) + 5)*sizeof(char);
04209    txt = ast_alloca(txtsize);
04210    /* Sprintf here would safe because we alloca'd exactly the right length,
04211     * but trying to eliminate all sprintf's anyhow
04212     */
04213    if (ast_check_realtime("voicemail_data")) {
04214       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04215    }
04216    snprintf(txt, txtsize, "%s.txt", file);
04217    unlink(txt);
04218    return ast_filedelete(file, NULL);
04219 }
04220 
04221 /*!
04222  * \brief utility used by inchar(), for base_encode()
04223  */
04224 static int inbuf(struct baseio *bio, FILE *fi)
04225 {
04226    int l;
04227 
04228    if (bio->ateof)
04229       return 0;
04230 
04231    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04232       if (ferror(fi))
04233          return -1;
04234 
04235       bio->ateof = 1;
04236       return 0;
04237    }
04238 
04239    bio->iolen = l;
04240    bio->iocp = 0;
04241 
04242    return 1;
04243 }
04244 
04245 /*!
04246  * \brief utility used by base_encode()
04247  */
04248 static int inchar(struct baseio *bio, FILE *fi)
04249 {
04250    if (bio->iocp>=bio->iolen) {
04251       if (!inbuf(bio, fi))
04252          return EOF;
04253    }
04254 
04255    return bio->iobuf[bio->iocp++];
04256 }
04257 
04258 /*!
04259  * \brief utility used by base_encode()
04260  */
04261 static int ochar(struct baseio *bio, int c, FILE *so)
04262 {
04263    if (bio->linelength >= BASELINELEN) {
04264       if (fputs(ENDL, so) == EOF) {
04265          return -1;
04266       }
04267 
04268       bio->linelength = 0;
04269    }
04270 
04271    if (putc(((unsigned char) c), so) == EOF) {
04272       return -1;
04273    }
04274 
04275    bio->linelength++;
04276 
04277    return 1;
04278 }
04279 
04280 /*!
04281  * \brief Performs a base 64 encode algorithm on the contents of a File
04282  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04283  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04284  *
04285  * 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 ?
04286  *
04287  * \return zero on success, -1 on error.
04288  */
04289 static int base_encode(char *filename, FILE *so)
04290 {
04291    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04292       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04293       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04294       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04295    int i, hiteof = 0;
04296    FILE *fi;
04297    struct baseio bio;
04298 
04299    memset(&bio, 0, sizeof(bio));
04300    bio.iocp = BASEMAXINLINE;
04301 
04302    if (!(fi = fopen(filename, "rb"))) {
04303       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04304       return -1;
04305    }
04306 
04307    while (!hiteof){
04308       unsigned char igroup[3], ogroup[4];
04309       int c, n;
04310 
04311       memset(igroup, 0, sizeof(igroup));
04312 
04313       for (n = 0; n < 3; n++) {
04314          if ((c = inchar(&bio, fi)) == EOF) {
04315             hiteof = 1;
04316             break;
04317          }
04318 
04319          igroup[n] = (unsigned char) c;
04320       }
04321 
04322       if (n > 0) {
04323          ogroup[0]= dtable[igroup[0] >> 2];
04324          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04325          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04326          ogroup[3]= dtable[igroup[2] & 0x3F];
04327 
04328          if (n < 3) {
04329             ogroup[3] = '=';
04330 
04331             if (n < 2)
04332                ogroup[2] = '=';
04333          }
04334 
04335          for (i = 0; i < 4; i++)
04336             ochar(&bio, ogroup[i], so);
04337       }
04338    }
04339 
04340    fclose(fi);
04341    
04342    if (fputs(ENDL, so) == EOF) {
04343       return 0;
04344    }
04345 
04346    return 1;
04347 }
04348 
04349 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)
04350 {
04351    char callerid[256];
04352    char num[12];
04353    char fromdir[256], fromfile[256];
04354    struct ast_config *msg_cfg;
04355    const char *origcallerid, *origtime;
04356    char origcidname[80], origcidnum[80], origdate[80];
04357    int inttime;
04358    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04359 
04360    /* Prepare variables for substitution in email body and subject */
04361    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04362    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04363    snprintf(num, sizeof(num), "%d", msgnum);
04364    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04365    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04366    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04367    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04368       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04369    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04370    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04371    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04372    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04373    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04374 
04375    /* Retrieve info from VM attribute file */
04376    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04377    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04378    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04379       strcat(fromfile, ".txt");
04380    }
04381    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04382       if (option_debug > 0) {
04383          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04384       }
04385       return;
04386    }
04387 
04388    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04389       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04390       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04391       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04392       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04393    }
04394 
04395    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04396       struct timeval tv = { inttime, };
04397       struct ast_tm tm;
04398       ast_localtime(&tv, &tm, NULL);
04399       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04400       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04401    }
04402    ast_config_destroy(msg_cfg);
04403 }
04404 
04405 /*!
04406  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04407  * \param from The string to work with.
04408  * \param buf The buffer into which to write the modified quoted string.
04409  * \param maxlen Always zero, but see \see ast_str
04410  * 
04411  * \return The destination string with quotes wrapped on it (the to field).
04412  */
04413 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04414 {
04415    const char *ptr;
04416 
04417    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04418    ast_str_set(buf, maxlen, "\"");
04419    for (ptr = from; *ptr; ptr++) {
04420       if (*ptr == '"' || *ptr == '\\') {
04421          ast_str_append(buf, maxlen, "\\%c", *ptr);
04422       } else {
04423          ast_str_append(buf, maxlen, "%c", *ptr);
04424       }
04425    }
04426    ast_str_append(buf, maxlen, "\"");
04427 
04428    return ast_str_buffer(*buf);
04429 }
04430 
04431 /*! \brief
04432  * fill in *tm for current time according to the proper timezone, if any.
04433  * \return tm so it can be used as a function argument.
04434  */
04435 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04436 {
04437    const struct vm_zone *z = NULL;
04438    struct timeval t = ast_tvnow();
04439 
04440    /* Does this user have a timezone specified? */
04441    if (!ast_strlen_zero(vmu->zonetag)) {
04442       /* Find the zone in the list */
04443       AST_LIST_LOCK(&zones);
04444       AST_LIST_TRAVERSE(&zones, z, list) {
04445          if (!strcmp(z->name, vmu->zonetag))
04446             break;
04447       }
04448       AST_LIST_UNLOCK(&zones);
04449    }
04450    ast_localtime(&t, tm, z ? z->timezone : NULL);
04451    return tm;
04452 }
04453 
04454 /*!\brief Check if the string would need encoding within the MIME standard, to
04455  * avoid confusing certain mail software that expects messages to be 7-bit
04456  * clean.
04457  */
04458 static int check_mime(const char *str)
04459 {
04460    for (; *str; str++) {
04461       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04462          return 1;
04463       }
04464    }
04465    return 0;
04466 }
04467 
04468 /*!\brief Encode a string according to the MIME rules for encoding strings
04469  * that are not 7-bit clean or contain control characters.
04470  *
04471  * Additionally, if the encoded string would exceed the MIME limit of 76
04472  * characters per line, then the encoding will be broken up into multiple
04473  * sections, separated by a space character, in order to facilitate
04474  * breaking up the associated header across multiple lines.
04475  *
04476  * \param end An expandable buffer for holding the result
04477  * \param maxlen Always zero, but see \see ast_str
04478  * \param start A string to be encoded
04479  * \param preamble The length of the first line already used for this string,
04480  * to ensure that each line maintains a maximum length of 76 chars.
04481  * \param postamble the length of any additional characters appended to the
04482  * line, used to ensure proper field wrapping.
04483  * \retval The encoded string.
04484  */
04485 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04486 {
04487    struct ast_str *tmp = ast_str_alloca(80);
04488    int first_section = 1;
04489 
04490    ast_str_reset(*end);
04491    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04492    for (; *start; start++) {
04493       int need_encoding = 0;
04494       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04495          need_encoding = 1;
04496       }
04497       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04498          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04499          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04500          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04501          /* Start new line */
04502          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04503          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04504          first_section = 0;
04505       }
04506       if (need_encoding && *start == ' ') {
04507          ast_str_append(&tmp, -1, "_");
04508       } else if (need_encoding) {
04509          ast_str_append(&tmp, -1, "=%hhX", *start);
04510       } else {
04511          ast_str_append(&tmp, -1, "%c", *start);
04512       }
04513    }
04514    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04515    return ast_str_buffer(*end);
04516 }
04517 
04518 /*!
04519  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04520  * \param p The output file to generate the email contents into.
04521  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04522  * \param vmu The voicemail user who is sending the voicemail.
04523  * \param msgnum The message index in the mailbox folder.
04524  * \param context 
04525  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04526  * \param fromfolder
04527  * \param cidnum The caller ID number.
04528  * \param cidname The caller ID name.
04529  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04530  * \param attach2 
04531  * \param format The message sound file format. i.e. .wav
04532  * \param duration The time of the message content, in seconds.
04533  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04534  * \param chan
04535  * \param category
04536  * \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.
04537  * \param flag
04538  *
04539  * 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.
04540  */
04541 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)
04542 {
04543    char date[256];
04544    char host[MAXHOSTNAMELEN] = "";
04545    char who[256];
04546    char bound[256];
04547    char dur[256];
04548    struct ast_tm tm;
04549    char enc_cidnum[256] = "", enc_cidname[256] = "";
04550    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04551    char *greeting_attachment; 
04552    char filename[256];
04553 
04554    if (!str1 || !str2) {
04555       ast_free(str1);
04556       ast_free(str2);
04557       return;
04558    }
04559 
04560    if (cidnum) {
04561       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04562    }
04563    if (cidname) {
04564       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04565    }
04566    gethostname(host, sizeof(host) - 1);
04567 
04568    if (strchr(srcemail, '@')) {
04569       ast_copy_string(who, srcemail, sizeof(who));
04570    } else {
04571       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04572    }
04573 
04574    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04575    if (greeting_attachment) {
04576       *greeting_attachment++ = '\0';
04577    }
04578 
04579    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04580    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04581    fprintf(p, "Date: %s" ENDL, date);
04582 
04583    /* Set date format for voicemail mail */
04584    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04585 
04586    if (!ast_strlen_zero(fromstring)) {
04587       struct ast_channel *ast;
04588       if ((ast = ast_dummy_channel_alloc())) {
04589          char *ptr;
04590          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04591          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04592 
04593          if (check_mime(ast_str_buffer(str1))) {
04594             int first_line = 1;
04595             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04596             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04597                *ptr = '\0';
04598                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", 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 ? "From:" : "", ast_str_buffer(str2), who);
04604          } else {
04605             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04606          }
04607          ast = ast_channel_unref(ast);
04608       } else {
04609          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04610       }
04611    } else {
04612       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04613    }
04614 
04615    if (check_mime(vmu->fullname)) {
04616       int first_line = 1;
04617       char *ptr;
04618       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04619       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04620          *ptr = '\0';
04621          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04622          first_line = 0;
04623          /* Substring is smaller, so this will never grow */
04624          ast_str_set(&str2, 0, "%s", ptr + 1);
04625       }
04626       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04627    } else {
04628       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04629    }
04630 
04631    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04632       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04633       struct ast_channel *ast;
04634       if ((ast = ast_dummy_channel_alloc())) {
04635          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04636          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04637          if (check_mime(ast_str_buffer(str1))) {
04638             int first_line = 1;
04639             char *ptr;
04640             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04641             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04642                *ptr = '\0';
04643                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04644                first_line = 0;
04645                /* Substring is smaller, so this will never grow */
04646                ast_str_set(&str2, 0, "%s", ptr + 1);
04647             }
04648             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04649          } else {
04650             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04651          }
04652          ast = ast_channel_unref(ast);
04653       } else {
04654          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04655       }
04656    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04657       if (ast_strlen_zero(flag)) {
04658          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04659       } else {
04660          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04661       }
04662    } else {
04663       if (ast_strlen_zero(flag)) {
04664          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04665       } else {
04666          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04667       }
04668    }
04669 
04670    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04671       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04672    if (imap) {
04673       /* additional information needed for IMAP searching */
04674       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04675       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04676       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04677       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04678 #ifdef IMAP_STORAGE
04679       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04680 #else
04681       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04682 #endif
04683       /* flag added for Urgent */
04684       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04685       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04686       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04687       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04688       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04689       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04690       if (!ast_strlen_zero(category)) {
04691          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04692       } else {
04693          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04694       }
04695       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04696       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04697       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04698    }
04699    if (!ast_strlen_zero(cidnum)) {
04700       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04701    }
04702    if (!ast_strlen_zero(cidname)) {
04703       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04704    }
04705    fprintf(p, "MIME-Version: 1.0" ENDL);
04706    if (attach_user_voicemail) {
04707       /* Something unique. */
04708       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04709          (int) getpid(), (unsigned int) ast_random());
04710 
04711       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04712       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04713       fprintf(p, "--%s" ENDL, bound);
04714    }
04715    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04716    if (emailbody || vmu->emailbody) {
04717       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04718       struct ast_channel *ast;
04719       if ((ast = ast_dummy_channel_alloc())) {
04720          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04721          ast_str_substitute_variables(&str1, 0, ast, e_body);
04722 #ifdef IMAP_STORAGE
04723             {
04724                /* Convert body to native line terminators for IMAP backend */
04725                char *line = ast_str_buffer(str1), *next;
04726                do {
04727                   /* Terminate line before outputting it to the file */
04728                   if ((next = strchr(line, '\n'))) {
04729                      *next++ = '\0';
04730                   }
04731                   fprintf(p, "%s" ENDL, line);
04732                   line = next;
04733                } while (!ast_strlen_zero(line));
04734             }
04735 #else
04736          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04737 #endif
04738          ast = ast_channel_unref(ast);
04739       } else {
04740          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04741       }
04742    } else if (msgnum > -1) {
04743       if (strcmp(vmu->mailbox, mailbox)) {
04744          /* Forwarded type */
04745          struct ast_config *msg_cfg;
04746          const char *v;
04747          int inttime;
04748          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04749          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04750          /* Retrieve info from VM attribute file */
04751          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04752          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04753          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04754             strcat(fromfile, ".txt");
04755          }
04756          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04757             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04758                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04759             }
04760 
04761             /* You might be tempted to do origdate, except that a) it's in the wrong
04762              * format, and b) it's missing for IMAP recordings. */
04763             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04764                struct timeval tv = { inttime, };
04765                struct ast_tm tm;
04766                ast_localtime(&tv, &tm, NULL);
04767                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04768             }
04769             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04770                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04771                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04772                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04773                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04774                date, origcallerid, origdate);
04775             ast_config_destroy(msg_cfg);
04776          } else {
04777             goto plain_message;
04778          }
04779       } else {
04780 plain_message:
04781          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04782             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04783             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04784             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04785             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04786       }
04787    } else {
04788       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04789             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04790    }
04791 
04792    if (imap || attach_user_voicemail) {
04793       if (!ast_strlen_zero(attach2)) {
04794          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04795          ast_debug(5, "creating second attachment filename %s\n", filename);
04796          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04797          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04798          ast_debug(5, "creating attachment filename %s\n", filename);
04799          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04800       } else {
04801          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04802          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04803          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04804       }
04805    }
04806    ast_free(str1);
04807    ast_free(str2);
04808 }
04809 
04810 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)
04811 {
04812    char tmpdir[256], newtmp[256];
04813    char fname[256];
04814    char tmpcmd[256];
04815    int tmpfd = -1;
04816    int soxstatus = 0;
04817 
04818    /* Eww. We want formats to tell us their own MIME type */
04819    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04820 
04821    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04822       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04823       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04824       tmpfd = mkstemp(newtmp);
04825       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04826       ast_debug(3, "newtmp: %s\n", newtmp);
04827       if (tmpfd > -1) {
04828          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04829          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04830             attach = newtmp;
04831             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04832          } else {
04833             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04834                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04835             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04836          }
04837       }
04838    }
04839    fprintf(p, "--%s" ENDL, bound);
04840    if (msgnum > -1)
04841       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04842    else
04843       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04844    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04845    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04846    if (msgnum > -1)
04847       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04848    else
04849       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04850    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04851    base_encode(fname, p);
04852    if (last)
04853       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04854    if (tmpfd > -1) {
04855       if (soxstatus == 0) {
04856          unlink(fname);
04857       }
04858       close(tmpfd);
04859       unlink(newtmp);
04860    }
04861    return 0;
04862 }
04863 
04864 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)
04865 {
04866    FILE *p = NULL;
04867    char tmp[80] = "/tmp/astmail-XXXXXX";
04868    char tmp2[256];
04869    char *stringp;
04870 
04871    if (vmu && ast_strlen_zero(vmu->email)) {
04872       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04873       return(0);
04874    }
04875 
04876    /* Mail only the first format */
04877    format = ast_strdupa(format);
04878    stringp = format;
04879    strsep(&stringp, "|");
04880 
04881    if (!strcmp(format, "wav49"))
04882       format = "WAV";
04883    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));
04884    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04885       command hangs */
04886    if ((p = vm_mkftemp(tmp)) == NULL) {
04887       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04888       return -1;
04889    } else {
04890       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04891       fclose(p);
04892       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04893       ast_safe_system(tmp2);
04894       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04895    }
04896    return 0;
04897 }
04898 
04899 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)
04900 {
04901    char enc_cidnum[256], enc_cidname[256];
04902    char date[256];
04903    char host[MAXHOSTNAMELEN] = "";
04904    char who[256];
04905    char dur[PATH_MAX];
04906    char tmp[80] = "/tmp/astmail-XXXXXX";
04907    char tmp2[PATH_MAX];
04908    struct ast_tm tm;
04909    FILE *p;
04910    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04911 
04912    if (!str1 || !str2) {
04913       ast_free(str1);
04914       ast_free(str2);
04915       return -1;
04916    }
04917 
04918    if (cidnum) {
04919       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04920    }
04921    if (cidname) {
04922       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04923    }
04924 
04925    if ((p = vm_mkftemp(tmp)) == NULL) {
04926       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04927       ast_free(str1);
04928       ast_free(str2);
04929       return -1;
04930    }
04931    gethostname(host, sizeof(host)-1);
04932    if (strchr(srcemail, '@')) {
04933       ast_copy_string(who, srcemail, sizeof(who));
04934    } else {
04935       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04936    }
04937    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04938    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04939    fprintf(p, "Date: %s\n", date);
04940 
04941    /* Reformat for custom pager format */
04942    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04943 
04944    if (!ast_strlen_zero(pagerfromstring)) {
04945       struct ast_channel *ast;
04946       if ((ast = ast_dummy_channel_alloc())) {
04947          char *ptr;
04948          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04949          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04950 
04951          if (check_mime(ast_str_buffer(str1))) {
04952             int first_line = 1;
04953             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04954             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04955                *ptr = '\0';
04956                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", 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 ? "From:" : "", ast_str_buffer(str2), who);
04962          } else {
04963             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04964          }
04965          ast = ast_channel_unref(ast);
04966       } else {
04967          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04968       }
04969    } else {
04970       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04971    }
04972 
04973    if (check_mime(vmu->fullname)) {
04974       int first_line = 1;
04975       char *ptr;
04976       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04977       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04978          *ptr = '\0';
04979          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04980          first_line = 0;
04981          /* Substring is smaller, so this will never grow */
04982          ast_str_set(&str2, 0, "%s", ptr + 1);
04983       }
04984       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04985    } else {
04986       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04987    }
04988 
04989    if (!ast_strlen_zero(pagersubject)) {
04990       struct ast_channel *ast;
04991       if ((ast = ast_dummy_channel_alloc())) {
04992          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04993          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04994          if (check_mime(ast_str_buffer(str1))) {
04995             int first_line = 1;
04996             char *ptr;
04997             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04998             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04999                *ptr = '\0';
05000                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05001                first_line = 0;
05002                /* Substring is smaller, so this will never grow */
05003                ast_str_set(&str2, 0, "%s", ptr + 1);
05004             }
05005             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05006          } else {
05007             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
05008          }
05009          ast = ast_channel_unref(ast);
05010       } else {
05011          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05012       }
05013    } else {
05014       if (ast_strlen_zero(flag)) {
05015          fprintf(p, "Subject: New VM\n\n");
05016       } else {
05017          fprintf(p, "Subject: New %s VM\n\n", flag);
05018       }
05019    }
05020 
05021    if (pagerbody) {
05022       struct ast_channel *ast;
05023       if ((ast = ast_dummy_channel_alloc())) {
05024          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05025          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05026          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05027          ast = ast_channel_unref(ast);
05028       } else {
05029          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05030       }
05031    } else {
05032       fprintf(p, "New %s long %s msg in box %s\n"
05033             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05034    }
05035 
05036    fclose(p);
05037    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05038    ast_safe_system(tmp2);
05039    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05040    ast_free(str1);
05041    ast_free(str2);
05042    return 0;
05043 }
05044 
05045 /*!
05046  * \brief Gets the current date and time, as formatted string.
05047  * \param s The buffer to hold the output formatted date.
05048  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05049  * 
05050  * The date format string used is "%a %b %e %r UTC %Y".
05051  * 
05052  * \return zero on success, -1 on error.
05053  */
05054 static int get_date(char *s, int len)
05055 {
05056    struct ast_tm tm;
05057    struct timeval t = ast_tvnow();
05058    
05059    ast_localtime(&t, &tm, "UTC");
05060 
05061    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05062 }
05063 
05064 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05065 {
05066    int res;
05067    char fn[PATH_MAX];
05068    char dest[PATH_MAX];
05069 
05070    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05071 
05072    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05073       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05074       return -1;
05075    }
05076 
05077    RETRIEVE(fn, -1, ext, context);
05078    if (ast_fileexists(fn, NULL, NULL) > 0) {
05079       res = ast_stream_and_wait(chan, fn, ecodes);
05080       if (res) {
05081          DISPOSE(fn, -1);
05082          return res;
05083       }
05084    } else {
05085       /* Dispose just in case */
05086       DISPOSE(fn, -1);
05087       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05088       if (res)
05089          return res;
05090       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05091       if (res)
05092          return res;
05093    }
05094    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05095    return res;
05096 }
05097 
05098 static void free_zone(struct vm_zone *z)
05099 {
05100    ast_free(z);
05101 }
05102 
05103 #ifdef ODBC_STORAGE
05104 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05105 {
05106    int x = -1;
05107    int res;
05108    SQLHSTMT stmt = NULL;
05109    char sql[PATH_MAX];
05110    char rowdata[20];
05111    char tmp[PATH_MAX] = "";
05112    struct odbc_obj *obj = NULL;
05113    char *context;
05114    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05115 
05116    if (newmsgs)
05117       *newmsgs = 0;
05118    if (oldmsgs)
05119       *oldmsgs = 0;
05120    if (urgentmsgs)
05121       *urgentmsgs = 0;
05122 
05123    /* If no mailbox, return immediately */
05124    if (ast_strlen_zero(mailbox))
05125       return 0;
05126 
05127    ast_copy_string(tmp, mailbox, sizeof(tmp));
05128 
05129    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05130       int u, n, o;
05131       char *next, *remaining = tmp;
05132       while ((next = strsep(&remaining, " ,"))) {
05133          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05134             return -1;
05135          }
05136          if (urgentmsgs) {
05137             *urgentmsgs += u;
05138          }
05139          if (newmsgs) {
05140             *newmsgs += n;
05141          }
05142          if (oldmsgs) {
05143             *oldmsgs += o;
05144          }
05145       }
05146       return 0;
05147    }
05148 
05149    context = strchr(tmp, '@');
05150    if (context) {
05151       *context = '\0';
05152       context++;
05153    } else
05154       context = "default";
05155 
05156    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05157       do {
05158          if (newmsgs) {
05159             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05160             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05161                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05162                break;
05163             }
05164             res = SQLFetch(stmt);
05165             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05166                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05167                break;
05168             }
05169             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05170             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05171                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05172                break;
05173             }
05174             *newmsgs = atoi(rowdata);
05175             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05176          }
05177 
05178          if (oldmsgs) {
05179             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05180             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05181                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05182                break;
05183             }
05184             res = SQLFetch(stmt);
05185             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05186                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05187                break;
05188             }
05189             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05190             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05191                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05192                break;
05193             }
05194             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05195             *oldmsgs = atoi(rowdata);
05196          }
05197 
05198          if (urgentmsgs) {
05199             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05200             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05201                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05202                break;
05203             }
05204             res = SQLFetch(stmt);
05205             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05206                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05207                break;
05208             }
05209             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05210             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05211                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05212                break;
05213             }
05214             *urgentmsgs = atoi(rowdata);
05215          }
05216 
05217          x = 0;
05218       } while (0);
05219    } else {
05220       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05221    }
05222 
05223    if (stmt) {
05224       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05225    }
05226    if (obj) {
05227       ast_odbc_release_obj(obj);
05228    }
05229    return x;
05230 }
05231 
05232 /*!
05233  * \brief Gets the number of messages that exist in a mailbox folder.
05234  * \param context
05235  * \param mailbox
05236  * \param folder
05237  * 
05238  * This method is used when ODBC backend is used.
05239  * \return The number of messages in this mailbox folder (zero or more).
05240  */
05241 static int messagecount(const char *context, const char *mailbox, const char *folder)
05242 {
05243    struct odbc_obj *obj = NULL;
05244    int nummsgs = 0;
05245    int res;
05246    SQLHSTMT stmt = NULL;
05247    char sql[PATH_MAX];
05248    char rowdata[20];
05249    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05250    if (!folder)
05251       folder = "INBOX";
05252    /* If no mailbox, return immediately */
05253    if (ast_strlen_zero(mailbox))
05254       return 0;
05255 
05256    obj = ast_odbc_request_obj(odbc_database, 0);
05257    if (obj) {
05258       if (!strcmp(folder, "INBOX")) {
05259          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);
05260       } else {
05261          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05262       }
05263       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05264       if (!stmt) {
05265          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05266          goto yuck;
05267       }
05268       res = SQLFetch(stmt);
05269       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05270          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05271          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05272          goto yuck;
05273       }
05274       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05275       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05276          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05277          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05278          goto yuck;
05279       }
05280       nummsgs = atoi(rowdata);
05281       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05282    } else
05283       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05284 
05285 yuck:
05286    if (obj)
05287       ast_odbc_release_obj(obj);
05288    return nummsgs;
05289 }
05290 
05291 /** 
05292  * \brief Determines if the given folder has messages.
05293  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05294  * 
05295  * This function is used when the mailbox is stored in an ODBC back end.
05296  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05297  * \return 1 if the folder has one or more messages. zero otherwise.
05298  */
05299 static int has_voicemail(const char *mailbox, const char *folder)
05300 {
05301    char tmp[256], *tmp2 = tmp, *box, *context;
05302    ast_copy_string(tmp, mailbox, sizeof(tmp));
05303    while ((context = box = strsep(&tmp2, ",&"))) {
05304       strsep(&context, "@");
05305       if (ast_strlen_zero(context))
05306          context = "default";
05307       if (messagecount(context, box, folder))
05308          return 1;
05309    }
05310    return 0;
05311 }
05312 #endif
05313 #ifndef IMAP_STORAGE
05314 /*! 
05315  * \brief Copies a message from one mailbox to another.
05316  * \param chan
05317  * \param vmu
05318  * \param imbox
05319  * \param msgnum
05320  * \param duration
05321  * \param recip
05322  * \param fmt
05323  * \param dir
05324  * \param flag
05325  *
05326  * This is only used by file storage based mailboxes.
05327  *
05328  * \return zero on success, -1 on error.
05329  */
05330 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)
05331 {
05332    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05333    const char *frombox = mbox(vmu, imbox);
05334    const char *userfolder;
05335    int recipmsgnum;
05336    int res = 0;
05337 
05338    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05339 
05340    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05341       userfolder = "Urgent";
05342    } else {
05343       userfolder = "INBOX";
05344    }
05345 
05346    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05347 
05348    if (!dir)
05349       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05350    else
05351       ast_copy_string(fromdir, dir, sizeof(fromdir));
05352 
05353    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05354    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05355 
05356    if (vm_lock_path(todir))
05357       return ERROR_LOCK_PATH;
05358 
05359    recipmsgnum = last_message_index(recip, todir) + 1;
05360    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05361       make_file(topath, sizeof(topath), todir, recipmsgnum);
05362 #ifndef ODBC_STORAGE
05363       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05364          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05365       } else {
05366 #endif
05367          /* If we are prepending a message for ODBC, then the message already
05368           * exists in the database, but we want to force copying from the
05369           * filesystem (since only the FS contains the prepend). */
05370          copy_plain_file(frompath, topath);
05371          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05372          vm_delete(topath);
05373 #ifndef ODBC_STORAGE
05374       }
05375 #endif
05376    } else {
05377       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05378       res = -1;
05379    }
05380    ast_unlock_path(todir);
05381    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05382       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05383       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05384       flag);
05385    
05386    return res;
05387 }
05388 #endif
05389 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05390 
05391 static int messagecount(const char *context, const char *mailbox, const char *folder)
05392 {
05393    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05394 }
05395 
05396 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05397 {
05398    DIR *dir;
05399    struct dirent *de;
05400    char fn[256];
05401    int ret = 0;
05402 
05403    /* If no mailbox, return immediately */
05404    if (ast_strlen_zero(mailbox))
05405       return 0;
05406 
05407    if (ast_strlen_zero(folder))
05408       folder = "INBOX";
05409    if (ast_strlen_zero(context))
05410       context = "default";
05411 
05412    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05413 
05414    if (!(dir = opendir(fn)))
05415       return 0;
05416 
05417    while ((de = readdir(dir))) {
05418       if (!strncasecmp(de->d_name, "msg", 3)) {
05419          if (shortcircuit) {
05420             ret = 1;
05421             break;
05422          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05423             ret++;
05424          }
05425       }
05426    }
05427 
05428    closedir(dir);
05429 
05430    return ret;
05431 }
05432 
05433 /** 
05434  * \brief Determines if the given folder has messages.
05435  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05436  * \param folder the folder to look in
05437  *
05438  * This function is used when the mailbox is stored in a filesystem back end.
05439  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05440  * \return 1 if the folder has one or more messages. zero otherwise.
05441  */
05442 static int has_voicemail(const char *mailbox, const char *folder)
05443 {
05444    char tmp[256], *tmp2 = tmp, *box, *context;
05445    ast_copy_string(tmp, mailbox, sizeof(tmp));
05446    if (ast_strlen_zero(folder)) {
05447       folder = "INBOX";
05448    }
05449    while ((box = strsep(&tmp2, ",&"))) {
05450       if ((context = strchr(box, '@')))
05451          *context++ = '\0';
05452       else
05453          context = "default";
05454       if (__has_voicemail(context, box, folder, 1))
05455          return 1;
05456       /* If we are checking INBOX, we should check Urgent as well */
05457       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05458          return 1;
05459       }
05460    }
05461    return 0;
05462 }
05463 
05464 
05465 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05466 {
05467    char tmp[256];
05468    char *context;
05469 
05470    /* If no mailbox, return immediately */
05471    if (ast_strlen_zero(mailbox))
05472       return 0;
05473 
05474    if (newmsgs)
05475       *newmsgs = 0;
05476    if (oldmsgs)
05477       *oldmsgs = 0;
05478    if (urgentmsgs)
05479       *urgentmsgs = 0;
05480 
05481    if (strchr(mailbox, ',')) {
05482       int tmpnew, tmpold, tmpurgent;
05483       char *mb, *cur;
05484 
05485       ast_copy_string(tmp, mailbox, sizeof(tmp));
05486       mb = tmp;
05487       while ((cur = strsep(&mb, ", "))) {
05488          if (!ast_strlen_zero(cur)) {
05489             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05490                return -1;
05491             else {
05492                if (newmsgs)
05493                   *newmsgs += tmpnew; 
05494                if (oldmsgs)
05495                   *oldmsgs += tmpold;
05496                if (urgentmsgs)
05497                   *urgentmsgs += tmpurgent;
05498             }
05499          }
05500       }
05501       return 0;
05502    }
05503 
05504    ast_copy_string(tmp, mailbox, sizeof(tmp));
05505    
05506    if ((context = strchr(tmp, '@')))
05507       *context++ = '\0';
05508    else
05509       context = "default";
05510 
05511    if (newmsgs)
05512       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05513    if (oldmsgs)
05514       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05515    if (urgentmsgs)
05516       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05517 
05518    return 0;
05519 }
05520 
05521 #endif
05522 
05523 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05524 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05525 {
05526    int urgentmsgs = 0;
05527    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05528    if (newmsgs) {
05529       *newmsgs += urgentmsgs;
05530    }
05531    return res;
05532 }
05533 
05534 static void run_externnotify(char *context, char *extension, const char *flag)
05535 {
05536    char arguments[255];
05537    char ext_context[256] = "";
05538    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05539    struct ast_smdi_mwi_message *mwi_msg;
05540 
05541    if (!ast_strlen_zero(context))
05542       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05543    else
05544       ast_copy_string(ext_context, extension, sizeof(ext_context));
05545 
05546    if (smdi_iface) {
05547       if (ast_app_has_voicemail(ext_context, NULL)) 
05548          ast_smdi_mwi_set(smdi_iface, extension);
05549       else
05550          ast_smdi_mwi_unset(smdi_iface, extension);
05551 
05552       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05553          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05554          if (!strncmp(mwi_msg->cause, "INV", 3))
05555             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05556          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05557             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05558          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05559          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05560       } else {
05561          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05562       }
05563    }
05564 
05565    if (!ast_strlen_zero(externnotify)) {
05566       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05567          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05568       } else {
05569          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05570          ast_debug(1, "Executing %s\n", arguments);
05571          ast_safe_system(arguments);
05572       }
05573    }
05574 }
05575 
05576 /*!
05577  * \brief Variables used for saving a voicemail.
05578  *
05579  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05580  */
05581 struct leave_vm_options {
05582    unsigned int flags;
05583    signed char record_gain;
05584    char *exitcontext;
05585 };
05586 
05587 /*!
05588  * \brief Prompts the user and records a voicemail to a mailbox.
05589  * \param chan
05590  * \param ext
05591  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05592  * 
05593  * 
05594  * 
05595  * \return zero on success, -1 on error.
05596  */
05597 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05598 {
05599 #ifdef IMAP_STORAGE
05600    int newmsgs, oldmsgs;
05601 #else
05602    char urgdir[PATH_MAX];
05603 #endif
05604    char txtfile[PATH_MAX];
05605    char tmptxtfile[PATH_MAX];
05606    struct vm_state *vms = NULL;
05607    char callerid[256];
05608    FILE *txt;
05609    char date[256];
05610    int txtdes;
05611    int res = 0;
05612    int msgnum;
05613    int duration = 0;
05614    int sound_duration = 0;
05615    int ausemacro = 0;
05616    int ousemacro = 0;
05617    int ouseexten = 0;
05618    char tmpdur[16];
05619    char priority[16];
05620    char origtime[16];
05621    char dir[PATH_MAX];
05622    char tmpdir[PATH_MAX];
05623    char fn[PATH_MAX];
05624    char prefile[PATH_MAX] = "";
05625    char tempfile[PATH_MAX] = "";
05626    char ext_context[256] = "";
05627    char fmt[80];
05628    char *context;
05629    char ecodes[17] = "#";
05630    struct ast_str *tmp = ast_str_create(16);
05631    char *tmpptr;
05632    struct ast_vm_user *vmu;
05633    struct ast_vm_user svm;
05634    const char *category = NULL;
05635    const char *code;
05636    const char *alldtmf = "0123456789ABCD*#";
05637    char flag[80];
05638 
05639    if (!tmp) {
05640       return -1;
05641    }
05642 
05643    ast_str_set(&tmp, 0, "%s", ext);
05644    ext = ast_str_buffer(tmp);
05645    if ((context = strchr(ext, '@'))) {
05646       *context++ = '\0';
05647       tmpptr = strchr(context, '&');
05648    } else {
05649       tmpptr = strchr(ext, '&');
05650    }
05651 
05652    if (tmpptr)
05653       *tmpptr++ = '\0';
05654 
05655    ast_channel_lock(chan);
05656    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05657       category = ast_strdupa(category);
05658    }
05659    ast_channel_unlock(chan);
05660 
05661    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05662       ast_copy_string(flag, "Urgent", sizeof(flag));
05663    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05664       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05665    } else {
05666       flag[0] = '\0';
05667    }
05668 
05669    ast_debug(3, "Before find_user\n");
05670    if (!(vmu = find_user(&svm, context, ext))) {
05671       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05672       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05673       ast_free(tmp);
05674       return res;
05675    }
05676    /* Setup pre-file if appropriate */
05677    if (strcmp(vmu->context, "default"))
05678       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05679    else
05680       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05681 
05682    /* Set the path to the prefile. Will be one of 
05683       VM_SPOOL_DIRcontext/ext/busy
05684       VM_SPOOL_DIRcontext/ext/unavail
05685       Depending on the flag set in options.
05686    */
05687    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05688       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05689    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05690       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05691    }
05692    /* Set the path to the tmpfile as
05693       VM_SPOOL_DIR/context/ext/temp
05694       and attempt to create the folder structure.
05695    */
05696    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05697    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05698       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05699       ast_free(tmp);
05700       return -1;
05701    }
05702    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05703    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05704       ast_copy_string(prefile, tempfile, sizeof(prefile));
05705 
05706    DISPOSE(tempfile, -1);
05707    /* It's easier just to try to make it than to check for its existence */
05708 #ifndef IMAP_STORAGE
05709    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05710 #else
05711    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05712    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05713       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05714    }
05715 #endif
05716 
05717    /* Check current or macro-calling context for special extensions */
05718    if (ast_test_flag(vmu, VM_OPERATOR)) {
05719       if (!ast_strlen_zero(vmu->exit)) {
05720          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05721             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05722             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05723             ouseexten = 1;
05724          }
05725       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05726          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05727          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05728          ouseexten = 1;
05729       } else if (!ast_strlen_zero(chan->macrocontext)
05730          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05731             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05732          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05733          ousemacro = 1;
05734       }
05735    }
05736 
05737    if (!ast_strlen_zero(vmu->exit)) {
05738       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05739          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05740          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05741       }
05742    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05743       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05744       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05745    } else if (!ast_strlen_zero(chan->macrocontext)
05746       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05747          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05748       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05749       ausemacro = 1;
05750    }
05751 
05752    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05753       for (code = alldtmf; *code; code++) {
05754          char e[2] = "";
05755          e[0] = *code;
05756          if (strchr(ecodes, e[0]) == NULL
05757             && ast_canmatch_extension(chan,
05758                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
05759                e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05760             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05761          }
05762       }
05763    }
05764 
05765    /* Play the beginning intro if desired */
05766    if (!ast_strlen_zero(prefile)) {
05767 #ifdef ODBC_STORAGE
05768       int success = 
05769 #endif
05770          RETRIEVE(prefile, -1, ext, context);
05771       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05772          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05773             res = ast_waitstream(chan, ecodes);
05774 #ifdef ODBC_STORAGE
05775          if (success == -1) {
05776             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05777             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05778             store_file(prefile, vmu->mailbox, vmu->context, -1);
05779          }
05780 #endif
05781       } else {
05782          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05783          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05784       }
05785       DISPOSE(prefile, -1);
05786       if (res < 0) {
05787          ast_debug(1, "Hang up during prefile playback\n");
05788          free_user(vmu);
05789          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05790          ast_free(tmp);
05791          return -1;
05792       }
05793    }
05794    if (res == '#') {
05795       /* On a '#' we skip the instructions */
05796       ast_set_flag(options, OPT_SILENT);
05797       res = 0;
05798    }
05799    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05800    if (vmu->maxmsg == 0) {
05801       if (option_debug > 2)
05802          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05803       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05804       goto leave_vm_out;
05805    }
05806    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05807       res = ast_stream_and_wait(chan, INTRO, ecodes);
05808       if (res == '#') {
05809          ast_set_flag(options, OPT_SILENT);
05810          res = 0;
05811       }
05812    }
05813    if (res > 0)
05814       ast_stopstream(chan);
05815    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05816     other than the operator -- an automated attendant or mailbox login for example */
05817    if (res == '*') {
05818       chan->exten[0] = 'a';
05819       chan->exten[1] = '\0';
05820       if (!ast_strlen_zero(vmu->exit)) {
05821          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05822       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05823          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05824       }
05825       chan->priority = 0;
05826       free_user(vmu);
05827       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05828       ast_free(tmp);
05829       return 0;
05830    }
05831 
05832    /* Check for a '0' here */
05833    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05834    transfer:
05835       if (ouseexten || ousemacro) {
05836          chan->exten[0] = 'o';
05837          chan->exten[1] = '\0';
05838          if (!ast_strlen_zero(vmu->exit)) {
05839             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05840          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05841             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05842          }
05843          ast_play_and_wait(chan, "transfer");
05844          chan->priority = 0;
05845          free_user(vmu);
05846          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05847       }
05848       ast_free(tmp);
05849       return OPERATOR_EXIT;
05850    }
05851 
05852    /* Allow all other digits to exit Voicemail and return to the dialplan */
05853    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05854       if (!ast_strlen_zero(options->exitcontext)) {
05855          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05856       }
05857       free_user(vmu);
05858       ast_free(tmp);
05859       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05860       return res;
05861    }
05862 
05863    if (res < 0) {
05864       free_user(vmu);
05865       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05866       ast_free(tmp);
05867       return -1;
05868    }
05869    /* The meat of recording the message...  All the announcements and beeps have been played*/
05870    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05871    if (!ast_strlen_zero(fmt)) {
05872       msgnum = 0;
05873 
05874 #ifdef IMAP_STORAGE
05875       /* Is ext a mailbox? */
05876       /* must open stream for this user to get info! */
05877       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05878       if (res < 0) {
05879          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05880          ast_free(tmp);
05881          return -1;
05882       }
05883       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05884       /* It is possible under certain circumstances that inboxcount did not
05885        * create a vm_state when it was needed. This is a catchall which will
05886        * rarely be used.
05887        */
05888          if (!(vms = create_vm_state_from_user(vmu))) {
05889             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05890             ast_free(tmp);
05891             return -1;
05892          }
05893       }
05894       vms->newmessages++;
05895       
05896       /* here is a big difference! We add one to it later */
05897       msgnum = newmsgs + oldmsgs;
05898       ast_debug(3, "Messagecount set to %d\n", msgnum);
05899       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05900       /* set variable for compatibility */
05901       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05902 
05903       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05904          goto leave_vm_out;
05905       }
05906 #else
05907       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05908          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05909          if (!res)
05910             res = ast_waitstream(chan, "");
05911          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05912          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05913          inprocess_count(vmu->mailbox, vmu->context, -1);
05914          goto leave_vm_out;
05915       }
05916 
05917 #endif
05918       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05919       txtdes = mkstemp(tmptxtfile);
05920       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05921       if (txtdes < 0) {
05922          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05923          if (!res)
05924             res = ast_waitstream(chan, "");
05925          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05926          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05927          inprocess_count(vmu->mailbox, vmu->context, -1);
05928          goto leave_vm_out;
05929       }
05930 
05931       /* Now play the beep once we have the message number for our next message. */
05932       if (res >= 0) {
05933          /* Unless we're *really* silent, try to send the beep */
05934          res = ast_stream_and_wait(chan, "beep", "");
05935       }
05936             
05937       /* Store information in real-time storage */
05938       if (ast_check_realtime("voicemail_data")) {
05939          snprintf(priority, sizeof(priority), "%d", chan->priority);
05940          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05941          get_date(date, sizeof(date));
05942          ast_callerid_merge(callerid, sizeof(callerid),
05943             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05944             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05945             "Unknown");
05946          ast_store_realtime("voicemail_data",
05947             "origmailbox", ext,
05948             "context", chan->context,
05949             "macrocontext", chan->macrocontext,
05950             "exten", chan->exten,
05951             "priority", priority,
05952             "callerchan", chan->name,
05953             "callerid", callerid,
05954             "origdate", date,
05955             "origtime", origtime,
05956             "category", S_OR(category, ""),
05957             "filename", tmptxtfile,
05958             SENTINEL);
05959       }
05960 
05961       /* Store information */
05962       txt = fdopen(txtdes, "w+");
05963       if (txt) {
05964          get_date(date, sizeof(date));
05965          ast_callerid_merge(callerid, sizeof(callerid),
05966             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05967             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05968             "Unknown");
05969          fprintf(txt, 
05970             ";\n"
05971             "; Message Information file\n"
05972             ";\n"
05973             "[message]\n"
05974             "origmailbox=%s\n"
05975             "context=%s\n"
05976             "macrocontext=%s\n"
05977             "exten=%s\n"
05978             "rdnis=%s\n"
05979             "priority=%d\n"
05980             "callerchan=%s\n"
05981             "callerid=%s\n"
05982             "origdate=%s\n"
05983             "origtime=%ld\n"
05984             "category=%s\n",
05985             ext,
05986             chan->context,
05987             chan->macrocontext, 
05988             chan->exten,
05989             S_COR(chan->redirecting.from.number.valid,
05990                chan->redirecting.from.number.str, "unknown"),
05991             chan->priority,
05992             chan->name,
05993             callerid,
05994             date, (long) time(NULL),
05995             category ? category : "");
05996       } else {
05997          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05998          inprocess_count(vmu->mailbox, vmu->context, -1);
05999          if (ast_check_realtime("voicemail_data")) {
06000             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06001          }
06002          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
06003          goto leave_vm_out;
06004       }
06005       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
06006 
06007       if (txt) {
06008          fprintf(txt, "flag=%s\n", flag);
06009          if (sound_duration < vmu->minsecs) {
06010             fclose(txt);
06011             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
06012             ast_filedelete(tmptxtfile, NULL);
06013             unlink(tmptxtfile);
06014             if (ast_check_realtime("voicemail_data")) {
06015                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06016             }
06017             inprocess_count(vmu->mailbox, vmu->context, -1);
06018          } else {
06019             fprintf(txt, "duration=%d\n", duration);
06020             fclose(txt);
06021             if (vm_lock_path(dir)) {
06022                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06023                /* Delete files */
06024                ast_filedelete(tmptxtfile, NULL);
06025                unlink(tmptxtfile);
06026                inprocess_count(vmu->mailbox, vmu->context, -1);
06027             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06028                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06029                unlink(tmptxtfile);
06030                ast_unlock_path(dir);
06031                inprocess_count(vmu->mailbox, vmu->context, -1);
06032                if (ast_check_realtime("voicemail_data")) {
06033                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06034                }
06035             } else {
06036 #ifndef IMAP_STORAGE
06037                msgnum = last_message_index(vmu, dir) + 1;
06038 #endif
06039                make_file(fn, sizeof(fn), dir, msgnum);
06040 
06041                /* assign a variable with the name of the voicemail file */ 
06042 #ifndef IMAP_STORAGE
06043                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06044 #else
06045                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06046 #endif
06047 
06048                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06049                ast_filerename(tmptxtfile, fn, NULL);
06050                rename(tmptxtfile, txtfile);
06051                inprocess_count(vmu->mailbox, vmu->context, -1);
06052 
06053                /* Properly set permissions on voicemail text descriptor file.
06054                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06055                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06056                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06057 
06058                ast_unlock_path(dir);
06059                if (ast_check_realtime("voicemail_data")) {
06060                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06061                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06062                }
06063                /* We must store the file first, before copying the message, because
06064                 * ODBC storage does the entire copy with SQL.
06065                 */
06066                if (ast_fileexists(fn, NULL, NULL) > 0) {
06067                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06068                }
06069 
06070                /* Are there to be more recipients of this message? */
06071                while (tmpptr) {
06072                   struct ast_vm_user recipu, *recip;
06073                   char *exten, *cntx;
06074 
06075                   exten = strsep(&tmpptr, "&");
06076                   cntx = strchr(exten, '@');
06077                   if (cntx) {
06078                      *cntx = '\0';
06079                      cntx++;
06080                   }
06081                   if ((recip = find_user(&recipu, cntx, exten))) {
06082                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06083                      free_user(recip);
06084                   }
06085                }
06086 #ifndef IMAP_STORAGE
06087                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06088                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06089                   char sfn[PATH_MAX];
06090                   char dfn[PATH_MAX];
06091                   int x;
06092                   /* It's easier just to try to make it than to check for its existence */
06093                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06094                   x = last_message_index(vmu, urgdir) + 1;
06095                   make_file(sfn, sizeof(sfn), dir, msgnum);
06096                   make_file(dfn, sizeof(dfn), urgdir, x);
06097                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06098                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06099                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06100                   ast_copy_string(fn, dfn, sizeof(fn));
06101                   msgnum = x;
06102                }
06103 #endif
06104                /* Notification needs to happen after the copy, though. */
06105                if (ast_fileexists(fn, NULL, NULL)) {
06106 #ifdef IMAP_STORAGE
06107                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06108                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06109                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06110                      flag);
06111 #else
06112                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06113                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06114                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06115                      flag);
06116 #endif
06117                }
06118 
06119                /* Disposal needs to happen after the optional move and copy */
06120                if (ast_fileexists(fn, NULL, NULL)) {
06121                   DISPOSE(dir, msgnum);
06122                }
06123             }
06124          }
06125       } else {
06126          inprocess_count(vmu->mailbox, vmu->context, -1);
06127       }
06128       if (res == '0') {
06129          goto transfer;
06130       } else if (res > 0 && res != 't')
06131          res = 0;
06132 
06133       if (sound_duration < vmu->minsecs)
06134          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06135          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06136       else
06137          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06138    } else
06139       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06140 leave_vm_out:
06141    free_user(vmu);
06142 
06143 #ifdef IMAP_STORAGE
06144    /* expunge message - use UID Expunge if supported on IMAP server*/
06145    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06146    if (expungeonhangup == 1) {
06147       ast_mutex_lock(&vms->lock);
06148 #ifdef HAVE_IMAP_TK2006
06149       if (LEVELUIDPLUS (vms->mailstream)) {
06150          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06151       } else 
06152 #endif
06153          mail_expunge(vms->mailstream);
06154       ast_mutex_unlock(&vms->lock);
06155    }
06156 #endif
06157 
06158    ast_free(tmp);
06159    return res;
06160 }
06161 
06162 #if !defined(IMAP_STORAGE)
06163 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06164 {
06165    /* we know the actual number of messages, so stop process when number is hit */
06166 
06167    int x, dest;
06168    char sfn[PATH_MAX];
06169    char dfn[PATH_MAX];
06170 
06171    if (vm_lock_path(dir)) {
06172       return ERROR_LOCK_PATH;
06173    }
06174 
06175    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06176       make_file(sfn, sizeof(sfn), dir, x);
06177       if (EXISTS(dir, x, sfn, NULL)) {
06178 
06179          if (x != dest) {
06180             make_file(dfn, sizeof(dfn), dir, dest);
06181             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06182          }
06183 
06184          dest++;
06185       }
06186    }
06187    ast_unlock_path(dir);
06188 
06189    return dest;
06190 }
06191 #endif
06192 
06193 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06194 {
06195    int d;
06196    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06197    return d;
06198 }
06199 
06200 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06201 {
06202 #ifdef IMAP_STORAGE
06203    /* we must use mbox(x) folder names, and copy the message there */
06204    /* simple. huh? */
06205    char sequence[10];
06206    char mailbox[256];
06207    int res;
06208 
06209    /* get the real IMAP message number for this message */
06210    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06211    
06212    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06213    ast_mutex_lock(&vms->lock);
06214    /* if save to Old folder, put in INBOX as read */
06215    if (box == OLD_FOLDER) {
06216       mail_setflag(vms->mailstream, sequence, "\\Seen");
06217       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06218    } else if (box == NEW_FOLDER) {
06219       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06220       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06221    }
06222    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06223       ast_mutex_unlock(&vms->lock);
06224       return 0;
06225    }
06226    /* Create the folder if it don't exist */
06227    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06228    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06229    if (mail_create(vms->mailstream, mailbox) == NIL) 
06230       ast_debug(5, "Folder exists.\n");
06231    else
06232       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06233    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06234    ast_mutex_unlock(&vms->lock);
06235    return res;
06236 #else
06237    char *dir = vms->curdir;
06238    char *username = vms->username;
06239    char *context = vmu->context;
06240    char sfn[PATH_MAX];
06241    char dfn[PATH_MAX];
06242    char ddir[PATH_MAX];
06243    const char *dbox = mbox(vmu, box);
06244    int x, i;
06245    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06246 
06247    if (vm_lock_path(ddir))
06248       return ERROR_LOCK_PATH;
06249 
06250    x = last_message_index(vmu, ddir) + 1;
06251 
06252    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06253       x--;
06254       for (i = 1; i <= x; i++) {
06255          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06256          make_file(sfn, sizeof(sfn), ddir, i);
06257          make_file(dfn, sizeof(dfn), ddir, i - 1);
06258          if (EXISTS(ddir, i, sfn, NULL)) {
06259             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06260          } else
06261             break;
06262       }
06263    } else {
06264       if (x >= vmu->maxmsg) {
06265          ast_unlock_path(ddir);
06266          return -1;
06267       }
06268    }
06269    make_file(sfn, sizeof(sfn), dir, msg);
06270    make_file(dfn, sizeof(dfn), ddir, x);
06271    if (strcmp(sfn, dfn)) {
06272       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06273    }
06274    ast_unlock_path(ddir);
06275 #endif
06276    return 0;
06277 }
06278 
06279 static int adsi_logo(unsigned char *buf)
06280 {
06281    int bytes = 0;
06282    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06283    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06284    return bytes;
06285 }
06286 
06287 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06288 {
06289    unsigned char buf[256];
06290    int bytes = 0;
06291    int x;
06292    char num[5];
06293 
06294    *useadsi = 0;
06295    bytes += ast_adsi_data_mode(buf + bytes);
06296    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06297 
06298    bytes = 0;
06299    bytes += adsi_logo(buf);
06300    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06301 #ifdef DISPLAY
06302    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06303 #endif
06304    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06305    bytes += ast_adsi_data_mode(buf + bytes);
06306    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06307 
06308    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06309       bytes = 0;
06310       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06311       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06312       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06313       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06314       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06315       return 0;
06316    }
06317 
06318 #ifdef DISPLAY
06319    /* Add a dot */
06320    bytes = 0;
06321    bytes += ast_adsi_logo(buf);
06322    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06323    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06324    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06325    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06326 #endif
06327    bytes = 0;
06328    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06329    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06330    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06331    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06332    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06333    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06334    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06335 
06336 #ifdef DISPLAY
06337    /* Add another dot */
06338    bytes = 0;
06339    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06340    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06341 
06342    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06343    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06344 #endif
06345 
06346    bytes = 0;
06347    /* These buttons we load but don't use yet */
06348    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06349    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06350    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06351    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06352    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06353    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06354    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06355 
06356 #ifdef DISPLAY
06357    /* Add another dot */
06358    bytes = 0;
06359    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06360    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06361    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06362 #endif
06363 
06364    bytes = 0;
06365    for (x = 0; x < 5; x++) {
06366       snprintf(num, sizeof(num), "%d", x);
06367       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06368    }
06369    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06370    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06371 
06372 #ifdef DISPLAY
06373    /* Add another dot */
06374    bytes = 0;
06375    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06376    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06377    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06378 #endif
06379 
06380    if (ast_adsi_end_download(chan)) {
06381       bytes = 0;
06382       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06383       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06384       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06385       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06386       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06387       return 0;
06388    }
06389    bytes = 0;
06390    bytes += ast_adsi_download_disconnect(buf + bytes);
06391    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06392    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06393 
06394    ast_debug(1, "Done downloading scripts...\n");
06395 
06396 #ifdef DISPLAY
06397    /* Add last dot */
06398    bytes = 0;
06399    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06400    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06401 #endif
06402    ast_debug(1, "Restarting session...\n");
06403 
06404    bytes = 0;
06405    /* Load the session now */
06406    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06407       *useadsi = 1;
06408       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06409    } else
06410       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06411 
06412    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06413    return 0;
06414 }
06415 
06416 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06417 {
06418    int x;
06419    if (!ast_adsi_available(chan))
06420       return;
06421    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06422    if (x < 0)
06423       return;
06424    if (!x) {
06425       if (adsi_load_vmail(chan, useadsi)) {
06426          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06427          return;
06428       }
06429    } else
06430       *useadsi = 1;
06431 }
06432 
06433 static void adsi_login(struct ast_channel *chan)
06434 {
06435    unsigned char buf[256];
06436    int bytes = 0;
06437    unsigned char keys[8];
06438    int x;
06439    if (!ast_adsi_available(chan))
06440       return;
06441 
06442    for (x = 0; x < 8; x++)
06443       keys[x] = 0;
06444    /* Set one key for next */
06445    keys[3] = ADSI_KEY_APPS + 3;
06446 
06447    bytes += adsi_logo(buf + bytes);
06448    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06449    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06450    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06451    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06452    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06453    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06454    bytes += ast_adsi_set_keys(buf + bytes, keys);
06455    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06456    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06457 }
06458 
06459 static void adsi_password(struct ast_channel *chan)
06460 {
06461    unsigned char buf[256];
06462    int bytes = 0;
06463    unsigned char keys[8];
06464    int x;
06465    if (!ast_adsi_available(chan))
06466       return;
06467 
06468    for (x = 0; x < 8; x++)
06469       keys[x] = 0;
06470    /* Set one key for next */
06471    keys[3] = ADSI_KEY_APPS + 3;
06472 
06473    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06474    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06475    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06476    bytes += ast_adsi_set_keys(buf + bytes, keys);
06477    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06478    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06479 }
06480 
06481 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06482 {
06483    unsigned char buf[256];
06484    int bytes = 0;
06485    unsigned char keys[8];
06486    int x, y;
06487 
06488    if (!ast_adsi_available(chan))
06489       return;
06490 
06491    for (x = 0; x < 5; x++) {
06492       y = ADSI_KEY_APPS + 12 + start + x;
06493       if (y > ADSI_KEY_APPS + 12 + 4)
06494          y = 0;
06495       keys[x] = ADSI_KEY_SKT | y;
06496    }
06497    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06498    keys[6] = 0;
06499    keys[7] = 0;
06500 
06501    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06502    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06503    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06504    bytes += ast_adsi_set_keys(buf + bytes, keys);
06505    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06506 
06507    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06508 }
06509 
06510 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06511 {
06512    int bytes = 0;
06513    unsigned char buf[256]; 
06514    char buf1[256], buf2[256];
06515    char fn2[PATH_MAX];
06516 
06517    char cid[256] = "";
06518    char *val;
06519    char *name, *num;
06520    char datetime[21] = "";
06521    FILE *f;
06522 
06523    unsigned char keys[8];
06524 
06525    int x;
06526 
06527    if (!ast_adsi_available(chan))
06528       return;
06529 
06530    /* Retrieve important info */
06531    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06532    f = fopen(fn2, "r");
06533    if (f) {
06534       while (!feof(f)) {   
06535          if (!fgets((char *) buf, sizeof(buf), f)) {
06536             continue;
06537          }
06538          if (!feof(f)) {
06539             char *stringp = NULL;
06540             stringp = (char *) buf;
06541             strsep(&stringp, "=");
06542             val = strsep(&stringp, "=");
06543             if (!ast_strlen_zero(val)) {
06544                if (!strcmp((char *) buf, "callerid"))
06545                   ast_copy_string(cid, val, sizeof(cid));
06546                if (!strcmp((char *) buf, "origdate"))
06547                   ast_copy_string(datetime, val, sizeof(datetime));
06548             }
06549          }
06550       }
06551       fclose(f);
06552    }
06553    /* New meaning for keys */
06554    for (x = 0; x < 5; x++)
06555       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06556    keys[6] = 0x0;
06557    keys[7] = 0x0;
06558 
06559    if (!vms->curmsg) {
06560       /* No prev key, provide "Folder" instead */
06561       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06562    }
06563    if (vms->curmsg >= vms->lastmsg) {
06564       /* If last message ... */
06565       if (vms->curmsg) {
06566          /* but not only message, provide "Folder" instead */
06567          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06568          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06569 
06570       } else {
06571          /* Otherwise if only message, leave blank */
06572          keys[3] = 1;
06573       }
06574    }
06575 
06576    if (!ast_strlen_zero(cid)) {
06577       ast_callerid_parse(cid, &name, &num);
06578       if (!name)
06579          name = num;
06580    } else
06581       name = "Unknown Caller";
06582 
06583    /* If deleted, show "undeleted" */
06584 #ifdef IMAP_STORAGE
06585    ast_mutex_lock(&vms->lock);
06586 #endif
06587    if (vms->deleted[vms->curmsg]) {
06588       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06589    }
06590 #ifdef IMAP_STORAGE
06591    ast_mutex_unlock(&vms->lock);
06592 #endif
06593 
06594    /* Except "Exit" */
06595    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06596    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06597       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06598    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06599 
06600    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06601    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06602    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06603    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06604    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06605    bytes += ast_adsi_set_keys(buf + bytes, keys);
06606    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06607 
06608    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06609 }
06610 
06611 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06612 {
06613    int bytes = 0;
06614    unsigned char buf[256];
06615    unsigned char keys[8];
06616 
06617    int x;
06618 
06619    if (!ast_adsi_available(chan))
06620       return;
06621 
06622    /* New meaning for keys */
06623    for (x = 0; x < 5; x++)
06624       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06625 
06626    keys[6] = 0x0;
06627    keys[7] = 0x0;
06628 
06629    if (!vms->curmsg) {
06630       /* No prev key, provide "Folder" instead */
06631       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06632    }
06633    if (vms->curmsg >= vms->lastmsg) {
06634       /* If last message ... */
06635       if (vms->curmsg) {
06636          /* but not only message, provide "Folder" instead */
06637          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06638       } else {
06639          /* Otherwise if only message, leave blank */
06640          keys[3] = 1;
06641       }
06642    }
06643 
06644    /* If deleted, show "undeleted" */
06645 #ifdef IMAP_STORAGE
06646    ast_mutex_lock(&vms->lock);
06647 #endif
06648    if (vms->deleted[vms->curmsg]) {
06649       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06650    }
06651 #ifdef IMAP_STORAGE
06652    ast_mutex_unlock(&vms->lock);
06653 #endif
06654 
06655    /* Except "Exit" */
06656    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06657    bytes += ast_adsi_set_keys(buf + bytes, keys);
06658    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06659 
06660    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06661 }
06662 
06663 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06664 {
06665    unsigned char buf[256] = "";
06666    char buf1[256] = "", buf2[256] = "";
06667    int bytes = 0;
06668    unsigned char keys[8];
06669    int x;
06670 
06671    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06672    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06673    if (!ast_adsi_available(chan))
06674       return;
06675    if (vms->newmessages) {
06676       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06677       if (vms->oldmessages) {
06678          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06679          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06680       } else {
06681          snprintf(buf2, sizeof(buf2), "%s.", newm);
06682       }
06683    } else if (vms->oldmessages) {
06684       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06685       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06686    } else {
06687       strcpy(buf1, "You have no messages.");
06688       buf2[0] = ' ';
06689       buf2[1] = '\0';
06690    }
06691    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06692    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06693    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06694 
06695    for (x = 0; x < 6; x++)
06696       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06697    keys[6] = 0;
06698    keys[7] = 0;
06699 
06700    /* Don't let them listen if there are none */
06701    if (vms->lastmsg < 0)
06702       keys[0] = 1;
06703    bytes += ast_adsi_set_keys(buf + bytes, keys);
06704 
06705    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06706 
06707    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06708 }
06709 
06710 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06711 {
06712    unsigned char buf[256] = "";
06713    char buf1[256] = "", buf2[256] = "";
06714    int bytes = 0;
06715    unsigned char keys[8];
06716    int x;
06717 
06718    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06719 
06720    if (!ast_adsi_available(chan))
06721       return;
06722 
06723    /* Original command keys */
06724    for (x = 0; x < 6; x++)
06725       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06726 
06727    keys[6] = 0;
06728    keys[7] = 0;
06729 
06730    if ((vms->lastmsg + 1) < 1)
06731       keys[0] = 0;
06732 
06733    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06734       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06735 
06736    if (vms->lastmsg + 1)
06737       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06738    else
06739       strcpy(buf2, "no messages.");
06740    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06741    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06742    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06743    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06744    bytes += ast_adsi_set_keys(buf + bytes, keys);
06745 
06746    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06747 
06748    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06749    
06750 }
06751 
06752 /*
06753 static void adsi_clear(struct ast_channel *chan)
06754 {
06755    char buf[256];
06756    int bytes=0;
06757    if (!ast_adsi_available(chan))
06758       return;
06759    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06760    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06761 
06762    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06763 }
06764 */
06765 
06766 static void adsi_goodbye(struct ast_channel *chan)
06767 {
06768    unsigned char buf[256];
06769    int bytes = 0;
06770 
06771    if (!ast_adsi_available(chan))
06772       return;
06773    bytes += adsi_logo(buf + bytes);
06774    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06775    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06776    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06777    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06778 
06779    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06780 }
06781 
06782 /*!\brief get_folder: Folder menu
06783  * Plays "press 1 for INBOX messages" etc.
06784  * Should possibly be internationalized
06785  */
06786 static int get_folder(struct ast_channel *chan, int start)
06787 {
06788    int x;
06789    int d;
06790    char fn[PATH_MAX];
06791    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06792    if (d)
06793       return d;
06794    for (x = start; x < 5; x++) { /* For all folders */
06795       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06796          return d;
06797       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06798       if (d)
06799          return d;
06800       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06801 
06802       /* The inbox folder can have its name changed under certain conditions
06803        * so this checks if the sound file exists for the inbox folder name and
06804        * if it doesn't, plays the default name instead. */
06805       if (x == 0) {
06806          if (ast_fileexists(fn, NULL, NULL)) {
06807             d = vm_play_folder_name(chan, fn);
06808          } else {
06809             ast_verb(1, "failed to find %s\n", fn);
06810             d = vm_play_folder_name(chan, "vm-INBOX");
06811          }
06812       } else {
06813          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06814          d = vm_play_folder_name(chan, fn);
06815       }
06816 
06817       if (d)
06818          return d;
06819       d = ast_waitfordigit(chan, 500);
06820       if (d)
06821          return d;
06822    }
06823 
06824    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06825    if (d)
06826       return d;
06827    d = ast_waitfordigit(chan, 4000);
06828    return d;
06829 }
06830 
06831 /*!
06832  * \brief plays a prompt and waits for a keypress.
06833  * \param chan
06834  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06835  * \param start Does not appear to be used at this time.
06836  *
06837  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06838  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06839  * prompting for the number inputs that correspond to the available folders.
06840  * 
06841  * \return zero on success, or -1 on error.
06842  */
06843 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06844 {
06845    int res = 0;
06846    int loops = 0;
06847 
06848    res = ast_play_and_wait(chan, fn);  /* Folder name */
06849    while (((res < '0') || (res > '9')) &&
06850          (res != '#') && (res >= 0) &&
06851          loops < 4) {
06852       res = get_folder(chan, 0);
06853       loops++;
06854    }
06855    if (loops == 4) { /* give up */
06856       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06857       return '#';
06858    }
06859    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06860    return res;
06861 }
06862 
06863 /*!
06864  * \brief presents the option to prepend to an existing message when forwarding it.
06865  * \param chan
06866  * \param vmu
06867  * \param curdir
06868  * \param curmsg
06869  * \param vm_fmts
06870  * \param context
06871  * \param record_gain
06872  * \param duration
06873  * \param vms
06874  * \param flag 
06875  *
06876  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06877  *
06878  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06879  * \return zero on success, -1 on error.
06880  */
06881 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06882          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06883 {
06884    int cmd = 0;
06885    int retries = 0, prepend_duration = 0, already_recorded = 0;
06886    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06887    char textfile[PATH_MAX];
06888    struct ast_config *msg_cfg;
06889    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06890 #ifndef IMAP_STORAGE
06891    signed char zero_gain = 0;
06892 #endif
06893    const char *duration_str;
06894 
06895    /* Must always populate duration correctly */
06896    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06897    strcpy(textfile, msgfile);
06898    strcpy(backup, msgfile);
06899    strcpy(backup_textfile, msgfile);
06900    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06901    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06902    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06903 
06904    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06905       *duration = atoi(duration_str);
06906    } else {
06907       *duration = 0;
06908    }
06909 
06910    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06911       if (cmd)
06912          retries = 0;
06913       switch (cmd) {
06914       case '1': 
06915 
06916 #ifdef IMAP_STORAGE
06917          /* Record new intro file */
06918          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06919          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06920          ast_play_and_wait(chan, INTRO);
06921          ast_play_and_wait(chan, "beep");
06922          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06923          if (cmd == -1) {
06924             break;
06925          }
06926          cmd = 't';
06927 #else
06928 
06929          /* prepend a message to the current message, update the metadata and return */
06930 
06931          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06932          strcpy(textfile, msgfile);
06933          strncat(textfile, ".txt", sizeof(textfile) - 1);
06934          *duration = 0;
06935 
06936          /* if we can't read the message metadata, stop now */
06937          if (!msg_cfg) {
06938             cmd = 0;
06939             break;
06940          }
06941 
06942          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06943 #ifndef IMAP_STORAGE
06944          if (already_recorded) {
06945             ast_filecopy(backup, msgfile, NULL);
06946             copy(backup_textfile, textfile);
06947          }
06948          else {
06949             ast_filecopy(msgfile, backup, NULL);
06950             copy(textfile, backup_textfile);
06951          }
06952 #endif
06953          already_recorded = 1;
06954 
06955          if (record_gain)
06956             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06957 
06958          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06959 
06960          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06961             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06962             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06963             ast_filerename(backup, msgfile, NULL);
06964          }
06965 
06966          if (record_gain)
06967             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06968 
06969          
06970          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06971             *duration = atoi(duration_str);
06972 
06973          if (prepend_duration) {
06974             struct ast_category *msg_cat;
06975             /* need enough space for a maximum-length message duration */
06976             char duration_buf[12];
06977 
06978             *duration += prepend_duration;
06979             msg_cat = ast_category_get(msg_cfg, "message");
06980             snprintf(duration_buf, 11, "%ld", *duration);
06981             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06982                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06983             }
06984          }
06985 
06986 #endif
06987          break;
06988       case '2': 
06989          /* NULL out introfile so we know there is no intro! */
06990 #ifdef IMAP_STORAGE
06991          *vms->introfn = '\0';
06992 #endif
06993          cmd = 't';
06994          break;
06995       case '*':
06996          cmd = '*';
06997          break;
06998       default: 
06999          /* If time_out and return to menu, reset already_recorded */
07000          already_recorded = 0;
07001 
07002          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
07003             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
07004          if (!cmd) {
07005             cmd = ast_play_and_wait(chan, "vm-starmain");
07006             /* "press star to return to the main menu" */
07007          }
07008          if (!cmd) {
07009             cmd = ast_waitfordigit(chan, 6000);
07010          }
07011          if (!cmd) {
07012             retries++;
07013          }
07014          if (retries > 3) {
07015             cmd = '*'; /* Let's cancel this beast */
07016          }
07017          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07018       }
07019    }
07020 
07021    if (msg_cfg)
07022       ast_config_destroy(msg_cfg);
07023    if (prepend_duration)
07024       *duration = prepend_duration;
07025 
07026    if (already_recorded && cmd == -1) {
07027       /* restore original message if prepention cancelled */
07028       ast_filerename(backup, msgfile, NULL);
07029       rename(backup_textfile, textfile);
07030    }
07031 
07032    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07033       cmd = 0;
07034    return cmd;
07035 }
07036 
07037 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07038 {
07039    struct ast_event *event;
07040    char *mailbox, *context;
07041 
07042    /* Strip off @default */
07043    context = mailbox = ast_strdupa(box);
07044    strsep(&context, "@");
07045    if (ast_strlen_zero(context))
07046       context = "default";
07047 
07048    if (!(event = ast_event_new(AST_EVENT_MWI,
07049          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07050          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07051          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07052          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07053          AST_EVENT_IE_END))) {
07054       return;
07055    }
07056 
07057    ast_event_queue_and_cache(event);
07058 }
07059 
07060 /*!
07061  * \brief Sends email notification that a user has a new voicemail waiting for them.
07062  * \param chan
07063  * \param vmu
07064  * \param vms
07065  * \param msgnum
07066  * \param duration
07067  * \param fmt
07068  * \param cidnum The Caller ID phone number value.
07069  * \param cidname The Caller ID name value.
07070  * \param flag
07071  *
07072  * \return zero on success, -1 on error.
07073  */
07074 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)
07075 {
07076    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07077    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07078    const char *category;
07079    char *myserveremail = serveremail;
07080 
07081    ast_channel_lock(chan);
07082    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07083       category = ast_strdupa(category);
07084    }
07085    ast_channel_unlock(chan);
07086 
07087 #ifndef IMAP_STORAGE
07088    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07089 #else
07090    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07091 #endif
07092    make_file(fn, sizeof(fn), todir, msgnum);
07093    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07094 
07095    if (!ast_strlen_zero(vmu->attachfmt)) {
07096       if (strstr(fmt, vmu->attachfmt))
07097          fmt = vmu->attachfmt;
07098       else
07099          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);
07100    }
07101 
07102    /* Attach only the first format */
07103    fmt = ast_strdupa(fmt);
07104    stringp = fmt;
07105    strsep(&stringp, "|");
07106 
07107    if (!ast_strlen_zero(vmu->serveremail))
07108       myserveremail = vmu->serveremail;
07109 
07110    if (!ast_strlen_zero(vmu->email)) {
07111       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07112 
07113       if (attach_user_voicemail)
07114          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07115 
07116       /* XXX possible imap issue, should category be NULL XXX */
07117       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07118 
07119       if (attach_user_voicemail)
07120          DISPOSE(todir, msgnum);
07121    }
07122 
07123    if (!ast_strlen_zero(vmu->pager)) {
07124       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07125    }
07126 
07127    if (ast_test_flag(vmu, VM_DELETE))
07128       DELETE(todir, msgnum, fn, vmu);
07129 
07130    /* Leave voicemail for someone */
07131    if (ast_app_has_voicemail(ext_context, NULL)) 
07132       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07133 
07134    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07135 
07136    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);
07137    run_externnotify(vmu->context, vmu->mailbox, flag);
07138 
07139 #ifdef IMAP_STORAGE
07140    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07141    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07142       vm_imap_delete(NULL, vms->curmsg, vmu);
07143       vms->newmessages--;  /* Fix new message count */
07144    }
07145 #endif
07146 
07147    return 0;
07148 }
07149 
07150 /*!
07151  * \brief Sends a voicemail message to a mailbox recipient.
07152  * \param chan
07153  * \param context
07154  * \param vms
07155  * \param sender
07156  * \param fmt
07157  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07158  *             Will be 0 when called to forward an existing message (option 8)
07159  *             Will be 1 when called to leave a message (option 3->5)
07160  * \param record_gain 
07161  * \param urgent
07162  *
07163  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07164  * 
07165  * When in the leave message mode (is_new_message == 1):
07166  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07167  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07168  *
07169  * When in the forward message mode (is_new_message == 0):
07170  *   - retreives the current message to be forwarded
07171  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07172  *   - determines the target mailbox and folders
07173  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07174  *
07175  * \return zero on success, -1 on error.
07176  */
07177 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)
07178 {
07179 #ifdef IMAP_STORAGE
07180    int todircount = 0;
07181    struct vm_state *dstvms;
07182 #endif
07183    char username[70]="";
07184    char fn[PATH_MAX]; /* for playback of name greeting */
07185    char ecodes[16] = "#";
07186    int res = 0, cmd = 0;
07187    struct ast_vm_user *receiver = NULL, *vmtmp;
07188    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07189    char *stringp;
07190    const char *s;
07191    int saved_messages = 0;
07192    int valid_extensions = 0;
07193    char *dir;
07194    int curmsg;
07195    char urgent_str[7] = "";
07196    int prompt_played = 0;
07197 #ifndef IMAP_STORAGE
07198    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07199 #endif
07200    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07201       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07202    }
07203 
07204    if (vms == NULL) return -1;
07205    dir = vms->curdir;
07206    curmsg = vms->curmsg;
07207 
07208    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07209    while (!res && !valid_extensions) {
07210       int use_directory = 0;
07211       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07212          int done = 0;
07213          int retries = 0;
07214          cmd = 0;
07215          while ((cmd >= 0) && !done ){
07216             if (cmd)
07217                retries = 0;
07218             switch (cmd) {
07219             case '1': 
07220                use_directory = 0;
07221                done = 1;
07222                break;
07223             case '2': 
07224                use_directory = 1;
07225                done = 1;
07226                break;
07227             case '*': 
07228                cmd = 't';
07229                done = 1;
07230                break;
07231             default: 
07232                /* Press 1 to enter an extension press 2 to use the directory */
07233                cmd = ast_play_and_wait(chan, "vm-forward");
07234                if (!cmd) {
07235                   cmd = ast_waitfordigit(chan, 3000);
07236                }
07237                if (!cmd) {
07238                   retries++;
07239                }
07240                if (retries > 3) {
07241                   cmd = 't';
07242                   done = 1;
07243                }
07244                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07245             }
07246          }
07247          if (cmd < 0 || cmd == 't')
07248             break;
07249       }
07250       
07251       if (use_directory) {
07252          /* use app_directory */
07253          
07254          char old_context[sizeof(chan->context)];
07255          char old_exten[sizeof(chan->exten)];
07256          int old_priority;
07257          struct ast_app* directory_app;
07258 
07259          directory_app = pbx_findapp("Directory");
07260          if (directory_app) {
07261             char vmcontext[256];
07262             /* make backup copies */
07263             memcpy(old_context, chan->context, sizeof(chan->context));
07264             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07265             old_priority = chan->priority;
07266             
07267             /* call the the Directory, changes the channel */
07268             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07269             res = pbx_exec(chan, directory_app, vmcontext);
07270             
07271             ast_copy_string(username, chan->exten, sizeof(username));
07272             
07273             /* restore the old context, exten, and priority */
07274             memcpy(chan->context, old_context, sizeof(chan->context));
07275             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07276             chan->priority = old_priority;
07277          } else {
07278             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07279             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07280          }
07281       } else {
07282          /* Ask for an extension */
07283          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07284          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07285          prompt_played++;
07286          if (res || prompt_played > 4)
07287             break;
07288          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07289             break;
07290       }
07291       
07292       /* start all over if no username */
07293       if (ast_strlen_zero(username))
07294          continue;
07295       stringp = username;
07296       s = strsep(&stringp, "*");
07297       /* start optimistic */
07298       valid_extensions = 1;
07299       while (s) {
07300          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07301             int oldmsgs;
07302             int newmsgs;
07303             int capacity;
07304             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07305                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07306                /* Shouldn't happen, but allow trying another extension if it does */
07307                res = ast_play_and_wait(chan, "pbx-invalid");
07308                valid_extensions = 0;
07309                break;
07310             }
07311             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07312             if ((newmsgs + oldmsgs) >= capacity) {
07313                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07314                res = ast_play_and_wait(chan, "vm-mailboxfull");
07315                valid_extensions = 0;
07316                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07317                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07318                   free_user(vmtmp);
07319                }
07320                inprocess_count(receiver->mailbox, receiver->context, -1);
07321                break;
07322             }
07323             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07324          } else {
07325             /* XXX Optimization for the future.  When we encounter a single bad extension,
07326              * bailing out on all of the extensions may not be the way to go.  We should
07327              * probably just bail on that single extension, then allow the user to enter
07328              * several more. XXX
07329              */
07330             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07331                free_user(receiver);
07332             }
07333             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07334             /* "I am sorry, that's not a valid extension.  Please try again." */
07335             res = ast_play_and_wait(chan, "pbx-invalid");
07336             valid_extensions = 0;
07337             break;
07338          }
07339 
07340          /* play name if available, else play extension number */
07341          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07342          RETRIEVE(fn, -1, s, receiver->context);
07343          if (ast_fileexists(fn, NULL, NULL) > 0) {
07344             res = ast_stream_and_wait(chan, fn, ecodes);
07345             if (res) {
07346                DISPOSE(fn, -1);
07347                return res;
07348             }
07349          } else {
07350             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07351          }
07352          DISPOSE(fn, -1);
07353 
07354          s = strsep(&stringp, "*");
07355       }
07356       /* break from the loop of reading the extensions */
07357       if (valid_extensions)
07358          break;
07359    }
07360    /* check if we're clear to proceed */
07361    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07362       return res;
07363    if (is_new_message == 1) {
07364       struct leave_vm_options leave_options;
07365       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07366       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07367 
07368       /* Send VoiceMail */
07369       memset(&leave_options, 0, sizeof(leave_options));
07370       leave_options.record_gain = record_gain;
07371       cmd = leave_voicemail(chan, mailbox, &leave_options);
07372    } else {
07373       /* Forward VoiceMail */
07374       long duration = 0;
07375       struct vm_state vmstmp;
07376       int copy_msg_result = 0;
07377       memcpy(&vmstmp, vms, sizeof(vmstmp));
07378 
07379       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07380 
07381       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07382       if (!cmd) {
07383          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07384 #ifdef IMAP_STORAGE
07385             int attach_user_voicemail;
07386             char *myserveremail = serveremail;
07387             
07388             /* get destination mailbox */
07389             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07390             if (!dstvms) {
07391                dstvms = create_vm_state_from_user(vmtmp);
07392             }
07393             if (dstvms) {
07394                init_mailstream(dstvms, 0);
07395                if (!dstvms->mailstream) {
07396                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07397                } else {
07398                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07399                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07400                }
07401             } else {
07402                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07403             }
07404             if (!ast_strlen_zero(vmtmp->serveremail))
07405                myserveremail = vmtmp->serveremail;
07406             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07407             /* NULL category for IMAP storage */
07408             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07409                dstvms->curbox,
07410                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07411                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07412                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07413                NULL, urgent_str);
07414 #else
07415             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07416 #endif
07417             saved_messages++;
07418             AST_LIST_REMOVE_CURRENT(list);
07419             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07420             free_user(vmtmp);
07421             if (res)
07422                break;
07423          }
07424          AST_LIST_TRAVERSE_SAFE_END;
07425          if (saved_messages > 0 && !copy_msg_result) {
07426             /* give confirmation that the message was saved */
07427             /* commented out since we can't forward batches yet
07428             if (saved_messages == 1)
07429                res = ast_play_and_wait(chan, "vm-message");
07430             else
07431                res = ast_play_and_wait(chan, "vm-messages");
07432             if (!res)
07433                res = ast_play_and_wait(chan, "vm-saved"); */
07434 #ifdef IMAP_STORAGE
07435             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07436             if (ast_strlen_zero(vmstmp.introfn))
07437 #endif
07438             res = ast_play_and_wait(chan, "vm-msgsaved");
07439          }
07440 #ifndef IMAP_STORAGE
07441          else {
07442             /* with IMAP, mailbox full warning played by imap_check_limits */
07443             res = ast_play_and_wait(chan, "vm-mailboxfull");
07444          }
07445          /* Restore original message without prepended message if backup exists */
07446          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07447          strcpy(textfile, msgfile);
07448          strcpy(backup, msgfile);
07449          strcpy(backup_textfile, msgfile);
07450          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07451          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07452          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07453          if (ast_fileexists(backup, NULL, NULL) > 0) {
07454             ast_filerename(backup, msgfile, NULL);
07455             rename(backup_textfile, textfile);
07456          }
07457 #endif
07458       }
07459       DISPOSE(dir, curmsg);
07460 #ifndef IMAP_STORAGE
07461       if (cmd) { /* assuming hangup, cleanup backup file */
07462          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07463          strcpy(textfile, msgfile);
07464          strcpy(backup_textfile, msgfile);
07465          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07466          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07467          rename(backup_textfile, textfile);
07468       }
07469 #endif
07470    }
07471 
07472    /* If anything failed above, we still have this list to free */
07473    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07474       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07475       free_user(vmtmp);
07476    }
07477    return res ? res : cmd;
07478 }
07479 
07480 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07481 {
07482    int res;
07483    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07484       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07485    return res;
07486 }
07487 
07488 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07489 {
07490    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07491    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);
07492 }
07493 
07494 static int play_message_category(struct ast_channel *chan, const char *category)
07495 {
07496    int res = 0;
07497 
07498    if (!ast_strlen_zero(category))
07499       res = ast_play_and_wait(chan, category);
07500 
07501    if (res) {
07502       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07503       res = 0;
07504    }
07505 
07506    return res;
07507 }
07508 
07509 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07510 {
07511    int res = 0;
07512    struct vm_zone *the_zone = NULL;
07513    time_t t;
07514 
07515    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07516       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07517       return 0;
07518    }
07519 
07520    /* Does this user have a timezone specified? */
07521    if (!ast_strlen_zero(vmu->zonetag)) {
07522       /* Find the zone in the list */
07523       struct vm_zone *z;
07524       AST_LIST_LOCK(&zones);
07525       AST_LIST_TRAVERSE(&zones, z, list) {
07526          if (!strcmp(z->name, vmu->zonetag)) {
07527             the_zone = z;
07528             break;
07529          }
07530       }
07531       AST_LIST_UNLOCK(&zones);
07532    }
07533 
07534 /* No internal variable parsing for now, so we'll comment it out for the time being */
07535 #if 0
07536    /* Set the DIFF_* variables */
07537    ast_localtime(&t, &time_now, NULL);
07538    tv_now = ast_tvnow();
07539    ast_localtime(&tv_now, &time_then, NULL);
07540 
07541    /* Day difference */
07542    if (time_now.tm_year == time_then.tm_year)
07543       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07544    else
07545       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07546    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07547 
07548    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07549 #endif
07550    if (the_zone) {
07551       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07552    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07553       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07554    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07555       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07556    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07557       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);
07558    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07559       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07560    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07561       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07562    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07563       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07564    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07565       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);
07566    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07567       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07568    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07569       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07570    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07571       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);
07572    } else {
07573       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07574    }
07575 #if 0
07576    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07577 #endif
07578    return res;
07579 }
07580 
07581 
07582 
07583 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07584 {
07585    int res = 0;
07586    int i;
07587    char *callerid, *name;
07588    char prefile[PATH_MAX] = "";
07589    
07590 
07591    /* If voicemail cid is not enabled, or we didn't get cid or context from
07592     * the attribute file, leave now.
07593     *
07594     * TODO Still need to change this so that if this function is called by the
07595     * message envelope (and someone is explicitly requesting to hear the CID),
07596     * it does not check to see if CID is enabled in the config file.
07597     */
07598    if ((cid == NULL)||(context == NULL))
07599       return res;
07600 
07601    /* Strip off caller ID number from name */
07602    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07603    ast_callerid_parse(cid, &name, &callerid);
07604    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07605       /* Check for internal contexts and only */
07606       /* say extension when the call didn't come from an internal context in the list */
07607       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07608          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07609          if ((strcmp(cidinternalcontexts[i], context) == 0))
07610             break;
07611       }
07612       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07613          if (!res) {
07614             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07615             if (!ast_strlen_zero(prefile)) {
07616             /* See if we can find a recorded name for this person instead of their extension number */
07617                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07618                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07619                   if (!callback)
07620                      res = wait_file2(chan, vms, "vm-from");
07621                   res = ast_stream_and_wait(chan, prefile, "");
07622                } else {
07623                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07624                   /* Say "from extension" as one saying to sound smoother */
07625                   if (!callback)
07626                      res = wait_file2(chan, vms, "vm-from-extension");
07627                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07628                }
07629             }
07630          }
07631       } else if (!res) {
07632          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07633          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07634          if (!callback)
07635             res = wait_file2(chan, vms, "vm-from-phonenumber");
07636          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07637       }
07638    } else {
07639       /* Number unknown */
07640       ast_debug(1, "VM-CID: From an unknown number\n");
07641       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07642       res = wait_file2(chan, vms, "vm-unknown-caller");
07643    }
07644    return res;
07645 }
07646 
07647 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07648 {
07649    int res = 0;
07650    int durationm;
07651    int durations;
07652    /* Verify that we have a duration for the message */
07653    if (duration == NULL)
07654       return res;
07655 
07656    /* Convert from seconds to minutes */
07657    durations = atoi(duration);
07658    durationm = (durations / 60);
07659 
07660    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07661 
07662    if ((!res) && (durationm >= minduration)) {
07663       res = wait_file2(chan, vms, "vm-duration");
07664 
07665       /* POLISH syntax */
07666       if (!strncasecmp(chan->language, "pl", 2)) {
07667          div_t num = div(durationm, 10);
07668 
07669          if (durationm == 1) {
07670             res = ast_play_and_wait(chan, "digits/1z");
07671             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07672          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07673             if (num.rem == 2) {
07674                if (!num.quot) {
07675                   res = ast_play_and_wait(chan, "digits/2-ie");
07676                } else {
07677                   res = say_and_wait(chan, durationm - 2 , chan->language);
07678                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07679                }
07680             } else {
07681                res = say_and_wait(chan, durationm, chan->language);
07682             }
07683             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07684          } else {
07685             res = say_and_wait(chan, durationm, chan->language);
07686             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07687          }
07688       /* DEFAULT syntax */
07689       } else {
07690          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07691          res = wait_file2(chan, vms, "vm-minutes");
07692       }
07693    }
07694    return res;
07695 }
07696 
07697 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07698 {
07699    int res = 0;
07700    char filename[256], *cid;
07701    const char *origtime, *context, *category, *duration, *flag;
07702    struct ast_config *msg_cfg;
07703    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07704 
07705    vms->starting = 0;
07706    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07707    adsi_message(chan, vms);
07708    if (!vms->curmsg) {
07709       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07710    } else if (vms->curmsg == vms->lastmsg) {
07711       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07712    }
07713 
07714    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07715    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07716    msg_cfg = ast_config_load(filename, config_flags);
07717    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07718       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07719       return 0;
07720    }
07721    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07722 
07723    /* Play the word urgent if we are listening to urgent messages */
07724    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07725       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07726    }
07727 
07728    if (!res) {
07729       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07730       /* POLISH syntax */
07731       if (!strncasecmp(chan->language, "pl", 2)) {
07732          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07733             int ten, one;
07734             char nextmsg[256];
07735             ten = (vms->curmsg + 1) / 10;
07736             one = (vms->curmsg + 1) % 10;
07737 
07738             if (vms->curmsg < 20) {
07739                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07740                res = wait_file2(chan, vms, nextmsg);
07741             } else {
07742                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07743                res = wait_file2(chan, vms, nextmsg);
07744                if (one > 0) {
07745                   if (!res) {
07746                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07747                      res = wait_file2(chan, vms, nextmsg);
07748                   }
07749                }
07750             }
07751          }
07752          if (!res)
07753             res = wait_file2(chan, vms, "vm-message");
07754       /* HEBREW syntax */
07755       } else if (!strncasecmp(chan->language, "he", 2)) {
07756          if (!vms->curmsg) {
07757             res = wait_file2(chan, vms, "vm-message");
07758             res = wait_file2(chan, vms, "vm-first");
07759          } else if (vms->curmsg == vms->lastmsg) {
07760             res = wait_file2(chan, vms, "vm-message");
07761             res = wait_file2(chan, vms, "vm-last");
07762          } else {
07763             res = wait_file2(chan, vms, "vm-message");
07764             res = wait_file2(chan, vms, "vm-number");
07765             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07766          }
07767       /* VIETNAMESE syntax */
07768       } else if (!strncasecmp(chan->language, "vi", 2)) {
07769          if (!vms->curmsg) {
07770             res = wait_file2(chan, vms, "vm-message");
07771             res = wait_file2(chan, vms, "vm-first");
07772          } else if (vms->curmsg == vms->lastmsg) {
07773             res = wait_file2(chan, vms, "vm-message");
07774             res = wait_file2(chan, vms, "vm-last");
07775          } else {
07776             res = wait_file2(chan, vms, "vm-message");
07777             res = wait_file2(chan, vms, "vm-number");
07778             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07779          }
07780       } else {
07781          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07782             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07783          } else { /* DEFAULT syntax */
07784             res = wait_file2(chan, vms, "vm-message");
07785          }
07786          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07787             if (!res) {
07788                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07789                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07790             }
07791          }
07792       }
07793    }
07794 
07795    if (!msg_cfg) {
07796       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07797       return 0;
07798    }
07799 
07800    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07801       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07802       DISPOSE(vms->curdir, vms->curmsg);
07803       ast_config_destroy(msg_cfg);
07804       return 0;
07805    }
07806 
07807    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07808    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07809    category = ast_variable_retrieve(msg_cfg, "message", "category");
07810 
07811    context = ast_variable_retrieve(msg_cfg, "message", "context");
07812    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07813       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07814    if (!res) {
07815       res = play_message_category(chan, category);
07816    }
07817    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07818       res = play_message_datetime(chan, vmu, origtime, filename);
07819    }
07820    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07821       res = play_message_callerid(chan, vms, cid, context, 0);
07822    }
07823    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07824       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07825    }
07826    /* Allow pressing '1' to skip envelope / callerid */
07827    if (res == '1') {
07828       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07829       res = 0;
07830    }
07831    ast_config_destroy(msg_cfg);
07832 
07833    if (!res) {
07834       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07835 #ifdef IMAP_STORAGE
07836       ast_mutex_lock(&vms->lock);
07837 #endif
07838       vms->heard[vms->curmsg] = 1;
07839 #ifdef IMAP_STORAGE
07840       ast_mutex_unlock(&vms->lock);
07841       /*IMAP storage stores any prepended message from a forward
07842        * as a separate file from the rest of the message
07843        */
07844       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07845          wait_file(chan, vms, vms->introfn);
07846       }
07847 #endif
07848       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07849          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07850          res = 0;
07851       }
07852       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07853    }
07854    DISPOSE(vms->curdir, vms->curmsg);
07855    return res;
07856 }
07857 
07858 #ifdef IMAP_STORAGE
07859 static int imap_remove_file(char *dir, int msgnum)
07860 {
07861    char fn[PATH_MAX];
07862    char full_fn[PATH_MAX];
07863    char intro[PATH_MAX] = {0,};
07864    
07865    if (msgnum > -1) {
07866       make_file(fn, sizeof(fn), dir, msgnum);
07867       snprintf(intro, sizeof(intro), "%sintro", fn);
07868    } else
07869       ast_copy_string(fn, dir, sizeof(fn));
07870    
07871    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07872       ast_filedelete(fn, NULL);
07873       if (!ast_strlen_zero(intro)) {
07874          ast_filedelete(intro, NULL);
07875       }
07876       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07877       unlink(full_fn);
07878    }
07879    return 0;
07880 }
07881 
07882 
07883 
07884 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07885 {
07886    char *file, *filename;
07887    char *attachment;
07888    char arg[10];
07889    int i;
07890    BODY* body;
07891 
07892    file = strrchr(ast_strdupa(dir), '/');
07893    if (file) {
07894       *file++ = '\0';
07895    } else {
07896       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07897       return -1;
07898    }
07899 
07900    ast_mutex_lock(&vms->lock);
07901    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07902       mail_fetchstructure(vms->mailstream, i + 1, &body);
07903       /* We have the body, now we extract the file name of the first attachment. */
07904       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07905          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07906       } else {
07907          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07908          ast_mutex_unlock(&vms->lock);
07909          return -1;
07910       }
07911       filename = strsep(&attachment, ".");
07912       if (!strcmp(filename, file)) {
07913          sprintf(arg, "%d", i + 1);
07914          mail_setflag(vms->mailstream, arg, "\\DELETED");
07915       }
07916    }
07917    mail_expunge(vms->mailstream);
07918    ast_mutex_unlock(&vms->lock);
07919    return 0;
07920 }
07921 
07922 #elif !defined(IMAP_STORAGE)
07923 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07924 {
07925    int count_msg, last_msg;
07926 
07927    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07928 
07929    /* Rename the member vmbox HERE so that we don't try to return before
07930     * we know what's going on.
07931     */
07932    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07933 
07934    /* Faster to make the directory than to check if it exists. */
07935    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07936 
07937    /* traverses directory using readdir (or select query for ODBC) */
07938    count_msg = count_messages(vmu, vms->curdir);
07939    if (count_msg < 0) {
07940       return count_msg;
07941    } else {
07942       vms->lastmsg = count_msg - 1;
07943    }
07944 
07945    if (vm_allocate_dh(vms, vmu, count_msg)) {
07946       return -1;
07947    }
07948 
07949    /*
07950    The following test is needed in case sequencing gets messed up.
07951    There appears to be more than one way to mess up sequence, so
07952    we will not try to find all of the root causes--just fix it when
07953    detected.
07954    */
07955 
07956    if (vm_lock_path(vms->curdir)) {
07957       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07958       return ERROR_LOCK_PATH;
07959    }
07960 
07961    /* for local storage, checks directory for messages up to maxmsg limit */
07962    last_msg = last_message_index(vmu, vms->curdir);
07963    ast_unlock_path(vms->curdir);
07964 
07965    if (last_msg < -1) {
07966       return last_msg;
07967    } else if (vms->lastmsg != last_msg) {
07968       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);
07969       resequence_mailbox(vmu, vms->curdir, count_msg);
07970    }
07971 
07972    return 0;
07973 }
07974 #endif
07975 
07976 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07977 {
07978    int x = 0;
07979    int last_msg_idx = 0;
07980 
07981 #ifndef IMAP_STORAGE
07982    int res = 0, nummsg;
07983    char fn2[PATH_MAX];
07984 #endif
07985 
07986    if (vms->lastmsg <= -1) {
07987       goto done;
07988    }
07989 
07990    vms->curmsg = -1;
07991 #ifndef IMAP_STORAGE
07992    /* Get the deleted messages fixed */
07993    if (vm_lock_path(vms->curdir)) {
07994       return ERROR_LOCK_PATH;
07995    }
07996 
07997    /* update count as message may have arrived while we've got mailbox open */
07998    last_msg_idx = last_message_index(vmu, vms->curdir);
07999    if (last_msg_idx != vms->lastmsg) {
08000       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
08001    }
08002 
08003    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
08004    for (x = 0; x < last_msg_idx + 1; x++) {
08005       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
08006          /* Save this message.  It's not in INBOX or hasn't been heard */
08007          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08008          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
08009             break;
08010          }
08011          vms->curmsg++;
08012          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
08013          if (strcmp(vms->fn, fn2)) {
08014             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
08015          }
08016       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
08017          /* Move to old folder before deleting */
08018          res = save_to_folder(vmu, vms, x, 1);
08019          if (res == ERROR_LOCK_PATH) {
08020             /* If save failed do not delete the message */
08021             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
08022             vms->deleted[x] = 0;
08023             vms->heard[x] = 0;
08024             --x;
08025          }
08026       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08027          /* Move to deleted folder */
08028          res = save_to_folder(vmu, vms, x, 10);
08029          if (res == ERROR_LOCK_PATH) {
08030             /* If save failed do not delete the message */
08031             vms->deleted[x] = 0;
08032             vms->heard[x] = 0;
08033             --x;
08034          }
08035       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08036          /* If realtime storage enabled - we should explicitly delete this message,
08037          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08038          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08039          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08040             DELETE(vms->curdir, x, vms->fn, vmu);
08041          }
08042       }
08043    }
08044 
08045    /* Delete ALL remaining messages */
08046    nummsg = x - 1;
08047    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08048       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08049       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08050          DELETE(vms->curdir, x, vms->fn, vmu);
08051       }
08052    }
08053    ast_unlock_path(vms->curdir);
08054 #else /* defined(IMAP_STORAGE) */
08055    ast_mutex_lock(&vms->lock);
08056    if (vms->deleted) {
08057       /* Since we now expunge after each delete, deleting in reverse order
08058        * ensures that no reordering occurs between each step. */
08059       last_msg_idx = vms->dh_arraysize;
08060       for (x = last_msg_idx - 1; x >= 0; x--) {
08061          if (vms->deleted[x]) {
08062             ast_debug(3, "IMAP delete of %d\n", x);
08063             DELETE(vms->curdir, x, vms->fn, vmu);
08064          }
08065       }
08066    }
08067 #endif
08068 
08069 done:
08070    if (vms->deleted) {
08071       ast_free(vms->deleted);
08072       vms->deleted = NULL;
08073    }
08074    if (vms->heard) {
08075       ast_free(vms->heard);
08076       vms->heard = NULL;
08077    }
08078    vms->dh_arraysize = 0;
08079 #ifdef IMAP_STORAGE
08080    ast_mutex_unlock(&vms->lock);
08081 #endif
08082 
08083    return 0;
08084 }
08085 
08086 /* In Greek even though we CAN use a syntax like "friends messages"
08087  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08088  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08089  * syntax for the above three categories which is more elegant.
08090  */
08091 
08092 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08093 {
08094    int cmd;
08095    char *buf;
08096 
08097    buf = ast_alloca(strlen(box) + 2);
08098    strcpy(buf, box);
08099    strcat(buf, "s");
08100 
08101    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08102       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08103       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08104    } else {
08105       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08106       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08107    }
08108 }
08109 
08110 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08111 {
08112    int cmd;
08113 
08114    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08115       if (!strcasecmp(box, "vm-INBOX"))
08116          cmd = ast_play_and_wait(chan, "vm-new-e");
08117       else
08118          cmd = ast_play_and_wait(chan, "vm-old-e");
08119       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08120    } else {
08121       cmd = ast_play_and_wait(chan, "vm-messages");
08122       return cmd ? cmd : ast_play_and_wait(chan, box);
08123    }
08124 }
08125 
08126 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08127 {
08128    int cmd;
08129 
08130    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08131       cmd = ast_play_and_wait(chan, "vm-messages");
08132       return cmd ? cmd : ast_play_and_wait(chan, box);
08133    } else {
08134       cmd = ast_play_and_wait(chan, box);
08135       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08136    }
08137 }
08138 
08139 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08140 {
08141    int cmd;
08142 
08143    if (  !strncasecmp(chan->language, "it", 2) ||
08144         !strncasecmp(chan->language, "es", 2) ||
08145         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08146       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08147       return cmd ? cmd : ast_play_and_wait(chan, box);
08148    } else if (!strncasecmp(chan->language, "gr", 2)) {
08149       return vm_play_folder_name_gr(chan, box);
08150    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08151       return ast_play_and_wait(chan, box);
08152    } else if (!strncasecmp(chan->language, "pl", 2)) {
08153       return vm_play_folder_name_pl(chan, box);
08154    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08155       return vm_play_folder_name_ua(chan, box);
08156    } else if (!strncasecmp(chan->language, "vi", 2)) {
08157       return ast_play_and_wait(chan, box);
08158    } else {  /* Default English */
08159       cmd = ast_play_and_wait(chan, box);
08160       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08161    }
08162 }
08163 
08164 /* GREEK SYNTAX
08165    In greek the plural for old/new is
08166    different so we need the following files
08167    We also need vm-denExeteMynhmata because
08168    this syntax is different.
08169 
08170    -> vm-Olds.wav : "Palia"
08171    -> vm-INBOXs.wav : "Nea"
08172    -> vm-denExeteMynhmata : "den exete mynhmata"
08173 */
08174 
08175 
08176 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08177 {
08178    int res = 0;
08179 
08180    if (vms->newmessages) {
08181       res = ast_play_and_wait(chan, "vm-youhave");
08182       if (!res) 
08183          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08184       if (!res) {
08185          if ((vms->newmessages == 1)) {
08186             res = ast_play_and_wait(chan, "vm-INBOX");
08187             if (!res)
08188                res = ast_play_and_wait(chan, "vm-message");
08189          } else {
08190             res = ast_play_and_wait(chan, "vm-INBOXs");
08191             if (!res)
08192                res = ast_play_and_wait(chan, "vm-messages");
08193          }
08194       }
08195    } else if (vms->oldmessages){
08196       res = ast_play_and_wait(chan, "vm-youhave");
08197       if (!res)
08198          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08199       if ((vms->oldmessages == 1)){
08200          res = ast_play_and_wait(chan, "vm-Old");
08201          if (!res)
08202             res = ast_play_and_wait(chan, "vm-message");
08203       } else {
08204          res = ast_play_and_wait(chan, "vm-Olds");
08205          if (!res)
08206             res = ast_play_and_wait(chan, "vm-messages");
08207       }
08208    } else if (!vms->oldmessages && !vms->newmessages) 
08209       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08210    return res;
08211 }
08212 
08213 /* Version of vm_intro() designed to work for many languages.
08214  *
08215  * It is hoped that this function can prevent the proliferation of 
08216  * language-specific vm_intro() functions and in time replace the language-
08217  * specific functions which already exist.  An examination of the language-
08218  * specific functions revealed that they all corrected the same deficiencies
08219  * in vm_intro_en() (which was the default function). Namely:
08220  *
08221  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08222  *     wording of the voicemail greeting hides this problem.  For example,
08223  *     vm-INBOX contains only the word "new".  This means that both of these
08224  *     sequences produce valid utterances:
08225  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08226  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08227  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08228  *     in many languages) the first utterance becomes "you have 1 the new message".
08229  *  2) The function contains hardcoded rules for pluralizing the word "message".
08230  *     These rules are correct for English, but not for many other languages.
08231  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08232  *     required in many languages.
08233  *  4) The gender of the word for "message" is not specified. This is a problem
08234  *     because in many languages the gender of the number in phrases such
08235  *     as "you have one new message" must match the gender of the word
08236  *     meaning "message".
08237  *
08238  * Fixing these problems for each new language has meant duplication of effort.
08239  * This new function solves the problems in the following general ways:
08240  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08241  *     and vm-Old respectively for those languages where it makes sense.
08242  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08243  *     on vm-message.
08244  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08245  *     prefix on vm-new and vm-old (none for English).
08246  *  4) Pass the gender of the language's word for "message" as an agument to
08247  *     this function which is can in turn pass on to the functions which 
08248  *     say numbers and put endings on nounds and adjectives.
08249  *
08250  * All languages require these messages:
08251  *  vm-youhave    "You have..."
08252  *  vm-and     "and"
08253  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08254  *
08255  * To use it for English, you will need these additional sound files:
08256  *  vm-new     "new"
08257  *  vm-message    "message", singular
08258  *  vm-messages      "messages", plural
08259  *
08260  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08261  *
08262  *  vm-newn    "novoye" (singular, neuter)
08263  *  vm-newx    "novikh" (counting plural form, genative plural)
08264  *  vm-message    "sobsheniye" (singular form)
08265  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08266  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08267  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08268  *  digits/2n     "dva" (neuter singular)
08269  */
08270 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08271 {
08272    int res;
08273    int lastnum = 0;
08274 
08275    res = ast_play_and_wait(chan, "vm-youhave");
08276 
08277    if (!res && vms->newmessages) {
08278       lastnum = vms->newmessages;
08279 
08280       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08281          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08282       }
08283 
08284       if (!res && vms->oldmessages) {
08285          res = ast_play_and_wait(chan, "vm-and");
08286       }
08287    }
08288 
08289    if (!res && vms->oldmessages) {
08290       lastnum = vms->oldmessages;
08291 
08292       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08293          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08294       }
08295    }
08296 
08297    if (!res) {
08298       if (lastnum == 0) {
08299          res = ast_play_and_wait(chan, "vm-no");
08300       }
08301       if (!res) {
08302          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08303       }
08304    }
08305 
08306    return res;
08307 }
08308 
08309 /* Default Hebrew syntax */
08310 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08311 {
08312    int res = 0;
08313 
08314    /* Introduce messages they have */
08315    if (!res) {
08316       if ((vms->newmessages) || (vms->oldmessages)) {
08317          res = ast_play_and_wait(chan, "vm-youhave");
08318       }
08319       /*
08320        * The word "shtei" refers to the number 2 in hebrew when performing a count
08321        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08322        * an element, this is one of them.
08323        */
08324       if (vms->newmessages) {
08325          if (!res) {
08326             if (vms->newmessages == 1) {
08327                res = ast_play_and_wait(chan, "vm-INBOX1");
08328             } else {
08329                if (vms->newmessages == 2) {
08330                   res = ast_play_and_wait(chan, "vm-shtei");
08331                } else {
08332                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08333                }
08334                res = ast_play_and_wait(chan, "vm-INBOX");
08335             }
08336          }
08337          if (vms->oldmessages && !res) {
08338             res = ast_play_and_wait(chan, "vm-and");
08339             if (vms->oldmessages == 1) {
08340                res = ast_play_and_wait(chan, "vm-Old1");
08341             } else {
08342                if (vms->oldmessages == 2) {
08343                   res = ast_play_and_wait(chan, "vm-shtei");
08344                } else {
08345                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08346                }
08347                res = ast_play_and_wait(chan, "vm-Old");
08348             }
08349          }
08350       }
08351       if (!res && vms->oldmessages && !vms->newmessages) {
08352          if (!res) {
08353             if (vms->oldmessages == 1) {
08354                res = ast_play_and_wait(chan, "vm-Old1");
08355             } else {
08356                if (vms->oldmessages == 2) {
08357                   res = ast_play_and_wait(chan, "vm-shtei");
08358                } else {
08359                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08360                }
08361                res = ast_play_and_wait(chan, "vm-Old");
08362             }
08363          }
08364       }
08365       if (!res) {
08366          if (!vms->oldmessages && !vms->newmessages) {
08367             if (!res) {
08368                res = ast_play_and_wait(chan, "vm-nomessages");
08369             }
08370          }
08371       }
08372    }
08373    return res;
08374 }
08375    
08376 /* Default English syntax */
08377 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08378 {
08379    int res;
08380 
08381    /* Introduce messages they have */
08382    res = ast_play_and_wait(chan, "vm-youhave");
08383    if (!res) {
08384       if (vms->urgentmessages) {
08385          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08386          if (!res)
08387             res = ast_play_and_wait(chan, "vm-Urgent");
08388          if ((vms->oldmessages || vms->newmessages) && !res) {
08389             res = ast_play_and_wait(chan, "vm-and");
08390          } else if (!res) {
08391             if ((vms->urgentmessages == 1))
08392                res = ast_play_and_wait(chan, "vm-message");
08393             else
08394                res = ast_play_and_wait(chan, "vm-messages");
08395          }
08396       }
08397       if (vms->newmessages) {
08398          res = say_and_wait(chan, vms->newmessages, chan->language);
08399          if (!res)
08400             res = ast_play_and_wait(chan, "vm-INBOX");
08401          if (vms->oldmessages && !res)
08402             res = ast_play_and_wait(chan, "vm-and");
08403          else if (!res) {
08404             if ((vms->newmessages == 1))
08405                res = ast_play_and_wait(chan, "vm-message");
08406             else
08407                res = ast_play_and_wait(chan, "vm-messages");
08408          }
08409             
08410       }
08411       if (!res && vms->oldmessages) {
08412          res = say_and_wait(chan, vms->oldmessages, chan->language);
08413          if (!res)
08414             res = ast_play_and_wait(chan, "vm-Old");
08415          if (!res) {
08416             if (vms->oldmessages == 1)
08417                res = ast_play_and_wait(chan, "vm-message");
08418             else
08419                res = ast_play_and_wait(chan, "vm-messages");
08420          }
08421       }
08422       if (!res) {
08423          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08424             res = ast_play_and_wait(chan, "vm-no");
08425             if (!res)
08426                res = ast_play_and_wait(chan, "vm-messages");
08427          }
08428       }
08429    }
08430    return res;
08431 }
08432 
08433 /* ITALIAN syntax */
08434 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08435 {
08436    /* Introduce messages they have */
08437    int res;
08438    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08439       res = ast_play_and_wait(chan, "vm-no") ||
08440          ast_play_and_wait(chan, "vm-message");
08441    else
08442       res = ast_play_and_wait(chan, "vm-youhave");
08443    if (!res && vms->newmessages) {
08444       res = (vms->newmessages == 1) ?
08445          ast_play_and_wait(chan, "digits/un") ||
08446          ast_play_and_wait(chan, "vm-nuovo") ||
08447          ast_play_and_wait(chan, "vm-message") :
08448          /* 2 or more new messages */
08449          say_and_wait(chan, vms->newmessages, chan->language) ||
08450          ast_play_and_wait(chan, "vm-nuovi") ||
08451          ast_play_and_wait(chan, "vm-messages");
08452       if (!res && vms->oldmessages)
08453          res = ast_play_and_wait(chan, "vm-and");
08454    }
08455    if (!res && vms->oldmessages) {
08456       res = (vms->oldmessages == 1) ?
08457          ast_play_and_wait(chan, "digits/un") ||
08458          ast_play_and_wait(chan, "vm-vecchio") ||
08459          ast_play_and_wait(chan, "vm-message") :
08460          /* 2 or more old messages */
08461          say_and_wait(chan, vms->oldmessages, chan->language) ||
08462          ast_play_and_wait(chan, "vm-vecchi") ||
08463          ast_play_and_wait(chan, "vm-messages");
08464    }
08465    return res;
08466 }
08467 
08468 /* POLISH syntax */
08469 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08470 {
08471    /* Introduce messages they have */
08472    int res;
08473    div_t num;
08474 
08475    if (!vms->oldmessages && !vms->newmessages) {
08476       res = ast_play_and_wait(chan, "vm-no");
08477       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08478       return res;
08479    } else {
08480       res = ast_play_and_wait(chan, "vm-youhave");
08481    }
08482 
08483    if (vms->newmessages) {
08484       num = div(vms->newmessages, 10);
08485       if (vms->newmessages == 1) {
08486          res = ast_play_and_wait(chan, "digits/1-a");
08487          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08488          res = res ? res : ast_play_and_wait(chan, "vm-message");
08489       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08490          if (num.rem == 2) {
08491             if (!num.quot) {
08492                res = ast_play_and_wait(chan, "digits/2-ie");
08493             } else {
08494                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08495                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08496             }
08497          } else {
08498             res = say_and_wait(chan, vms->newmessages, chan->language);
08499          }
08500          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08501          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08502       } else {
08503          res = say_and_wait(chan, vms->newmessages, chan->language);
08504          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08505          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08506       }
08507       if (!res && vms->oldmessages)
08508          res = ast_play_and_wait(chan, "vm-and");
08509    }
08510    if (!res && vms->oldmessages) {
08511       num = div(vms->oldmessages, 10);
08512       if (vms->oldmessages == 1) {
08513          res = ast_play_and_wait(chan, "digits/1-a");
08514          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08515          res = res ? res : ast_play_and_wait(chan, "vm-message");
08516       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08517          if (num.rem == 2) {
08518             if (!num.quot) {
08519                res = ast_play_and_wait(chan, "digits/2-ie");
08520             } else {
08521                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08522                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08523             }
08524          } else {
08525             res = say_and_wait(chan, vms->oldmessages, chan->language);
08526          }
08527          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08528          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08529       } else {
08530          res = say_and_wait(chan, vms->oldmessages, chan->language);
08531          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08532          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08533       }
08534    }
08535 
08536    return res;
08537 }
08538 
08539 /* SWEDISH syntax */
08540 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08541 {
08542    /* Introduce messages they have */
08543    int res;
08544 
08545    res = ast_play_and_wait(chan, "vm-youhave");
08546    if (res)
08547       return res;
08548 
08549    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08550       res = ast_play_and_wait(chan, "vm-no");
08551       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08552       return res;
08553    }
08554 
08555    if (vms->newmessages) {
08556       if ((vms->newmessages == 1)) {
08557          res = ast_play_and_wait(chan, "digits/ett");
08558          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08559          res = res ? res : ast_play_and_wait(chan, "vm-message");
08560       } else {
08561          res = say_and_wait(chan, vms->newmessages, chan->language);
08562          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08563          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08564       }
08565       if (!res && vms->oldmessages)
08566          res = ast_play_and_wait(chan, "vm-and");
08567    }
08568    if (!res && vms->oldmessages) {
08569       if (vms->oldmessages == 1) {
08570          res = ast_play_and_wait(chan, "digits/ett");
08571          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08572          res = res ? res : ast_play_and_wait(chan, "vm-message");
08573       } else {
08574          res = say_and_wait(chan, vms->oldmessages, chan->language);
08575          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08576          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08577       }
08578    }
08579 
08580    return res;
08581 }
08582 
08583 /* NORWEGIAN syntax */
08584 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08585 {
08586    /* Introduce messages they have */
08587    int res;
08588 
08589    res = ast_play_and_wait(chan, "vm-youhave");
08590    if (res)
08591       return res;
08592 
08593    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08594       res = ast_play_and_wait(chan, "vm-no");
08595       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08596       return res;
08597    }
08598 
08599    if (vms->newmessages) {
08600       if ((vms->newmessages == 1)) {
08601          res = ast_play_and_wait(chan, "digits/1");
08602          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08603          res = res ? res : ast_play_and_wait(chan, "vm-message");
08604       } else {
08605          res = say_and_wait(chan, vms->newmessages, chan->language);
08606          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08607          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08608       }
08609       if (!res && vms->oldmessages)
08610          res = ast_play_and_wait(chan, "vm-and");
08611    }
08612    if (!res && vms->oldmessages) {
08613       if (vms->oldmessages == 1) {
08614          res = ast_play_and_wait(chan, "digits/1");
08615          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08616          res = res ? res : ast_play_and_wait(chan, "vm-message");
08617       } else {
08618          res = say_and_wait(chan, vms->oldmessages, chan->language);
08619          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08620          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08621       }
08622    }
08623 
08624    return res;
08625 }
08626 
08627 /* GERMAN syntax */
08628 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08629 {
08630    /* Introduce messages they have */
08631    int res;
08632    res = ast_play_and_wait(chan, "vm-youhave");
08633    if (!res) {
08634       if (vms->newmessages) {
08635          if ((vms->newmessages == 1))
08636             res = ast_play_and_wait(chan, "digits/1F");
08637          else
08638             res = say_and_wait(chan, vms->newmessages, chan->language);
08639          if (!res)
08640             res = ast_play_and_wait(chan, "vm-INBOX");
08641          if (vms->oldmessages && !res)
08642             res = ast_play_and_wait(chan, "vm-and");
08643          else if (!res) {
08644             if ((vms->newmessages == 1))
08645                res = ast_play_and_wait(chan, "vm-message");
08646             else
08647                res = ast_play_and_wait(chan, "vm-messages");
08648          }
08649             
08650       }
08651       if (!res && vms->oldmessages) {
08652          if (vms->oldmessages == 1)
08653             res = ast_play_and_wait(chan, "digits/1F");
08654          else
08655             res = say_and_wait(chan, vms->oldmessages, chan->language);
08656          if (!res)
08657             res = ast_play_and_wait(chan, "vm-Old");
08658          if (!res) {
08659             if (vms->oldmessages == 1)
08660                res = ast_play_and_wait(chan, "vm-message");
08661             else
08662                res = ast_play_and_wait(chan, "vm-messages");
08663          }
08664       }
08665       if (!res) {
08666          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08667             res = ast_play_and_wait(chan, "vm-no");
08668             if (!res)
08669                res = ast_play_and_wait(chan, "vm-messages");
08670          }
08671       }
08672    }
08673    return res;
08674 }
08675 
08676 /* SPANISH syntax */
08677 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08678 {
08679    /* Introduce messages they have */
08680    int res;
08681    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08682       res = ast_play_and_wait(chan, "vm-youhaveno");
08683       if (!res)
08684          res = ast_play_and_wait(chan, "vm-messages");
08685    } else {
08686       res = ast_play_and_wait(chan, "vm-youhave");
08687    }
08688    if (!res) {
08689       if (vms->newmessages) {
08690          if (!res) {
08691             if ((vms->newmessages == 1)) {
08692                res = ast_play_and_wait(chan, "digits/1M");
08693                if (!res)
08694                   res = ast_play_and_wait(chan, "vm-message");
08695                if (!res)
08696                   res = ast_play_and_wait(chan, "vm-INBOXs");
08697             } else {
08698                res = say_and_wait(chan, vms->newmessages, chan->language);
08699                if (!res)
08700                   res = ast_play_and_wait(chan, "vm-messages");
08701                if (!res)
08702                   res = ast_play_and_wait(chan, "vm-INBOX");
08703             }
08704          }
08705          if (vms->oldmessages && !res)
08706             res = ast_play_and_wait(chan, "vm-and");
08707       }
08708       if (vms->oldmessages) {
08709          if (!res) {
08710             if (vms->oldmessages == 1) {
08711                res = ast_play_and_wait(chan, "digits/1M");
08712                if (!res)
08713                   res = ast_play_and_wait(chan, "vm-message");
08714                if (!res)
08715                   res = ast_play_and_wait(chan, "vm-Olds");
08716             } else {
08717                res = say_and_wait(chan, vms->oldmessages, chan->language);
08718                if (!res)
08719                   res = ast_play_and_wait(chan, "vm-messages");
08720                if (!res)
08721                   res = ast_play_and_wait(chan, "vm-Old");
08722             }
08723          }
08724       }
08725    }
08726 return res;
08727 }
08728 
08729 /* BRAZILIAN PORTUGUESE syntax */
08730 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08731    /* Introduce messages they have */
08732    int res;
08733    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08734       res = ast_play_and_wait(chan, "vm-nomessages");
08735       return res;
08736    } else {
08737       res = ast_play_and_wait(chan, "vm-youhave");
08738    }
08739    if (vms->newmessages) {
08740       if (!res)
08741          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08742       if ((vms->newmessages == 1)) {
08743          if (!res)
08744             res = ast_play_and_wait(chan, "vm-message");
08745          if (!res)
08746             res = ast_play_and_wait(chan, "vm-INBOXs");
08747       } else {
08748          if (!res)
08749             res = ast_play_and_wait(chan, "vm-messages");
08750          if (!res)
08751             res = ast_play_and_wait(chan, "vm-INBOX");
08752       }
08753       if (vms->oldmessages && !res)
08754          res = ast_play_and_wait(chan, "vm-and");
08755    }
08756    if (vms->oldmessages) {
08757       if (!res)
08758          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08759       if (vms->oldmessages == 1) {
08760          if (!res)
08761             res = ast_play_and_wait(chan, "vm-message");
08762          if (!res)
08763             res = ast_play_and_wait(chan, "vm-Olds");
08764       } else {
08765          if (!res)
08766             res = ast_play_and_wait(chan, "vm-messages");
08767          if (!res)
08768             res = ast_play_and_wait(chan, "vm-Old");
08769       }
08770    }
08771    return res;
08772 }
08773 
08774 /* FRENCH syntax */
08775 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08776 {
08777    /* Introduce messages they have */
08778    int res;
08779    res = ast_play_and_wait(chan, "vm-youhave");
08780    if (!res) {
08781       if (vms->newmessages) {
08782          res = say_and_wait(chan, vms->newmessages, chan->language);
08783          if (!res)
08784             res = ast_play_and_wait(chan, "vm-INBOX");
08785          if (vms->oldmessages && !res)
08786             res = ast_play_and_wait(chan, "vm-and");
08787          else if (!res) {
08788             if ((vms->newmessages == 1))
08789                res = ast_play_and_wait(chan, "vm-message");
08790             else
08791                res = ast_play_and_wait(chan, "vm-messages");
08792          }
08793             
08794       }
08795       if (!res && vms->oldmessages) {
08796          res = say_and_wait(chan, vms->oldmessages, chan->language);
08797          if (!res)
08798             res = ast_play_and_wait(chan, "vm-Old");
08799          if (!res) {
08800             if (vms->oldmessages == 1)
08801                res = ast_play_and_wait(chan, "vm-message");
08802             else
08803                res = ast_play_and_wait(chan, "vm-messages");
08804          }
08805       }
08806       if (!res) {
08807          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08808             res = ast_play_and_wait(chan, "vm-no");
08809             if (!res)
08810                res = ast_play_and_wait(chan, "vm-messages");
08811          }
08812       }
08813    }
08814    return res;
08815 }
08816 
08817 /* DUTCH syntax */
08818 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08819 {
08820    /* Introduce messages they have */
08821    int res;
08822    res = ast_play_and_wait(chan, "vm-youhave");
08823    if (!res) {
08824       if (vms->newmessages) {
08825          res = say_and_wait(chan, vms->newmessages, chan->language);
08826          if (!res) {
08827             if (vms->newmessages == 1)
08828                res = ast_play_and_wait(chan, "vm-INBOXs");
08829             else
08830                res = ast_play_and_wait(chan, "vm-INBOX");
08831          }
08832          if (vms->oldmessages && !res)
08833             res = ast_play_and_wait(chan, "vm-and");
08834          else if (!res) {
08835             if ((vms->newmessages == 1))
08836                res = ast_play_and_wait(chan, "vm-message");
08837             else
08838                res = ast_play_and_wait(chan, "vm-messages");
08839          }
08840             
08841       }
08842       if (!res && vms->oldmessages) {
08843          res = say_and_wait(chan, vms->oldmessages, chan->language);
08844          if (!res) {
08845             if (vms->oldmessages == 1)
08846                res = ast_play_and_wait(chan, "vm-Olds");
08847             else
08848                res = ast_play_and_wait(chan, "vm-Old");
08849          }
08850          if (!res) {
08851             if (vms->oldmessages == 1)
08852                res = ast_play_and_wait(chan, "vm-message");
08853             else
08854                res = ast_play_and_wait(chan, "vm-messages");
08855          }
08856       }
08857       if (!res) {
08858          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08859             res = ast_play_and_wait(chan, "vm-no");
08860             if (!res)
08861                res = ast_play_and_wait(chan, "vm-messages");
08862          }
08863       }
08864    }
08865    return res;
08866 }
08867 
08868 /* PORTUGUESE syntax */
08869 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08870 {
08871    /* Introduce messages they have */
08872    int res;
08873    res = ast_play_and_wait(chan, "vm-youhave");
08874    if (!res) {
08875       if (vms->newmessages) {
08876          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08877          if (!res) {
08878             if ((vms->newmessages == 1)) {
08879                res = ast_play_and_wait(chan, "vm-message");
08880                if (!res)
08881                   res = ast_play_and_wait(chan, "vm-INBOXs");
08882             } else {
08883                res = ast_play_and_wait(chan, "vm-messages");
08884                if (!res)
08885                   res = ast_play_and_wait(chan, "vm-INBOX");
08886             }
08887          }
08888          if (vms->oldmessages && !res)
08889             res = ast_play_and_wait(chan, "vm-and");
08890       }
08891       if (!res && vms->oldmessages) {
08892          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08893          if (!res) {
08894             if (vms->oldmessages == 1) {
08895                res = ast_play_and_wait(chan, "vm-message");
08896                if (!res)
08897                   res = ast_play_and_wait(chan, "vm-Olds");
08898             } else {
08899                res = ast_play_and_wait(chan, "vm-messages");
08900                if (!res)
08901                   res = ast_play_and_wait(chan, "vm-Old");
08902             }
08903          }
08904       }
08905       if (!res) {
08906          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08907             res = ast_play_and_wait(chan, "vm-no");
08908             if (!res)
08909                res = ast_play_and_wait(chan, "vm-messages");
08910          }
08911       }
08912    }
08913    return res;
08914 }
08915 
08916 
08917 /* CZECH syntax */
08918 /* in czech there must be declension of word new and message
08919  * czech        : english        : czech      : english
08920  * --------------------------------------------------------
08921  * vm-youhave   : you have 
08922  * vm-novou     : one new        : vm-zpravu  : message
08923  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08924  * vm-novych    : 5-infinite new : vm-zprav   : messages
08925  * vm-starou   : one old
08926  * vm-stare     : 2-4 old 
08927  * vm-starych   : 5-infinite old
08928  * jednu        : one   - falling 4. 
08929  * vm-no        : no  ( no messages )
08930  */
08931 
08932 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08933 {
08934    int res;
08935    res = ast_play_and_wait(chan, "vm-youhave");
08936    if (!res) {
08937       if (vms->newmessages) {
08938          if (vms->newmessages == 1) {
08939             res = ast_play_and_wait(chan, "digits/jednu");
08940          } else {
08941             res = say_and_wait(chan, vms->newmessages, chan->language);
08942          }
08943          if (!res) {
08944             if ((vms->newmessages == 1))
08945                res = ast_play_and_wait(chan, "vm-novou");
08946             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08947                res = ast_play_and_wait(chan, "vm-nove");
08948             if (vms->newmessages > 4)
08949                res = ast_play_and_wait(chan, "vm-novych");
08950          }
08951          if (vms->oldmessages && !res)
08952             res = ast_play_and_wait(chan, "vm-and");
08953          else if (!res) {
08954             if ((vms->newmessages == 1))
08955                res = ast_play_and_wait(chan, "vm-zpravu");
08956             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08957                res = ast_play_and_wait(chan, "vm-zpravy");
08958             if (vms->newmessages > 4)
08959                res = ast_play_and_wait(chan, "vm-zprav");
08960          }
08961       }
08962       if (!res && vms->oldmessages) {
08963          res = say_and_wait(chan, vms->oldmessages, chan->language);
08964          if (!res) {
08965             if ((vms->oldmessages == 1))
08966                res = ast_play_and_wait(chan, "vm-starou");
08967             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08968                res = ast_play_and_wait(chan, "vm-stare");
08969             if (vms->oldmessages > 4)
08970                res = ast_play_and_wait(chan, "vm-starych");
08971          }
08972          if (!res) {
08973             if ((vms->oldmessages == 1))
08974                res = ast_play_and_wait(chan, "vm-zpravu");
08975             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08976                res = ast_play_and_wait(chan, "vm-zpravy");
08977             if (vms->oldmessages > 4)
08978                res = ast_play_and_wait(chan, "vm-zprav");
08979          }
08980       }
08981       if (!res) {
08982          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08983             res = ast_play_and_wait(chan, "vm-no");
08984             if (!res)
08985                res = ast_play_and_wait(chan, "vm-zpravy");
08986          }
08987       }
08988    }
08989    return res;
08990 }
08991 
08992 /* CHINESE (Taiwan) syntax */
08993 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08994 {
08995    int res;
08996    /* Introduce messages they have */
08997    res = ast_play_and_wait(chan, "vm-you");
08998 
08999    if (!res && vms->newmessages) {
09000       res = ast_play_and_wait(chan, "vm-have");
09001       if (!res)
09002          res = say_and_wait(chan, vms->newmessages, chan->language);
09003       if (!res)
09004          res = ast_play_and_wait(chan, "vm-tong");
09005       if (!res)
09006          res = ast_play_and_wait(chan, "vm-INBOX");
09007       if (vms->oldmessages && !res)
09008          res = ast_play_and_wait(chan, "vm-and");
09009       else if (!res) 
09010          res = ast_play_and_wait(chan, "vm-messages");
09011    }
09012    if (!res && vms->oldmessages) {
09013       res = ast_play_and_wait(chan, "vm-have");
09014       if (!res)
09015          res = say_and_wait(chan, vms->oldmessages, chan->language);
09016       if (!res)
09017          res = ast_play_and_wait(chan, "vm-tong");
09018       if (!res)
09019          res = ast_play_and_wait(chan, "vm-Old");
09020       if (!res)
09021          res = ast_play_and_wait(chan, "vm-messages");
09022    }
09023    if (!res && !vms->oldmessages && !vms->newmessages) {
09024       res = ast_play_and_wait(chan, "vm-haveno");
09025       if (!res)
09026          res = ast_play_and_wait(chan, "vm-messages");
09027    }
09028    return res;
09029 }
09030 
09031 /* Vietnamese syntax */
09032 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09033 {
09034    int res;
09035 
09036    /* Introduce messages they have */
09037    res = ast_play_and_wait(chan, "vm-youhave");
09038    if (!res) {
09039       if (vms->newmessages) {
09040          res = say_and_wait(chan, vms->newmessages, chan->language);
09041          if (!res)
09042             res = ast_play_and_wait(chan, "vm-INBOX");
09043          if (vms->oldmessages && !res)
09044             res = ast_play_and_wait(chan, "vm-and");
09045       }
09046       if (!res && vms->oldmessages) {
09047          res = say_and_wait(chan, vms->oldmessages, chan->language);
09048          if (!res)
09049             res = ast_play_and_wait(chan, "vm-Old");        
09050       }
09051       if (!res) {
09052          if (!vms->oldmessages && !vms->newmessages) {
09053             res = ast_play_and_wait(chan, "vm-no");
09054             if (!res)
09055                res = ast_play_and_wait(chan, "vm-message");
09056          }
09057       }
09058    }
09059    return res;
09060 }
09061 
09062 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09063 {
09064    char prefile[256];
09065    
09066    /* Notify the user that the temp greeting is set and give them the option to remove it */
09067    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09068    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09069       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09070       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09071          ast_play_and_wait(chan, "vm-tempgreetactive");
09072       }
09073       DISPOSE(prefile, -1);
09074    }
09075 
09076    /* Play voicemail intro - syntax is different for different languages */
09077    if (0) {
09078       return 0;
09079    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09080       return vm_intro_cs(chan, vms);
09081    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09082       static int deprecation_warning = 0;
09083       if (deprecation_warning++ % 10 == 0) {
09084          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09085       }
09086       return vm_intro_cs(chan, vms);
09087    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09088       return vm_intro_de(chan, vms);
09089    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09090       return vm_intro_es(chan, vms);
09091    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09092       return vm_intro_fr(chan, vms);
09093    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09094       return vm_intro_gr(chan, vms);
09095    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09096       return vm_intro_he(chan, vms);
09097    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09098       return vm_intro_it(chan, vms);
09099    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09100       return vm_intro_nl(chan, vms);
09101    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09102       return vm_intro_no(chan, vms);
09103    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09104       return vm_intro_pl(chan, vms);
09105    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09106       return vm_intro_pt_BR(chan, vms);
09107    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09108       return vm_intro_pt(chan, vms);
09109    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09110       return vm_intro_multilang(chan, vms, "n");
09111    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09112       return vm_intro_se(chan, vms);
09113    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09114       return vm_intro_multilang(chan, vms, "n");
09115    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09116       return vm_intro_vi(chan, vms);
09117    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09118       return vm_intro_zh(chan, vms);
09119    } else {                                             /* Default to ENGLISH */
09120       return vm_intro_en(chan, vms);
09121    }
09122 }
09123 
09124 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09125 {
09126    int res = 0;
09127    /* Play instructions and wait for new command */
09128    while (!res) {
09129       if (vms->starting) {
09130          if (vms->lastmsg > -1) {
09131             if (skipadvanced)
09132                res = ast_play_and_wait(chan, "vm-onefor-full");
09133             else
09134                res = ast_play_and_wait(chan, "vm-onefor");
09135             if (!res)
09136                res = vm_play_folder_name(chan, vms->vmbox);
09137          }
09138          if (!res) {
09139             if (skipadvanced)
09140                res = ast_play_and_wait(chan, "vm-opts-full");
09141             else
09142                res = ast_play_and_wait(chan, "vm-opts");
09143          }
09144       } else {
09145          /* Added for additional help */
09146          if (skipadvanced) {
09147             res = ast_play_and_wait(chan, "vm-onefor-full");
09148             if (!res)
09149                res = vm_play_folder_name(chan, vms->vmbox);
09150             res = ast_play_and_wait(chan, "vm-opts-full");
09151          }
09152          /* Logic:
09153           * If the current message is not the first OR
09154           * if we're listening to the first new message and there are
09155           * also urgent messages, then prompt for navigation to the
09156           * previous message
09157           */
09158          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09159             res = ast_play_and_wait(chan, "vm-prev");
09160          }
09161          if (!res && !skipadvanced)
09162             res = ast_play_and_wait(chan, "vm-advopts");
09163          if (!res)
09164             res = ast_play_and_wait(chan, "vm-repeat");
09165          /* Logic:
09166           * If we're not listening to the last message OR
09167           * we're listening to the last urgent message and there are
09168           * also new non-urgent messages, then prompt for navigation
09169           * to the next message
09170           */
09171          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09172             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09173             res = ast_play_and_wait(chan, "vm-next");
09174          }
09175          if (!res) {
09176             int curmsg_deleted;
09177 #ifdef IMAP_STORAGE
09178             ast_mutex_lock(&vms->lock);
09179 #endif
09180             curmsg_deleted = vms->deleted[vms->curmsg];
09181 #ifdef IMAP_STORAGE
09182             ast_mutex_unlock(&vms->lock);
09183 #endif
09184             if (!curmsg_deleted) {
09185                res = ast_play_and_wait(chan, "vm-delete");
09186             } else {
09187                res = ast_play_and_wait(chan, "vm-undelete");
09188             }
09189             if (!res) {
09190                res = ast_play_and_wait(chan, "vm-toforward");
09191             }
09192             if (!res) {
09193                res = ast_play_and_wait(chan, "vm-savemessage");
09194             }
09195          }
09196       }
09197       if (!res) {
09198          res = ast_play_and_wait(chan, "vm-helpexit");
09199       }
09200       if (!res)
09201          res = ast_waitfordigit(chan, 6000);
09202       if (!res) {
09203          vms->repeats++;
09204          if (vms->repeats > 2) {
09205             res = 't';
09206          }
09207       }
09208    }
09209    return res;
09210 }
09211 
09212 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09213 {
09214    int res = 0;
09215    /* Play instructions and wait for new command */
09216    while (!res) {
09217       if (vms->lastmsg > -1) {
09218          res = ast_play_and_wait(chan, "vm-listen");
09219          if (!res)
09220             res = vm_play_folder_name(chan, vms->vmbox);
09221          if (!res)
09222             res = ast_play_and_wait(chan, "press");
09223          if (!res)
09224             res = ast_play_and_wait(chan, "digits/1");
09225       }
09226       if (!res)
09227          res = ast_play_and_wait(chan, "vm-opts");
09228       if (!res) {
09229          vms->starting = 0;
09230          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09231       }
09232    }
09233    return res;
09234 }
09235 
09236 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09237 {
09238    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09239       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09240    } else {             /* Default to ENGLISH */
09241       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09242    }
09243 }
09244 
09245 
09246 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09247 {
09248    int cmd = 0;
09249    int duration = 0;
09250    int tries = 0;
09251    char newpassword[80] = "";
09252    char newpassword2[80] = "";
09253    char prefile[PATH_MAX] = "";
09254    unsigned char buf[256];
09255    int bytes = 0;
09256 
09257    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09258    if (ast_adsi_available(chan)) {
09259       bytes += adsi_logo(buf + bytes);
09260       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09261       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09262       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09263       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09264       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09265    }
09266 
09267    /* If forcename is set, have the user record their name */
09268    if (ast_test_flag(vmu, VM_FORCENAME)) {
09269       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09270       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09271          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09272          if (cmd < 0 || cmd == 't' || cmd == '#')
09273             return cmd;
09274       }
09275    }
09276 
09277    /* If forcegreetings is set, have the user record their greetings */
09278    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09279       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09280       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09281          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09282          if (cmd < 0 || cmd == 't' || cmd == '#')
09283             return cmd;
09284       }
09285 
09286       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09287       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09288          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09289          if (cmd < 0 || cmd == 't' || cmd == '#')
09290             return cmd;
09291       }
09292    }
09293 
09294    /*
09295     * Change the password last since new users will be able to skip over any steps this one comes before
09296     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09297     */
09298    for (;;) {
09299       newpassword[1] = '\0';
09300       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09301       if (cmd == '#')
09302          newpassword[0] = '\0';
09303       if (cmd < 0 || cmd == 't' || cmd == '#')
09304          return cmd;
09305       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09306       if (cmd < 0 || cmd == 't' || cmd == '#')
09307          return cmd;
09308       cmd = check_password(vmu, newpassword); /* perform password validation */
09309       if (cmd != 0) {
09310          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09311          cmd = ast_play_and_wait(chan, vm_invalid_password);
09312       } else {
09313          newpassword2[1] = '\0';
09314          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09315          if (cmd == '#')
09316             newpassword2[0] = '\0';
09317          if (cmd < 0 || cmd == 't' || cmd == '#')
09318             return cmd;
09319          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09320          if (cmd < 0 || cmd == 't' || cmd == '#')
09321             return cmd;
09322          if (!strcmp(newpassword, newpassword2))
09323             break;
09324          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09325          cmd = ast_play_and_wait(chan, vm_mismatch);
09326       }
09327       if (++tries == 3)
09328          return -1;
09329       if (cmd != 0) {
09330          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09331       }
09332    }
09333    if (pwdchange & PWDCHANGE_INTERNAL)
09334       vm_change_password(vmu, newpassword);
09335    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09336       vm_change_password_shell(vmu, newpassword);
09337 
09338    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09339    cmd = ast_play_and_wait(chan, vm_passchanged);
09340 
09341    return cmd;
09342 }
09343 
09344 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09345 {
09346    int cmd = 0;
09347    int retries = 0;
09348    int duration = 0;
09349    char newpassword[80] = "";
09350    char newpassword2[80] = "";
09351    char prefile[PATH_MAX] = "";
09352    unsigned char buf[256];
09353    int bytes = 0;
09354 
09355    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09356    if (ast_adsi_available(chan)) {
09357       bytes += adsi_logo(buf + bytes);
09358       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09359       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09360       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09361       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09362       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09363    }
09364    while ((cmd >= 0) && (cmd != 't')) {
09365       if (cmd)
09366          retries = 0;
09367       switch (cmd) {
09368       case '1': /* Record your unavailable message */
09369          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09370          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09371          break;
09372       case '2':  /* Record your busy message */
09373          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09374          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09375          break;
09376       case '3': /* Record greeting */
09377          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09378          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09379          break;
09380       case '4':  /* manage the temporary greeting */
09381          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09382          break;
09383       case '5': /* change password */
09384          if (vmu->password[0] == '-') {
09385             cmd = ast_play_and_wait(chan, "vm-no");
09386             break;
09387          }
09388          newpassword[1] = '\0';
09389          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09390          if (cmd == '#')
09391             newpassword[0] = '\0';
09392          else {
09393             if (cmd < 0)
09394                break;
09395             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09396                break;
09397             }
09398          }
09399          cmd = check_password(vmu, newpassword); /* perform password validation */
09400          if (cmd != 0) {
09401             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09402             cmd = ast_play_and_wait(chan, vm_invalid_password);
09403             if (!cmd) {
09404                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09405             }
09406             break;
09407          }
09408          newpassword2[1] = '\0';
09409          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09410          if (cmd == '#')
09411             newpassword2[0] = '\0';
09412          else {
09413             if (cmd < 0)
09414                break;
09415 
09416             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09417                break;
09418             }
09419          }
09420          if (strcmp(newpassword, newpassword2)) {
09421             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09422             cmd = ast_play_and_wait(chan, vm_mismatch);
09423             if (!cmd) {
09424                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09425             }
09426             break;
09427          }
09428 
09429          if (pwdchange & PWDCHANGE_INTERNAL) {
09430             vm_change_password(vmu, newpassword);
09431          }
09432          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09433             vm_change_password_shell(vmu, newpassword);
09434          }
09435 
09436          ast_debug(1, "User %s set password to %s of length %d\n",
09437             vms->username, newpassword, (int) strlen(newpassword));
09438          cmd = ast_play_and_wait(chan, vm_passchanged);
09439          break;
09440       case '*': 
09441          cmd = 't';
09442          break;
09443       default: 
09444          cmd = 0;
09445          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09446          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09447          if (ast_fileexists(prefile, NULL, NULL)) {
09448             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09449          }
09450          DISPOSE(prefile, -1);
09451          if (!cmd) {
09452             cmd = ast_play_and_wait(chan, "vm-options");
09453          }
09454          if (!cmd) {
09455             cmd = ast_waitfordigit(chan, 6000);
09456          }
09457          if (!cmd) {
09458             retries++;
09459          }
09460          if (retries > 3) {
09461             cmd = 't';
09462          }
09463          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09464       }
09465    }
09466    if (cmd == 't')
09467       cmd = 0;
09468    return cmd;
09469 }
09470 
09471 /*!
09472  * \brief The handler for 'record a temporary greeting'. 
09473  * \param chan
09474  * \param vmu
09475  * \param vms
09476  * \param fmtc
09477  * \param record_gain
09478  *
09479  * This is option 4 from the mailbox options menu.
09480  * This function manages the following promptings:
09481  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09482  * 2: remove (delete) the temporary greeting.
09483  * *: return to the main menu.
09484  *
09485  * \return zero on success, -1 on error.
09486  */
09487 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09488 {
09489    int cmd = 0;
09490    int retries = 0;
09491    int duration = 0;
09492    char prefile[PATH_MAX] = "";
09493    unsigned char buf[256];
09494    int bytes = 0;
09495 
09496    if (ast_adsi_available(chan)) {
09497       bytes += adsi_logo(buf + bytes);
09498       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09499       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09500       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09501       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09502       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09503    }
09504 
09505    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09506    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09507    while ((cmd >= 0) && (cmd != 't')) {
09508       if (cmd)
09509          retries = 0;
09510       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09511       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09512          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09513          if (cmd == -1) {
09514             break;
09515          }
09516          cmd = 't';  
09517       } else {
09518          switch (cmd) {
09519          case '1':
09520             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09521             break;
09522          case '2':
09523             DELETE(prefile, -1, prefile, vmu);
09524             ast_play_and_wait(chan, "vm-tempremoved");
09525             cmd = 't';  
09526             break;
09527          case '*': 
09528             cmd = 't';
09529             break;
09530          default:
09531             cmd = ast_play_and_wait(chan,
09532                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09533                   "vm-tempgreeting2" : "vm-tempgreeting");
09534             if (!cmd) {
09535                cmd = ast_waitfordigit(chan, 6000);
09536             }
09537             if (!cmd) {
09538                retries++;
09539             }
09540             if (retries > 3) {
09541                cmd = 't';
09542             }
09543             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09544          }
09545       }
09546       DISPOSE(prefile, -1);
09547    }
09548    if (cmd == 't')
09549       cmd = 0;
09550    return cmd;
09551 }
09552 
09553 /*!
09554  * \brief Greek syntax for 'You have N messages' greeting.
09555  * \param chan
09556  * \param vms
09557  * \param vmu
09558  *
09559  * \return zero on success, -1 on error.
09560  */   
09561 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09562 {
09563    int cmd = 0;
09564 
09565    if (vms->lastmsg > -1) {
09566       cmd = play_message(chan, vmu, vms);
09567    } else {
09568       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09569       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09570          if (!cmd) {
09571             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09572             cmd = ast_play_and_wait(chan, vms->fn);
09573          }
09574          if (!cmd)
09575             cmd = ast_play_and_wait(chan, "vm-messages");
09576       } else {
09577          if (!cmd)
09578             cmd = ast_play_and_wait(chan, "vm-messages");
09579          if (!cmd) {
09580             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09581             cmd = ast_play_and_wait(chan, vms->fn);
09582          }
09583       }
09584    } 
09585    return cmd;
09586 }
09587 
09588 /* Hebrew Syntax */
09589 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09590 {
09591    int cmd = 0;
09592 
09593    if (vms->lastmsg > -1) {
09594       cmd = play_message(chan, vmu, vms);
09595    } else {
09596       if (!strcasecmp(vms->fn, "INBOX")) {
09597          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09598       } else {
09599          cmd = ast_play_and_wait(chan, "vm-nomessages");
09600       }
09601    }
09602    return cmd;
09603 }
09604 
09605 /*! 
09606  * \brief Default English syntax for 'You have N messages' greeting.
09607  * \param chan
09608  * \param vms
09609  * \param vmu
09610  *
09611  * \return zero on success, -1 on error.
09612  */
09613 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09614 {
09615    int cmd = 0;
09616 
09617    if (vms->lastmsg > -1) {
09618       cmd = play_message(chan, vmu, vms);
09619    } else {
09620       cmd = ast_play_and_wait(chan, "vm-youhave");
09621       if (!cmd) 
09622          cmd = ast_play_and_wait(chan, "vm-no");
09623       if (!cmd) {
09624          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09625          cmd = ast_play_and_wait(chan, vms->fn);
09626       }
09627       if (!cmd)
09628          cmd = ast_play_and_wait(chan, "vm-messages");
09629    }
09630    return cmd;
09631 }
09632 
09633 /*! 
09634  *\brief Italian syntax for 'You have N messages' greeting.
09635  * \param chan
09636  * \param vms
09637  * \param vmu
09638  *
09639  * \return zero on success, -1 on error.
09640  */
09641 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09642 {
09643    int cmd;
09644 
09645    if (vms->lastmsg > -1) {
09646       cmd = play_message(chan, vmu, vms);
09647    } else {
09648       cmd = ast_play_and_wait(chan, "vm-no");
09649       if (!cmd)
09650          cmd = ast_play_and_wait(chan, "vm-message");
09651       if (!cmd) {
09652          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09653          cmd = ast_play_and_wait(chan, vms->fn);
09654       }
09655    }
09656    return cmd;
09657 }
09658 
09659 /*! 
09660  * \brief Spanish syntax for 'You have N messages' greeting.
09661  * \param chan
09662  * \param vms
09663  * \param vmu
09664  *
09665  * \return zero on success, -1 on error.
09666  */
09667 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09668 {
09669    int cmd;
09670 
09671    if (vms->lastmsg > -1) {
09672       cmd = play_message(chan, vmu, vms);
09673    } else {
09674       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09675       if (!cmd)
09676          cmd = ast_play_and_wait(chan, "vm-messages");
09677       if (!cmd) {
09678          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09679          cmd = ast_play_and_wait(chan, vms->fn);
09680       }
09681    }
09682    return cmd;
09683 }
09684 
09685 /*! 
09686  * \brief Portuguese syntax for 'You have N messages' greeting.
09687  * \param chan
09688  * \param vms
09689  * \param vmu
09690  *
09691  * \return zero on success, -1 on error.
09692  */
09693 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09694 {
09695    int cmd;
09696 
09697    if (vms->lastmsg > -1) {
09698       cmd = play_message(chan, vmu, vms);
09699    } else {
09700       cmd = ast_play_and_wait(chan, "vm-no");
09701       if (!cmd) {
09702          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09703          cmd = ast_play_and_wait(chan, vms->fn);
09704       }
09705       if (!cmd)
09706          cmd = ast_play_and_wait(chan, "vm-messages");
09707    }
09708    return cmd;
09709 }
09710 
09711 /*! 
09712  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09713  * \param chan
09714  * \param vms
09715  * \param vmu
09716  *
09717  * \return zero on success, -1 on error.
09718  */
09719 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09720 {
09721    int cmd;
09722 
09723    if (vms->lastmsg > -1) {
09724       cmd = play_message(chan, vmu, vms);
09725    } else {
09726       cmd = ast_play_and_wait(chan, "vm-you");
09727       if (!cmd) 
09728          cmd = ast_play_and_wait(chan, "vm-haveno");
09729       if (!cmd)
09730          cmd = ast_play_and_wait(chan, "vm-messages");
09731       if (!cmd) {
09732          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09733          cmd = ast_play_and_wait(chan, vms->fn);
09734       }
09735    }
09736    return cmd;
09737 }
09738 
09739 /*! 
09740  * \brief Vietnamese syntax for 'You have N messages' greeting.
09741  * \param chan
09742  * \param vms
09743  * \param vmu
09744  *
09745  * \return zero on success, -1 on error.
09746  */
09747 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09748 {
09749    int cmd = 0;
09750 
09751    if (vms->lastmsg > -1) {
09752       cmd = play_message(chan, vmu, vms);
09753    } else {
09754       cmd = ast_play_and_wait(chan, "vm-no");
09755       if (!cmd) {
09756          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09757          cmd = ast_play_and_wait(chan, vms->fn);
09758       }
09759    }
09760    return cmd;
09761 }
09762 
09763 /*!
09764  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09765  * \param chan The channel for the current user. We read the language property from this.
09766  * \param vms passed into the language-specific vm_browse_messages function.
09767  * \param vmu passed into the language-specific vm_browse_messages function.
09768  * 
09769  * The method to be invoked is determined by the value of language code property in the user's channel.
09770  * The default (when unable to match) is to use english.
09771  *
09772  * \return zero on success, -1 on error.
09773  */
09774 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09775 {
09776    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09777       return vm_browse_messages_es(chan, vms, vmu);
09778    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09779       return vm_browse_messages_gr(chan, vms, vmu);
09780    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09781       return vm_browse_messages_he(chan, vms, vmu);
09782    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09783       return vm_browse_messages_it(chan, vms, vmu);
09784    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09785       return vm_browse_messages_pt(chan, vms, vmu);
09786    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09787       return vm_browse_messages_vi(chan, vms, vmu);
09788    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09789       return vm_browse_messages_zh(chan, vms, vmu);
09790    } else {                                             /* Default to English syntax */
09791       return vm_browse_messages_en(chan, vms, vmu);
09792    }
09793 }
09794 
09795 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09796          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09797          int skipuser, int max_logins, int silent)
09798 {
09799    int useadsi = 0, valid = 0, logretries = 0;
09800    char password[AST_MAX_EXTENSION]="", *passptr;
09801    struct ast_vm_user vmus, *vmu = NULL;
09802 
09803    /* If ADSI is supported, setup login screen */
09804    adsi_begin(chan, &useadsi);
09805    if (!skipuser && useadsi)
09806       adsi_login(chan);
09807    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09808    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09809       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09810       return -1;
09811    }
09812 
09813    /* Authenticate them and get their mailbox/password */
09814 
09815    while (!valid && (logretries < max_logins)) {
09816       /* Prompt for, and read in the username */
09817       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09818          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09819          return -1;
09820       }
09821       if (ast_strlen_zero(mailbox)) {
09822          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09823             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09824          } else {
09825             ast_verb(3, "Username not entered\n"); 
09826             return -1;
09827          }
09828       } else if (mailbox[0] == '*') {
09829          /* user entered '*' */
09830          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09831          if (ast_exists_extension(chan, chan->context, "a", 1,
09832             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09833             return -1;
09834          }
09835          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09836          mailbox[0] = '\0';
09837       }
09838 
09839       if (useadsi)
09840          adsi_password(chan);
09841 
09842       if (!ast_strlen_zero(prefix)) {
09843          char fullusername[80] = "";
09844          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09845          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09846          ast_copy_string(mailbox, fullusername, mailbox_size);
09847       }
09848 
09849       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09850       vmu = find_user(&vmus, context, mailbox);
09851       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09852          /* saved password is blank, so don't bother asking */
09853          password[0] = '\0';
09854       } else {
09855          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09856          if (ast_streamfile(chan, vm_password, chan->language)) {
09857             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09858             return -1;
09859          }
09860          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09861             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09862             return -1;
09863          } else if (password[0] == '*') {
09864             /* user entered '*' */
09865             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09866             if (ast_exists_extension(chan, chan->context, "a", 1,
09867                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09868                mailbox[0] = '*';
09869                return -1;
09870             }
09871             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09872             mailbox[0] = '\0';
09873             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09874             vmu = NULL;
09875          }
09876       }
09877 
09878       if (vmu) {
09879          passptr = vmu->password;
09880          if (passptr[0] == '-') passptr++;
09881       }
09882       if (vmu && !strcmp(passptr, password))
09883          valid++;
09884       else {
09885          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09886          if (!ast_strlen_zero(prefix))
09887             mailbox[0] = '\0';
09888       }
09889       logretries++;
09890       if (!valid) {
09891          if (skipuser || logretries >= max_logins) {
09892             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09893             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09894                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09895                return -1;
09896             }
09897          } else {
09898             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09899             if (useadsi)
09900                adsi_login(chan);
09901             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09902                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09903                return -1;
09904             }
09905          }
09906          if (ast_waitstream(chan, "")) /* Channel is hung up */
09907             return -1;
09908       }
09909    }
09910    if (!valid && (logretries >= max_logins)) {
09911       ast_stopstream(chan);
09912       ast_play_and_wait(chan, "vm-goodbye");
09913       return -1;
09914    }
09915    if (vmu && !skipuser) {
09916       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09917    }
09918    return 0;
09919 }
09920 
09921 static int vm_execmain(struct ast_channel *chan, const char *data)
09922 {
09923    /* XXX This is, admittedly, some pretty horrendous code.  For some
09924       reason it just seemed a lot easier to do with GOTO's.  I feel
09925       like I'm back in my GWBASIC days. XXX */
09926    int res = -1;
09927    int cmd = 0;
09928    int valid = 0;
09929    char prefixstr[80] ="";
09930    char ext_context[256]="";
09931    int box;
09932    int useadsi = 0;
09933    int skipuser = 0;
09934    struct vm_state vms;
09935    struct ast_vm_user *vmu = NULL, vmus;
09936    char *context = NULL;
09937    int silentexit = 0;
09938    struct ast_flags flags = { 0 };
09939    signed char record_gain = 0;
09940    int play_auto = 0;
09941    int play_folder = 0;
09942    int in_urgent = 0;
09943 #ifdef IMAP_STORAGE
09944    int deleted = 0;
09945 #endif
09946 
09947    /* Add the vm_state to the active list and keep it active */
09948    memset(&vms, 0, sizeof(vms));
09949 
09950    vms.lastmsg = -1;
09951 
09952    memset(&vmus, 0, sizeof(vmus));
09953 
09954    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09955    if (chan->_state != AST_STATE_UP) {
09956       ast_debug(1, "Before ast_answer\n");
09957       ast_answer(chan);
09958    }
09959 
09960    if (!ast_strlen_zero(data)) {
09961       char *opts[OPT_ARG_ARRAY_SIZE];
09962       char *parse;
09963       AST_DECLARE_APP_ARGS(args,
09964          AST_APP_ARG(argv0);
09965          AST_APP_ARG(argv1);
09966       );
09967 
09968       parse = ast_strdupa(data);
09969 
09970       AST_STANDARD_APP_ARGS(args, parse);
09971 
09972       if (args.argc == 2) {
09973          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09974             return -1;
09975          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09976             int gain;
09977             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09978                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09979                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09980                   return -1;
09981                } else {
09982                   record_gain = (signed char) gain;
09983                }
09984             } else {
09985                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09986             }
09987          }
09988          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09989             play_auto = 1;
09990             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09991                /* See if it is a folder name first */
09992                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09993                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09994                      play_folder = -1;
09995                   }
09996                } else {
09997                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09998                }
09999             } else {
10000                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
10001             }
10002             if (play_folder > 9 || play_folder < 0) {
10003                ast_log(AST_LOG_WARNING,
10004                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
10005                   opts[OPT_ARG_PLAYFOLDER]);
10006                play_folder = 0;
10007             }
10008          }
10009       } else {
10010          /* old style options parsing */
10011          while (*(args.argv0)) {
10012             if (*(args.argv0) == 's')
10013                ast_set_flag(&flags, OPT_SILENT);
10014             else if (*(args.argv0) == 'p')
10015                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
10016             else 
10017                break;
10018             (args.argv0)++;
10019          }
10020 
10021       }
10022 
10023       valid = ast_test_flag(&flags, OPT_SILENT);
10024 
10025       if ((context = strchr(args.argv0, '@')))
10026          *context++ = '\0';
10027 
10028       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10029          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10030       else
10031          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10032 
10033       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10034          skipuser++;
10035       else
10036          valid = 0;
10037    }
10038 
10039    if (!valid)
10040       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10041 
10042    ast_debug(1, "After vm_authenticate\n");
10043 
10044    if (vms.username[0] == '*') {
10045       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10046 
10047       /* user entered '*' */
10048       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10049          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10050          res = 0; /* prevent hangup */
10051          goto out;
10052       }
10053    }
10054 
10055    if (!res) {
10056       valid = 1;
10057       if (!skipuser)
10058          vmu = &vmus;
10059    } else {
10060       res = 0;
10061    }
10062 
10063    /* If ADSI is supported, setup login screen */
10064    adsi_begin(chan, &useadsi);
10065 
10066    ast_test_suite_assert(valid);
10067    if (!valid) {
10068       goto out;
10069    }
10070    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10071 
10072 #ifdef IMAP_STORAGE
10073    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10074    pthread_setspecific(ts_vmstate.key, &vms);
10075 
10076    vms.interactive = 1;
10077    vms.updated = 1;
10078    if (vmu)
10079       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10080    vmstate_insert(&vms);
10081    init_vm_state(&vms);
10082 #endif
10083    
10084    /* Set language from config to override channel language */
10085    if (!ast_strlen_zero(vmu->language))
10086       ast_string_field_set(chan, language, vmu->language);
10087 
10088    /* Retrieve urgent, old and new message counts */
10089    ast_debug(1, "Before open_mailbox\n");
10090    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10091    if (res < 0)
10092       goto out;
10093    vms.oldmessages = vms.lastmsg + 1;
10094    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10095    /* check INBOX */
10096    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10097    if (res < 0)
10098       goto out;
10099    vms.newmessages = vms.lastmsg + 1;
10100    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10101    /* Start in Urgent */
10102    in_urgent = 1;
10103    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10104    if (res < 0)
10105       goto out;
10106    vms.urgentmessages = vms.lastmsg + 1;
10107    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10108 
10109    /* Select proper mailbox FIRST!! */
10110    if (play_auto) {
10111       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10112       if (vms.urgentmessages) {
10113          in_urgent = 1;
10114          res = open_mailbox(&vms, vmu, 11);
10115       } else {
10116          in_urgent = 0;
10117          res = open_mailbox(&vms, vmu, play_folder);
10118       }
10119       if (res < 0)
10120          goto out;
10121 
10122       /* If there are no new messages, inform the user and hangup */
10123       if (vms.lastmsg == -1) {
10124          in_urgent = 0;
10125          cmd = vm_browse_messages(chan, &vms, vmu);
10126          res = 0;
10127          goto out;
10128       }
10129    } else {
10130       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10131          /* If we only have old messages start here */
10132          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10133          in_urgent = 0;
10134          play_folder = 1;
10135          if (res < 0)
10136             goto out;
10137       } else if (!vms.urgentmessages && vms.newmessages) {
10138          /* If we have new messages but none are urgent */
10139          in_urgent = 0;
10140          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10141          if (res < 0)
10142             goto out;
10143       }
10144    }
10145 
10146    if (useadsi)
10147       adsi_status(chan, &vms);
10148    res = 0;
10149 
10150    /* Check to see if this is a new user */
10151    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10152       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10153       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10154          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10155       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10156       if ((cmd == 't') || (cmd == '#')) {
10157          /* Timeout */
10158          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10159          res = 0;
10160          goto out;
10161       } else if (cmd < 0) {
10162          /* Hangup */
10163          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10164          res = -1;
10165          goto out;
10166       }
10167    }
10168 #ifdef IMAP_STORAGE
10169       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10170       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10171          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10172          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10173       }
10174       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10175       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10176          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10177          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10178       }
10179 #endif
10180 
10181    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10182    if (play_auto) {
10183       cmd = '1';
10184    } else {
10185       cmd = vm_intro(chan, vmu, &vms);
10186    }
10187    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10188 
10189    vms.repeats = 0;
10190    vms.starting = 1;
10191    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10192       /* Run main menu */
10193       switch (cmd) {
10194       case '1': /* First message */
10195          vms.curmsg = 0;
10196          /* Fall through */
10197       case '5': /* Play current message */
10198          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10199          cmd = vm_browse_messages(chan, &vms, vmu);
10200          break;
10201       case '2': /* Change folders */
10202          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10203          if (useadsi)
10204             adsi_folders(chan, 0, "Change to folder...");
10205 
10206          cmd = get_folder2(chan, "vm-changeto", 0);
10207          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10208          if (cmd == '#') {
10209             cmd = 0;
10210          } else if (cmd > 0) {
10211             cmd = cmd - '0';
10212             res = close_mailbox(&vms, vmu);
10213             if (res == ERROR_LOCK_PATH)
10214                goto out;
10215             /* If folder is not urgent, set in_urgent to zero! */
10216             if (cmd != 11) in_urgent = 0;
10217             res = open_mailbox(&vms, vmu, cmd);
10218             if (res < 0)
10219                goto out;
10220             play_folder = cmd;
10221             cmd = 0;
10222          }
10223          if (useadsi)
10224             adsi_status2(chan, &vms);
10225 
10226          if (!cmd) {
10227             cmd = vm_play_folder_name(chan, vms.vmbox);
10228          }
10229 
10230          vms.starting = 1;
10231          vms.curmsg = 0;
10232          break;
10233       case '3': /* Advanced options */
10234          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10235          cmd = 0;
10236          vms.repeats = 0;
10237          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10238             switch (cmd) {
10239             case '1': /* Reply */
10240                if (vms.lastmsg > -1 && !vms.starting) {
10241                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10242                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10243                      res = cmd;
10244                      goto out;
10245                   }
10246                } else {
10247                   cmd = ast_play_and_wait(chan, "vm-sorry");
10248                }
10249                cmd = 't';
10250                break;
10251             case '2': /* Callback */
10252                if (!vms.starting)
10253                   ast_verb(3, "Callback Requested\n");
10254                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10255                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10256                   if (cmd == 9) {
10257                      silentexit = 1;
10258                      goto out;
10259                   } else if (cmd == ERROR_LOCK_PATH) {
10260                      res = cmd;
10261                      goto out;
10262                   }
10263                } else {
10264                   cmd = ast_play_and_wait(chan, "vm-sorry");
10265                }
10266                cmd = 't';
10267                break;
10268             case '3': /* Envelope */
10269                if (vms.lastmsg > -1 && !vms.starting) {
10270                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10271                   if (cmd == ERROR_LOCK_PATH) {
10272                      res = cmd;
10273                      goto out;
10274                   }
10275                } else {
10276                   cmd = ast_play_and_wait(chan, "vm-sorry");
10277                }
10278                cmd = 't';
10279                break;
10280             case '4': /* Dialout */
10281                if (!ast_strlen_zero(vmu->dialout)) {
10282                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10283                   if (cmd == 9) {
10284                      silentexit = 1;
10285                      goto out;
10286                   }
10287                } else {
10288                   cmd = ast_play_and_wait(chan, "vm-sorry");
10289                }
10290                cmd = 't';
10291                break;
10292 
10293             case '5': /* Leave VoiceMail */
10294                if (ast_test_flag(vmu, VM_SVMAIL)) {
10295                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10296                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10297                      res = cmd;
10298                      goto out;
10299                   }
10300                } else {
10301                   cmd = ast_play_and_wait(chan, "vm-sorry");
10302                }
10303                cmd = 't';
10304                break;
10305 
10306             case '*': /* Return to main menu */
10307                cmd = 't';
10308                break;
10309 
10310             default:
10311                cmd = 0;
10312                if (!vms.starting) {
10313                   cmd = ast_play_and_wait(chan, "vm-toreply");
10314                }
10315                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10316                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10317                }
10318                if (!cmd && !vms.starting) {
10319                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10320                }
10321                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10322                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10323                }
10324                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10325                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10326                }
10327                if (!cmd) {
10328                   cmd = ast_play_and_wait(chan, "vm-starmain");
10329                }
10330                if (!cmd) {
10331                   cmd = ast_waitfordigit(chan, 6000);
10332                }
10333                if (!cmd) {
10334                   vms.repeats++;
10335                }
10336                if (vms.repeats > 3) {
10337                   cmd = 't';
10338                }
10339                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10340             }
10341          }
10342          if (cmd == 't') {
10343             cmd = 0;
10344             vms.repeats = 0;
10345          }
10346          break;
10347       case '4': /* Go to the previous message */
10348          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10349          if (vms.curmsg > 0) {
10350             vms.curmsg--;
10351             cmd = play_message(chan, vmu, &vms);
10352          } else {
10353             /* Check if we were listening to new
10354                messages.  If so, go to Urgent messages
10355                instead of saying "no more messages"
10356             */
10357             if (in_urgent == 0 && vms.urgentmessages > 0) {
10358                /* Check for Urgent messages */
10359                in_urgent = 1;
10360                res = close_mailbox(&vms, vmu);
10361                if (res == ERROR_LOCK_PATH)
10362                   goto out;
10363                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10364                if (res < 0)
10365                   goto out;
10366                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10367                vms.curmsg = vms.lastmsg;
10368                if (vms.lastmsg < 0) {
10369                   cmd = ast_play_and_wait(chan, "vm-nomore");
10370                }
10371             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10372                vms.curmsg = vms.lastmsg;
10373                cmd = play_message(chan, vmu, &vms);
10374             } else {
10375                cmd = ast_play_and_wait(chan, "vm-nomore");
10376             }
10377          }
10378          break;
10379       case '6': /* Go to the next message */
10380          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10381          if (vms.curmsg < vms.lastmsg) {
10382             vms.curmsg++;
10383             cmd = play_message(chan, vmu, &vms);
10384          } else {
10385             if (in_urgent && vms.newmessages > 0) {
10386                /* Check if we were listening to urgent
10387                 * messages.  If so, go to regular new messages
10388                 * instead of saying "no more messages"
10389                 */
10390                in_urgent = 0;
10391                res = close_mailbox(&vms, vmu);
10392                if (res == ERROR_LOCK_PATH)
10393                   goto out;
10394                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10395                if (res < 0)
10396                   goto out;
10397                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10398                vms.curmsg = -1;
10399                if (vms.lastmsg < 0) {
10400                   cmd = ast_play_and_wait(chan, "vm-nomore");
10401                }
10402             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10403                vms.curmsg = 0;
10404                cmd = play_message(chan, vmu, &vms);
10405             } else {
10406                cmd = ast_play_and_wait(chan, "vm-nomore");
10407             }
10408          }
10409          break;
10410       case '7': /* Delete the current message */
10411          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10412             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10413             if (useadsi)
10414                adsi_delete(chan, &vms);
10415             if (vms.deleted[vms.curmsg]) {
10416                if (play_folder == 0) {
10417                   if (in_urgent) {
10418                      vms.urgentmessages--;
10419                   } else {
10420                      vms.newmessages--;
10421                   }
10422                }
10423                else if (play_folder == 1)
10424                   vms.oldmessages--;
10425                cmd = ast_play_and_wait(chan, "vm-deleted");
10426             } else {
10427                if (play_folder == 0) {
10428                   if (in_urgent) {
10429                      vms.urgentmessages++;
10430                   } else {
10431                      vms.newmessages++;
10432                   }
10433                }
10434                else if (play_folder == 1)
10435                   vms.oldmessages++;
10436                cmd = ast_play_and_wait(chan, "vm-undeleted");
10437             }
10438             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10439                if (vms.curmsg < vms.lastmsg) {
10440                   vms.curmsg++;
10441                   cmd = play_message(chan, vmu, &vms);
10442                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10443                   vms.curmsg = 0;
10444                   cmd = play_message(chan, vmu, &vms);
10445                } else {
10446                   /* Check if we were listening to urgent
10447                      messages.  If so, go to regular new messages
10448                      instead of saying "no more messages"
10449                   */
10450                   if (in_urgent == 1) {
10451                      /* Check for new messages */
10452                      in_urgent = 0;
10453                      res = close_mailbox(&vms, vmu);
10454                      if (res == ERROR_LOCK_PATH)
10455                         goto out;
10456                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10457                      if (res < 0)
10458                         goto out;
10459                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10460                      vms.curmsg = -1;
10461                      if (vms.lastmsg < 0) {
10462                         cmd = ast_play_and_wait(chan, "vm-nomore");
10463                      }
10464                   } else {
10465                      cmd = ast_play_and_wait(chan, "vm-nomore");
10466                   }
10467                }
10468             }
10469          } else /* Delete not valid if we haven't selected a message */
10470             cmd = 0;
10471 #ifdef IMAP_STORAGE
10472          deleted = 1;
10473 #endif
10474          break;
10475    
10476       case '8': /* Forward the current message */
10477          if (vms.lastmsg > -1) {
10478             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10479             if (cmd == ERROR_LOCK_PATH) {
10480                res = cmd;
10481                goto out;
10482             }
10483          } else {
10484             /* Check if we were listening to urgent
10485                messages.  If so, go to regular new messages
10486                instead of saying "no more messages"
10487             */
10488             if (in_urgent == 1 && vms.newmessages > 0) {
10489                /* Check for new messages */
10490                in_urgent = 0;
10491                res = close_mailbox(&vms, vmu);
10492                if (res == ERROR_LOCK_PATH)
10493                   goto out;
10494                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10495                if (res < 0)
10496                   goto out;
10497                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10498                vms.curmsg = -1;
10499                if (vms.lastmsg < 0) {
10500                   cmd = ast_play_and_wait(chan, "vm-nomore");
10501                }
10502             } else {
10503                cmd = ast_play_and_wait(chan, "vm-nomore");
10504             }
10505          }
10506          break;
10507       case '9': /* Save message to folder */
10508          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10509          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10510             /* No message selected */
10511             cmd = 0;
10512             break;
10513          }
10514          if (useadsi)
10515             adsi_folders(chan, 1, "Save to folder...");
10516          cmd = get_folder2(chan, "vm-savefolder", 1);
10517          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10518          box = 0; /* Shut up compiler */
10519          if (cmd == '#') {
10520             cmd = 0;
10521             break;
10522          } else if (cmd > 0) {
10523             box = cmd = cmd - '0';
10524             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10525             if (cmd == ERROR_LOCK_PATH) {
10526                res = cmd;
10527                goto out;
10528 #ifndef IMAP_STORAGE
10529             } else if (!cmd) {
10530                vms.deleted[vms.curmsg] = 1;
10531 #endif
10532             } else {
10533                vms.deleted[vms.curmsg] = 0;
10534                vms.heard[vms.curmsg] = 0;
10535             }
10536          }
10537          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10538          if (useadsi)
10539             adsi_message(chan, &vms);
10540          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10541          if (!cmd) {
10542             cmd = ast_play_and_wait(chan, "vm-message");
10543             if (!cmd) 
10544                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10545             if (!cmd)
10546                cmd = ast_play_and_wait(chan, "vm-savedto");
10547             if (!cmd)
10548                cmd = vm_play_folder_name(chan, vms.fn);
10549          } else {
10550             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10551          }
10552          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10553             if (vms.curmsg < vms.lastmsg) {
10554                vms.curmsg++;
10555                cmd = play_message(chan, vmu, &vms);
10556             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10557                vms.curmsg = 0;
10558                cmd = play_message(chan, vmu, &vms);
10559             } else {
10560                /* Check if we were listening to urgent
10561                   messages.  If so, go to regular new messages
10562                   instead of saying "no more messages"
10563                */
10564                if (in_urgent == 1 && vms.newmessages > 0) {
10565                   /* Check for new messages */
10566                   in_urgent = 0;
10567                   res = close_mailbox(&vms, vmu);
10568                   if (res == ERROR_LOCK_PATH)
10569                      goto out;
10570                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10571                   if (res < 0)
10572                      goto out;
10573                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10574                   vms.curmsg = -1;
10575                   if (vms.lastmsg < 0) {
10576                      cmd = ast_play_and_wait(chan, "vm-nomore");
10577                   }
10578                } else {
10579                   cmd = ast_play_and_wait(chan, "vm-nomore");
10580                }
10581             }
10582          }
10583          break;
10584       case '*': /* Help */
10585          if (!vms.starting) {
10586             cmd = ast_play_and_wait(chan, "vm-onefor");
10587             if (!strncasecmp(chan->language, "he", 2)) {
10588                cmd = ast_play_and_wait(chan, "vm-for");
10589             }
10590             if (!cmd)
10591                cmd = vm_play_folder_name(chan, vms.vmbox);
10592             if (!cmd)
10593                cmd = ast_play_and_wait(chan, "vm-opts");
10594             if (!cmd)
10595                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10596          } else
10597             cmd = 0;
10598          break;
10599       case '0': /* Mailbox options */
10600          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10601          if (useadsi)
10602             adsi_status(chan, &vms);
10603          break;
10604       default: /* Nothing */
10605          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10606          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10607          break;
10608       }
10609    }
10610    if ((cmd == 't') || (cmd == '#')) {
10611       /* Timeout */
10612       res = 0;
10613    } else {
10614       /* Hangup */
10615       res = -1;
10616    }
10617 
10618 out:
10619    if (res > -1) {
10620       ast_stopstream(chan);
10621       adsi_goodbye(chan);
10622       if (valid && res != OPERATOR_EXIT) {
10623          if (silentexit)
10624             res = ast_play_and_wait(chan, "vm-dialout");
10625          else 
10626             res = ast_play_and_wait(chan, "vm-goodbye");
10627       }
10628       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10629          res = 0;
10630       }
10631       if (useadsi)
10632          ast_adsi_unload_session(chan);
10633    }
10634    if (vmu)
10635       close_mailbox(&vms, vmu);
10636    if (valid) {
10637       int new = 0, old = 0, urgent = 0;
10638       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10639       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10640       /* Urgent flag not passwd to externnotify here */
10641       run_externnotify(vmu->context, vmu->mailbox, NULL);
10642       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10643       queue_mwi_event(ext_context, urgent, new, old);
10644    }
10645 #ifdef IMAP_STORAGE
10646    /* expunge message - use UID Expunge if supported on IMAP server*/
10647    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10648    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10649       ast_mutex_lock(&vms.lock);
10650 #ifdef HAVE_IMAP_TK2006
10651       if (LEVELUIDPLUS (vms.mailstream)) {
10652          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10653       } else 
10654 #endif
10655          mail_expunge(vms.mailstream);
10656       ast_mutex_unlock(&vms.lock);
10657    }
10658    /*  before we delete the state, we should copy pertinent info
10659     *  back to the persistent model */
10660    if (vmu) {
10661       vmstate_delete(&vms);
10662    }
10663 #endif
10664    if (vmu)
10665       free_user(vmu);
10666 
10667 #ifdef IMAP_STORAGE
10668    pthread_setspecific(ts_vmstate.key, NULL);
10669 #endif
10670    return res;
10671 }
10672 
10673 static int vm_exec(struct ast_channel *chan, const char *data)
10674 {
10675    int res = 0;
10676    char *tmp;
10677    struct leave_vm_options leave_options;
10678    struct ast_flags flags = { 0 };
10679    char *opts[OPT_ARG_ARRAY_SIZE];
10680    AST_DECLARE_APP_ARGS(args,
10681       AST_APP_ARG(argv0);
10682       AST_APP_ARG(argv1);
10683    );
10684    
10685    memset(&leave_options, 0, sizeof(leave_options));
10686 
10687    if (chan->_state != AST_STATE_UP)
10688       ast_answer(chan);
10689 
10690    if (!ast_strlen_zero(data)) {
10691       tmp = ast_strdupa(data);
10692       AST_STANDARD_APP_ARGS(args, tmp);
10693       if (args.argc == 2) {
10694          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10695             return -1;
10696          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10697          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10698             int gain;
10699 
10700             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10701                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10702                return -1;
10703             } else {
10704                leave_options.record_gain = (signed char) gain;
10705             }
10706          }
10707          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10708             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10709                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10710          }
10711       }
10712    } else {
10713       char temp[256];
10714       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10715       if (res < 0)
10716          return res;
10717       if (ast_strlen_zero(temp))
10718          return 0;
10719       args.argv0 = ast_strdupa(temp);
10720    }
10721 
10722    res = leave_voicemail(chan, args.argv0, &leave_options);
10723    if (res == 't') {
10724       ast_play_and_wait(chan, "vm-goodbye");
10725       res = 0;
10726    }
10727 
10728    if (res == OPERATOR_EXIT) {
10729       res = 0;
10730    }
10731 
10732    if (res == ERROR_LOCK_PATH) {
10733       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10734       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10735       res = 0;
10736    }
10737 
10738    return res;
10739 }
10740 
10741 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10742 {
10743    struct ast_vm_user *vmu;
10744 
10745    if (!ast_strlen_zero(box) && box[0] == '*') {
10746       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10747             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10748             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10749             "\n\tand will be ignored.\n", box, context);
10750       return NULL;
10751    }
10752 
10753    AST_LIST_TRAVERSE(&users, vmu, list) {
10754       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10755          if (strcasecmp(vmu->context, context)) {
10756             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10757                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10758                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10759                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10760          }
10761          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10762          return NULL;
10763       }
10764       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10765          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10766          return NULL;
10767       }
10768    }
10769    
10770    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10771       return NULL;
10772    
10773    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10774    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10775 
10776    AST_LIST_INSERT_TAIL(&users, vmu, list);
10777    
10778    return vmu;
10779 }
10780 
10781 static int append_mailbox(const char *context, const char *box, const char *data)
10782 {
10783    /* Assumes lock is already held */
10784    char *tmp;
10785    char *stringp;
10786    char *s;
10787    struct ast_vm_user *vmu;
10788    char *mailbox_full;
10789    int new = 0, old = 0, urgent = 0;
10790    char secretfn[PATH_MAX] = "";
10791 
10792    tmp = ast_strdupa(data);
10793 
10794    if (!(vmu = find_or_create(context, box)))
10795       return -1;
10796 
10797    populate_defaults(vmu);
10798 
10799    stringp = tmp;
10800    if ((s = strsep(&stringp, ","))) {
10801       if (!ast_strlen_zero(s) && s[0] == '*') {
10802          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10803             "\n\tmust be reset in voicemail.conf.\n", box);
10804       }
10805       /* assign password regardless of validity to prevent NULL password from being assigned */
10806       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10807    }
10808    if (stringp && (s = strsep(&stringp, ","))) {
10809       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10810    }
10811    if (stringp && (s = strsep(&stringp, ","))) {
10812       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10813    }
10814    if (stringp && (s = strsep(&stringp, ","))) {
10815       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10816    }
10817    if (stringp && (s = strsep(&stringp, ","))) {
10818       apply_options(vmu, s);
10819    }
10820 
10821    switch (vmu->passwordlocation) {
10822    case OPT_PWLOC_SPOOLDIR:
10823       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10824       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10825    }
10826 
10827    mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10828    strcpy(mailbox_full, box);
10829    strcat(mailbox_full, "@");
10830    strcat(mailbox_full, context);
10831 
10832    inboxcount2(mailbox_full, &urgent, &new, &old);
10833    queue_mwi_event(mailbox_full, urgent, new, old);
10834 
10835    return 0;
10836 }
10837 
10838 AST_TEST_DEFINE(test_voicemail_vmuser)
10839 {
10840    int res = 0;
10841    struct ast_vm_user *vmu;
10842    /* language parameter seems to only be used for display in manager action */
10843    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10844       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10845       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10846       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10847       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10848       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10849       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10850       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10851       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10852 #ifdef IMAP_STORAGE
10853    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10854       "imapfolder=INBOX|imapvmshareid=6000";
10855 #endif
10856 
10857    switch (cmd) {
10858    case TEST_INIT:
10859       info->name = "vmuser";
10860       info->category = "/apps/app_voicemail/";
10861       info->summary = "Vmuser unit test";
10862       info->description =
10863          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10864       return AST_TEST_NOT_RUN;
10865    case TEST_EXECUTE:
10866       break;
10867    }
10868 
10869    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10870       return AST_TEST_NOT_RUN;
10871    }
10872    populate_defaults(vmu);
10873    ast_set_flag(vmu, VM_ALLOCED);
10874 
10875    apply_options(vmu, options_string);
10876 
10877    if (!ast_test_flag(vmu, VM_ATTACH)) {
10878       ast_test_status_update(test, "Parse failure for attach option\n");
10879       res = 1;
10880    }
10881    if (strcasecmp(vmu->attachfmt, "wav49")) {
10882       ast_test_status_update(test, "Parse failure for attachftm option\n");
10883       res = 1;
10884    }
10885    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10886       ast_test_status_update(test, "Parse failure for serveremail option\n");
10887       res = 1;
10888    }
10889    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10890       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10891       res = 1;
10892    }
10893    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10894       ast_test_status_update(test, "Parse failure for emailbody option\n");
10895       res = 1;
10896    }
10897    if (strcasecmp(vmu->zonetag, "central")) {
10898       ast_test_status_update(test, "Parse failure for tz option\n");
10899       res = 1;
10900    }
10901    if (!ast_test_flag(vmu, VM_DELETE)) {
10902       ast_test_status_update(test, "Parse failure for delete option\n");
10903       res = 1;
10904    }
10905    if (!ast_test_flag(vmu, VM_SAYCID)) {
10906       ast_test_status_update(test, "Parse failure for saycid option\n");
10907       res = 1;
10908    }
10909    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10910       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10911       res = 1;
10912    }
10913    if (!ast_test_flag(vmu, VM_REVIEW)) {
10914       ast_test_status_update(test, "Parse failure for review option\n");
10915       res = 1;
10916    }
10917    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10918       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10919       res = 1;
10920    }
10921    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10922       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10923       res = 1;
10924    }
10925    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10926       ast_test_status_update(test, "Parse failure for operator option\n");
10927       res = 1;
10928    }
10929    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10930       ast_test_status_update(test, "Parse failure for envelope option\n");
10931       res = 1;
10932    }
10933    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10934       ast_test_status_update(test, "Parse failure for moveheard option\n");
10935       res = 1;
10936    }
10937    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10938       ast_test_status_update(test, "Parse failure for sayduration option\n");
10939       res = 1;
10940    }
10941    if (vmu->saydurationm != 5) {
10942       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10943       res = 1;
10944    }
10945    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10946       ast_test_status_update(test, "Parse failure for forcename option\n");
10947       res = 1;
10948    }
10949    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10950       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10951       res = 1;
10952    }
10953    if (strcasecmp(vmu->callback, "somecontext")) {
10954       ast_test_status_update(test, "Parse failure for callbacks option\n");
10955       res = 1;
10956    }
10957    if (strcasecmp(vmu->dialout, "somecontext2")) {
10958       ast_test_status_update(test, "Parse failure for dialout option\n");
10959       res = 1;
10960    }
10961    if (strcasecmp(vmu->exit, "somecontext3")) {
10962       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10963       res = 1;
10964    }
10965    if (vmu->minsecs != 10) {
10966       ast_test_status_update(test, "Parse failure for minsecs option\n");
10967       res = 1;
10968    }
10969    if (vmu->maxsecs != 100) {
10970       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10971       res = 1;
10972    }
10973    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10974       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10975       res = 1;
10976    }
10977    if (vmu->maxdeletedmsg != 50) {
10978       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10979       res = 1;
10980    }
10981    if (vmu->volgain != 1.3) {
10982       ast_test_status_update(test, "Parse failure for volgain option\n");
10983       res = 1;
10984    }
10985    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10986       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10987       res = 1;
10988    }
10989 #ifdef IMAP_STORAGE
10990    apply_options(vmu, option_string2);
10991 
10992    if (strcasecmp(vmu->imapuser, "imapuser")) {
10993       ast_test_status_update(test, "Parse failure for imapuser option\n");
10994       res = 1;
10995    }
10996    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10997       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10998       res = 1;
10999    }
11000    if (strcasecmp(vmu->imapfolder, "INBOX")) {
11001       ast_test_status_update(test, "Parse failure for imapfolder option\n");
11002       res = 1;
11003    }
11004    if (strcasecmp(vmu->imapvmshareid, "6000")) {
11005       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
11006       res = 1;
11007    }
11008 #endif
11009 
11010    free_user(vmu);
11011    return res ? AST_TEST_FAIL : AST_TEST_PASS;
11012 }
11013 
11014 static int vm_box_exists(struct ast_channel *chan, const char *data) 
11015 {
11016    struct ast_vm_user svm;
11017    char *context, *box;
11018    AST_DECLARE_APP_ARGS(args,
11019       AST_APP_ARG(mbox);
11020       AST_APP_ARG(options);
11021    );
11022    static int dep_warning = 0;
11023 
11024    if (ast_strlen_zero(data)) {
11025       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
11026       return -1;
11027    }
11028 
11029    if (!dep_warning) {
11030       dep_warning = 1;
11031       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
11032    }
11033 
11034    box = ast_strdupa(data);
11035 
11036    AST_STANDARD_APP_ARGS(args, box);
11037 
11038    if (args.options) {
11039    }
11040 
11041    if ((context = strchr(args.mbox, '@'))) {
11042       *context = '\0';
11043       context++;
11044    }
11045 
11046    if (find_user(&svm, context, args.mbox)) {
11047       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11048    } else
11049       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11050 
11051    return 0;
11052 }
11053 
11054 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11055 {
11056    struct ast_vm_user svm;
11057    AST_DECLARE_APP_ARGS(arg,
11058       AST_APP_ARG(mbox);
11059       AST_APP_ARG(context);
11060    );
11061 
11062    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11063 
11064    if (ast_strlen_zero(arg.mbox)) {
11065       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11066       return -1;
11067    }
11068 
11069    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11070    return 0;
11071 }
11072 
11073 static struct ast_custom_function mailbox_exists_acf = {
11074    .name = "MAILBOX_EXISTS",
11075    .read = acf_mailbox_exists,
11076 };
11077 
11078 static int vmauthenticate(struct ast_channel *chan, const char *data)
11079 {
11080    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11081    struct ast_vm_user vmus;
11082    char *options = NULL;
11083    int silent = 0, skipuser = 0;
11084    int res = -1;
11085    
11086    if (data) {
11087       s = ast_strdupa(data);
11088       user = strsep(&s, ",");
11089       options = strsep(&s, ",");
11090       if (user) {
11091          s = user;
11092          user = strsep(&s, "@");
11093          context = strsep(&s, "");
11094          if (!ast_strlen_zero(user))
11095             skipuser++;
11096          ast_copy_string(mailbox, user, sizeof(mailbox));
11097       }
11098    }
11099 
11100    if (options) {
11101       silent = (strchr(options, 's')) != NULL;
11102    }
11103 
11104    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11105       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11106       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11107       ast_play_and_wait(chan, "auth-thankyou");
11108       res = 0;
11109    } else if (mailbox[0] == '*') {
11110       /* user entered '*' */
11111       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11112          res = 0; /* prevent hangup */
11113       }
11114    }
11115 
11116    return res;
11117 }
11118 
11119 static char *show_users_realtime(int fd, const char *context)
11120 {
11121    struct ast_config *cfg;
11122    const char *cat = NULL;
11123 
11124    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11125       "context", context, SENTINEL))) {
11126       return CLI_FAILURE;
11127    }
11128 
11129    ast_cli(fd,
11130       "\n"
11131       "=============================================================\n"
11132       "=== Configured Voicemail Users ==============================\n"
11133       "=============================================================\n"
11134       "===\n");
11135 
11136    while ((cat = ast_category_browse(cfg, cat))) {
11137       struct ast_variable *var = NULL;
11138       ast_cli(fd,
11139          "=== Mailbox ...\n"
11140          "===\n");
11141       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11142          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11143       ast_cli(fd,
11144          "===\n"
11145          "=== ---------------------------------------------------------\n"
11146          "===\n");
11147    }
11148 
11149    ast_cli(fd,
11150       "=============================================================\n"
11151       "\n");
11152 
11153    ast_config_destroy(cfg);
11154 
11155    return CLI_SUCCESS;
11156 }
11157 
11158 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11159 {
11160    int which = 0;
11161    int wordlen;
11162    struct ast_vm_user *vmu;
11163    const char *context = "";
11164 
11165    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11166    if (pos > 4)
11167       return NULL;
11168    if (pos == 3)
11169       return (state == 0) ? ast_strdup("for") : NULL;
11170    wordlen = strlen(word);
11171    AST_LIST_TRAVERSE(&users, vmu, list) {
11172       if (!strncasecmp(word, vmu->context, wordlen)) {
11173          if (context && strcmp(context, vmu->context) && ++which > state)
11174             return ast_strdup(vmu->context);
11175          /* ignore repeated contexts ? */
11176          context = vmu->context;
11177       }
11178    }
11179    return NULL;
11180 }
11181 
11182 /*! \brief Show a list of voicemail users in the CLI */
11183 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11184 {
11185    struct ast_vm_user *vmu;
11186 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11187    const char *context = NULL;
11188    int users_counter = 0;
11189 
11190    switch (cmd) {
11191    case CLI_INIT:
11192       e->command = "voicemail show users";
11193       e->usage =
11194          "Usage: voicemail show users [for <context>]\n"
11195          "       Lists all mailboxes currently set up\n";
11196       return NULL;
11197    case CLI_GENERATE:
11198       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11199    }  
11200 
11201    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11202       return CLI_SHOWUSAGE;
11203    if (a->argc == 5) {
11204       if (strcmp(a->argv[3],"for"))
11205          return CLI_SHOWUSAGE;
11206       context = a->argv[4];
11207    }
11208 
11209    if (ast_check_realtime("voicemail")) {
11210       if (!context) {
11211          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11212          return CLI_SHOWUSAGE;
11213       }
11214       return show_users_realtime(a->fd, context);
11215    }
11216 
11217    AST_LIST_LOCK(&users);
11218    if (AST_LIST_EMPTY(&users)) {
11219       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11220       AST_LIST_UNLOCK(&users);
11221       return CLI_FAILURE;
11222    }
11223    if (!context) {
11224       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11225    } else {
11226       int count = 0;
11227       AST_LIST_TRAVERSE(&users, vmu, list) {
11228          if (!strcmp(context, vmu->context)) {
11229             count++;
11230             break;
11231          }
11232       }
11233       if (count) {
11234          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11235       } else {
11236          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11237          AST_LIST_UNLOCK(&users);
11238          return CLI_FAILURE;
11239       }
11240    }
11241    AST_LIST_TRAVERSE(&users, vmu, list) {
11242       int newmsgs = 0, oldmsgs = 0;
11243       char count[12], tmp[256] = "";
11244 
11245       if (!context || !strcmp(context, vmu->context)) {
11246          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11247          inboxcount(tmp, &newmsgs, &oldmsgs);
11248          snprintf(count, sizeof(count), "%d", newmsgs);
11249          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11250          users_counter++;
11251       }
11252    }
11253    AST_LIST_UNLOCK(&users);
11254    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11255    return CLI_SUCCESS;
11256 }
11257 
11258 /*! \brief Show a list of voicemail zones in the CLI */
11259 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11260 {
11261    struct vm_zone *zone;
11262 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11263    char *res = CLI_SUCCESS;
11264 
11265    switch (cmd) {
11266    case CLI_INIT:
11267       e->command = "voicemail show zones";
11268       e->usage =
11269          "Usage: voicemail show zones\n"
11270          "       Lists zone message formats\n";
11271       return NULL;
11272    case CLI_GENERATE:
11273       return NULL;
11274    }
11275 
11276    if (a->argc != 3)
11277       return CLI_SHOWUSAGE;
11278 
11279    AST_LIST_LOCK(&zones);
11280    if (!AST_LIST_EMPTY(&zones)) {
11281       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11282       AST_LIST_TRAVERSE(&zones, zone, list) {
11283          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11284       }
11285    } else {
11286       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11287       res = CLI_FAILURE;
11288    }
11289    AST_LIST_UNLOCK(&zones);
11290 
11291    return res;
11292 }
11293 
11294 /*! \brief Reload voicemail configuration from the CLI */
11295 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11296 {
11297    switch (cmd) {
11298    case CLI_INIT:
11299       e->command = "voicemail reload";
11300       e->usage =
11301          "Usage: voicemail reload\n"
11302          "       Reload voicemail configuration\n";
11303       return NULL;
11304    case CLI_GENERATE:
11305       return NULL;
11306    }
11307 
11308    if (a->argc != 2)
11309       return CLI_SHOWUSAGE;
11310 
11311    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11312    load_config(1);
11313    
11314    return CLI_SUCCESS;
11315 }
11316 
11317 static struct ast_cli_entry cli_voicemail[] = {
11318    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11319    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11320    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11321 };
11322 
11323 #ifdef IMAP_STORAGE
11324    #define DATA_EXPORT_VM_USERS(USER)              \
11325       USER(ast_vm_user, context, AST_DATA_STRING)        \
11326       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11327       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11328       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11329       USER(ast_vm_user, email, AST_DATA_STRING)       \
11330       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11331       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11332       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11333       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11334       USER(ast_vm_user, language, AST_DATA_STRING)       \
11335       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11336       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11337       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11338       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11339       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11340       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11341       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11342       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11343       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11344       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11345       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11346       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11347       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11348       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11349       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11350 #else
11351    #define DATA_EXPORT_VM_USERS(USER)              \
11352       USER(ast_vm_user, context, AST_DATA_STRING)        \
11353       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11354       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11355       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11356       USER(ast_vm_user, email, AST_DATA_STRING)       \
11357       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11358       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11359       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11360       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11361       USER(ast_vm_user, language, AST_DATA_STRING)       \
11362       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11363       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11364       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11365       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11366       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11367       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11368       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11369       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11370       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11371       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11372       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11373       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11374 #endif
11375 
11376 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11377 
11378 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11379    ZONE(vm_zone, name, AST_DATA_STRING)      \
11380    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11381    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11382 
11383 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11384 
11385 /*!
11386  * \internal
11387  * \brief Add voicemail user to the data_root.
11388  * \param[in] search The search tree.
11389  * \param[in] data_root The main result node.
11390  * \param[in] user The voicemail user.
11391  */
11392 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11393     struct ast_data *data_root, struct ast_vm_user *user)
11394 {
11395    struct ast_data *data_user, *data_zone;
11396    struct ast_data *data_state;
11397    struct vm_zone *zone = NULL;
11398    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11399    char ext_context[256] = "";
11400 
11401    data_user = ast_data_add_node(data_root, "user");
11402    if (!data_user) {
11403       return -1;
11404    }
11405 
11406    ast_data_add_structure(ast_vm_user, data_user, user);
11407 
11408    AST_LIST_LOCK(&zones);
11409    AST_LIST_TRAVERSE(&zones, zone, list) {
11410       if (!strcmp(zone->name, user->zonetag)) {
11411          break;
11412       }
11413    }
11414    AST_LIST_UNLOCK(&zones);
11415 
11416    /* state */
11417    data_state = ast_data_add_node(data_user, "state");
11418    if (!data_state) {
11419       return -1;
11420    }
11421    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11422    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11423    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11424    ast_data_add_int(data_state, "newmsg", newmsg);
11425    ast_data_add_int(data_state, "oldmsg", oldmsg);
11426 
11427    if (zone) {
11428       data_zone = ast_data_add_node(data_user, "zone");
11429       ast_data_add_structure(vm_zone, data_zone, zone);
11430    }
11431 
11432    if (!ast_data_search_match(search, data_user)) {
11433       ast_data_remove_node(data_root, data_user);
11434    }
11435 
11436    return 0;
11437 }
11438 
11439 static int vm_users_data_provider_get(const struct ast_data_search *search,
11440    struct ast_data *data_root)
11441 {
11442    struct ast_vm_user *user;
11443 
11444    AST_LIST_LOCK(&users);
11445    AST_LIST_TRAVERSE(&users, user, list) {
11446       vm_users_data_provider_get_helper(search, data_root, user);
11447    }
11448    AST_LIST_UNLOCK(&users);
11449 
11450    return 0;
11451 }
11452 
11453 static const struct ast_data_handler vm_users_data_provider = {
11454    .version = AST_DATA_HANDLER_VERSION,
11455    .get = vm_users_data_provider_get
11456 };
11457 
11458 static const struct ast_data_entry vm_data_providers[] = {
11459    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11460 };
11461 
11462 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11463 {
11464    int new = 0, old = 0, urgent = 0;
11465 
11466    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11467 
11468    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11469       mwi_sub->old_urgent = urgent;
11470       mwi_sub->old_new = new;
11471       mwi_sub->old_old = old;
11472       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11473       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11474    }
11475 }
11476 
11477 static void poll_subscribed_mailboxes(void)
11478 {
11479    struct mwi_sub *mwi_sub;
11480 
11481    AST_RWLIST_RDLOCK(&mwi_subs);
11482    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11483       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11484          poll_subscribed_mailbox(mwi_sub);
11485       }
11486    }
11487    AST_RWLIST_UNLOCK(&mwi_subs);
11488 }
11489 
11490 static void *mb_poll_thread(void *data)
11491 {
11492    while (poll_thread_run) {
11493       struct timespec ts = { 0, };
11494       struct timeval wait;
11495 
11496       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11497       ts.tv_sec = wait.tv_sec;
11498       ts.tv_nsec = wait.tv_usec * 1000;
11499 
11500       ast_mutex_lock(&poll_lock);
11501       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11502       ast_mutex_unlock(&poll_lock);
11503 
11504       if (!poll_thread_run)
11505          break;
11506 
11507       poll_subscribed_mailboxes();
11508    }
11509 
11510    return NULL;
11511 }
11512 
11513 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11514 {
11515    ast_free(mwi_sub);
11516 }
11517 
11518 static int handle_unsubscribe(void *datap)
11519 {
11520    struct mwi_sub *mwi_sub;
11521    uint32_t *uniqueid = datap;
11522    
11523    AST_RWLIST_WRLOCK(&mwi_subs);
11524    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11525       if (mwi_sub->uniqueid == *uniqueid) {
11526          AST_LIST_REMOVE_CURRENT(entry);
11527          break;
11528       }
11529    }
11530    AST_RWLIST_TRAVERSE_SAFE_END
11531    AST_RWLIST_UNLOCK(&mwi_subs);
11532 
11533    if (mwi_sub)
11534       mwi_sub_destroy(mwi_sub);
11535 
11536    ast_free(uniqueid);  
11537    return 0;
11538 }
11539 
11540 static int handle_subscribe(void *datap)
11541 {
11542    unsigned int len;
11543    struct mwi_sub *mwi_sub;
11544    struct mwi_sub_task *p = datap;
11545 
11546    len = sizeof(*mwi_sub);
11547    if (!ast_strlen_zero(p->mailbox))
11548       len += strlen(p->mailbox);
11549 
11550    if (!ast_strlen_zero(p->context))
11551       len += strlen(p->context) + 1; /* Allow for seperator */
11552 
11553    if (!(mwi_sub = ast_calloc(1, len)))
11554       return -1;
11555 
11556    mwi_sub->uniqueid = p->uniqueid;
11557    if (!ast_strlen_zero(p->mailbox))
11558       strcpy(mwi_sub->mailbox, p->mailbox);
11559 
11560    if (!ast_strlen_zero(p->context)) {
11561       strcat(mwi_sub->mailbox, "@");
11562       strcat(mwi_sub->mailbox, p->context);
11563    }
11564 
11565    AST_RWLIST_WRLOCK(&mwi_subs);
11566    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11567    AST_RWLIST_UNLOCK(&mwi_subs);
11568    ast_free((void *) p->mailbox);
11569    ast_free((void *) p->context);
11570    ast_free(p);
11571    poll_subscribed_mailbox(mwi_sub);
11572    return 0;
11573 }
11574 
11575 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11576 {
11577    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11578 
11579    if (!uniqueid) {
11580       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11581       return;
11582    }
11583 
11584    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11585       ast_free(uniqueid);
11586       return;
11587    }
11588 
11589    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11590       ast_free(uniqueid);
11591       return;
11592    }
11593 
11594    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11595    *uniqueid = u;
11596    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11597       ast_free(uniqueid);
11598    }
11599 }
11600 
11601 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11602 {
11603    struct mwi_sub_task *mwist;
11604    
11605    if (ast_event_get_type(event) != AST_EVENT_SUB)
11606       return;
11607 
11608    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11609       return;
11610 
11611    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11612       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11613       return;
11614    }
11615    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11616    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11617    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11618    
11619    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11620       ast_free(mwist);
11621    }
11622 }
11623 
11624 static void start_poll_thread(void)
11625 {
11626    int errcode;
11627    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11628       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11629       AST_EVENT_IE_END);
11630 
11631    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11632       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11633       AST_EVENT_IE_END);
11634 
11635    if (mwi_sub_sub)
11636       ast_event_report_subs(mwi_sub_sub);
11637 
11638    poll_thread_run = 1;
11639 
11640    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11641       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11642    }
11643 }
11644 
11645 static void stop_poll_thread(void)
11646 {
11647    poll_thread_run = 0;
11648 
11649    if (mwi_sub_sub) {
11650       ast_event_unsubscribe(mwi_sub_sub);
11651       mwi_sub_sub = NULL;
11652    }
11653 
11654    if (mwi_unsub_sub) {
11655       ast_event_unsubscribe(mwi_unsub_sub);
11656       mwi_unsub_sub = NULL;
11657    }
11658 
11659    ast_mutex_lock(&poll_lock);
11660    ast_cond_signal(&poll_cond);
11661    ast_mutex_unlock(&poll_lock);
11662 
11663    pthread_join(poll_thread, NULL);
11664 
11665    poll_thread = AST_PTHREADT_NULL;
11666 }
11667 
11668 /*! \brief Manager list voicemail users command */
11669 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11670 {
11671    struct ast_vm_user *vmu = NULL;
11672    const char *id = astman_get_header(m, "ActionID");
11673    char actionid[128] = "";
11674 
11675    if (!ast_strlen_zero(id))
11676       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11677 
11678    AST_LIST_LOCK(&users);
11679 
11680    if (AST_LIST_EMPTY(&users)) {
11681       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11682       AST_LIST_UNLOCK(&users);
11683       return RESULT_SUCCESS;
11684    }
11685    
11686    astman_send_ack(s, m, "Voicemail user list will follow");
11687    
11688    AST_LIST_TRAVERSE(&users, vmu, list) {
11689       char dirname[256];
11690 
11691 #ifdef IMAP_STORAGE
11692       int new, old;
11693       inboxcount(vmu->mailbox, &new, &old);
11694 #endif
11695       
11696       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11697       astman_append(s,
11698          "%s"
11699          "Event: VoicemailUserEntry\r\n"
11700          "VMContext: %s\r\n"
11701          "VoiceMailbox: %s\r\n"
11702          "Fullname: %s\r\n"
11703          "Email: %s\r\n"
11704          "Pager: %s\r\n"
11705          "ServerEmail: %s\r\n"
11706          "MailCommand: %s\r\n"
11707          "Language: %s\r\n"
11708          "TimeZone: %s\r\n"
11709          "Callback: %s\r\n"
11710          "Dialout: %s\r\n"
11711          "UniqueID: %s\r\n"
11712          "ExitContext: %s\r\n"
11713          "SayDurationMinimum: %d\r\n"
11714          "SayEnvelope: %s\r\n"
11715          "SayCID: %s\r\n"
11716          "AttachMessage: %s\r\n"
11717          "AttachmentFormat: %s\r\n"
11718          "DeleteMessage: %s\r\n"
11719          "VolumeGain: %.2f\r\n"
11720          "CanReview: %s\r\n"
11721          "CallOperator: %s\r\n"
11722          "MaxMessageCount: %d\r\n"
11723          "MaxMessageLength: %d\r\n"
11724          "NewMessageCount: %d\r\n"
11725 #ifdef IMAP_STORAGE
11726          "OldMessageCount: %d\r\n"
11727          "IMAPUser: %s\r\n"
11728 #endif
11729          "\r\n",
11730          actionid,
11731          vmu->context,
11732          vmu->mailbox,
11733          vmu->fullname,
11734          vmu->email,
11735          vmu->pager,
11736          ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11737          mailcmd,
11738          vmu->language,
11739          vmu->zonetag,
11740          vmu->callback,
11741          vmu->dialout,
11742          vmu->uniqueid,
11743          vmu->exit,
11744          vmu->saydurationm,
11745          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11746          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11747          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11748          vmu->attachfmt,
11749          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11750          vmu->volgain,
11751          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11752          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11753          vmu->maxmsg,
11754          vmu->maxsecs,
11755 #ifdef IMAP_STORAGE
11756          new, old, vmu->imapuser
11757 #else
11758          count_messages(vmu, dirname)
11759 #endif
11760          );
11761    }     
11762    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11763 
11764    AST_LIST_UNLOCK(&users);
11765 
11766    return RESULT_SUCCESS;
11767 }
11768 
11769 /*! \brief Free the users structure. */
11770 static void free_vm_users(void) 
11771 {
11772    struct ast_vm_user *current;
11773    AST_LIST_LOCK(&users);
11774    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11775       ast_set_flag(current, VM_ALLOCED);
11776       free_user(current);
11777    }
11778    AST_LIST_UNLOCK(&users);
11779 }
11780 
11781 /*! \brief Free the zones structure. */
11782 static void free_vm_zones(void)
11783 {
11784    struct vm_zone *zcur;
11785    AST_LIST_LOCK(&zones);
11786    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11787       free_zone(zcur);
11788    AST_LIST_UNLOCK(&zones);
11789 }
11790 
11791 static const char *substitute_escapes(const char *value)
11792 {
11793    char *current;
11794 
11795    /* Add 16 for fudge factor */
11796    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11797 
11798    ast_str_reset(str);
11799    
11800    /* Substitute strings \r, \n, and \t into the appropriate characters */
11801    for (current = (char *) value; *current; current++) {
11802       if (*current == '\\') {
11803          current++;
11804          if (!*current) {
11805             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11806             break;
11807          }
11808          switch (*current) {
11809          case '\\':
11810             ast_str_append(&str, 0, "\\");
11811             break;
11812          case 'r':
11813             ast_str_append(&str, 0, "\r");
11814             break;
11815          case 'n':
11816 #ifdef IMAP_STORAGE
11817             if (!str->used || str->str[str->used - 1] != '\r') {
11818                ast_str_append(&str, 0, "\r");
11819             }
11820 #endif
11821             ast_str_append(&str, 0, "\n");
11822             break;
11823          case 't':
11824             ast_str_append(&str, 0, "\t");
11825             break;
11826          default:
11827             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11828             break;
11829          }
11830       } else {
11831          ast_str_append(&str, 0, "%c", *current);
11832       }
11833    }
11834 
11835    return ast_str_buffer(str);
11836 }
11837 
11838 static int load_config(int reload)
11839 {
11840    struct ast_config *cfg, *ucfg;
11841    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11842    int res;
11843 
11844    ast_unload_realtime("voicemail");
11845    ast_unload_realtime("voicemail_data");
11846 
11847    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11848       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11849          return 0;
11850       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11851          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11852          ucfg = NULL;
11853       }
11854       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11855       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11856          ast_config_destroy(ucfg);
11857          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11858          return 0;
11859       }
11860    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11861       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11862       return 0;
11863    } else {
11864       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11865       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11866          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11867          ucfg = NULL;
11868       }
11869    }
11870 
11871    res = actual_load_config(reload, cfg, ucfg);
11872 
11873    ast_config_destroy(cfg);
11874    ast_config_destroy(ucfg);
11875 
11876    return res;
11877 }
11878 
11879 #ifdef TEST_FRAMEWORK
11880 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11881 {
11882    ast_unload_realtime("voicemail");
11883    ast_unload_realtime("voicemail_data");
11884    return actual_load_config(reload, cfg, ucfg);
11885 }
11886 #endif
11887 
11888 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11889 {
11890    struct ast_vm_user *current;
11891    char *cat;
11892    struct ast_variable *var;
11893    const char *val;
11894    char *q, *stringp, *tmp;
11895    int x;
11896    int tmpadsi[4];
11897    char secretfn[PATH_MAX] = "";
11898 
11899 #ifdef IMAP_STORAGE
11900    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11901 #endif
11902    /* set audio control prompts */
11903    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11904    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11905    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11906    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11907    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11908 
11909    /* Free all the users structure */  
11910    free_vm_users();
11911 
11912    /* Free all the zones structure */
11913    free_vm_zones();
11914 
11915    AST_LIST_LOCK(&users);  
11916 
11917    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11918    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11919 
11920    if (cfg) {
11921       /* General settings */
11922 
11923       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11924          val = "default";
11925       ast_copy_string(userscontext, val, sizeof(userscontext));
11926       /* Attach voice message to mail message ? */
11927       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11928          val = "yes";
11929       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11930 
11931       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11932          val = "no";
11933       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11934 
11935       volgain = 0.0;
11936       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11937          sscanf(val, "%30lf", &volgain);
11938 
11939 #ifdef ODBC_STORAGE
11940       strcpy(odbc_database, "asterisk");
11941       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11942          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11943       }
11944       strcpy(odbc_table, "voicemessages");
11945       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11946          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11947       }
11948 #endif      
11949       /* Mail command */
11950       strcpy(mailcmd, SENDMAIL);
11951       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11952          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11953 
11954       maxsilence = 0;
11955       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11956          maxsilence = atoi(val);
11957          if (maxsilence > 0)
11958             maxsilence *= 1000;
11959       }
11960       
11961       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11962          maxmsg = MAXMSG;
11963       } else {
11964          maxmsg = atoi(val);
11965          if (maxmsg < 0) {
11966             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11967             maxmsg = MAXMSG;
11968          } else if (maxmsg > MAXMSGLIMIT) {
11969             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11970             maxmsg = MAXMSGLIMIT;
11971          }
11972       }
11973 
11974       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11975          maxdeletedmsg = 0;
11976       } else {
11977          if (sscanf(val, "%30d", &x) == 1)
11978             maxdeletedmsg = x;
11979          else if (ast_true(val))
11980             maxdeletedmsg = MAXMSG;
11981          else
11982             maxdeletedmsg = 0;
11983 
11984          if (maxdeletedmsg < 0) {
11985             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11986             maxdeletedmsg = MAXMSG;
11987          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11988             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11989             maxdeletedmsg = MAXMSGLIMIT;
11990          }
11991       }
11992 
11993       /* Load date format config for voicemail mail */
11994       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11995          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11996       }
11997 
11998       /* Load date format config for voicemail pager mail */
11999       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
12000          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
12001       }
12002 
12003       /* External password changing command */
12004       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
12005          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12006          pwdchange = PWDCHANGE_EXTERNAL;
12007       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
12008          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12009          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
12010       }
12011  
12012       /* External password validation command */
12013       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
12014          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
12015          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
12016       }
12017 
12018 #ifdef IMAP_STORAGE
12019       /* IMAP server address */
12020       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
12021          ast_copy_string(imapserver, val, sizeof(imapserver));
12022       } else {
12023          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
12024       }
12025       /* IMAP server port */
12026       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
12027          ast_copy_string(imapport, val, sizeof(imapport));
12028       } else {
12029          ast_copy_string(imapport, "143", sizeof(imapport));
12030       }
12031       /* IMAP server flags */
12032       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
12033          ast_copy_string(imapflags, val, sizeof(imapflags));
12034       }
12035       /* IMAP server master username */
12036       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12037          ast_copy_string(authuser, val, sizeof(authuser));
12038       }
12039       /* IMAP server master password */
12040       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12041          ast_copy_string(authpassword, val, sizeof(authpassword));
12042       }
12043       /* Expunge on exit */
12044       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12045          if (ast_false(val))
12046             expungeonhangup = 0;
12047          else
12048             expungeonhangup = 1;
12049       } else {
12050          expungeonhangup = 1;
12051       }
12052       /* IMAP voicemail folder */
12053       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12054          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12055       } else {
12056          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12057       }
12058       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12059          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12060       }
12061       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12062          imapgreetings = ast_true(val);
12063       } else {
12064          imapgreetings = 0;
12065       }
12066       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12067          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12068       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12069          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12070          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12071       } else {
12072          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12073       }
12074 
12075       /* There is some very unorthodox casting done here. This is due
12076        * to the way c-client handles the argument passed in. It expects a 
12077        * void pointer and casts the pointer directly to a long without
12078        * first dereferencing it. */
12079       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12080          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12081       } else {
12082          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12083       }
12084 
12085       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12086          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12087       } else {
12088          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12089       }
12090 
12091       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12092          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12093       } else {
12094          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12095       }
12096 
12097       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12098          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12099       } else {
12100          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12101       }
12102 
12103       /* Increment configuration version */
12104       imapversion++;
12105 #endif
12106       /* External voicemail notify application */
12107       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12108          ast_copy_string(externnotify, val, sizeof(externnotify));
12109          ast_debug(1, "found externnotify: %s\n", externnotify);
12110       } else {
12111          externnotify[0] = '\0';
12112       }
12113 
12114       /* SMDI voicemail notification */
12115       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12116          ast_debug(1, "Enabled SMDI voicemail notification\n");
12117          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12118             smdi_iface = ast_smdi_interface_find(val);
12119          } else {
12120             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12121             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12122          }
12123          if (!smdi_iface) {
12124             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12125          } 
12126       }
12127 
12128       /* Silence treshold */
12129       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12130       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12131          silencethreshold = atoi(val);
12132       
12133       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12134          val = ASTERISK_USERNAME;
12135       ast_copy_string(serveremail, val, sizeof(serveremail));
12136       
12137       vmmaxsecs = 0;
12138       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12139          if (sscanf(val, "%30d", &x) == 1) {
12140             vmmaxsecs = x;
12141          } else {
12142             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12143          }
12144       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12145          static int maxmessage_deprecate = 0;
12146          if (maxmessage_deprecate == 0) {
12147             maxmessage_deprecate = 1;
12148             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12149          }
12150          if (sscanf(val, "%30d", &x) == 1) {
12151             vmmaxsecs = x;
12152          } else {
12153             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12154          }
12155       }
12156 
12157       vmminsecs = 0;
12158       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12159          if (sscanf(val, "%30d", &x) == 1) {
12160             vmminsecs = x;
12161             if (maxsilence / 1000 >= vmminsecs) {
12162                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12163             }
12164          } else {
12165             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12166          }
12167       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12168          static int maxmessage_deprecate = 0;
12169          if (maxmessage_deprecate == 0) {
12170             maxmessage_deprecate = 1;
12171             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12172          }
12173          if (sscanf(val, "%30d", &x) == 1) {
12174             vmminsecs = x;
12175             if (maxsilence / 1000 >= vmminsecs) {
12176                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12177             }
12178          } else {
12179             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12180          }
12181       }
12182 
12183       val = ast_variable_retrieve(cfg, "general", "format");
12184       if (!val) {
12185          val = "wav";   
12186       } else {
12187          tmp = ast_strdupa(val);
12188          val = ast_format_str_reduce(tmp);
12189          if (!val) {
12190             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12191             val = "wav";
12192          }
12193       }
12194       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12195 
12196       skipms = 3000;
12197       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12198          if (sscanf(val, "%30d", &x) == 1) {
12199             maxgreet = x;
12200          } else {
12201             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12202          }
12203       }
12204 
12205       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12206          if (sscanf(val, "%30d", &x) == 1) {
12207             skipms = x;
12208          } else {
12209             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12210          }
12211       }
12212 
12213       maxlogins = 3;
12214       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12215          if (sscanf(val, "%30d", &x) == 1) {
12216             maxlogins = x;
12217          } else {
12218             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12219          }
12220       }
12221 
12222       minpassword = MINPASSWORD;
12223       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12224          if (sscanf(val, "%30d", &x) == 1) {
12225             minpassword = x;
12226          } else {
12227             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12228          }
12229       }
12230 
12231       /* Force new user to record name ? */
12232       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12233          val = "no";
12234       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12235 
12236       /* Force new user to record greetings ? */
12237       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12238          val = "no";
12239       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12240 
12241       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12242          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12243          stringp = ast_strdupa(val);
12244          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12245             if (!ast_strlen_zero(stringp)) {
12246                q = strsep(&stringp, ",");
12247                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12248                   q++;
12249                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12250                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12251             } else {
12252                cidinternalcontexts[x][0] = '\0';
12253             }
12254          }
12255       }
12256       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12257          ast_debug(1, "VM Review Option disabled globally\n");
12258          val = "no";
12259       }
12260       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12261 
12262       /* Temporary greeting reminder */
12263       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12264          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12265          val = "no";
12266       } else {
12267          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12268       }
12269       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12270       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12271          ast_debug(1, "VM next message wrap disabled globally\n");
12272          val = "no";
12273       }
12274       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12275 
12276       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12277          ast_debug(1, "VM Operator break disabled globally\n");
12278          val = "no";
12279       }
12280       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12281 
12282       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12283          ast_debug(1, "VM CID Info before msg disabled globally\n");
12284          val = "no";
12285       } 
12286       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12287 
12288       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12289          ast_debug(1, "Send Voicemail msg disabled globally\n");
12290          val = "no";
12291       }
12292       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12293    
12294       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12295          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12296          val = "yes";
12297       }
12298       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12299 
12300       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12301          ast_debug(1, "Move Heard enabled globally\n");
12302          val = "yes";
12303       }
12304       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12305 
12306       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12307          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12308          val = "no";
12309       }
12310       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12311 
12312       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12313          ast_debug(1, "Duration info before msg enabled globally\n");
12314          val = "yes";
12315       }
12316       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12317 
12318       saydurationminfo = 2;
12319       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12320          if (sscanf(val, "%30d", &x) == 1) {
12321             saydurationminfo = x;
12322          } else {
12323             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12324          }
12325       }
12326 
12327       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12328          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12329          val = "no";
12330       }
12331       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12332 
12333       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12334          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12335          ast_debug(1, "found dialout context: %s\n", dialcontext);
12336       } else {
12337          dialcontext[0] = '\0';  
12338       }
12339       
12340       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12341          ast_copy_string(callcontext, val, sizeof(callcontext));
12342          ast_debug(1, "found callback context: %s\n", callcontext);
12343       } else {
12344          callcontext[0] = '\0';
12345       }
12346 
12347       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12348          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12349          ast_debug(1, "found operator context: %s\n", exitcontext);
12350       } else {
12351          exitcontext[0] = '\0';
12352       }
12353       
12354       /* load password sounds configuration */
12355       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12356          ast_copy_string(vm_password, val, sizeof(vm_password));
12357       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12358          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12359       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12360          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12361       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12362          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12363       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12364          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12365       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12366          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12367       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12368          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12369       }
12370       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12371          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12372       }
12373       /* load configurable audio prompts */
12374       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12375          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12376       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12377          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12378       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12379          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12380       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12381          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12382       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12383          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12384 
12385       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12386          val = "no";
12387       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12388 
12389       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12390          val = "voicemail.conf";
12391       }
12392       if (!(strcmp(val, "spooldir"))) {
12393          passwordlocation = OPT_PWLOC_SPOOLDIR;
12394       } else {
12395          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12396       }
12397 
12398       poll_freq = DEFAULT_POLL_FREQ;
12399       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12400          if (sscanf(val, "%30u", &poll_freq) != 1) {
12401             poll_freq = DEFAULT_POLL_FREQ;
12402             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12403          }
12404       }
12405 
12406       poll_mailboxes = 0;
12407       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12408          poll_mailboxes = ast_true(val);
12409 
12410       memset(fromstring, 0, sizeof(fromstring));
12411       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12412       strcpy(charset, "ISO-8859-1");
12413       if (emailbody) {
12414          ast_free(emailbody);
12415          emailbody = NULL;
12416       }
12417       if (emailsubject) {
12418          ast_free(emailsubject);
12419          emailsubject = NULL;
12420       }
12421       if (pagerbody) {
12422          ast_free(pagerbody);
12423          pagerbody = NULL;
12424       }
12425       if (pagersubject) {
12426          ast_free(pagersubject);
12427          pagersubject = NULL;
12428       }
12429       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12430          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12431       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12432          ast_copy_string(fromstring, val, sizeof(fromstring));
12433       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12434          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12435       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12436          ast_copy_string(charset, val, sizeof(charset));
12437       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12438          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12439          for (x = 0; x < 4; x++) {
12440             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12441          }
12442       }
12443       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12444          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12445          for (x = 0; x < 4; x++) {
12446             memcpy(&adsisec[x], &tmpadsi[x], 1);
12447          }
12448       }
12449       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12450          if (atoi(val)) {
12451             adsiver = atoi(val);
12452          }
12453       }
12454       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12455          ast_copy_string(zonetag, val, sizeof(zonetag));
12456       }
12457       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12458          ast_copy_string(locale, val, sizeof(locale));
12459       }
12460       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12461          emailsubject = ast_strdup(substitute_escapes(val));
12462       }
12463       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12464          emailbody = ast_strdup(substitute_escapes(val));
12465       }
12466       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12467          pagersubject = ast_strdup(substitute_escapes(val));
12468       }
12469       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12470          pagerbody = ast_strdup(substitute_escapes(val));
12471       }
12472 
12473       /* load mailboxes from users.conf */
12474       if (ucfg) { 
12475          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12476             if (!strcasecmp(cat, "general")) {
12477                continue;
12478             }
12479             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12480                continue;
12481             if ((current = find_or_create(userscontext, cat))) {
12482                populate_defaults(current);
12483                apply_options_full(current, ast_variable_browse(ucfg, cat));
12484                ast_copy_string(current->context, userscontext, sizeof(current->context));
12485                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12486                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12487                }
12488 
12489                switch (current->passwordlocation) {
12490                case OPT_PWLOC_SPOOLDIR:
12491                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12492                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12493                }
12494             }
12495          }
12496       }
12497 
12498       /* load mailboxes from voicemail.conf */
12499       cat = ast_category_browse(cfg, NULL);
12500       while (cat) {
12501          if (strcasecmp(cat, "general")) {
12502             var = ast_variable_browse(cfg, cat);
12503             if (strcasecmp(cat, "zonemessages")) {
12504                /* Process mailboxes in this context */
12505                while (var) {
12506                   append_mailbox(cat, var->name, var->value);
12507                   var = var->next;
12508                }
12509             } else {
12510                /* Timezones in this context */
12511                while (var) {
12512                   struct vm_zone *z;
12513                   if ((z = ast_malloc(sizeof(*z)))) {
12514                      char *msg_format, *tzone;
12515                      msg_format = ast_strdupa(var->value);
12516                      tzone = strsep(&msg_format, "|,");
12517                      if (msg_format) {
12518                         ast_copy_string(z->name, var->name, sizeof(z->name));
12519                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12520                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12521                         AST_LIST_LOCK(&zones);
12522                         AST_LIST_INSERT_HEAD(&zones, z, list);
12523                         AST_LIST_UNLOCK(&zones);
12524                      } else {
12525                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12526                         ast_free(z);
12527                      }
12528                   } else {
12529                      AST_LIST_UNLOCK(&users);
12530                      return -1;
12531                   }
12532                   var = var->next;
12533                }
12534             }
12535          }
12536          cat = ast_category_browse(cfg, cat);
12537       }
12538 
12539       AST_LIST_UNLOCK(&users);
12540 
12541       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12542          start_poll_thread();
12543       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12544          stop_poll_thread();;
12545 
12546       return 0;
12547    } else {
12548       AST_LIST_UNLOCK(&users);
12549       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12550       return 0;
12551    }
12552 }
12553 
12554 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12555 {
12556    int res = -1;
12557    char dir[PATH_MAX];
12558    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12559    ast_debug(2, "About to try retrieving name file %s\n", dir);
12560    RETRIEVE(dir, -1, mailbox, context);
12561    if (ast_fileexists(dir, NULL, NULL)) {
12562       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12563    }
12564    DISPOSE(dir, -1);
12565    return res;
12566 }
12567 
12568 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12569    struct ast_config *pwconf;
12570    struct ast_flags config_flags = { 0 };
12571 
12572    pwconf = ast_config_load(secretfn, config_flags);
12573    if (pwconf) {
12574       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12575       if (val) {
12576          ast_copy_string(password, val, passwordlen);
12577          ast_config_destroy(pwconf);
12578          return;
12579       }
12580       ast_config_destroy(pwconf);
12581    }
12582    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12583 }
12584 
12585 static int write_password_to_file(const char *secretfn, const char *password) {
12586    struct ast_config *conf;
12587    struct ast_category *cat;
12588    struct ast_variable *var;
12589    int res = -1;
12590 
12591    if (!(conf = ast_config_new())) {
12592       ast_log(LOG_ERROR, "Error creating new config structure\n");
12593       return res;
12594    }
12595    if (!(cat = ast_category_new("general", "", 1))) {
12596       ast_log(LOG_ERROR, "Error creating new category structure\n");
12597       ast_config_destroy(conf);
12598       return res;
12599    }
12600    if (!(var = ast_variable_new("password", password, ""))) {
12601       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12602       ast_config_destroy(conf);
12603       ast_category_destroy(cat);
12604       return res;
12605    }
12606    ast_category_append(conf, cat);
12607    ast_variable_append(cat, var);
12608    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12609       res = 0;
12610    } else {
12611       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12612    }
12613 
12614    ast_config_destroy(conf);
12615    return res;
12616 }
12617 
12618 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12619 {
12620    char *context;
12621    char *args_copy;
12622    int res;
12623 
12624    if (ast_strlen_zero(data)) {
12625       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12626       return -1;
12627    }
12628 
12629    args_copy = ast_strdupa(data);
12630    if ((context = strchr(args_copy, '@'))) {
12631       *context++ = '\0';
12632    } else {
12633       context = "default";
12634    }
12635 
12636    if ((res = sayname(chan, args_copy, context) < 0)) {
12637       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12638       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12639       if (!res) {
12640          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12641       }
12642    }
12643 
12644    return res;
12645 }
12646 
12647 #ifdef TEST_FRAMEWORK
12648 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12649 {
12650    return 0;
12651 }
12652 
12653 static struct ast_frame *fake_read(struct ast_channel *ast)
12654 {
12655    return &ast_null_frame;
12656 }
12657 
12658 AST_TEST_DEFINE(test_voicemail_vmsayname)
12659 {
12660    char dir[PATH_MAX];
12661    char dir2[PATH_MAX];
12662    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12663    static const char TEST_EXTENSION[] = "1234";
12664 
12665    struct ast_channel *test_channel1 = NULL;
12666    int res = -1;
12667 
12668    static const struct ast_channel_tech fake_tech = {
12669       .write = fake_write,
12670       .read = fake_read,
12671    };
12672 
12673    switch (cmd) {
12674    case TEST_INIT:
12675       info->name = "vmsayname_exec";
12676       info->category = "/apps/app_voicemail/";
12677       info->summary = "Vmsayname unit test";
12678       info->description =
12679          "This tests passing various parameters to vmsayname";
12680       return AST_TEST_NOT_RUN;
12681    case TEST_EXECUTE:
12682       break;
12683    }
12684 
12685    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12686         NULL, NULL, 0, 0, "TestChannel1"))) {
12687       goto exit_vmsayname_test;
12688    }
12689 
12690    /* normally this is done in the channel driver */
12691    test_channel1->nativeformats = AST_FORMAT_GSM;
12692    test_channel1->writeformat = AST_FORMAT_GSM;
12693    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12694    test_channel1->readformat = AST_FORMAT_GSM;
12695    test_channel1->rawreadformat = AST_FORMAT_GSM;
12696    test_channel1->tech = &fake_tech;
12697 
12698    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12699    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12700    if (!(res = vmsayname_exec(test_channel1, dir))) {
12701       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12702       if (ast_fileexists(dir, NULL, NULL)) {
12703          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12704          res = -1;
12705          goto exit_vmsayname_test;
12706       } else {
12707          /* no greeting already exists as expected, let's create one to fully test sayname */
12708          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12709             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12710             goto exit_vmsayname_test;
12711          }
12712          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12713          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12714          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12715          if ((res = symlink(dir, dir2))) {
12716             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12717             goto exit_vmsayname_test;
12718          }
12719          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12720          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12721          res = vmsayname_exec(test_channel1, dir);
12722 
12723          /* TODO: there may be a better way to do this */
12724          unlink(dir2);
12725          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12726          rmdir(dir2);
12727          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12728          rmdir(dir2);
12729       }
12730    }
12731 
12732 exit_vmsayname_test:
12733 
12734    if (test_channel1) {
12735       ast_hangup(test_channel1);
12736    }
12737 
12738    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12739 }
12740 
12741 AST_TEST_DEFINE(test_voicemail_msgcount)
12742 {
12743    int i, j, res = AST_TEST_PASS, syserr;
12744    struct ast_vm_user *vmu;
12745    struct vm_state vms;
12746 #ifdef IMAP_STORAGE
12747    struct ast_channel *chan = NULL;
12748 #endif
12749    struct {
12750       char dir[256];
12751       char file[256];
12752       char txtfile[256];
12753    } tmp[3];
12754    char syscmd[256];
12755    const char origweasels[] = "tt-weasels";
12756    const char testcontext[] = "test";
12757    const char testmailbox[] = "00000000";
12758    const char testspec[] = "00000000@test";
12759    FILE *txt;
12760    int new, old, urgent;
12761    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12762    const int folder2mbox[3] = { 1, 11, 0 };
12763    const int expected_results[3][12] = {
12764       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12765       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12766       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12767       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12768    };
12769 
12770    switch (cmd) {
12771    case TEST_INIT:
12772       info->name = "test_voicemail_msgcount";
12773       info->category = "/apps/app_voicemail/";
12774       info->summary = "Test Voicemail status checks";
12775       info->description =
12776          "Verify that message counts are correct when retrieved through the public API";
12777       return AST_TEST_NOT_RUN;
12778    case TEST_EXECUTE:
12779       break;
12780    }
12781 
12782    /* Make sure the original path was completely empty */
12783    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12784    if ((syserr = ast_safe_system(syscmd))) {
12785       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12786          syserr > 0 ? strerror(syserr) : "unable to fork()");
12787       return AST_TEST_FAIL;
12788    }
12789 
12790 #ifdef IMAP_STORAGE
12791    if (!(chan = ast_dummy_channel_alloc())) {
12792       ast_test_status_update(test, "Unable to create dummy channel\n");
12793       return AST_TEST_FAIL;
12794    }
12795 #endif
12796 
12797    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12798       !(vmu = find_or_create(testcontext, testmailbox))) {
12799       ast_test_status_update(test, "Cannot create vmu structure\n");
12800       ast_unreplace_sigchld();
12801 #ifdef IMAP_STORAGE
12802       chan = ast_channel_unref(chan);
12803 #endif
12804       return AST_TEST_FAIL;
12805    }
12806 
12807    populate_defaults(vmu);
12808    memset(&vms, 0, sizeof(vms));
12809 
12810    /* Create temporary voicemail */
12811    for (i = 0; i < 3; i++) {
12812       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12813       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12814       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12815 
12816       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12817          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12818             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12819          if ((syserr = ast_safe_system(syscmd))) {
12820             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12821                syserr > 0 ? strerror(syserr) : "unable to fork()");
12822             ast_unreplace_sigchld();
12823 #ifdef IMAP_STORAGE
12824             chan = ast_channel_unref(chan);
12825 #endif
12826             return AST_TEST_FAIL;
12827          }
12828       }
12829 
12830       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12831          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12832          fclose(txt);
12833       } else {
12834          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12835          res = AST_TEST_FAIL;
12836          break;
12837       }
12838       open_mailbox(&vms, vmu, folder2mbox[i]);
12839       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12840 
12841       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12842       for (j = 0; j < 3; j++) {
12843          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12844          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12845             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12846                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12847             res = AST_TEST_FAIL;
12848          }
12849       }
12850 
12851       new = old = urgent = 0;
12852       if (ast_app_inboxcount(testspec, &new, &old)) {
12853          ast_test_status_update(test, "inboxcount returned failure\n");
12854          res = AST_TEST_FAIL;
12855       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12856          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12857             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12858          res = AST_TEST_FAIL;
12859       }
12860 
12861       new = old = urgent = 0;
12862       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12863          ast_test_status_update(test, "inboxcount2 returned failure\n");
12864          res = AST_TEST_FAIL;
12865       } else if (old != expected_results[i][6 + 0] ||
12866             urgent != expected_results[i][6 + 1] ||
12867                new != expected_results[i][6 + 2]    ) {
12868          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12869             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12870          res = AST_TEST_FAIL;
12871       }
12872 
12873       new = old = urgent = 0;
12874       for (j = 0; j < 3; j++) {
12875          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12876             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12877                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12878             res = AST_TEST_FAIL;
12879          }
12880       }
12881    }
12882 
12883    for (i = 0; i < 3; i++) {
12884       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12885        * server, in which case, the rm below will not affect the
12886        * voicemails. */
12887       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12888       DISPOSE(tmp[i].dir, 0);
12889    }
12890 
12891    if (vms.deleted) {
12892       ast_free(vms.deleted);
12893    }
12894    if (vms.heard) {
12895       ast_free(vms.heard);
12896    }
12897 
12898 #ifdef IMAP_STORAGE
12899    chan = ast_channel_unref(chan);
12900 #endif
12901 
12902    /* And remove test directory */
12903    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12904    if ((syserr = ast_safe_system(syscmd))) {
12905       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12906          syserr > 0 ? strerror(syserr) : "unable to fork()");
12907    }
12908 
12909    return res;
12910 }
12911 
12912 AST_TEST_DEFINE(test_voicemail_notify_endl)
12913 {
12914    int res = AST_TEST_PASS;
12915    char testcontext[] = "test";
12916    char testmailbox[] = "00000000";
12917    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12918    char attach[256], attach2[256];
12919    char buf[256] = ""; /* No line should actually be longer than 80 */
12920    struct ast_channel *chan = NULL;
12921    struct ast_vm_user *vmu, vmus = {
12922       .flags = 0,
12923    };
12924    FILE *file;
12925    struct {
12926       char *name;
12927       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12928       void *location;
12929       union {
12930          int intval;
12931          char *strval;
12932       } u;
12933    } test_items[] = {
12934       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12935       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12936       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12937       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12938       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12939       { "attach2", STRPTR, attach2, .u.strval = "" },
12940       { "attach", STRPTR, attach, .u.strval = "" },
12941    };
12942    int which;
12943 
12944    switch (cmd) {
12945    case TEST_INIT:
12946       info->name = "test_voicemail_notify_endl";
12947       info->category = "/apps/app_voicemail/";
12948       info->summary = "Test Voicemail notification end-of-line";
12949       info->description =
12950          "Verify that notification emails use a consistent end-of-line character";
12951       return AST_TEST_NOT_RUN;
12952    case TEST_EXECUTE:
12953       break;
12954    }
12955 
12956    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12957    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12958 
12959    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12960       !(vmu = find_or_create(testcontext, testmailbox))) {
12961       ast_test_status_update(test, "Cannot create vmu structure\n");
12962       return AST_TEST_NOT_RUN;
12963    }
12964 
12965    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12966       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12967       return AST_TEST_NOT_RUN;
12968    }
12969 
12970    populate_defaults(vmu);
12971    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12972 #ifdef IMAP_STORAGE
12973    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12974 #endif
12975 
12976    file = tmpfile();
12977    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12978       /* Kill previous test, if any */
12979       rewind(file);
12980       if (ftruncate(fileno(file), 0)) {
12981          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12982          res = AST_TEST_FAIL;
12983          break;
12984       }
12985 
12986       /* Make each change, in order, to the test mailbox */
12987       if (test_items[which].type == INT) {
12988          *((int *) test_items[which].location) = test_items[which].u.intval;
12989       } else if (test_items[which].type == FLAGVAL) {
12990          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12991             ast_clear_flag(vmu, test_items[which].u.intval);
12992          } else {
12993             ast_set_flag(vmu, test_items[which].u.intval);
12994          }
12995       } else if (test_items[which].type == STATIC) {
12996          strcpy(test_items[which].location, test_items[which].u.strval);
12997       } else if (test_items[which].type == STRPTR) {
12998          test_items[which].location = test_items[which].u.strval;
12999       }
13000 
13001       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
13002       rewind(file);
13003       while (fgets(buf, sizeof(buf), file)) {
13004          if (
13005 #ifdef IMAP_STORAGE
13006          buf[strlen(buf) - 2] != '\r'
13007 #else
13008          buf[strlen(buf) - 2] == '\r'
13009 #endif
13010          || buf[strlen(buf) - 1] != '\n') {
13011             res = AST_TEST_FAIL;
13012          }
13013       }
13014    }
13015    fclose(file);
13016    return res;
13017 }
13018 
13019 AST_TEST_DEFINE(test_voicemail_load_config)
13020 {
13021    int res = AST_TEST_PASS;
13022    struct ast_vm_user *vmu;
13023    struct ast_config *cfg;
13024    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
13025    int fd;
13026    FILE *file;
13027    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
13028 
13029    switch (cmd) {
13030    case TEST_INIT:
13031       info->name = "test_voicemail_load_config";
13032       info->category = "/apps/app_voicemail/";
13033       info->summary = "Test loading Voicemail config";
13034       info->description =
13035          "Verify that configuration is loaded consistently. "
13036          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13037          "some options were loaded after the mailboxes were instantiated, causing "
13038          "those options not to be set correctly.";
13039       return AST_TEST_NOT_RUN;
13040    case TEST_EXECUTE:
13041       break;
13042    }
13043 
13044    /* build a config file by hand... */
13045    if ((fd = mkstemp(config_filename)) < 0) {
13046       return AST_TEST_FAIL;
13047    }
13048    if (!(file = fdopen(fd, "w"))) {
13049       close(fd);
13050       unlink(config_filename);
13051       return AST_TEST_FAIL;
13052    }
13053    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13054    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13055    fputs("00000002 => 9999,Mrs. Test\n", file);
13056    fclose(file);
13057 
13058    if (!(cfg = ast_config_load(config_filename, config_flags))) {
13059       res = AST_TEST_FAIL;
13060       goto cleanup;
13061    }
13062 
13063    load_config_from_memory(1, cfg, NULL);
13064    ast_config_destroy(cfg);
13065 
13066 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13067    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13068    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13069 
13070    AST_LIST_LOCK(&users);
13071    AST_LIST_TRAVERSE(&users, vmu, list) {
13072       if (!strcmp(vmu->mailbox, "00000001")) {
13073          if (0); /* trick to get CHECK to work */
13074          CHECK(vmu, callback, "othercontext")
13075          CHECK(vmu, locale, "nl_NL.UTF-8")
13076          CHECK(vmu, zonetag, "central")
13077       } else if (!strcmp(vmu->mailbox, "00000002")) {
13078          if (0); /* trick to get CHECK to work */
13079          CHECK(vmu, callback, "somecontext")
13080          CHECK(vmu, locale, "de_DE.UTF-8")
13081          CHECK(vmu, zonetag, "european")
13082       }
13083    }
13084    AST_LIST_UNLOCK(&users);
13085 
13086 #undef CHECK
13087 
13088    /* restore config */
13089    load_config(1); /* this might say "Failed to load configuration file." */
13090 
13091 cleanup:
13092    unlink(config_filename);
13093    return res;
13094 }
13095 
13096 #endif /* defined(TEST_FRAMEWORK) */
13097 
13098 static int reload(void)
13099 {
13100    return load_config(1);
13101 }
13102 
13103 static int unload_module(void)
13104 {
13105    int res;
13106 
13107    res = ast_unregister_application(app);
13108    res |= ast_unregister_application(app2);
13109    res |= ast_unregister_application(app3);
13110    res |= ast_unregister_application(app4);
13111    res |= ast_unregister_application(sayname_app);
13112    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13113    res |= ast_manager_unregister("VoicemailUsersList");
13114    res |= ast_data_unregister(NULL);
13115 #ifdef TEST_FRAMEWORK
13116    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13117    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13118    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13119    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13120    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13121 #endif
13122    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13123    ast_uninstall_vm_functions();
13124    ao2_ref(inprocess_container, -1);
13125 
13126    if (poll_thread != AST_PTHREADT_NULL)
13127       stop_poll_thread();
13128 
13129    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13130    ast_unload_realtime("voicemail");
13131    ast_unload_realtime("voicemail_data");
13132 
13133    free_vm_users();
13134    free_vm_zones();
13135    return res;
13136 }
13137 
13138 static int load_module(void)
13139 {
13140    int res;
13141    my_umask = umask(0);
13142    umask(my_umask);
13143 
13144    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13145       return AST_MODULE_LOAD_DECLINE;
13146    }
13147 
13148    /* compute the location of the voicemail spool directory */
13149    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13150    
13151    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13152       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13153    }
13154 
13155    if ((res = load_config(0)))
13156       return res;
13157 
13158    res = ast_register_application_xml(app, vm_exec);
13159    res |= ast_register_application_xml(app2, vm_execmain);
13160    res |= ast_register_application_xml(app3, vm_box_exists);
13161    res |= ast_register_application_xml(app4, vmauthenticate);
13162    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13163    res |= ast_custom_function_register(&mailbox_exists_acf);
13164    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13165 #ifdef TEST_FRAMEWORK
13166    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13167    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13168    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13169    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13170    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13171 #endif
13172 
13173    if (res)
13174       return res;
13175 
13176    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13177    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13178 
13179    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13180    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13181    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13182 
13183    return res;
13184 }
13185 
13186 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13187 {
13188    int cmd = 0;
13189    char destination[80] = "";
13190    int retries = 0;
13191 
13192    if (!num) {
13193       ast_verb(3, "Destination number will be entered manually\n");
13194       while (retries < 3 && cmd != 't') {
13195          destination[1] = '\0';
13196          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13197          if (!cmd)
13198             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13199          if (!cmd)
13200             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13201          if (!cmd) {
13202             cmd = ast_waitfordigit(chan, 6000);
13203             if (cmd)
13204                destination[0] = cmd;
13205          }
13206          if (!cmd) {
13207             retries++;
13208          } else {
13209 
13210             if (cmd < 0)
13211                return 0;
13212             if (cmd == '*') {
13213                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13214                return 0;
13215             }
13216             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13217                retries++;
13218             else
13219                cmd = 't';
13220          }
13221          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13222       }
13223       if (retries >= 3) {
13224          return 0;
13225       }
13226       
13227    } else {
13228       if (option_verbose > 2)
13229          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13230       ast_copy_string(destination, num, sizeof(destination));
13231    }
13232 
13233    if (!ast_strlen_zero(destination)) {
13234       if (destination[strlen(destination) -1 ] == '*')
13235          return 0; 
13236       if (option_verbose > 2)
13237          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13238       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13239       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13240       chan->priority = 0;
13241       return 9;
13242    }
13243    return 0;
13244 }
13245 
13246 /*!
13247  * \brief The advanced options within a message.
13248  * \param chan
13249  * \param vmu 
13250  * \param vms
13251  * \param msg
13252  * \param option
13253  * \param record_gain
13254  *
13255  * Provides handling for the play message envelope, call the person back, or reply to message. 
13256  *
13257  * \return zero on success, -1 on error.
13258  */
13259 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)
13260 {
13261    int res = 0;
13262    char filename[PATH_MAX];
13263    struct ast_config *msg_cfg = NULL;
13264    const char *origtime, *context;
13265    char *name, *num;
13266    int retries = 0;
13267    char *cid;
13268    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13269 
13270    vms->starting = 0; 
13271 
13272    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13273 
13274    /* Retrieve info from VM attribute file */
13275    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13276    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13277    msg_cfg = ast_config_load(filename, config_flags);
13278    DISPOSE(vms->curdir, vms->curmsg);
13279    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13280       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13281       return 0;
13282    }
13283 
13284    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13285       ast_config_destroy(msg_cfg);
13286       return 0;
13287    }
13288 
13289    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13290 
13291    context = ast_variable_retrieve(msg_cfg, "message", "context");
13292    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13293       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13294    switch (option) {
13295    case 3: /* Play message envelope */
13296       if (!res)
13297          res = play_message_datetime(chan, vmu, origtime, filename);
13298       if (!res)
13299          res = play_message_callerid(chan, vms, cid, context, 0);
13300 
13301       res = 't';
13302       break;
13303 
13304    case 2:  /* Call back */
13305 
13306       if (ast_strlen_zero(cid))
13307          break;
13308 
13309       ast_callerid_parse(cid, &name, &num);
13310       while ((res > -1) && (res != 't')) {
13311          switch (res) {
13312          case '1':
13313             if (num) {
13314                /* Dial the CID number */
13315                res = dialout(chan, vmu, num, vmu->callback);
13316                if (res) {
13317                   ast_config_destroy(msg_cfg);
13318                   return 9;
13319                }
13320             } else {
13321                res = '2';
13322             }
13323             break;
13324 
13325          case '2':
13326             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13327             if (!ast_strlen_zero(vmu->dialout)) {
13328                res = dialout(chan, vmu, NULL, vmu->dialout);
13329                if (res) {
13330                   ast_config_destroy(msg_cfg);
13331                   return 9;
13332                }
13333             } else {
13334                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13335                res = ast_play_and_wait(chan, "vm-sorry");
13336             }
13337             ast_config_destroy(msg_cfg);
13338             return res;
13339          case '*':
13340             res = 't';
13341             break;
13342          case '3':
13343          case '4':
13344          case '5':
13345          case '6':
13346          case '7':
13347          case '8':
13348          case '9':
13349          case '0':
13350 
13351             res = ast_play_and_wait(chan, "vm-sorry");
13352             retries++;
13353             break;
13354          default:
13355             if (num) {
13356                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13357                res = ast_play_and_wait(chan, "vm-num-i-have");
13358                if (!res)
13359                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13360                if (!res)
13361                   res = ast_play_and_wait(chan, "vm-tocallnum");
13362                /* Only prompt for a caller-specified number if there is a dialout context specified */
13363                if (!ast_strlen_zero(vmu->dialout)) {
13364                   if (!res)
13365                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13366                }
13367             } else {
13368                res = ast_play_and_wait(chan, "vm-nonumber");
13369                if (!ast_strlen_zero(vmu->dialout)) {
13370                   if (!res)
13371                      res = ast_play_and_wait(chan, "vm-toenternumber");
13372                }
13373             }
13374             if (!res) {
13375                res = ast_play_and_wait(chan, "vm-star-cancel");
13376             }
13377             if (!res) {
13378                res = ast_waitfordigit(chan, 6000);
13379             }
13380             if (!res) {
13381                retries++;
13382                if (retries > 3) {
13383                   res = 't';
13384                }
13385             }
13386             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13387             break; 
13388             
13389          }
13390          if (res == 't')
13391             res = 0;
13392          else if (res == '*')
13393             res = -1;
13394       }
13395       break;
13396       
13397    case 1:  /* Reply */
13398       /* Send reply directly to sender */
13399       if (ast_strlen_zero(cid))
13400          break;
13401 
13402       ast_callerid_parse(cid, &name, &num);
13403       if (!num) {
13404          ast_verb(3, "No CID number available, no reply sent\n");
13405          if (!res)
13406             res = ast_play_and_wait(chan, "vm-nonumber");
13407          ast_config_destroy(msg_cfg);
13408          return res;
13409       } else {
13410          struct ast_vm_user vmu2;
13411          if (find_user(&vmu2, vmu->context, num)) {
13412             struct leave_vm_options leave_options;
13413             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13414             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13415 
13416             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13417             
13418             memset(&leave_options, 0, sizeof(leave_options));
13419             leave_options.record_gain = record_gain;
13420             res = leave_voicemail(chan, mailbox, &leave_options);
13421             if (!res)
13422                res = 't';
13423             ast_config_destroy(msg_cfg);
13424             return res;
13425          } else {
13426             /* Sender has no mailbox, can't reply */
13427             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13428             ast_play_and_wait(chan, "vm-nobox");
13429             res = 't';
13430             ast_config_destroy(msg_cfg);
13431             return res;
13432          }
13433       } 
13434       res = 0;
13435 
13436       break;
13437    }
13438 
13439 #ifndef IMAP_STORAGE
13440    ast_config_destroy(msg_cfg);
13441 
13442    if (!res) {
13443       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13444       vms->heard[msg] = 1;
13445       res = wait_file(chan, vms, vms->fn);
13446    }
13447 #endif
13448    return res;
13449 }
13450 
13451 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13452          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13453          signed char record_gain, struct vm_state *vms, char *flag)
13454 {
13455    /* Record message & let caller review or re-record it, or set options if applicable */
13456    int res = 0;
13457    int cmd = 0;
13458    int max_attempts = 3;
13459    int attempts = 0;
13460    int recorded = 0;
13461    int msg_exists = 0;
13462    signed char zero_gain = 0;
13463    char tempfile[PATH_MAX];
13464    char *acceptdtmf = "#";
13465    char *canceldtmf = "";
13466    int canceleddtmf = 0;
13467 
13468    /* Note that urgent and private are for flagging messages as such in the future */
13469 
13470    /* barf if no pointer passed to store duration in */
13471    if (duration == NULL) {
13472       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13473       return -1;
13474    }
13475 
13476    if (!outsidecaller)
13477       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13478    else
13479       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13480 
13481    cmd = '3';  /* Want to start by recording */
13482 
13483    while ((cmd >= 0) && (cmd != 't')) {
13484       switch (cmd) {
13485       case '1':
13486          if (!msg_exists) {
13487             /* In this case, 1 is to record a message */
13488             cmd = '3';
13489             break;
13490          } else {
13491             /* Otherwise 1 is to save the existing message */
13492             ast_verb(3, "Saving message as is\n");
13493             if (!outsidecaller) 
13494                ast_filerename(tempfile, recordfile, NULL);
13495             ast_stream_and_wait(chan, "vm-msgsaved", "");
13496             if (!outsidecaller) {
13497                /* Saves to IMAP server only if imapgreeting=yes */
13498                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13499                DISPOSE(recordfile, -1);
13500             }
13501             cmd = 't';
13502             return res;
13503          }
13504       case '2':
13505          /* Review */
13506          ast_verb(3, "Reviewing the message\n");
13507          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13508          break;
13509       case '3':
13510          msg_exists = 0;
13511          /* Record */
13512          if (recorded == 1) 
13513             ast_verb(3, "Re-recording the message\n");
13514          else  
13515             ast_verb(3, "Recording the message\n");
13516          
13517          if (recorded && outsidecaller) {
13518             cmd = ast_play_and_wait(chan, INTRO);
13519             cmd = ast_play_and_wait(chan, "beep");
13520          }
13521          recorded = 1;
13522          /* 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 */
13523          if (record_gain)
13524             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13525          if (ast_test_flag(vmu, VM_OPERATOR))
13526             canceldtmf = "0";
13527          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13528          if (strchr(canceldtmf, cmd)) {
13529          /* need this flag here to distinguish between pressing '0' during message recording or after */
13530             canceleddtmf = 1;
13531          }
13532          if (record_gain)
13533             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13534          if (cmd == -1) {
13535             /* User has hung up, no options to give */
13536             if (!outsidecaller) {
13537                /* user was recording a greeting and they hung up, so let's delete the recording. */
13538                ast_filedelete(tempfile, NULL);
13539             }     
13540             return cmd;
13541          }
13542          if (cmd == '0') {
13543             break;
13544          } else if (cmd == '*') {
13545             break;
13546 #if 0
13547          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13548             /* Message is too short */
13549             ast_verb(3, "Message too short\n");
13550             cmd = ast_play_and_wait(chan, "vm-tooshort");
13551             cmd = ast_filedelete(tempfile, NULL);
13552             break;
13553          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13554             /* Message is all silence */
13555             ast_verb(3, "Nothing recorded\n");
13556             cmd = ast_filedelete(tempfile, NULL);
13557             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13558             if (!cmd)
13559                cmd = ast_play_and_wait(chan, "vm-speakup");
13560             break;
13561 #endif
13562          } else {
13563             /* If all is well, a message exists */
13564             msg_exists = 1;
13565             cmd = 0;
13566          }
13567          break;
13568       case '4':
13569          if (outsidecaller) {  /* only mark vm messages */
13570             /* Mark Urgent */
13571             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13572                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13573                res = ast_play_and_wait(chan, "vm-marked-urgent");
13574                strcpy(flag, "Urgent");
13575             } else if (flag) {
13576                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13577                res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13578                strcpy(flag, "");
13579             } else {
13580                ast_play_and_wait(chan, "vm-sorry");
13581             }
13582             cmd = 0;
13583          } else {
13584             cmd = ast_play_and_wait(chan, "vm-sorry");
13585          }
13586          break;
13587       case '5':
13588       case '6':
13589       case '7':
13590       case '8':
13591       case '9':
13592       case '*':
13593       case '#':
13594          cmd = ast_play_and_wait(chan, "vm-sorry");
13595          break;
13596 #if 0 
13597 /*  XXX Commented out for the moment because of the dangers of deleting
13598     a message while recording (can put the message numbers out of sync) */
13599       case '*':
13600          /* Cancel recording, delete message, offer to take another message*/
13601          cmd = ast_play_and_wait(chan, "vm-deleted");
13602          cmd = ast_filedelete(tempfile, NULL);
13603          if (outsidecaller) {
13604             res = vm_exec(chan, NULL);
13605             return res;
13606          }
13607          else
13608             return 1;
13609 #endif
13610       case '0':
13611          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13612             cmd = ast_play_and_wait(chan, "vm-sorry");
13613             break;
13614          }
13615          if (msg_exists || recorded) {
13616             cmd = ast_play_and_wait(chan, "vm-saveoper");
13617             if (!cmd)
13618                cmd = ast_waitfordigit(chan, 3000);
13619             if (cmd == '1') {
13620                ast_filerename(tempfile, recordfile, NULL);
13621                ast_play_and_wait(chan, "vm-msgsaved");
13622                cmd = '0';
13623             } else if (cmd == '4') {
13624                if (flag) {
13625                   ast_play_and_wait(chan, "vm-marked-urgent");
13626                   strcpy(flag, "Urgent");
13627                }
13628                ast_play_and_wait(chan, "vm-msgsaved");
13629                cmd = '0';
13630             } else {
13631                ast_play_and_wait(chan, "vm-deleted");
13632                DELETE(tempfile, -1, tempfile, vmu);
13633                cmd = '0';
13634             }
13635          }
13636          return cmd;
13637       default:
13638          /* If the caller is an ouside caller, and the review option is enabled,
13639             allow them to review the message, but let the owner of the box review
13640             their OGM's */
13641          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13642             return cmd;
13643          if (msg_exists) {
13644             cmd = ast_play_and_wait(chan, "vm-review");
13645             if (!cmd && outsidecaller) {
13646                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13647                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13648                } else if (flag) {
13649                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13650                }
13651             }
13652          } else {
13653             cmd = ast_play_and_wait(chan, "vm-torerecord");
13654             if (!cmd)
13655                cmd = ast_waitfordigit(chan, 600);
13656          }
13657          
13658          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13659             cmd = ast_play_and_wait(chan, "vm-reachoper");
13660             if (!cmd)
13661                cmd = ast_waitfordigit(chan, 600);
13662          }
13663 #if 0
13664          if (!cmd)
13665             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13666 #endif
13667          if (!cmd)
13668             cmd = ast_waitfordigit(chan, 6000);
13669          if (!cmd) {
13670             attempts++;
13671          }
13672          if (attempts > max_attempts) {
13673             cmd = 't';
13674          }
13675       }
13676    }
13677    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13678       /* Hang up or timeout, so delete the recording. */
13679       ast_filedelete(tempfile, NULL);
13680    }
13681 
13682    if (cmd != 't' && outsidecaller)
13683       ast_play_and_wait(chan, "vm-goodbye");
13684 
13685    return cmd;
13686 }
13687 
13688 /* This is a workaround so that menuselect displays a proper description
13689  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13690  */
13691 
13692 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13693       .load = load_module,
13694       .unload = unload_module,
13695       .reload = reload,
13696       .nonoptreq = "res_adsi,res_smdi",
13697       );

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1