Mon Mar 12 2012 21:18:56

Asterisk developer's documentation


app_voicemail.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \author Mark Spencer <markster@digium.com>
00022  * \brief Comedian Mail - Voicemail System
00023  *
00024  * \extref unixODBC (http://www.unixodbc.org/)
00025  * \extref A source distribution of University of Washington's IMAP c-client
00026  *         (http://www.washington.edu/imap/)
00027  *
00028  * \par See also
00029  * \arg \ref Config_vm
00030  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
00031  * \ingroup applications
00032  * \note This module requires res_adsi to load. This needs to be optional
00033  * during compilation.
00034  *
00035  * \note This file is now almost impossible to work with, due to all \#ifdefs.
00036  *       Feels like the database code before realtime. Someone - please come up
00037  *       with a plan to clean this up.
00038  */
00039 
00040 /*** MODULEINFO
00041    <use>res_adsi</use>
00042    <use>res_smdi</use>
00043    <support_level>core</support_level>
00044  ***/
00045 
00046 /*** MAKEOPTS
00047 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
00048    <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
00049       <conflict>ODBC_STORAGE</conflict>
00050       <conflict>IMAP_STORAGE</conflict>
00051       <defaultenabled>yes</defaultenabled>
00052       <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: 347131 $")
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 mailcmd[160];               /*!< Configurable mail command */
00652    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00653    char zonetag[80];                /*!< Time zone */
00654    char locale[20];                 /*!< The locale (for presentation of date/time) */
00655    char callback[80];
00656    char dialout[80];
00657    char uniqueid[80];               /*!< Unique integer identifier */
00658    char exit[80];
00659    char attachfmt[20];              /*!< Attachment format */
00660    unsigned int flags;              /*!< VM_ flags */ 
00661    int saydurationm;
00662    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00663    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00664    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00665    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00666    int passwordlocation;            /*!< Storage location of the password */
00667 #ifdef IMAP_STORAGE
00668    char imapuser[80];               /*!< IMAP server login */
00669    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00670    char imapfolder[64];             /*!< IMAP voicemail folder */
00671    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00672    int imapversion;                 /*!< If configuration changes, use the new values */
00673 #endif
00674    double volgain;                  /*!< Volume gain for voicemails sent via email */
00675    AST_LIST_ENTRY(ast_vm_user) list;
00676 };
00677 
00678 /*! Voicemail time zones */
00679 struct vm_zone {
00680    AST_LIST_ENTRY(vm_zone) list;
00681    char name[80];
00682    char timezone[80];
00683    char msg_format[512];
00684 };
00685 
00686 #define VMSTATE_MAX_MSG_ARRAY 256
00687 
00688 /*! Voicemail mailbox state */
00689 struct vm_state {
00690    char curbox[80];
00691    char username[80];
00692    char context[80];
00693    char curdir[PATH_MAX];
00694    char vmbox[PATH_MAX];
00695    char fn[PATH_MAX];
00696    char intro[PATH_MAX];
00697    int *deleted;
00698    int *heard;
00699    int dh_arraysize; /* used for deleted / heard allocation */
00700    int curmsg;
00701    int lastmsg;
00702    int newmessages;
00703    int oldmessages;
00704    int urgentmessages;
00705    int starting;
00706    int repeats;
00707 #ifdef IMAP_STORAGE
00708    ast_mutex_t lock;
00709    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00710    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00711    MAILSTREAM *mailstream;
00712    int vmArrayIndex;
00713    char imapuser[80];                   /*!< IMAP server login */
00714    char imapfolder[64];                 /*!< IMAP voicemail folder */
00715    int imapversion;
00716    int interactive;
00717    char introfn[PATH_MAX];              /*!< Name of prepended file */
00718    unsigned int quota_limit;
00719    unsigned int quota_usage;
00720    struct vm_state *persist_vms;
00721 #endif
00722 };
00723 
00724 #ifdef ODBC_STORAGE
00725 static char odbc_database[80];
00726 static char odbc_table[80];
00727 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00728 #define DISPOSE(a,b) remove_file(a,b)
00729 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00730 #define EXISTS(a,b,c,d) (message_exists(a,b))
00731 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00732 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00733 #define DELETE(a,b,c,d) (delete_file(a,b))
00734 #else
00735 #ifdef IMAP_STORAGE
00736 #define DISPOSE(a,b) (imap_remove_file(a,b))
00737 #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))
00738 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00739 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00740 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00741 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00742 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00743 #else
00744 #define RETRIEVE(a,b,c,d)
00745 #define DISPOSE(a,b)
00746 #define STORE(a,b,c,d,e,f,g,h,i,j)
00747 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00748 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00749 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00750 #define DELETE(a,b,c,d) (vm_delete(c))
00751 #endif
00752 #endif
00753 
00754 static char VM_SPOOL_DIR[PATH_MAX];
00755 
00756 static char ext_pass_cmd[128];
00757 static char ext_pass_check_cmd[128];
00758 
00759 static int my_umask;
00760 
00761 #define PWDCHANGE_INTERNAL (1 << 1)
00762 #define PWDCHANGE_EXTERNAL (1 << 2)
00763 static int pwdchange = PWDCHANGE_INTERNAL;
00764 
00765 #ifdef ODBC_STORAGE
00766 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00767 #else
00768 # ifdef IMAP_STORAGE
00769 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00770 # else
00771 # define tdesc "Comedian Mail (Voicemail System)"
00772 # endif
00773 #endif
00774 
00775 static char userscontext[AST_MAX_EXTENSION] = "default";
00776 
00777 static char *addesc = "Comedian Mail";
00778 
00779 /* Leave a message */
00780 static char *app = "VoiceMail";
00781 
00782 /* Check mail, control, etc */
00783 static char *app2 = "VoiceMailMain";
00784 
00785 static char *app3 = "MailboxExists";
00786 static char *app4 = "VMAuthenticate";
00787 
00788 static char *sayname_app = "VMSayName";
00789 
00790 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00791 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00792 static char zonetag[80];
00793 static char locale[20];
00794 static int maxsilence;
00795 static int maxmsg;
00796 static int maxdeletedmsg;
00797 static int silencethreshold = 128;
00798 static char serveremail[80];
00799 static char mailcmd[160];  /* Configurable mail cmd */
00800 static char externnotify[160]; 
00801 static struct ast_smdi_interface *smdi_iface = NULL;
00802 static char vmfmts[80];
00803 static double volgain;
00804 static int vmminsecs;
00805 static int vmmaxsecs;
00806 static int maxgreet;
00807 static int skipms;
00808 static int maxlogins;
00809 static int minpassword;
00810 static int passwordlocation;
00811 
00812 /*! Poll mailboxes for changes since there is something external to
00813  *  app_voicemail that may change them. */
00814 static unsigned int poll_mailboxes;
00815 
00816 /*! Polling frequency */
00817 static unsigned int poll_freq;
00818 /*! By default, poll every 30 seconds */
00819 #define DEFAULT_POLL_FREQ 30
00820 
00821 AST_MUTEX_DEFINE_STATIC(poll_lock);
00822 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00823 static pthread_t poll_thread = AST_PTHREADT_NULL;
00824 static unsigned char poll_thread_run;
00825 
00826 /*! Subscription to ... MWI event subscriptions */
00827 static struct ast_event_sub *mwi_sub_sub;
00828 /*! Subscription to ... MWI event un-subscriptions */
00829 static struct ast_event_sub *mwi_unsub_sub;
00830 
00831 /*!
00832  * \brief An MWI subscription
00833  *
00834  * This is so we can keep track of which mailboxes are subscribed to.
00835  * This way, we know which mailboxes to poll when the pollmailboxes
00836  * option is being used.
00837  */
00838 struct mwi_sub {
00839    AST_RWLIST_ENTRY(mwi_sub) entry;
00840    int old_urgent;
00841    int old_new;
00842    int old_old;
00843    uint32_t uniqueid;
00844    char mailbox[1];
00845 };
00846 
00847 struct mwi_sub_task {
00848    const char *mailbox;
00849    const char *context;
00850    uint32_t uniqueid;
00851 };
00852 
00853 static struct ast_taskprocessor *mwi_subscription_tps;
00854 
00855 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00856 
00857 /* custom audio control prompts for voicemail playback */
00858 static char listen_control_forward_key[12];
00859 static char listen_control_reverse_key[12];
00860 static char listen_control_pause_key[12];
00861 static char listen_control_restart_key[12];
00862 static char listen_control_stop_key[12];
00863 
00864 /* custom password sounds */
00865 static char vm_password[80] = "vm-password";
00866 static char vm_newpassword[80] = "vm-newpassword";
00867 static char vm_passchanged[80] = "vm-passchanged";
00868 static char vm_reenterpassword[80] = "vm-reenterpassword";
00869 static char vm_mismatch[80] = "vm-mismatch";
00870 static char vm_invalid_password[80] = "vm-invalid-password";
00871 static char vm_pls_try_again[80] = "vm-pls-try-again";
00872 
00873 /*
00874  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00875  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00876  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00877  * app.c's __ast_play_and_record function
00878  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00879  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00880  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00881  * more effort than either of the other two.
00882  */
00883 static char vm_prepend_timeout[80] = "vm-then-pound";
00884 
00885 static struct ast_flags globalflags = {0};
00886 
00887 static int saydurationminfo;
00888 
00889 static char dialcontext[AST_MAX_CONTEXT] = "";
00890 static char callcontext[AST_MAX_CONTEXT] = "";
00891 static char exitcontext[AST_MAX_CONTEXT] = "";
00892 
00893 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00894 
00895 
00896 static char *emailbody = NULL;
00897 static char *emailsubject = NULL;
00898 static char *pagerbody = NULL;
00899 static char *pagersubject = NULL;
00900 static char fromstring[100];
00901 static char pagerfromstring[100];
00902 static char charset[32] = "ISO-8859-1";
00903 
00904 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00905 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00906 static int adsiver = 1;
00907 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00908 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00909 
00910 /* Forward declarations - generic */
00911 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00912 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);
00913 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00914 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00915          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00916          signed char record_gain, struct vm_state *vms, char *flag);
00917 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00918 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00919 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);
00920 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);
00921 static void apply_options(struct ast_vm_user *vmu, const char *options);
00922 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);
00923 static int is_valid_dtmf(const char *key);
00924 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00925 static int write_password_to_file(const char *secretfn, const char *password);
00926 static const char *substitute_escapes(const char *value);
00927 static void free_user(struct ast_vm_user *vmu);
00928 
00929 struct ao2_container *inprocess_container;
00930 
00931 struct inprocess {
00932    int count;
00933    char *context;
00934    char mailbox[0];
00935 };
00936 
00937 static int inprocess_hash_fn(const void *obj, const int flags)
00938 {
00939    const struct inprocess *i = obj;
00940    return atoi(i->mailbox);
00941 }
00942 
00943 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00944 {
00945    struct inprocess *i = obj, *j = arg;
00946    if (strcmp(i->mailbox, j->mailbox)) {
00947       return 0;
00948    }
00949    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00950 }
00951 
00952 static int inprocess_count(const char *context, const char *mailbox, int delta)
00953 {
00954    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00955    arg->context = arg->mailbox + strlen(mailbox) + 1;
00956    strcpy(arg->mailbox, mailbox); /* SAFE */
00957    strcpy(arg->context, context); /* SAFE */
00958    ao2_lock(inprocess_container);
00959    if ((i = ao2_find(inprocess_container, arg, 0))) {
00960       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00961       ao2_unlock(inprocess_container);
00962       ao2_ref(i, -1);
00963       return ret;
00964    }
00965    if (delta < 0) {
00966       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00967    }
00968    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00969       ao2_unlock(inprocess_container);
00970       return 0;
00971    }
00972    i->context = i->mailbox + strlen(mailbox) + 1;
00973    strcpy(i->mailbox, mailbox); /* SAFE */
00974    strcpy(i->context, context); /* SAFE */
00975    i->count = delta;
00976    ao2_link(inprocess_container, i);
00977    ao2_unlock(inprocess_container);
00978    ao2_ref(i, -1);
00979    return 0;
00980 }
00981 
00982 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00983 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00984 #endif
00985 
00986 /*!
00987  * \brief Strips control and non 7-bit clean characters from input string.
00988  *
00989  * \note To map control and none 7-bit characters to a 7-bit clean characters
00990  *  please use ast_str_encode_mine().
00991  */
00992 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00993 {
00994    char *bufptr = buf;
00995    for (; *input; input++) {
00996       if (*input < 32) {
00997          continue;
00998       }
00999       *bufptr++ = *input;
01000       if (bufptr == buf + buflen - 1) {
01001          break;
01002       }
01003    }
01004    *bufptr = '\0';
01005    return buf;
01006 }
01007 
01008 
01009 /*!
01010  * \brief Sets default voicemail system options to a voicemail user.
01011  *
01012  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01013  * - all the globalflags
01014  * - the saydurationminfo
01015  * - the callcontext
01016  * - the dialcontext
01017  * - the exitcontext
01018  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01019  * - volume gain
01020  * - emailsubject, emailbody set to NULL
01021  */
01022 static void populate_defaults(struct ast_vm_user *vmu)
01023 {
01024    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01025    vmu->passwordlocation = passwordlocation;
01026    if (saydurationminfo) {
01027       vmu->saydurationm = saydurationminfo;
01028    }
01029    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01030    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01031    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01032    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01033    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01034    if (vmminsecs) {
01035       vmu->minsecs = vmminsecs;
01036    }
01037    if (vmmaxsecs) {
01038       vmu->maxsecs = vmmaxsecs;
01039    }
01040    if (maxmsg) {
01041       vmu->maxmsg = maxmsg;
01042    }
01043    if (maxdeletedmsg) {
01044       vmu->maxdeletedmsg = maxdeletedmsg;
01045    }
01046    vmu->volgain = volgain;
01047    ast_free(vmu->emailsubject);
01048    vmu->emailsubject = NULL;
01049    ast_free(vmu->emailbody);
01050    vmu->emailbody = NULL;
01051 #ifdef IMAP_STORAGE
01052    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01053 #endif
01054 }
01055 
01056 /*!
01057  * \brief Sets a a specific property value.
01058  * \param vmu The voicemail user object to work with.
01059  * \param var The name of the property to be set.
01060  * \param value The value to be set to the property.
01061  * 
01062  * The property name must be one of the understood properties. See the source for details.
01063  */
01064 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01065 {
01066    int x;
01067    if (!strcasecmp(var, "attach")) {
01068       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01069    } else if (!strcasecmp(var, "attachfmt")) {
01070       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01071    } else if (!strcasecmp(var, "serveremail")) {
01072       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01073    } else if (!strcasecmp(var, "emailbody")) {
01074       vmu->emailbody = ast_strdup(substitute_escapes(value));
01075    } else if (!strcasecmp(var, "emailsubject")) {
01076       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01077    } else if (!strcasecmp(var, "language")) {
01078       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01079    } else if (!strcasecmp(var, "tz")) {
01080       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01081    } else if (!strcasecmp(var, "locale")) {
01082       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01083 #ifdef IMAP_STORAGE
01084    } else if (!strcasecmp(var, "imapuser")) {
01085       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01086       vmu->imapversion = imapversion;
01087    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01088       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01089       vmu->imapversion = imapversion;
01090    } else if (!strcasecmp(var, "imapfolder")) {
01091       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01092    } else if (!strcasecmp(var, "imapvmshareid")) {
01093       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01094       vmu->imapversion = imapversion;
01095 #endif
01096    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01097       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01098    } else if (!strcasecmp(var, "saycid")){
01099       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01100    } else if (!strcasecmp(var, "sendvoicemail")){
01101       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01102    } else if (!strcasecmp(var, "review")){
01103       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01104    } else if (!strcasecmp(var, "tempgreetwarn")){
01105       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01106    } else if (!strcasecmp(var, "messagewrap")){
01107       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01108    } else if (!strcasecmp(var, "operator")) {
01109       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01110    } else if (!strcasecmp(var, "envelope")){
01111       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01112    } else if (!strcasecmp(var, "moveheard")){
01113       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01114    } else if (!strcasecmp(var, "sayduration")){
01115       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01116    } else if (!strcasecmp(var, "saydurationm")){
01117       if (sscanf(value, "%30d", &x) == 1) {
01118          vmu->saydurationm = x;
01119       } else {
01120          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01121       }
01122    } else if (!strcasecmp(var, "forcename")){
01123       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01124    } else if (!strcasecmp(var, "forcegreetings")){
01125       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01126    } else if (!strcasecmp(var, "callback")) {
01127       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01128    } else if (!strcasecmp(var, "dialout")) {
01129       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01130    } else if (!strcasecmp(var, "exitcontext")) {
01131       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01132    } else if (!strcasecmp(var, "minsecs")) {
01133       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01134          vmu->minsecs = x;
01135       } else {
01136          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01137          vmu->minsecs = vmminsecs;
01138       }
01139    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01140       vmu->maxsecs = atoi(value);
01141       if (vmu->maxsecs <= 0) {
01142          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01143          vmu->maxsecs = vmmaxsecs;
01144       } else {
01145          vmu->maxsecs = atoi(value);
01146       }
01147       if (!strcasecmp(var, "maxmessage"))
01148          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01149    } else if (!strcasecmp(var, "maxmsg")) {
01150       vmu->maxmsg = atoi(value);
01151       /* Accept maxmsg=0 (Greetings only voicemail) */
01152       if (vmu->maxmsg < 0) {
01153          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01154          vmu->maxmsg = MAXMSG;
01155       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01156          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01157          vmu->maxmsg = MAXMSGLIMIT;
01158       }
01159    } else if (!strcasecmp(var, "nextaftercmd")) {
01160       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01161    } else if (!strcasecmp(var, "backupdeleted")) {
01162       if (sscanf(value, "%30d", &x) == 1)
01163          vmu->maxdeletedmsg = x;
01164       else if (ast_true(value))
01165          vmu->maxdeletedmsg = MAXMSG;
01166       else
01167          vmu->maxdeletedmsg = 0;
01168 
01169       if (vmu->maxdeletedmsg < 0) {
01170          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01171          vmu->maxdeletedmsg = MAXMSG;
01172       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01173          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01174          vmu->maxdeletedmsg = MAXMSGLIMIT;
01175       }
01176    } else if (!strcasecmp(var, "volgain")) {
01177       sscanf(value, "%30lf", &vmu->volgain);
01178    } else if (!strcasecmp(var, "passwordlocation")) {
01179       if (!strcasecmp(value, "spooldir")) {
01180          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01181       } else {
01182          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01183       }
01184    } else if (!strcasecmp(var, "options")) {
01185       apply_options(vmu, value);
01186    }
01187 }
01188 
01189 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01190 {
01191    int fds[2], pid = 0;
01192 
01193    memset(buf, 0, len);
01194 
01195    if (pipe(fds)) {
01196       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01197    } else {
01198       /* good to go*/
01199       pid = ast_safe_fork(0);
01200 
01201       if (pid < 0) {
01202          /* ok maybe not */
01203          close(fds[0]);
01204          close(fds[1]);
01205          snprintf(buf, len, "FAILURE: Fork failed");
01206       } else if (pid) {
01207          /* parent */
01208          close(fds[1]);
01209          if (read(fds[0], buf, len) < 0) {
01210             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01211          }
01212          close(fds[0]);
01213       } else {
01214          /*  child */
01215          AST_DECLARE_APP_ARGS(arg,
01216             AST_APP_ARG(v)[20];
01217          );
01218          char *mycmd = ast_strdupa(command);
01219 
01220          close(fds[0]);
01221          dup2(fds[1], STDOUT_FILENO);
01222          close(fds[1]);
01223          ast_close_fds_above_n(STDOUT_FILENO);
01224 
01225          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01226 
01227          execv(arg.v[0], arg.v); 
01228          printf("FAILURE: %s", strerror(errno));
01229          _exit(0);
01230       }
01231    }
01232    return buf;
01233 }
01234 
01235 /*!
01236  * \brief Check that password meets minimum required length
01237  * \param vmu The voicemail user to change the password for.
01238  * \param password The password string to check
01239  *
01240  * \return zero on ok, 1 on not ok.
01241  */
01242 static int check_password(struct ast_vm_user *vmu, char *password)
01243 {
01244    /* check minimum length */
01245    if (strlen(password) < minpassword)
01246       return 1;
01247    /* check that password does not contain '*' character */
01248    if (!ast_strlen_zero(password) && password[0] == '*')
01249       return 1;
01250    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01251       char cmd[255], buf[255];
01252 
01253       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01254 
01255       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01256       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01257          ast_debug(5, "Result: %s\n", buf);
01258          if (!strncasecmp(buf, "VALID", 5)) {
01259             ast_debug(3, "Passed password check: '%s'\n", buf);
01260             return 0;
01261          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01262             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01263             return 0;
01264          } else {
01265             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01266             return 1;
01267          }
01268       }
01269    }
01270    return 0;
01271 }
01272 
01273 /*! 
01274  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01275  * \param vmu The voicemail user to change the password for.
01276  * \param password The new value to be set to the password for this user.
01277  * 
01278  * This only works if there is a realtime engine configured.
01279  * This is called from the (top level) vm_change_password.
01280  *
01281  * \return zero on success, -1 on error.
01282  */
01283 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01284 {
01285    int res = -1;
01286    if (!strcmp(vmu->password, password)) {
01287       /* No change (but an update would return 0 rows updated, so we opt out here) */
01288       return 0;
01289    }
01290 
01291    if (strlen(password) > 10) {
01292       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01293    }
01294    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01295       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01296       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01297       res = 0;
01298    }
01299    return res;
01300 }
01301 
01302 /*!
01303  * \brief Destructively Parse options and apply.
01304  */
01305 static void apply_options(struct ast_vm_user *vmu, const char *options)
01306 {  
01307    char *stringp;
01308    char *s;
01309    char *var, *value;
01310    stringp = ast_strdupa(options);
01311    while ((s = strsep(&stringp, "|"))) {
01312       value = s;
01313       if ((var = strsep(&value, "=")) && value) {
01314          apply_option(vmu, var, value);
01315       }
01316    }  
01317 }
01318 
01319 /*!
01320  * \brief Loads the options specific to a voicemail user.
01321  * 
01322  * This is called when a vm_user structure is being set up, such as from load_options.
01323  */
01324 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01325 {
01326    for (; var; var = var->next) {
01327       if (!strcasecmp(var->name, "vmsecret")) {
01328          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01329       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01330          if (ast_strlen_zero(retval->password)) {
01331             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01332                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01333                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01334             } else {
01335                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01336             }
01337          }
01338       } else if (!strcasecmp(var->name, "uniqueid")) {
01339          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01340       } else if (!strcasecmp(var->name, "pager")) {
01341          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01342       } else if (!strcasecmp(var->name, "email")) {
01343          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01344       } else if (!strcasecmp(var->name, "fullname")) {
01345          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01346       } else if (!strcasecmp(var->name, "context")) {
01347          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01348       } else if (!strcasecmp(var->name, "emailsubject")) {
01349          ast_free(retval->emailsubject);
01350          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01351       } else if (!strcasecmp(var->name, "emailbody")) {
01352          ast_free(retval->emailbody);
01353          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01354 #ifdef IMAP_STORAGE
01355       } else if (!strcasecmp(var->name, "imapuser")) {
01356          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01357          retval->imapversion = imapversion;
01358       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01359          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01360          retval->imapversion = imapversion;
01361       } else if (!strcasecmp(var->name, "imapfolder")) {
01362          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01363       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01364          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01365          retval->imapversion = imapversion;
01366 #endif
01367       } else
01368          apply_option(retval, var->name, var->value);
01369    }
01370 }
01371 
01372 /*!
01373  * \brief Determines if a DTMF key entered is valid.
01374  * \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.
01375  *
01376  * Tests the character entered against the set of valid DTMF characters. 
01377  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01378  */
01379 static int is_valid_dtmf(const char *key)
01380 {
01381    int i;
01382    char *local_key = ast_strdupa(key);
01383 
01384    for (i = 0; i < strlen(key); ++i) {
01385       if (!strchr(VALID_DTMF, *local_key)) {
01386          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01387          return 0;
01388       }
01389       local_key++;
01390    }
01391    return 1;
01392 }
01393 
01394 /*!
01395  * \brief Finds a voicemail user from the realtime engine.
01396  * \param ivm
01397  * \param context
01398  * \param mailbox
01399  *
01400  * 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.
01401  *
01402  * \return The ast_vm_user structure for the user that was found.
01403  */
01404 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01405 {
01406    struct ast_variable *var;
01407    struct ast_vm_user *retval;
01408 
01409    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01410       if (!ivm)
01411          ast_set_flag(retval, VM_ALLOCED);   
01412       else
01413          memset(retval, 0, sizeof(*retval));
01414       if (mailbox) 
01415          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01416       populate_defaults(retval);
01417       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01418          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01419       else
01420          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01421       if (var) {
01422          apply_options_full(retval, var);
01423          ast_variables_destroy(var);
01424       } else { 
01425          if (!ivm) 
01426             free_user(retval);
01427          retval = NULL;
01428       }  
01429    } 
01430    return retval;
01431 }
01432 
01433 /*!
01434  * \brief Finds a voicemail user from the users file or the realtime engine.
01435  * \param ivm
01436  * \param context
01437  * \param mailbox
01438  * 
01439  * \return The ast_vm_user structure for the user that was found.
01440  */
01441 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01442 {
01443    /* This function could be made to generate one from a database, too */
01444    struct ast_vm_user *vmu = NULL, *cur;
01445    AST_LIST_LOCK(&users);
01446 
01447    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01448       context = "default";
01449 
01450    AST_LIST_TRAVERSE(&users, cur, list) {
01451 #ifdef IMAP_STORAGE
01452       if (cur->imapversion != imapversion) {
01453          continue;
01454       }
01455 #endif
01456       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01457          break;
01458       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01459          break;
01460    }
01461    if (cur) {
01462       /* Make a copy, so that on a reload, we have no race */
01463       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01464          *vmu = *cur;
01465          if (!ivm) {
01466             vmu->emailbody = ast_strdup(cur->emailbody);
01467             vmu->emailsubject = ast_strdup(cur->emailsubject);
01468          }
01469          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01470          AST_LIST_NEXT(vmu, list) = NULL;
01471       }
01472    } else
01473       vmu = find_user_realtime(ivm, context, mailbox);
01474    AST_LIST_UNLOCK(&users);
01475    return vmu;
01476 }
01477 
01478 /*!
01479  * \brief Resets a user password to a specified password.
01480  * \param context
01481  * \param mailbox
01482  * \param newpass
01483  *
01484  * This does the actual change password work, called by the vm_change_password() function.
01485  *
01486  * \return zero on success, -1 on error.
01487  */
01488 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01489 {
01490    /* This function could be made to generate one from a database, too */
01491    struct ast_vm_user *cur;
01492    int res = -1;
01493    AST_LIST_LOCK(&users);
01494    AST_LIST_TRAVERSE(&users, cur, list) {
01495       if ((!context || !strcasecmp(context, cur->context)) &&
01496          (!strcasecmp(mailbox, cur->mailbox)))
01497             break;
01498    }
01499    if (cur) {
01500       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01501       res = 0;
01502    }
01503    AST_LIST_UNLOCK(&users);
01504    return res;
01505 }
01506 
01507 /*! 
01508  * \brief The handler for the change password option.
01509  * \param vmu The voicemail user to work with.
01510  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01511  * 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.
01512  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01513  */
01514 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01515 {
01516    struct ast_config   *cfg = NULL;
01517    struct ast_variable *var = NULL;
01518    struct ast_category *cat = NULL;
01519    char *category = NULL, *value = NULL, *new = NULL;
01520    const char *tmp = NULL;
01521    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01522    char secretfn[PATH_MAX] = "";
01523    int found = 0;
01524 
01525    if (!change_password_realtime(vmu, newpassword))
01526       return;
01527 
01528    /* check if we should store the secret in the spool directory next to the messages */
01529    switch (vmu->passwordlocation) {
01530    case OPT_PWLOC_SPOOLDIR:
01531       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01532       if (write_password_to_file(secretfn, newpassword) == 0) {
01533          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01534          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01535          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01536          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01537          break;
01538       } else {
01539          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01540       }
01541       /* Fall-through */
01542    case OPT_PWLOC_VOICEMAILCONF:
01543       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01544          while ((category = ast_category_browse(cfg, category))) {
01545             if (!strcasecmp(category, vmu->context)) {
01546                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01547                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01548                   break;
01549                }
01550                value = strstr(tmp, ",");
01551                if (!value) {
01552                   new = alloca(strlen(newpassword)+1);
01553                   sprintf(new, "%s", newpassword);
01554                } else {
01555                   new = alloca((strlen(value) + strlen(newpassword) + 1));
01556                   sprintf(new, "%s%s", newpassword, value);
01557                }
01558                if (!(cat = ast_category_get(cfg, category))) {
01559                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01560                   break;
01561                }
01562                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01563                found = 1;
01564             }
01565          }
01566          /* save the results */
01567          if (found) {
01568             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01569             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01570             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01571             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01572             break;
01573          }
01574       }
01575       /* Fall-through */
01576    case OPT_PWLOC_USERSCONF:
01577       /* check users.conf and update the password stored for the mailbox */
01578       /* if no vmsecret entry exists create one. */
01579       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01580          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01581          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01582             ast_debug(4, "users.conf: %s\n", category);
01583             if (!strcasecmp(category, vmu->mailbox)) {
01584                if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01585                   ast_debug(3, "looks like we need to make vmsecret!\n");
01586                   var = ast_variable_new("vmsecret", newpassword, "");
01587                } else {
01588                   var = NULL;
01589                }
01590                new = alloca(strlen(newpassword) + 1);
01591                sprintf(new, "%s", newpassword);
01592                if (!(cat = ast_category_get(cfg, category))) {
01593                   ast_debug(4, "failed to get category!\n");
01594                   ast_free(var);
01595                   break;
01596                }
01597                if (!var) {
01598                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01599                } else {
01600                   ast_variable_append(cat, var);
01601                }
01602                found = 1;
01603                break;
01604             }
01605          }
01606          /* save the results and clean things up */
01607          if (found) {
01608             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01609             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01610             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01611             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01612          }
01613       }
01614    }
01615 }
01616 
01617 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01618 {
01619    char buf[255];
01620    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01621    ast_debug(1, "External password: %s\n",buf);
01622    if (!ast_safe_system(buf)) {
01623       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01624       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01625       /* Reset the password in memory, too */
01626       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01627    }
01628 }
01629 
01630 /*! 
01631  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01632  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01633  * \param len The length of the path string that was written out.
01634  * \param context
01635  * \param ext 
01636  * \param folder 
01637  * 
01638  * The path is constructed as 
01639  *    VM_SPOOL_DIRcontext/ext/folder
01640  *
01641  * \return zero on success, -1 on error.
01642  */
01643 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01644 {
01645    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01646 }
01647 
01648 /*! 
01649  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01650  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01651  * \param len The length of the path string that was written out.
01652  * \param dir 
01653  * \param num 
01654  * 
01655  * The path is constructed as 
01656  *    VM_SPOOL_DIRcontext/ext/folder
01657  *
01658  * \return zero on success, -1 on error.
01659  */
01660 static int make_file(char *dest, const int len, const char *dir, const int num)
01661 {
01662    return snprintf(dest, len, "%s/msg%04d", dir, num);
01663 }
01664 
01665 /* same as mkstemp, but return a FILE * */
01666 static FILE *vm_mkftemp(char *template)
01667 {
01668    FILE *p = NULL;
01669    int pfd = mkstemp(template);
01670    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01671    if (pfd > -1) {
01672       p = fdopen(pfd, "w+");
01673       if (!p) {
01674          close(pfd);
01675          pfd = -1;
01676       }
01677    }
01678    return p;
01679 }
01680 
01681 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01682  * \param dest    String. base directory.
01683  * \param len     Length of dest.
01684  * \param context String. Ignored if is null or empty string.
01685  * \param ext     String. Ignored if is null or empty string.
01686  * \param folder  String. Ignored if is null or empty string. 
01687  * \return -1 on failure, 0 on success.
01688  */
01689 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01690 {
01691    mode_t   mode = VOICEMAIL_DIR_MODE;
01692    int res;
01693 
01694    make_dir(dest, len, context, ext, folder);
01695    if ((res = ast_mkdir(dest, mode))) {
01696       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01697       return -1;
01698    }
01699    return 0;
01700 }
01701 
01702 static const char * const mailbox_folders[] = {
01703 #ifdef IMAP_STORAGE
01704    imapfolder,
01705 #else
01706    "INBOX",
01707 #endif
01708    "Old",
01709    "Work",
01710    "Family",
01711    "Friends",
01712    "Cust1",
01713    "Cust2",
01714    "Cust3",
01715    "Cust4",
01716    "Cust5",
01717    "Deleted",
01718    "Urgent",
01719 };
01720 
01721 static const char *mbox(struct ast_vm_user *vmu, int id)
01722 {
01723 #ifdef IMAP_STORAGE
01724    if (vmu && id == 0) {
01725       return vmu->imapfolder;
01726    }
01727 #endif
01728    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01729 }
01730 
01731 static int get_folder_by_name(const char *name)
01732 {
01733    size_t i;
01734 
01735    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01736       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01737          return i;
01738       }
01739    }
01740 
01741    return -1;
01742 }
01743 
01744 static void free_user(struct ast_vm_user *vmu)
01745 {
01746    if (ast_test_flag(vmu, VM_ALLOCED)) {
01747 
01748       ast_free(vmu->emailbody);
01749       vmu->emailbody = NULL;
01750 
01751       ast_free(vmu->emailsubject);
01752       vmu->emailsubject = NULL;
01753 
01754       ast_free(vmu);
01755    }
01756 }
01757 
01758 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01759 
01760    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01761    if (!vms->dh_arraysize) {
01762       /* initial allocation */
01763       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01764          return -1;
01765       }
01766       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01767          return -1;
01768       }
01769       vms->dh_arraysize = arraysize;
01770    } else if (vms->dh_arraysize < arraysize) {
01771       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01772          return -1;
01773       }
01774       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01775          return -1;
01776       }
01777       memset(vms->deleted, 0, arraysize * sizeof(int));
01778       memset(vms->heard, 0, arraysize * sizeof(int));
01779       vms->dh_arraysize = arraysize;
01780    }
01781 
01782    return 0;
01783 }
01784 
01785 /* All IMAP-specific functions should go in this block. This
01786  * keeps them from being spread out all over the code */
01787 #ifdef IMAP_STORAGE
01788 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01789 {
01790    char arg[10];
01791    struct vm_state *vms;
01792    unsigned long messageNum;
01793 
01794    /* If greetings aren't stored in IMAP, just delete the file */
01795    if (msgnum < 0 && !imapgreetings) {
01796       ast_filedelete(file, NULL);
01797       return;
01798    }
01799 
01800    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01801       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);
01802       return;
01803    }
01804 
01805    /* find real message number based on msgnum */
01806    /* this may be an index into vms->msgArray based on the msgnum. */
01807    messageNum = vms->msgArray[msgnum];
01808    if (messageNum == 0) {
01809       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01810       return;
01811    }
01812    if (option_debug > 2)
01813       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01814    /* delete message */
01815    snprintf (arg, sizeof(arg), "%lu", messageNum);
01816    ast_mutex_lock(&vms->lock);
01817    mail_setflag (vms->mailstream, arg, "\\DELETED");
01818    mail_expunge(vms->mailstream);
01819    ast_mutex_unlock(&vms->lock);
01820 }
01821 
01822 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01823 {
01824    struct vm_state *vms_p;
01825    char *file, *filename;
01826    char *attachment;
01827    int ret = 0, i;
01828    BODY *body;
01829 
01830    /* This function is only used for retrieval of IMAP greetings
01831     * regular messages are not retrieved this way, nor are greetings
01832     * if they are stored locally*/
01833    if (msgnum > -1 || !imapgreetings) {
01834       return 0;
01835    } else {
01836       file = strrchr(ast_strdupa(dir), '/');
01837       if (file)
01838          *file++ = '\0';
01839       else {
01840          ast_debug (1, "Failed to procure file name from directory passed.\n");
01841          return -1;
01842       }
01843    }
01844 
01845    /* check if someone is accessing this box right now... */
01846    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01847       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01848       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01849       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01850       * that's all we need to do.
01851       */
01852       if (!(vms_p = create_vm_state_from_user(vmu))) {
01853          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01854          return -1;
01855       }
01856    }
01857    
01858    /* Greetings will never have a prepended message */
01859    *vms_p->introfn = '\0';
01860 
01861    ast_mutex_lock(&vms_p->lock);
01862    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01863    if (!vms_p->mailstream) {
01864       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01865       ast_mutex_unlock(&vms_p->lock);
01866       return -1;
01867    }
01868 
01869    /*XXX Yuck, this could probably be done a lot better */
01870    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01871       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01872       /* We have the body, now we extract the file name of the first attachment. */
01873       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01874          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01875       } else {
01876          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01877          ast_mutex_unlock(&vms_p->lock);
01878          return -1;
01879       }
01880       filename = strsep(&attachment, ".");
01881       if (!strcmp(filename, file)) {
01882          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01883          vms_p->msgArray[vms_p->curmsg] = i + 1;
01884          save_body(body, vms_p, "2", attachment, 0);
01885          ast_mutex_unlock(&vms_p->lock);
01886          return 0;
01887       }
01888    }
01889    ast_mutex_unlock(&vms_p->lock);
01890 
01891    return -1;
01892 }
01893 
01894 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01895 {
01896    BODY *body;
01897    char *header_content;
01898    char *attachedfilefmt;
01899    char buf[80];
01900    struct vm_state *vms;
01901    char text_file[PATH_MAX];
01902    FILE *text_file_ptr;
01903    int res = 0;
01904    struct ast_vm_user *vmu;
01905 
01906    if (!(vmu = find_user(NULL, context, mailbox))) {
01907       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01908       return -1;
01909    }
01910    
01911    if (msgnum < 0) {
01912       if (imapgreetings) {
01913          res = imap_retrieve_greeting(dir, msgnum, vmu);
01914          goto exit;
01915       } else {
01916          res = 0;
01917          goto exit;
01918       }
01919    }
01920 
01921    /* Before anything can happen, we need a vm_state so that we can
01922     * actually access the imap server through the vms->mailstream
01923     */
01924    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01925       /* This should not happen. If it does, then I guess we'd
01926        * need to create the vm_state, extract which mailbox to
01927        * open, and then set up the msgArray so that the correct
01928        * IMAP message could be accessed. If I have seen correctly
01929        * though, the vms should be obtainable from the vmstates list
01930        * and should have its msgArray properly set up.
01931        */
01932       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01933       res = -1;
01934       goto exit;
01935    }
01936    
01937    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01938    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01939 
01940    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01941    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01942       res = 0;
01943       goto exit;
01944    }
01945 
01946    if (option_debug > 2)
01947       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01948    if (vms->msgArray[msgnum] == 0) {
01949       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01950       res = -1;
01951       goto exit;
01952    }
01953 
01954    /* This will only work for new messages... */
01955    ast_mutex_lock(&vms->lock);
01956    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01957    ast_mutex_unlock(&vms->lock);
01958    /* empty string means no valid header */
01959    if (ast_strlen_zero(header_content)) {
01960       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01961       res = -1;
01962       goto exit;
01963    }
01964 
01965    ast_mutex_lock(&vms->lock);
01966    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01967    ast_mutex_unlock(&vms->lock);
01968 
01969    /* We have the body, now we extract the file name of the first attachment. */
01970    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01971       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01972    } else {
01973       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01974       res = -1;
01975       goto exit;
01976    }
01977    
01978    /* Find the format of the attached file */
01979 
01980    strsep(&attachedfilefmt, ".");
01981    if (!attachedfilefmt) {
01982       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01983       res = -1;
01984       goto exit;
01985    }
01986    
01987    save_body(body, vms, "2", attachedfilefmt, 0);
01988    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01989       *vms->introfn = '\0';
01990    }
01991 
01992    /* Get info from headers!! */
01993    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01994 
01995    if (!(text_file_ptr = fopen(text_file, "w"))) {
01996       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01997    }
01998 
01999    fprintf(text_file_ptr, "%s\n", "[message]");
02000 
02001    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
02002    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
02003    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
02004    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02005    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
02006    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02007    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02008    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02009    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02010    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02011    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02012    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02013    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02014    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02015    fclose(text_file_ptr);
02016 
02017 exit:
02018    free_user(vmu);
02019    return res;
02020 }
02021 
02022 static int folder_int(const char *folder)
02023 {
02024    /*assume a NULL folder means INBOX*/
02025    if (!folder) {
02026       return 0;
02027    }
02028    if (!strcasecmp(folder, imapfolder)) {
02029       return 0;
02030    } else if (!strcasecmp(folder, "Old")) {
02031       return 1;
02032    } else if (!strcasecmp(folder, "Work")) {
02033       return 2;
02034    } else if (!strcasecmp(folder, "Family")) {
02035       return 3;
02036    } else if (!strcasecmp(folder, "Friends")) {
02037       return 4;
02038    } else if (!strcasecmp(folder, "Cust1")) {
02039       return 5;
02040    } else if (!strcasecmp(folder, "Cust2")) {
02041       return 6;
02042    } else if (!strcasecmp(folder, "Cust3")) {
02043       return 7;
02044    } else if (!strcasecmp(folder, "Cust4")) {
02045       return 8;
02046    } else if (!strcasecmp(folder, "Cust5")) {
02047       return 9;
02048    } else if (!strcasecmp(folder, "Urgent")) {
02049       return 11;
02050    } else { /*assume they meant INBOX if folder is not found otherwise*/
02051       return 0;
02052    }
02053 }
02054 
02055 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02056 {
02057    SEARCHPGM *pgm;
02058    SEARCHHEADER *hdr;
02059 
02060    struct ast_vm_user *vmu, vmus;
02061    struct vm_state *vms_p;
02062    int ret = 0;
02063    int fold = folder_int(folder);
02064    int urgent = 0;
02065    
02066    /* If URGENT, then look at INBOX */
02067    if (fold == 11) {
02068       fold = NEW_FOLDER;
02069       urgent = 1;
02070    }
02071 
02072    if (ast_strlen_zero(mailbox))
02073       return 0;
02074 
02075    /* We have to get the user before we can open the stream! */
02076    vmu = find_user(&vmus, context, mailbox);
02077    if (!vmu) {
02078       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02079       return -1;
02080    } else {
02081       /* No IMAP account available */
02082       if (vmu->imapuser[0] == '\0') {
02083          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02084          return -1;
02085       }
02086    }
02087    
02088    /* No IMAP account available */
02089    if (vmu->imapuser[0] == '\0') {
02090       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02091       free_user(vmu);
02092       return -1;
02093    }
02094 
02095    /* check if someone is accessing this box right now... */
02096    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02097    if (!vms_p) {
02098       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02099    }
02100    if (vms_p) {
02101       ast_debug(3, "Returning before search - user is logged in\n");
02102       if (fold == 0) { /* INBOX */
02103          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02104       }
02105       if (fold == 1) { /* Old messages */
02106          return vms_p->oldmessages;
02107       }
02108    }
02109 
02110    /* add one if not there... */
02111    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02112    if (!vms_p) {
02113       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02114    }
02115 
02116    if (!vms_p) {
02117       vms_p = create_vm_state_from_user(vmu);
02118    }
02119    ret = init_mailstream(vms_p, fold);
02120    if (!vms_p->mailstream) {
02121       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02122       return -1;
02123    }
02124    if (ret == 0) {
02125       ast_mutex_lock(&vms_p->lock);
02126       pgm = mail_newsearchpgm ();
02127       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02128       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02129       pgm->header = hdr;
02130       if (fold != OLD_FOLDER) {
02131          pgm->unseen = 1;
02132          pgm->seen = 0;
02133       }
02134       /* In the special case where fold is 1 (old messages) we have to do things a bit
02135        * differently. Old messages are stored in the INBOX but are marked as "seen"
02136        */
02137       else {
02138          pgm->unseen = 0;
02139          pgm->seen = 1;
02140       }
02141       /* look for urgent messages */
02142       if (fold == NEW_FOLDER) {
02143          if (urgent) {
02144             pgm->flagged = 1;
02145             pgm->unflagged = 0;
02146          } else {
02147             pgm->flagged = 0;
02148             pgm->unflagged = 1;
02149          }
02150       }
02151       pgm->undeleted = 1;
02152       pgm->deleted = 0;
02153 
02154       vms_p->vmArrayIndex = 0;
02155       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02156       if (fold == 0 && urgent == 0)
02157          vms_p->newmessages = vms_p->vmArrayIndex;
02158       if (fold == 1)
02159          vms_p->oldmessages = vms_p->vmArrayIndex;
02160       if (fold == 0 && urgent == 1)
02161          vms_p->urgentmessages = vms_p->vmArrayIndex;
02162       /*Freeing the searchpgm also frees the searchhdr*/
02163       mail_free_searchpgm(&pgm);
02164       ast_mutex_unlock(&vms_p->lock);
02165       vms_p->updated = 0;
02166       return vms_p->vmArrayIndex;
02167    } else {
02168       ast_mutex_lock(&vms_p->lock);
02169       mail_ping(vms_p->mailstream);
02170       ast_mutex_unlock(&vms_p->lock);
02171    }
02172    return 0;
02173 }
02174 
02175 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02176 {
02177    /* Check if mailbox is full */
02178    check_quota(vms, vmu->imapfolder);
02179    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02180       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02181       ast_play_and_wait(chan, "vm-mailboxfull");
02182       return -1;
02183    }
02184    
02185    /* Check if we have exceeded maxmsg */
02186    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));
02187    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02188       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02189       ast_play_and_wait(chan, "vm-mailboxfull");
02190       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02191       return -1;
02192    }
02193 
02194    return 0;
02195 }
02196 
02197 /*!
02198  * \brief Gets the number of messages that exist in a mailbox folder.
02199  * \param context
02200  * \param mailbox
02201  * \param folder
02202  * 
02203  * This method is used when IMAP backend is used.
02204  * \return The number of messages in this mailbox folder (zero or more).
02205  */
02206 static int messagecount(const char *context, const char *mailbox, const char *folder)
02207 {
02208    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02209       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02210    } else {
02211       return __messagecount(context, mailbox, folder);
02212    }
02213 }
02214 
02215 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)
02216 {
02217    char *myserveremail = serveremail;
02218    char fn[PATH_MAX];
02219    char introfn[PATH_MAX];
02220    char mailbox[256];
02221    char *stringp;
02222    FILE *p = NULL;
02223    char tmp[80] = "/tmp/astmail-XXXXXX";
02224    long len;
02225    void *buf;
02226    int tempcopy = 0;
02227    STRING str;
02228    int ret; /* for better error checking */
02229    char *imap_flags = NIL;
02230    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02231    int box = NEW_FOLDER;
02232 
02233    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02234    if (msgnum < 0) {
02235       if(!imapgreetings) {
02236          return 0;
02237       } else {
02238          box = GREETINGS_FOLDER;
02239       }
02240    }
02241    
02242    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02243       return -1;
02244    }
02245 
02246    /* Set urgent flag for IMAP message */
02247    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02248       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02249       imap_flags = "\\FLAGGED";
02250    }
02251    
02252    /* Attach only the first format */
02253    fmt = ast_strdupa(fmt);
02254    stringp = fmt;
02255    strsep(&stringp, "|");
02256 
02257    if (!ast_strlen_zero(vmu->serveremail))
02258       myserveremail = vmu->serveremail;
02259 
02260    if (msgnum > -1)
02261       make_file(fn, sizeof(fn), dir, msgnum);
02262    else
02263       ast_copy_string (fn, dir, sizeof(fn));
02264 
02265    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02266    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02267       *introfn = '\0';
02268    }
02269    
02270    if (ast_strlen_zero(vmu->email)) {
02271       /* We need the vmu->email to be set when we call make_email_file, but
02272        * if we keep it set, a duplicate e-mail will be created. So at the end
02273        * of this function, we will revert back to an empty string if tempcopy
02274        * is 1.
02275        */
02276       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02277       tempcopy = 1;
02278    }
02279 
02280    if (!strcmp(fmt, "wav49"))
02281       fmt = "WAV";
02282    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02283 
02284    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02285       command hangs. */
02286    if (!(p = vm_mkftemp(tmp))) {
02287       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02288       if (tempcopy)
02289          *(vmu->email) = '\0';
02290       return -1;
02291    }
02292 
02293    if (msgnum < 0 && imapgreetings) {
02294       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02295          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02296          return -1;
02297       }
02298       imap_delete_old_greeting(fn, vms);
02299    }
02300 
02301    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02302       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02303       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02304       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02305    /* read mail file to memory */
02306    len = ftell(p);
02307    rewind(p);
02308    if (!(buf = ast_malloc(len + 1))) {
02309       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02310       fclose(p);
02311       if (tempcopy)
02312          *(vmu->email) = '\0';
02313       return -1;
02314    }
02315    if (fread(buf, len, 1, p) < len) {
02316       if (ferror(p)) {
02317          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02318          return -1;
02319       }
02320    }
02321    ((char *) buf)[len] = '\0';
02322    INIT(&str, mail_string, buf, len);
02323    ret = init_mailstream(vms, box);
02324    if (ret == 0) {
02325       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02326       ast_mutex_lock(&vms->lock);
02327       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02328          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02329       ast_mutex_unlock(&vms->lock);
02330       fclose(p);
02331       unlink(tmp);
02332       ast_free(buf);
02333    } else {
02334       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02335       fclose(p);
02336       unlink(tmp);
02337       ast_free(buf);
02338       return -1;
02339    }
02340    ast_debug(3, "%s stored\n", fn);
02341    
02342    if (tempcopy)
02343       *(vmu->email) = '\0';
02344    inprocess_count(vmu->mailbox, vmu->context, -1);
02345    return 0;
02346 
02347 }
02348 
02349 /*!
02350  * \brief Gets the number of messages that exist in the inbox folder.
02351  * \param mailbox_context
02352  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02353  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02354  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02355  * 
02356  * This method is used when IMAP backend is used.
02357  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02358  *
02359  * \return zero on success, -1 on error.
02360  */
02361 
02362 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02363 {
02364    char tmp[PATH_MAX] = "";
02365    char *mailboxnc;
02366    char *context;
02367    char *mb;
02368    char *cur;
02369    if (newmsgs)
02370       *newmsgs = 0;
02371    if (oldmsgs)
02372       *oldmsgs = 0;
02373    if (urgentmsgs)
02374       *urgentmsgs = 0;
02375 
02376    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02377    /* If no mailbox, return immediately */
02378    if (ast_strlen_zero(mailbox_context))
02379       return 0;
02380    
02381    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02382    context = strchr(tmp, '@');
02383    if (strchr(mailbox_context, ',')) {
02384       int tmpnew, tmpold, tmpurgent;
02385       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02386       mb = tmp;
02387       while ((cur = strsep(&mb, ", "))) {
02388          if (!ast_strlen_zero(cur)) {
02389             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02390                return -1;
02391             else {
02392                if (newmsgs)
02393                   *newmsgs += tmpnew; 
02394                if (oldmsgs)
02395                   *oldmsgs += tmpold;
02396                if (urgentmsgs)
02397                   *urgentmsgs += tmpurgent;
02398             }
02399          }
02400       }
02401       return 0;
02402    }
02403    if (context) {
02404       *context = '\0';
02405       mailboxnc = tmp;
02406       context++;
02407    } else {
02408       context = "default";
02409       mailboxnc = (char *) mailbox_context;
02410    }
02411 
02412    if (newmsgs) {
02413       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02414       if (!vmu) {
02415          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02416          return -1;
02417       }
02418       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02419          return -1;
02420       }
02421    }
02422    if (oldmsgs) {
02423       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02424          return -1;
02425       }
02426    }
02427    if (urgentmsgs) {
02428       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02429          return -1;
02430       }
02431    }
02432    return 0;
02433 }
02434 
02435 /** 
02436  * \brief Determines if the given folder has messages.
02437  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02438  * \param folder the folder to look in
02439  *
02440  * This function is used when the mailbox is stored in an IMAP back end.
02441  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02442  * \return 1 if the folder has one or more messages. zero otherwise.
02443  */
02444 
02445 static int has_voicemail(const char *mailbox, const char *folder)
02446 {
02447    char tmp[256], *tmp2, *box, *context;
02448    ast_copy_string(tmp, mailbox, sizeof(tmp));
02449    tmp2 = tmp;
02450    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02451       while ((box = strsep(&tmp2, ",&"))) {
02452          if (!ast_strlen_zero(box)) {
02453             if (has_voicemail(box, folder)) {
02454                return 1;
02455             }
02456          }
02457       }
02458    }
02459    if ((context = strchr(tmp, '@'))) {
02460       *context++ = '\0';
02461    } else {
02462       context = "default";
02463    }
02464    return __messagecount(context, tmp, folder) ? 1 : 0;
02465 }
02466 
02467 /*!
02468  * \brief Copies a message from one mailbox to another.
02469  * \param chan
02470  * \param vmu
02471  * \param imbox
02472  * \param msgnum
02473  * \param duration
02474  * \param recip
02475  * \param fmt
02476  * \param dir
02477  *
02478  * This works with IMAP storage based mailboxes.
02479  *
02480  * \return zero on success, -1 on error.
02481  */
02482 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)
02483 {
02484    struct vm_state *sendvms = NULL, *destvms = NULL;
02485    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02486    if (msgnum >= recip->maxmsg) {
02487       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02488       return -1;
02489    }
02490    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02491       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02492       return -1;
02493    }
02494    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02495       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02496       return -1;
02497    }
02498    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02499    ast_mutex_lock(&sendvms->lock);
02500    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02501       ast_mutex_unlock(&sendvms->lock);
02502       return 0;
02503    }
02504    ast_mutex_unlock(&sendvms->lock);
02505    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02506    return -1;
02507 }
02508 
02509 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02510 {
02511    char tmp[256], *t = tmp;
02512    size_t left = sizeof(tmp);
02513    
02514    if (box == OLD_FOLDER) {
02515       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02516    } else {
02517       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02518    }
02519 
02520    if (box == NEW_FOLDER) {
02521       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02522    } else {
02523       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02524    }
02525 
02526    /* Build up server information */
02527    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02528 
02529    /* Add authentication user if present */
02530    if (!ast_strlen_zero(authuser))
02531       ast_build_string(&t, &left, "/authuser=%s", authuser);
02532 
02533    /* Add flags if present */
02534    if (!ast_strlen_zero(imapflags))
02535       ast_build_string(&t, &left, "/%s", imapflags);
02536 
02537    /* End with username */
02538 #if 1
02539    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02540 #else
02541    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02542 #endif
02543    if (box == NEW_FOLDER || box == OLD_FOLDER)
02544       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02545    else if (box == GREETINGS_FOLDER)
02546       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02547    else {   /* Other folders such as Friends, Family, etc... */
02548       if (!ast_strlen_zero(imapparentfolder)) {
02549          /* imapparentfolder would typically be set to INBOX */
02550          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02551       } else {
02552          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02553       }
02554    }
02555 }
02556 
02557 static int init_mailstream(struct vm_state *vms, int box)
02558 {
02559    MAILSTREAM *stream = NIL;
02560    long debug;
02561    char tmp[256];
02562    
02563    if (!vms) {
02564       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02565       return -1;
02566    }
02567    if (option_debug > 2)
02568       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02569    if (vms->mailstream == NIL || !vms->mailstream) {
02570       if (option_debug)
02571          ast_log(LOG_DEBUG, "mailstream not set.\n");
02572    } else {
02573       stream = vms->mailstream;
02574    }
02575    /* debug = T;  user wants protocol telemetry? */
02576    debug = NIL;  /* NO protocol telemetry? */
02577 
02578    if (delimiter == '\0') {      /* did not probe the server yet */
02579       char *cp;
02580 #ifdef USE_SYSTEM_IMAP
02581 #include <imap/linkage.c>
02582 #elif defined(USE_SYSTEM_CCLIENT)
02583 #include <c-client/linkage.c>
02584 #else
02585 #include "linkage.c"
02586 #endif
02587       /* Connect to INBOX first to get folders delimiter */
02588       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02589       ast_mutex_lock(&vms->lock);
02590       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02591       ast_mutex_unlock(&vms->lock);
02592       if (stream == NIL) {
02593          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02594          return -1;
02595       }
02596       get_mailbox_delimiter(stream);
02597       /* update delimiter in imapfolder */
02598       for (cp = vms->imapfolder; *cp; cp++)
02599          if (*cp == '/')
02600             *cp = delimiter;
02601    }
02602    /* Now connect to the target folder */
02603    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02604    if (option_debug > 2)
02605       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02606    ast_mutex_lock(&vms->lock);
02607    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02608    ast_mutex_unlock(&vms->lock);
02609    if (vms->mailstream == NIL) {
02610       return -1;
02611    } else {
02612       return 0;
02613    }
02614 }
02615 
02616 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02617 {
02618    SEARCHPGM *pgm;
02619    SEARCHHEADER *hdr;
02620    int ret, urgent = 0;
02621 
02622    /* If Urgent, then look at INBOX */
02623    if (box == 11) {
02624       box = NEW_FOLDER;
02625       urgent = 1;
02626    }
02627 
02628    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02629    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02630    vms->imapversion = vmu->imapversion;
02631    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02632 
02633    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02634       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02635       return -1;
02636    }
02637    
02638    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02639    
02640    /* Check Quota */
02641    if  (box == 0)  {
02642       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02643       check_quota(vms, (char *) mbox(vmu, box));
02644    }
02645 
02646    ast_mutex_lock(&vms->lock);
02647    pgm = mail_newsearchpgm();
02648 
02649    /* Check IMAP folder for Asterisk messages only... */
02650    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02651    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02652    pgm->header = hdr;
02653    pgm->deleted = 0;
02654    pgm->undeleted = 1;
02655 
02656    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02657    if (box == NEW_FOLDER && urgent == 1) {
02658       pgm->unseen = 1;
02659       pgm->seen = 0;
02660       pgm->flagged = 1;
02661       pgm->unflagged = 0;
02662    } else if (box == NEW_FOLDER && urgent == 0) {
02663       pgm->unseen = 1;
02664       pgm->seen = 0;
02665       pgm->flagged = 0;
02666       pgm->unflagged = 1;
02667    } else if (box == OLD_FOLDER) {
02668       pgm->seen = 1;
02669       pgm->unseen = 0;
02670    }
02671 
02672    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02673 
02674    vms->vmArrayIndex = 0;
02675    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02676    vms->lastmsg = vms->vmArrayIndex - 1;
02677    mail_free_searchpgm(&pgm);
02678    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02679     * ensure to allocate enough space to account for all of them. Warn if old messages
02680     * have not been checked first as that is required.
02681     */
02682    if (box == 0 && !vms->dh_arraysize) {
02683       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02684    }
02685    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02686       ast_mutex_unlock(&vms->lock);
02687       return -1;
02688    }
02689 
02690    ast_mutex_unlock(&vms->lock);
02691    return 0;
02692 }
02693 
02694 static void write_file(char *filename, char *buffer, unsigned long len)
02695 {
02696    FILE *output;
02697 
02698    output = fopen (filename, "w");
02699    if (fwrite(buffer, len, 1, output) != 1) {
02700       if (ferror(output)) {
02701          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02702       }
02703    }
02704    fclose (output);
02705 }
02706 
02707 static void update_messages_by_imapuser(const char *user, unsigned long number)
02708 {
02709    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02710 
02711    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02712       return;
02713    }
02714 
02715    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02716    vms->msgArray[vms->vmArrayIndex++] = number;
02717 }
02718 
02719 void mm_searched(MAILSTREAM *stream, unsigned long number)
02720 {
02721    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02722 
02723    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02724       return;
02725 
02726    update_messages_by_imapuser(user, number);
02727 }
02728 
02729 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02730 {
02731    struct ast_variable *var;
02732    struct ast_vm_user *vmu;
02733 
02734    vmu = ast_calloc(1, sizeof *vmu);
02735    if (!vmu)
02736       return NULL;
02737    ast_set_flag(vmu, VM_ALLOCED);
02738    populate_defaults(vmu);
02739 
02740    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02741    if (var) {
02742       apply_options_full(vmu, var);
02743       ast_variables_destroy(var);
02744       return vmu;
02745    } else {
02746       ast_free(vmu);
02747       return NULL;
02748    }
02749 }
02750 
02751 /* Interfaces to C-client */
02752 
02753 void mm_exists(MAILSTREAM * stream, unsigned long number)
02754 {
02755    /* mail_ping will callback here if new mail! */
02756    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02757    if (number == 0) return;
02758    set_update(stream);
02759 }
02760 
02761 
02762 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02763 {
02764    /* mail_ping will callback here if expunged mail! */
02765    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02766    if (number == 0) return;
02767    set_update(stream);
02768 }
02769 
02770 
02771 void mm_flags(MAILSTREAM * stream, unsigned long number)
02772 {
02773    /* mail_ping will callback here if read mail! */
02774    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02775    if (number == 0) return;
02776    set_update(stream);
02777 }
02778 
02779 
02780 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02781 {
02782    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02783    mm_log (string, errflg);
02784 }
02785 
02786 
02787 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02788 {
02789    if (delimiter == '\0') {
02790       delimiter = delim;
02791    }
02792 
02793    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02794    if (attributes & LATT_NOINFERIORS)
02795       ast_debug(5, "no inferiors\n");
02796    if (attributes & LATT_NOSELECT)
02797       ast_debug(5, "no select\n");
02798    if (attributes & LATT_MARKED)
02799       ast_debug(5, "marked\n");
02800    if (attributes & LATT_UNMARKED)
02801       ast_debug(5, "unmarked\n");
02802 }
02803 
02804 
02805 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
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_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02820 {
02821    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02822    if (status->flags & SA_MESSAGES)
02823       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02824    if (status->flags & SA_RECENT)
02825       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02826    if (status->flags & SA_UNSEEN)
02827       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02828    if (status->flags & SA_UIDVALIDITY)
02829       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02830    if (status->flags & SA_UIDNEXT)
02831       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02832    ast_log(AST_LOG_NOTICE, "\n");
02833 }
02834 
02835 
02836 void mm_log(char *string, long errflg)
02837 {
02838    switch ((short) errflg) {
02839       case NIL:
02840          ast_debug(1, "IMAP Info: %s\n", string);
02841          break;
02842       case PARSE:
02843       case WARN:
02844          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02845          break;
02846       case ERROR:
02847          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02848          break;
02849    }
02850 }
02851 
02852 
02853 void mm_dlog(char *string)
02854 {
02855    ast_log(AST_LOG_NOTICE, "%s\n", string);
02856 }
02857 
02858 
02859 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02860 {
02861    struct ast_vm_user *vmu;
02862 
02863    ast_debug(4, "Entering callback mm_login\n");
02864 
02865    ast_copy_string(user, mb->user, MAILTMPLEN);
02866 
02867    /* We should only do this when necessary */
02868    if (!ast_strlen_zero(authpassword)) {
02869       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02870    } else {
02871       AST_LIST_TRAVERSE(&users, vmu, list) {
02872          if (!strcasecmp(mb->user, vmu->imapuser)) {
02873             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02874             break;
02875          }
02876       }
02877       if (!vmu) {
02878          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02879             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02880             free_user(vmu);
02881          }
02882       }
02883    }
02884 }
02885 
02886 
02887 void mm_critical(MAILSTREAM * stream)
02888 {
02889 }
02890 
02891 
02892 void mm_nocritical(MAILSTREAM * stream)
02893 {
02894 }
02895 
02896 
02897 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02898 {
02899    kill (getpid (), SIGSTOP);
02900    return NIL;
02901 }
02902 
02903 
02904 void mm_fatal(char *string)
02905 {
02906    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02907 }
02908 
02909 /* C-client callback to handle quota */
02910 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02911 {
02912    struct vm_state *vms;
02913    char *mailbox = stream->mailbox, *user;
02914    char buf[1024] = "";
02915    unsigned long usage = 0, limit = 0;
02916    
02917    while (pquota) {
02918       usage = pquota->usage;
02919       limit = pquota->limit;
02920       pquota = pquota->next;
02921    }
02922    
02923    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)))) {
02924       ast_log(AST_LOG_ERROR, "No state found.\n");
02925       return;
02926    }
02927 
02928    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02929 
02930    vms->quota_usage = usage;
02931    vms->quota_limit = limit;
02932 }
02933 
02934 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02935 {
02936    char *start, *eol_pnt;
02937    int taglen;
02938 
02939    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02940       return NULL;
02941 
02942    taglen = strlen(tag) + 1;
02943    if (taglen < 1)
02944       return NULL;
02945 
02946    if (!(start = strstr(header, tag)))
02947       return NULL;
02948 
02949    /* Since we can be called multiple times we should clear our buffer */
02950    memset(buf, 0, len);
02951 
02952    ast_copy_string(buf, start+taglen, len);
02953    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02954       *eol_pnt = '\0';
02955    return buf;
02956 }
02957 
02958 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02959 {
02960    char *start, *quote, *eol_pnt;
02961 
02962    if (ast_strlen_zero(mailbox))
02963       return NULL;
02964 
02965    if (!(start = strstr(mailbox, "/user=")))
02966       return NULL;
02967 
02968    ast_copy_string(buf, start+6, len);
02969 
02970    if (!(quote = strchr(buf, '\"'))) {
02971       if (!(eol_pnt = strchr(buf, '/')))
02972          eol_pnt = strchr(buf,'}');
02973       *eol_pnt = '\0';
02974       return buf;
02975    } else {
02976       eol_pnt = strchr(buf+1,'\"');
02977       *eol_pnt = '\0';
02978       return buf+1;
02979    }
02980 }
02981 
02982 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02983 {
02984    struct vm_state *vms_p;
02985 
02986    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02987    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02988       return vms_p;
02989    }
02990    if (option_debug > 4)
02991       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02992    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02993       return NULL;
02994    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02995    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02996    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02997    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02998    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02999    vms_p->imapversion = vmu->imapversion;
03000    if (option_debug > 4)
03001       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
03002    vms_p->updated = 1;
03003    /* set mailbox to INBOX! */
03004    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03005    init_vm_state(vms_p);
03006    vmstate_insert(vms_p);
03007    return vms_p;
03008 }
03009 
03010 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03011 {
03012    struct vmstate *vlist = NULL;
03013 
03014    if (interactive) {
03015       struct vm_state *vms;
03016       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03017       vms = pthread_getspecific(ts_vmstate.key);
03018       return vms;
03019    }
03020 
03021    AST_LIST_LOCK(&vmstates);
03022    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03023       if (!vlist->vms) {
03024          ast_debug(3, "error: vms is NULL for %s\n", user);
03025          continue;
03026       }
03027       if (vlist->vms->imapversion != imapversion) {
03028          continue;
03029       }
03030       if (!vlist->vms->imapuser) {
03031          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03032          continue;
03033       }
03034 
03035       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03036          AST_LIST_UNLOCK(&vmstates);
03037          return vlist->vms;
03038       }
03039    }
03040    AST_LIST_UNLOCK(&vmstates);
03041 
03042    ast_debug(3, "%s not found in vmstates\n", user);
03043 
03044    return NULL;
03045 }
03046 
03047 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03048 {
03049 
03050    struct vmstate *vlist = NULL;
03051    const char *local_context = S_OR(context, "default");
03052 
03053    if (interactive) {
03054       struct vm_state *vms;
03055       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03056       vms = pthread_getspecific(ts_vmstate.key);
03057       return vms;
03058    }
03059 
03060    AST_LIST_LOCK(&vmstates);
03061    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03062       if (!vlist->vms) {
03063          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03064          continue;
03065       }
03066       if (vlist->vms->imapversion != imapversion) {
03067          continue;
03068       }
03069       if (!vlist->vms->username || !vlist->vms->context) {
03070          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03071          continue;
03072       }
03073 
03074       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);
03075       
03076       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03077          ast_debug(3, "Found it!\n");
03078          AST_LIST_UNLOCK(&vmstates);
03079          return vlist->vms;
03080       }
03081    }
03082    AST_LIST_UNLOCK(&vmstates);
03083 
03084    ast_debug(3, "%s not found in vmstates\n", mailbox);
03085 
03086    return NULL;
03087 }
03088 
03089 static void vmstate_insert(struct vm_state *vms) 
03090 {
03091    struct vmstate *v;
03092    struct vm_state *altvms;
03093 
03094    /* If interactive, it probably already exists, and we should
03095       use the one we already have since it is more up to date.
03096       We can compare the username to find the duplicate */
03097    if (vms->interactive == 1) {
03098       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03099       if (altvms) {  
03100          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03101          vms->newmessages = altvms->newmessages;
03102          vms->oldmessages = altvms->oldmessages;
03103          vms->vmArrayIndex = altvms->vmArrayIndex;
03104          vms->lastmsg = altvms->lastmsg;
03105          vms->curmsg = altvms->curmsg;
03106          /* get a pointer to the persistent store */
03107          vms->persist_vms = altvms;
03108          /* Reuse the mailstream? */
03109 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03110          vms->mailstream = altvms->mailstream;
03111 #else
03112          vms->mailstream = NIL;
03113 #endif
03114       }
03115       return;
03116    }
03117 
03118    if (!(v = ast_calloc(1, sizeof(*v))))
03119       return;
03120    
03121    v->vms = vms;
03122 
03123    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03124 
03125    AST_LIST_LOCK(&vmstates);
03126    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03127    AST_LIST_UNLOCK(&vmstates);
03128 }
03129 
03130 static void vmstate_delete(struct vm_state *vms) 
03131 {
03132    struct vmstate *vc = NULL;
03133    struct vm_state *altvms = NULL;
03134 
03135    /* If interactive, we should copy pertinent info
03136       back to the persistent state (to make update immediate) */
03137    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03138       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03139       altvms->newmessages = vms->newmessages;
03140       altvms->oldmessages = vms->oldmessages;
03141       altvms->updated = 1;
03142       vms->mailstream = mail_close(vms->mailstream);
03143 
03144       /* Interactive states are not stored within the persistent list */
03145       return;
03146    }
03147    
03148    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03149    
03150    AST_LIST_LOCK(&vmstates);
03151    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03152       if (vc->vms == vms) {
03153          AST_LIST_REMOVE_CURRENT(list);
03154          break;
03155       }
03156    }
03157    AST_LIST_TRAVERSE_SAFE_END
03158    AST_LIST_UNLOCK(&vmstates);
03159    
03160    if (vc) {
03161       ast_mutex_destroy(&vc->vms->lock);
03162       ast_free(vc);
03163    }
03164    else
03165       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03166 }
03167 
03168 static void set_update(MAILSTREAM * stream) 
03169 {
03170    struct vm_state *vms;
03171    char *mailbox = stream->mailbox, *user;
03172    char buf[1024] = "";
03173 
03174    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03175       if (user && option_debug > 2)
03176          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03177       return;
03178    }
03179 
03180    ast_debug(3, "User %s mailbox set for update.\n", user);
03181 
03182    vms->updated = 1; /* Set updated flag since mailbox changed */
03183 }
03184 
03185 static void init_vm_state(struct vm_state *vms) 
03186 {
03187    int x;
03188    vms->vmArrayIndex = 0;
03189    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03190       vms->msgArray[x] = 0;
03191    }
03192    ast_mutex_init(&vms->lock);
03193 }
03194 
03195 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03196 {
03197    char *body_content;
03198    char *body_decoded;
03199    char *fn = is_intro ? vms->introfn : vms->fn;
03200    unsigned long len;
03201    unsigned long newlen;
03202    char filename[256];
03203    
03204    if (!body || body == NIL)
03205       return -1;
03206 
03207    ast_mutex_lock(&vms->lock);
03208    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03209    ast_mutex_unlock(&vms->lock);
03210    if (body_content != NIL) {
03211       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03212       /* ast_debug(1,body_content); */
03213       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03214       /* If the body of the file is empty, return an error */
03215       if (!newlen) {
03216          return -1;
03217       }
03218       write_file(filename, (char *) body_decoded, newlen);
03219    } else {
03220       ast_debug(5, "Body of message is NULL.\n");
03221       return -1;
03222    }
03223    return 0;
03224 }
03225 
03226 /*! 
03227  * \brief Get delimiter via mm_list callback 
03228  * \param stream
03229  *
03230  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03231  */
03232 /* MUTEX should already be held */
03233 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03234    char tmp[50];
03235    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03236    mail_list(stream, tmp, "*");
03237 }
03238 
03239 /*! 
03240  * \brief Check Quota for user 
03241  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03242  * \param mailbox the mailbox to check the quota for.
03243  *
03244  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03245  */
03246 static void check_quota(struct vm_state *vms, char *mailbox) {
03247    ast_mutex_lock(&vms->lock);
03248    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03249    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03250    if (vms && vms->mailstream != NULL) {
03251       imap_getquotaroot(vms->mailstream, mailbox);
03252    } else {
03253       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03254    }
03255    ast_mutex_unlock(&vms->lock);
03256 }
03257 
03258 #endif /* IMAP_STORAGE */
03259 
03260 /*! \brief Lock file path
03261  * only return failure if ast_lock_path returns 'timeout',
03262  * not if the path does not exist or any other reason
03263  */
03264 static int vm_lock_path(const char *path)
03265 {
03266    switch (ast_lock_path(path)) {
03267    case AST_LOCK_TIMEOUT:
03268       return -1;
03269    default:
03270       return 0;
03271    }
03272 }
03273 
03274 
03275 #ifdef ODBC_STORAGE
03276 struct generic_prepare_struct {
03277    char *sql;
03278    int argc;
03279    char **argv;
03280 };
03281 
03282 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03283 {
03284    struct generic_prepare_struct *gps = data;
03285    int res, i;
03286    SQLHSTMT stmt;
03287 
03288    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03289    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03290       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03291       return NULL;
03292    }
03293    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03294    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03295       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03296       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03297       return NULL;
03298    }
03299    for (i = 0; i < gps->argc; i++)
03300       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03301 
03302    return stmt;
03303 }
03304 
03305 /*!
03306  * \brief Retrieves a file from an ODBC data store.
03307  * \param dir the path to the file to be retreived.
03308  * \param msgnum the message number, such as within a mailbox folder.
03309  * 
03310  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03311  * 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.
03312  *
03313  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03314  * The output is the message information file with the name msgnum and the extension .txt
03315  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03316  * 
03317  * \return 0 on success, -1 on error.
03318  */
03319 static int retrieve_file(char *dir, int msgnum)
03320 {
03321    int x = 0;
03322    int res;
03323    int fd = -1;
03324    size_t fdlen = 0;
03325    void *fdm = MAP_FAILED;
03326    SQLSMALLINT colcount = 0;
03327    SQLHSTMT stmt;
03328    char sql[PATH_MAX];
03329    char fmt[80]="";
03330    char *c;
03331    char coltitle[256];
03332    SQLSMALLINT collen;
03333    SQLSMALLINT datatype;
03334    SQLSMALLINT decimaldigits;
03335    SQLSMALLINT nullable;
03336    SQLULEN colsize;
03337    SQLLEN colsize2;
03338    FILE *f = NULL;
03339    char rowdata[80];
03340    char fn[PATH_MAX];
03341    char full_fn[PATH_MAX];
03342    char msgnums[80];
03343    char *argv[] = { dir, msgnums };
03344    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03345 
03346    struct odbc_obj *obj;
03347    obj = ast_odbc_request_obj(odbc_database, 0);
03348    if (obj) {
03349       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03350       c = strchr(fmt, '|');
03351       if (c)
03352          *c = '\0';
03353       if (!strcasecmp(fmt, "wav49"))
03354          strcpy(fmt, "WAV");
03355       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03356       if (msgnum > -1)
03357          make_file(fn, sizeof(fn), dir, msgnum);
03358       else
03359          ast_copy_string(fn, dir, sizeof(fn));
03360 
03361       /* Create the information file */
03362       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03363       
03364       if (!(f = fopen(full_fn, "w+"))) {
03365          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03366          goto yuck;
03367       }
03368       
03369       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03370       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03371       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03372       if (!stmt) {
03373          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03374          ast_odbc_release_obj(obj);
03375          goto yuck;
03376       }
03377       res = SQLFetch(stmt);
03378       if (res == SQL_NO_DATA) {
03379          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03380          ast_odbc_release_obj(obj);
03381          goto yuck;
03382       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03383          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03384          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03385          ast_odbc_release_obj(obj);
03386          goto yuck;
03387       }
03388       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03389       if (fd < 0) {
03390          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03391          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03392          ast_odbc_release_obj(obj);
03393          goto yuck;
03394       }
03395       res = SQLNumResultCols(stmt, &colcount);
03396       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03397          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03398          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03399          ast_odbc_release_obj(obj);
03400          goto yuck;
03401       }
03402       if (f) 
03403          fprintf(f, "[message]\n");
03404       for (x = 0; x < colcount; x++) {
03405          rowdata[0] = '\0';
03406          colsize = 0;
03407          collen = sizeof(coltitle);
03408          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03409                   &datatype, &colsize, &decimaldigits, &nullable);
03410          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03411             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03412             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03413             ast_odbc_release_obj(obj);
03414             goto yuck;
03415          }
03416          if (!strcasecmp(coltitle, "recording")) {
03417             off_t offset;
03418             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03419             fdlen = colsize2;
03420             if (fd > -1) {
03421                char tmp[1]="";
03422                lseek(fd, fdlen - 1, SEEK_SET);
03423                if (write(fd, tmp, 1) != 1) {
03424                   close(fd);
03425                   fd = -1;
03426                   continue;
03427                }
03428                /* Read out in small chunks */
03429                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03430                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03431                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03432                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03433                      ast_odbc_release_obj(obj);
03434                      goto yuck;
03435                   } else {
03436                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03437                      munmap(fdm, CHUNKSIZE);
03438                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03439                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03440                         unlink(full_fn);
03441                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03442                         ast_odbc_release_obj(obj);
03443                         goto yuck;
03444                      }
03445                   }
03446                }
03447                if (truncate(full_fn, fdlen) < 0) {
03448                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03449                }
03450             }
03451          } else {
03452             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03453             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03454                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03455                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03456                ast_odbc_release_obj(obj);
03457                goto yuck;
03458             }
03459             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03460                fprintf(f, "%s=%s\n", coltitle, rowdata);
03461          }
03462       }
03463       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03464       ast_odbc_release_obj(obj);
03465    } else
03466       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03467 yuck:
03468    if (f)
03469       fclose(f);
03470    if (fd > -1)
03471       close(fd);
03472    return x - 1;
03473 }
03474 
03475 /*!
03476  * \brief Determines the highest message number in use for a given user and mailbox folder.
03477  * \param vmu 
03478  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03479  *
03480  * This method is used when mailboxes are stored in an ODBC back end.
03481  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03482  *
03483  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03484 
03485  */
03486 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03487 {
03488    int x = 0;
03489    int res;
03490    SQLHSTMT stmt;
03491    char sql[PATH_MAX];
03492    char rowdata[20];
03493    char *argv[] = { dir };
03494    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03495 
03496    struct odbc_obj *obj;
03497    obj = ast_odbc_request_obj(odbc_database, 0);
03498    if (obj) {
03499       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03500 
03501       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03502       if (!stmt) {
03503          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03504          ast_odbc_release_obj(obj);
03505          goto yuck;
03506       }
03507       res = SQLFetch(stmt);
03508       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03509          if (res == SQL_NO_DATA) {
03510             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03511          } else {
03512             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03513          }
03514 
03515          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03516          ast_odbc_release_obj(obj);
03517          goto yuck;
03518       }
03519       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03520       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03521          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03522          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03523          ast_odbc_release_obj(obj);
03524          goto yuck;
03525       }
03526       if (sscanf(rowdata, "%30d", &x) != 1)
03527          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03528       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03529       ast_odbc_release_obj(obj);
03530       return x;
03531    } else
03532       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03533 yuck:
03534    return x - 1;
03535 }
03536 
03537 /*!
03538  * \brief Determines if the specified message exists.
03539  * \param dir the folder the mailbox folder to look for messages. 
03540  * \param msgnum the message index to query for.
03541  *
03542  * This method is used when mailboxes are stored in an ODBC back end.
03543  *
03544  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03545  */
03546 static int message_exists(char *dir, int msgnum)
03547 {
03548    int x = 0;
03549    int res;
03550    SQLHSTMT stmt;
03551    char sql[PATH_MAX];
03552    char rowdata[20];
03553    char msgnums[20];
03554    char *argv[] = { dir, msgnums };
03555    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03556 
03557    struct odbc_obj *obj;
03558    obj = ast_odbc_request_obj(odbc_database, 0);
03559    if (obj) {
03560       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03561       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03562       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03563       if (!stmt) {
03564          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03565          ast_odbc_release_obj(obj);
03566          goto yuck;
03567       }
03568       res = SQLFetch(stmt);
03569       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03570          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03571          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03572          ast_odbc_release_obj(obj);
03573          goto yuck;
03574       }
03575       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03576       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03577          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03578          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03579          ast_odbc_release_obj(obj);
03580          goto yuck;
03581       }
03582       if (sscanf(rowdata, "%30d", &x) != 1)
03583          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03584       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03585       ast_odbc_release_obj(obj);
03586    } else
03587       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03588 yuck:
03589    return x;
03590 }
03591 
03592 /*!
03593  * \brief returns the number of messages found.
03594  * \param vmu
03595  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03596  *
03597  * This method is used when mailboxes are stored in an ODBC back end.
03598  *
03599  * \return The count of messages being zero or more, less than zero on error.
03600  */
03601 static int count_messages(struct ast_vm_user *vmu, char *dir)
03602 {
03603    int x = 0;
03604    int res;
03605    SQLHSTMT stmt;
03606    char sql[PATH_MAX];
03607    char rowdata[20];
03608    char *argv[] = { dir };
03609    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03610 
03611    struct odbc_obj *obj;
03612    obj = ast_odbc_request_obj(odbc_database, 0);
03613    if (obj) {
03614       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03615       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03616       if (!stmt) {
03617          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03618          ast_odbc_release_obj(obj);
03619          goto yuck;
03620       }
03621       res = SQLFetch(stmt);
03622       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03623          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03624          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03625          ast_odbc_release_obj(obj);
03626          goto yuck;
03627       }
03628       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03629       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03630          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03631          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03632          ast_odbc_release_obj(obj);
03633          goto yuck;
03634       }
03635       if (sscanf(rowdata, "%30d", &x) != 1)
03636          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03637       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03638       ast_odbc_release_obj(obj);
03639       return x;
03640    } else
03641       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03642 yuck:
03643    return x - 1;
03644 
03645 }
03646 
03647 /*!
03648  * \brief Deletes a message from the mailbox folder.
03649  * \param sdir The mailbox folder to work in.
03650  * \param smsg The message index to be deleted.
03651  *
03652  * This method is used when mailboxes are stored in an ODBC back end.
03653  * The specified message is directly deleted from the database 'voicemessages' table.
03654  * 
03655  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03656  */
03657 static void delete_file(const char *sdir, int smsg)
03658 {
03659    SQLHSTMT stmt;
03660    char sql[PATH_MAX];
03661    char msgnums[20];
03662    char *argv[] = { NULL, msgnums };
03663    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03664    struct odbc_obj *obj;
03665 
03666    argv[0] = ast_strdupa(sdir);
03667 
03668    obj = ast_odbc_request_obj(odbc_database, 0);
03669    if (obj) {
03670       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03671       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03672       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03673       if (!stmt)
03674          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03675       else
03676          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03677       ast_odbc_release_obj(obj);
03678    } else
03679       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03680    return;  
03681 }
03682 
03683 /*!
03684  * \brief Copies a voicemail from one mailbox to another.
03685  * \param sdir the folder for which to look for the message to be copied.
03686  * \param smsg the index of the message to be copied.
03687  * \param ddir the destination folder to copy the message into.
03688  * \param dmsg the index to be used for the copied message.
03689  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03690  * \param dmailboxcontext The context for the destination user.
03691  *
03692  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03693  */
03694 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03695 {
03696    SQLHSTMT stmt;
03697    char sql[512];
03698    char msgnums[20];
03699    char msgnumd[20];
03700    struct odbc_obj *obj;
03701    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03702    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03703 
03704    delete_file(ddir, dmsg);
03705    obj = ast_odbc_request_obj(odbc_database, 0);
03706    if (obj) {
03707       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03708       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03709       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);
03710       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03711       if (!stmt)
03712          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03713       else
03714          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03715       ast_odbc_release_obj(obj);
03716    } else
03717       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03718    return;  
03719 }
03720 
03721 struct insert_data {
03722    char *sql;
03723    const char *dir;
03724    const char *msgnums;
03725    void *data;
03726    SQLLEN datalen;
03727    SQLLEN indlen;
03728    const char *context;
03729    const char *macrocontext;
03730    const char *callerid;
03731    const char *origtime;
03732    const char *duration;
03733    const char *mailboxuser;
03734    const char *mailboxcontext;
03735    const char *category;
03736    const char *flag;
03737 };
03738 
03739 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03740 {
03741    struct insert_data *data = vdata;
03742    int res;
03743    SQLHSTMT stmt;
03744 
03745    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03746    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03747       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03748       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03749       return NULL;
03750    }
03751 
03752    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03753    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03754    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03755    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03756    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03757    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03758    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03759    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03760    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03761    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03762    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03763    if (!ast_strlen_zero(data->category)) {
03764       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03765    }
03766    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03767    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03768       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03769       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03770       return NULL;
03771    }
03772 
03773    return stmt;
03774 }
03775 
03776 /*!
03777  * \brief Stores a voicemail into the database.
03778  * \param dir the folder the mailbox folder to store the message.
03779  * \param mailboxuser the user owning the mailbox folder.
03780  * \param mailboxcontext
03781  * \param msgnum the message index for the message to be stored.
03782  *
03783  * This method is used when mailboxes are stored in an ODBC back end.
03784  * The message sound file and information file is looked up on the file system. 
03785  * A SQL query is invoked to store the message into the (MySQL) database.
03786  *
03787  * \return the zero on success -1 on error.
03788  */
03789 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03790 {
03791    int res = 0;
03792    int fd = -1;
03793    void *fdm = MAP_FAILED;
03794    size_t fdlen = -1;
03795    SQLHSTMT stmt;
03796    char sql[PATH_MAX];
03797    char msgnums[20];
03798    char fn[PATH_MAX];
03799    char full_fn[PATH_MAX];
03800    char fmt[80]="";
03801    char *c;
03802    struct ast_config *cfg = NULL;
03803    struct odbc_obj *obj;
03804    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03805       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03806    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03807 
03808    delete_file(dir, msgnum);
03809    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03810       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03811       return -1;
03812    }
03813 
03814    do {
03815       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03816       c = strchr(fmt, '|');
03817       if (c)
03818          *c = '\0';
03819       if (!strcasecmp(fmt, "wav49"))
03820          strcpy(fmt, "WAV");
03821       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03822       if (msgnum > -1)
03823          make_file(fn, sizeof(fn), dir, msgnum);
03824       else
03825          ast_copy_string(fn, dir, sizeof(fn));
03826       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03827       cfg = ast_config_load(full_fn, config_flags);
03828       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03829       fd = open(full_fn, O_RDWR);
03830       if (fd < 0) {
03831          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03832          res = -1;
03833          break;
03834       }
03835       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03836          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03837             idata.context = "";
03838          }
03839          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03840             idata.macrocontext = "";
03841          }
03842          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03843             idata.callerid = "";
03844          }
03845          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03846             idata.origtime = "";
03847          }
03848          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03849             idata.duration = "";
03850          }
03851          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03852             idata.category = "";
03853          }
03854          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03855             idata.flag = "";
03856          }
03857       }
03858       fdlen = lseek(fd, 0, SEEK_END);
03859       lseek(fd, 0, SEEK_SET);
03860       printf("Length is %zd\n", fdlen);
03861       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03862       if (fdm == MAP_FAILED) {
03863          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03864          res = -1;
03865          break;
03866       } 
03867       idata.data = fdm;
03868       idata.datalen = idata.indlen = fdlen;
03869 
03870       if (!ast_strlen_zero(idata.category)) 
03871          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03872       else
03873          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03874 
03875       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03876          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03877       } else {
03878          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03879          res = -1;
03880       }
03881    } while (0);
03882    if (obj) {
03883       ast_odbc_release_obj(obj);
03884    }
03885    if (cfg)
03886       ast_config_destroy(cfg);
03887    if (fdm != MAP_FAILED)
03888       munmap(fdm, fdlen);
03889    if (fd > -1)
03890       close(fd);
03891    return res;
03892 }
03893 
03894 /*!
03895  * \brief Renames a message in a mailbox folder.
03896  * \param sdir The folder of the message to be renamed.
03897  * \param smsg The index of the message to be renamed.
03898  * \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.
03899  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03900  * \param ddir The destination folder for the message to be renamed into
03901  * \param dmsg The destination message for the message to be renamed.
03902  *
03903  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03904  * 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.
03905  * 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.
03906  */
03907 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03908 {
03909    SQLHSTMT stmt;
03910    char sql[PATH_MAX];
03911    char msgnums[20];
03912    char msgnumd[20];
03913    struct odbc_obj *obj;
03914    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03915    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03916 
03917    delete_file(ddir, dmsg);
03918    obj = ast_odbc_request_obj(odbc_database, 0);
03919    if (obj) {
03920       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03921       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03922       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03923       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03924       if (!stmt)
03925          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03926       else
03927          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03928       ast_odbc_release_obj(obj);
03929    } else
03930       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03931    return;  
03932 }
03933 
03934 /*!
03935  * \brief Removes a voicemail message file.
03936  * \param dir the path to the message file.
03937  * \param msgnum the unique number for the message within the mailbox.
03938  *
03939  * Removes the message content file and the information file.
03940  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03941  * Typical use is to clean up after a RETRIEVE operation. 
03942  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03943  * \return zero on success, -1 on error.
03944  */
03945 static int remove_file(char *dir, int msgnum)
03946 {
03947    char fn[PATH_MAX];
03948    char full_fn[PATH_MAX];
03949    char msgnums[80];
03950    
03951    if (msgnum > -1) {
03952       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03953       make_file(fn, sizeof(fn), dir, msgnum);
03954    } else
03955       ast_copy_string(fn, dir, sizeof(fn));
03956    ast_filedelete(fn, NULL);  
03957    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03958    unlink(full_fn);
03959    return 0;
03960 }
03961 #else
03962 #ifndef IMAP_STORAGE
03963 /*!
03964  * \brief Find all .txt files - even if they are not in sequence from 0000.
03965  * \param vmu
03966  * \param dir
03967  *
03968  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03969  *
03970  * \return the count of messages, zero or more.
03971  */
03972 static int count_messages(struct ast_vm_user *vmu, char *dir)
03973 {
03974 
03975    int vmcount = 0;
03976    DIR *vmdir = NULL;
03977    struct dirent *vment = NULL;
03978 
03979    if (vm_lock_path(dir))
03980       return ERROR_LOCK_PATH;
03981 
03982    if ((vmdir = opendir(dir))) {
03983       while ((vment = readdir(vmdir))) {
03984          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03985             vmcount++;
03986          }
03987       }
03988       closedir(vmdir);
03989    }
03990    ast_unlock_path(dir);
03991    
03992    return vmcount;
03993 }
03994 
03995 /*!
03996  * \brief Renames a message in a mailbox folder.
03997  * \param sfn The path to the mailbox information and data file to be renamed.
03998  * \param dfn The path for where the message data and information files will be renamed to.
03999  *
04000  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04001  */
04002 static void rename_file(char *sfn, char *dfn)
04003 {
04004    char stxt[PATH_MAX];
04005    char dtxt[PATH_MAX];
04006    ast_filerename(sfn, dfn, NULL);
04007    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04008    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04009    if (ast_check_realtime("voicemail_data")) {
04010       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04011    }
04012    rename(stxt, dtxt);
04013 }
04014 
04015 /*! 
04016  * \brief Determines the highest message number in use for a given user and mailbox folder.
04017  * \param vmu 
04018  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04019  *
04020  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04021  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04022  *
04023  * \note Should always be called with a lock already set on dir.
04024  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04025  */
04026 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04027 {
04028    int x;
04029    unsigned char map[MAXMSGLIMIT] = "";
04030    DIR *msgdir;
04031    struct dirent *msgdirent;
04032    int msgdirint;
04033    char extension[4];
04034    int stopcount = 0;
04035 
04036    /* Reading the entire directory into a file map scales better than
04037     * doing a stat repeatedly on a predicted sequence.  I suspect this
04038     * is partially due to stat(2) internally doing a readdir(2) itself to
04039     * find each file. */
04040    if (!(msgdir = opendir(dir))) {
04041       return -1;
04042    }
04043 
04044    while ((msgdirent = readdir(msgdir))) {
04045       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04046          map[msgdirint] = 1;
04047          stopcount++;
04048          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04049       }
04050    }
04051    closedir(msgdir);
04052 
04053    for (x = 0; x < vmu->maxmsg; x++) {
04054       if (map[x] == 1) {
04055          stopcount--;
04056       } else if (map[x] == 0 && !stopcount) {
04057          break;
04058       }
04059    }
04060 
04061    return x - 1;
04062 }
04063 
04064 #endif /* #ifndef IMAP_STORAGE */
04065 #endif /* #else of #ifdef ODBC_STORAGE */
04066 #ifndef IMAP_STORAGE
04067 /*!
04068  * \brief Utility function to copy a file.
04069  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04070  * \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.
04071  *
04072  * 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.
04073  * The copy operation copies up to 4096 bytes at once.
04074  *
04075  * \return zero on success, -1 on error.
04076  */
04077 static int copy(char *infile, char *outfile)
04078 {
04079    int ifd;
04080    int ofd;
04081    int res;
04082    int len;
04083    char buf[4096];
04084 
04085 #ifdef HARDLINK_WHEN_POSSIBLE
04086    /* Hard link if possible; saves disk space & is faster */
04087    if (link(infile, outfile)) {
04088 #endif
04089       if ((ifd = open(infile, O_RDONLY)) < 0) {
04090          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04091          return -1;
04092       }
04093       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04094          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04095          close(ifd);
04096          return -1;
04097       }
04098       do {
04099          len = read(ifd, buf, sizeof(buf));
04100          if (len < 0) {
04101             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04102             close(ifd);
04103             close(ofd);
04104             unlink(outfile);
04105          }
04106          if (len) {
04107             res = write(ofd, buf, len);
04108             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04109                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04110                close(ifd);
04111                close(ofd);
04112                unlink(outfile);
04113             }
04114          }
04115       } while (len);
04116       close(ifd);
04117       close(ofd);
04118       return 0;
04119 #ifdef HARDLINK_WHEN_POSSIBLE
04120    } else {
04121       /* Hard link succeeded */
04122       return 0;
04123    }
04124 #endif
04125 }
04126 
04127 /*!
04128  * \brief Copies a voicemail information (envelope) file.
04129  * \param frompath
04130  * \param topath 
04131  *
04132  * Every voicemail has the data (.wav) file, and the information file.
04133  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04134  * This is used by the COPY macro when not using IMAP storage.
04135  */
04136 static void copy_plain_file(char *frompath, char *topath)
04137 {
04138    char frompath2[PATH_MAX], topath2[PATH_MAX];
04139    struct ast_variable *tmp,*var = NULL;
04140    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04141    ast_filecopy(frompath, topath, NULL);
04142    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04143    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04144    if (ast_check_realtime("voicemail_data")) {
04145       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04146       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04147       for (tmp = var; tmp; tmp = tmp->next) {
04148          if (!strcasecmp(tmp->name, "origmailbox")) {
04149             origmailbox = tmp->value;
04150          } else if (!strcasecmp(tmp->name, "context")) {
04151             context = tmp->value;
04152          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04153             macrocontext = tmp->value;
04154          } else if (!strcasecmp(tmp->name, "exten")) {
04155             exten = tmp->value;
04156          } else if (!strcasecmp(tmp->name, "priority")) {
04157             priority = tmp->value;
04158          } else if (!strcasecmp(tmp->name, "callerchan")) {
04159             callerchan = tmp->value;
04160          } else if (!strcasecmp(tmp->name, "callerid")) {
04161             callerid = tmp->value;
04162          } else if (!strcasecmp(tmp->name, "origdate")) {
04163             origdate = tmp->value;
04164          } else if (!strcasecmp(tmp->name, "origtime")) {
04165             origtime = tmp->value;
04166          } else if (!strcasecmp(tmp->name, "category")) {
04167             category = tmp->value;
04168          } else if (!strcasecmp(tmp->name, "duration")) {
04169             duration = tmp->value;
04170          }
04171       }
04172       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);
04173    }
04174    copy(frompath2, topath2);
04175    ast_variables_destroy(var);
04176 }
04177 #endif
04178 
04179 /*! 
04180  * \brief Removes the voicemail sound and information file.
04181  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04182  *
04183  * This is used by the DELETE macro when voicemails are stored on the file system.
04184  *
04185  * \return zero on success, -1 on error.
04186  */
04187 static int vm_delete(char *file)
04188 {
04189    char *txt;
04190    int txtsize = 0;
04191 
04192    txtsize = (strlen(file) + 5)*sizeof(char);
04193    txt = alloca(txtsize);
04194    /* Sprintf here would safe because we alloca'd exactly the right length,
04195     * but trying to eliminate all sprintf's anyhow
04196     */
04197    if (ast_check_realtime("voicemail_data")) {
04198       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04199    }
04200    snprintf(txt, txtsize, "%s.txt", file);
04201    unlink(txt);
04202    return ast_filedelete(file, NULL);
04203 }
04204 
04205 /*!
04206  * \brief utility used by inchar(), for base_encode()
04207  */
04208 static int inbuf(struct baseio *bio, FILE *fi)
04209 {
04210    int l;
04211 
04212    if (bio->ateof)
04213       return 0;
04214 
04215    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04216       if (ferror(fi))
04217          return -1;
04218 
04219       bio->ateof = 1;
04220       return 0;
04221    }
04222 
04223    bio->iolen = l;
04224    bio->iocp = 0;
04225 
04226    return 1;
04227 }
04228 
04229 /*!
04230  * \brief utility used by base_encode()
04231  */
04232 static int inchar(struct baseio *bio, FILE *fi)
04233 {
04234    if (bio->iocp>=bio->iolen) {
04235       if (!inbuf(bio, fi))
04236          return EOF;
04237    }
04238 
04239    return bio->iobuf[bio->iocp++];
04240 }
04241 
04242 /*!
04243  * \brief utility used by base_encode()
04244  */
04245 static int ochar(struct baseio *bio, int c, FILE *so)
04246 {
04247    if (bio->linelength >= BASELINELEN) {
04248       if (fputs(ENDL, so) == EOF) {
04249          return -1;
04250       }
04251 
04252       bio->linelength = 0;
04253    }
04254 
04255    if (putc(((unsigned char) c), so) == EOF) {
04256       return -1;
04257    }
04258 
04259    bio->linelength++;
04260 
04261    return 1;
04262 }
04263 
04264 /*!
04265  * \brief Performs a base 64 encode algorithm on the contents of a File
04266  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04267  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04268  *
04269  * 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 ?
04270  *
04271  * \return zero on success, -1 on error.
04272  */
04273 static int base_encode(char *filename, FILE *so)
04274 {
04275    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04276       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04277       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04278       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04279    int i, hiteof = 0;
04280    FILE *fi;
04281    struct baseio bio;
04282 
04283    memset(&bio, 0, sizeof(bio));
04284    bio.iocp = BASEMAXINLINE;
04285 
04286    if (!(fi = fopen(filename, "rb"))) {
04287       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04288       return -1;
04289    }
04290 
04291    while (!hiteof){
04292       unsigned char igroup[3], ogroup[4];
04293       int c, n;
04294 
04295       memset(igroup, 0, sizeof(igroup));
04296 
04297       for (n = 0; n < 3; n++) {
04298          if ((c = inchar(&bio, fi)) == EOF) {
04299             hiteof = 1;
04300             break;
04301          }
04302 
04303          igroup[n] = (unsigned char) c;
04304       }
04305 
04306       if (n > 0) {
04307          ogroup[0]= dtable[igroup[0] >> 2];
04308          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04309          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04310          ogroup[3]= dtable[igroup[2] & 0x3F];
04311 
04312          if (n < 3) {
04313             ogroup[3] = '=';
04314 
04315             if (n < 2)
04316                ogroup[2] = '=';
04317          }
04318 
04319          for (i = 0; i < 4; i++)
04320             ochar(&bio, ogroup[i], so);
04321       }
04322    }
04323 
04324    fclose(fi);
04325    
04326    if (fputs(ENDL, so) == EOF) {
04327       return 0;
04328    }
04329 
04330    return 1;
04331 }
04332 
04333 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)
04334 {
04335    char callerid[256];
04336    char num[12];
04337    char fromdir[256], fromfile[256];
04338    struct ast_config *msg_cfg;
04339    const char *origcallerid, *origtime;
04340    char origcidname[80], origcidnum[80], origdate[80];
04341    int inttime;
04342    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04343 
04344    /* Prepare variables for substitution in email body and subject */
04345    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04346    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04347    snprintf(num, sizeof(num), "%d", msgnum);
04348    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04349    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04350    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04351    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04352       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04353    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04354    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04355    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04356    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04357    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04358 
04359    /* Retrieve info from VM attribute file */
04360    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04361    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04362    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04363       strcat(fromfile, ".txt");
04364    }
04365    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04366       if (option_debug > 0) {
04367          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04368       }
04369       return;
04370    }
04371 
04372    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04373       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04374       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04375       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04376       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04377    }
04378 
04379    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04380       struct timeval tv = { inttime, };
04381       struct ast_tm tm;
04382       ast_localtime(&tv, &tm, NULL);
04383       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04384       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04385    }
04386    ast_config_destroy(msg_cfg);
04387 }
04388 
04389 /*!
04390  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04391  * \param from The string to work with.
04392  * \param buf The buffer into which to write the modified quoted string.
04393  * \param maxlen Always zero, but see \see ast_str
04394  * 
04395  * \return The destination string with quotes wrapped on it (the to field).
04396  */
04397 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04398 {
04399    const char *ptr;
04400 
04401    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04402    ast_str_set(buf, maxlen, "\"");
04403    for (ptr = from; *ptr; ptr++) {
04404       if (*ptr == '"' || *ptr == '\\') {
04405          ast_str_append(buf, maxlen, "\\%c", *ptr);
04406       } else {
04407          ast_str_append(buf, maxlen, "%c", *ptr);
04408       }
04409    }
04410    ast_str_append(buf, maxlen, "\"");
04411 
04412    return ast_str_buffer(*buf);
04413 }
04414 
04415 /*! \brief
04416  * fill in *tm for current time according to the proper timezone, if any.
04417  * \return tm so it can be used as a function argument.
04418  */
04419 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04420 {
04421    const struct vm_zone *z = NULL;
04422    struct timeval t = ast_tvnow();
04423 
04424    /* Does this user have a timezone specified? */
04425    if (!ast_strlen_zero(vmu->zonetag)) {
04426       /* Find the zone in the list */
04427       AST_LIST_LOCK(&zones);
04428       AST_LIST_TRAVERSE(&zones, z, list) {
04429          if (!strcmp(z->name, vmu->zonetag))
04430             break;
04431       }
04432       AST_LIST_UNLOCK(&zones);
04433    }
04434    ast_localtime(&t, tm, z ? z->timezone : NULL);
04435    return tm;
04436 }
04437 
04438 /*!\brief Check if the string would need encoding within the MIME standard, to
04439  * avoid confusing certain mail software that expects messages to be 7-bit
04440  * clean.
04441  */
04442 static int check_mime(const char *str)
04443 {
04444    for (; *str; str++) {
04445       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04446          return 1;
04447       }
04448    }
04449    return 0;
04450 }
04451 
04452 /*!\brief Encode a string according to the MIME rules for encoding strings
04453  * that are not 7-bit clean or contain control characters.
04454  *
04455  * Additionally, if the encoded string would exceed the MIME limit of 76
04456  * characters per line, then the encoding will be broken up into multiple
04457  * sections, separated by a space character, in order to facilitate
04458  * breaking up the associated header across multiple lines.
04459  *
04460  * \param end An expandable buffer for holding the result
04461  * \param maxlen Always zero, but see \see ast_str
04462  * \param start A string to be encoded
04463  * \param preamble The length of the first line already used for this string,
04464  * to ensure that each line maintains a maximum length of 76 chars.
04465  * \param postamble the length of any additional characters appended to the
04466  * line, used to ensure proper field wrapping.
04467  * \retval The encoded string.
04468  */
04469 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04470 {
04471    struct ast_str *tmp = ast_str_alloca(80);
04472    int first_section = 1;
04473 
04474    ast_str_reset(*end);
04475    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04476    for (; *start; start++) {
04477       int need_encoding = 0;
04478       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04479          need_encoding = 1;
04480       }
04481       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04482          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04483          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04484          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04485          /* Start new line */
04486          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04487          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04488          first_section = 0;
04489       }
04490       if (need_encoding && *start == ' ') {
04491          ast_str_append(&tmp, -1, "_");
04492       } else if (need_encoding) {
04493          ast_str_append(&tmp, -1, "=%hhX", *start);
04494       } else {
04495          ast_str_append(&tmp, -1, "%c", *start);
04496       }
04497    }
04498    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04499    return ast_str_buffer(*end);
04500 }
04501 
04502 /*!
04503  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04504  * \param p The output file to generate the email contents into.
04505  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04506  * \param vmu The voicemail user who is sending the voicemail.
04507  * \param msgnum The message index in the mailbox folder.
04508  * \param context 
04509  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04510  * \param fromfolder
04511  * \param cidnum The caller ID number.
04512  * \param cidname The caller ID name.
04513  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04514  * \param attach2 
04515  * \param format The message sound file format. i.e. .wav
04516  * \param duration The time of the message content, in seconds.
04517  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04518  * \param chan
04519  * \param category
04520  * \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.
04521  * \param flag
04522  *
04523  * 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.
04524  */
04525 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)
04526 {
04527    char date[256];
04528    char host[MAXHOSTNAMELEN] = "";
04529    char who[256];
04530    char bound[256];
04531    char dur[256];
04532    struct ast_tm tm;
04533    char enc_cidnum[256] = "", enc_cidname[256] = "";
04534    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04535    char *greeting_attachment; 
04536    char filename[256];
04537 
04538    if (!str1 || !str2) {
04539       ast_free(str1);
04540       ast_free(str2);
04541       return;
04542    }
04543 
04544    if (cidnum) {
04545       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04546    }
04547    if (cidname) {
04548       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04549    }
04550    gethostname(host, sizeof(host) - 1);
04551 
04552    if (strchr(srcemail, '@')) {
04553       ast_copy_string(who, srcemail, sizeof(who));
04554    } else {
04555       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04556    }
04557 
04558    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04559    if (greeting_attachment) {
04560       *greeting_attachment++ = '\0';
04561    }
04562 
04563    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04564    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04565    fprintf(p, "Date: %s" ENDL, date);
04566 
04567    /* Set date format for voicemail mail */
04568    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04569 
04570    if (!ast_strlen_zero(fromstring)) {
04571       struct ast_channel *ast;
04572       if ((ast = ast_dummy_channel_alloc())) {
04573          char *ptr;
04574          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04575          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04576 
04577          if (check_mime(ast_str_buffer(str1))) {
04578             int first_line = 1;
04579             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04580             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04581                *ptr = '\0';
04582                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04583                first_line = 0;
04584                /* Substring is smaller, so this will never grow */
04585                ast_str_set(&str2, 0, "%s", ptr + 1);
04586             }
04587             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04588          } else {
04589             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04590          }
04591          ast = ast_channel_unref(ast);
04592       } else {
04593          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04594       }
04595    } else {
04596       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04597    }
04598 
04599    if (check_mime(vmu->fullname)) {
04600       int first_line = 1;
04601       char *ptr;
04602       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04603       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04604          *ptr = '\0';
04605          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04606          first_line = 0;
04607          /* Substring is smaller, so this will never grow */
04608          ast_str_set(&str2, 0, "%s", ptr + 1);
04609       }
04610       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04611    } else {
04612       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04613    }
04614 
04615    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04616       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04617       struct ast_channel *ast;
04618       if ((ast = ast_dummy_channel_alloc())) {
04619          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04620          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04621          if (check_mime(ast_str_buffer(str1))) {
04622             int first_line = 1;
04623             char *ptr;
04624             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04625             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04626                *ptr = '\0';
04627                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04628                first_line = 0;
04629                /* Substring is smaller, so this will never grow */
04630                ast_str_set(&str2, 0, "%s", ptr + 1);
04631             }
04632             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04633          } else {
04634             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04635          }
04636          ast = ast_channel_unref(ast);
04637       } else {
04638          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04639       }
04640    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04641       if (ast_strlen_zero(flag)) {
04642          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04643       } else {
04644          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04645       }
04646    } else {
04647       if (ast_strlen_zero(flag)) {
04648          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04649       } else {
04650          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04651       }
04652    }
04653 
04654    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04655       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04656    if (imap) {
04657       /* additional information needed for IMAP searching */
04658       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04659       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04660       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04661       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04662 #ifdef IMAP_STORAGE
04663       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04664 #else
04665       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04666 #endif
04667       /* flag added for Urgent */
04668       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04669       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04670       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04671       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04672       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04673       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04674       if (!ast_strlen_zero(category)) {
04675          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04676       } else {
04677          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04678       }
04679       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04680       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04681       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04682    }
04683    if (!ast_strlen_zero(cidnum)) {
04684       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04685    }
04686    if (!ast_strlen_zero(cidname)) {
04687       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04688    }
04689    fprintf(p, "MIME-Version: 1.0" ENDL);
04690    if (attach_user_voicemail) {
04691       /* Something unique. */
04692       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04693          (int) getpid(), (unsigned int) ast_random());
04694 
04695       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04696       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04697       fprintf(p, "--%s" ENDL, bound);
04698    }
04699    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04700    if (emailbody || vmu->emailbody) {
04701       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04702       struct ast_channel *ast;
04703       if ((ast = ast_dummy_channel_alloc())) {
04704          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04705          ast_str_substitute_variables(&str1, 0, ast, e_body);
04706 #ifdef IMAP_STORAGE
04707             {
04708                /* Convert body to native line terminators for IMAP backend */
04709                char *line = ast_str_buffer(str1), *next;
04710                do {
04711                   /* Terminate line before outputting it to the file */
04712                   if ((next = strchr(line, '\n'))) {
04713                      *next++ = '\0';
04714                   }
04715                   fprintf(p, "%s" ENDL, line);
04716                   line = next;
04717                } while (!ast_strlen_zero(line));
04718             }
04719 #else
04720          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04721 #endif
04722          ast = ast_channel_unref(ast);
04723       } else {
04724          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04725       }
04726    } else if (msgnum > -1) {
04727       if (strcmp(vmu->mailbox, mailbox)) {
04728          /* Forwarded type */
04729          struct ast_config *msg_cfg;
04730          const char *v;
04731          int inttime;
04732          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04733          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04734          /* Retrieve info from VM attribute file */
04735          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04736          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04737          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04738             strcat(fromfile, ".txt");
04739          }
04740          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04741             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04742                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04743             }
04744 
04745             /* You might be tempted to do origdate, except that a) it's in the wrong
04746              * format, and b) it's missing for IMAP recordings. */
04747             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04748                struct timeval tv = { inttime, };
04749                struct ast_tm tm;
04750                ast_localtime(&tv, &tm, NULL);
04751                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04752             }
04753             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04754                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04755                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04756                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04757                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04758                date, origcallerid, origdate);
04759             ast_config_destroy(msg_cfg);
04760          } else {
04761             goto plain_message;
04762          }
04763       } else {
04764 plain_message:
04765          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04766             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04767             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04768             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04769             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04770       }
04771    } else {
04772       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04773             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04774    }
04775 
04776    if (imap || attach_user_voicemail) {
04777       if (!ast_strlen_zero(attach2)) {
04778          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04779          ast_debug(5, "creating second attachment filename %s\n", filename);
04780          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04781          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04782          ast_debug(5, "creating attachment filename %s\n", filename);
04783          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04784       } else {
04785          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04786          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04787          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04788       }
04789    }
04790    ast_free(str1);
04791    ast_free(str2);
04792 }
04793 
04794 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)
04795 {
04796    char tmpdir[256], newtmp[256];
04797    char fname[256];
04798    char tmpcmd[256];
04799    int tmpfd = -1;
04800    int soxstatus = 0;
04801 
04802    /* Eww. We want formats to tell us their own MIME type */
04803    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04804 
04805    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04806       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04807       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04808       tmpfd = mkstemp(newtmp);
04809       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04810       ast_debug(3, "newtmp: %s\n", newtmp);
04811       if (tmpfd > -1) {
04812          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04813          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04814             attach = newtmp;
04815             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04816          } else {
04817             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04818                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04819             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04820          }
04821       }
04822    }
04823    fprintf(p, "--%s" ENDL, bound);
04824    if (msgnum > -1)
04825       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04826    else
04827       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04828    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04829    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04830    if (msgnum > -1)
04831       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04832    else
04833       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04834    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04835    base_encode(fname, p);
04836    if (last)
04837       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04838    if (tmpfd > -1) {
04839       if (soxstatus == 0) {
04840          unlink(fname);
04841       }
04842       close(tmpfd);
04843       unlink(newtmp);
04844    }
04845    return 0;
04846 }
04847 
04848 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)
04849 {
04850    FILE *p = NULL;
04851    char tmp[80] = "/tmp/astmail-XXXXXX";
04852    char tmp2[256];
04853    char *stringp;
04854 
04855    if (vmu && ast_strlen_zero(vmu->email)) {
04856       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04857       return(0);
04858    }
04859 
04860    /* Mail only the first format */
04861    format = ast_strdupa(format);
04862    stringp = format;
04863    strsep(&stringp, "|");
04864 
04865    if (!strcmp(format, "wav49"))
04866       format = "WAV";
04867    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));
04868    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04869       command hangs */
04870    if ((p = vm_mkftemp(tmp)) == NULL) {
04871       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04872       return -1;
04873    } else {
04874       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04875       fclose(p);
04876       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04877       ast_safe_system(tmp2);
04878       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04879    }
04880    return 0;
04881 }
04882 
04883 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)
04884 {
04885    char enc_cidnum[256], enc_cidname[256];
04886    char date[256];
04887    char host[MAXHOSTNAMELEN] = "";
04888    char who[256];
04889    char dur[PATH_MAX];
04890    char tmp[80] = "/tmp/astmail-XXXXXX";
04891    char tmp2[PATH_MAX];
04892    struct ast_tm tm;
04893    FILE *p;
04894    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04895 
04896    if (!str1 || !str2) {
04897       ast_free(str1);
04898       ast_free(str2);
04899       return -1;
04900    }
04901 
04902    if (cidnum) {
04903       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04904    }
04905    if (cidname) {
04906       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04907    }
04908 
04909    if ((p = vm_mkftemp(tmp)) == NULL) {
04910       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04911       ast_free(str1);
04912       ast_free(str2);
04913       return -1;
04914    }
04915    gethostname(host, sizeof(host)-1);
04916    if (strchr(srcemail, '@')) {
04917       ast_copy_string(who, srcemail, sizeof(who));
04918    } else {
04919       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04920    }
04921    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04922    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04923    fprintf(p, "Date: %s\n", date);
04924 
04925    /* Reformat for custom pager format */
04926    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04927 
04928    if (!ast_strlen_zero(pagerfromstring)) {
04929       struct ast_channel *ast;
04930       if ((ast = ast_dummy_channel_alloc())) {
04931          char *ptr;
04932          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04933          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04934 
04935          if (check_mime(ast_str_buffer(str1))) {
04936             int first_line = 1;
04937             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04938             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04939                *ptr = '\0';
04940                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04941                first_line = 0;
04942                /* Substring is smaller, so this will never grow */
04943                ast_str_set(&str2, 0, "%s", ptr + 1);
04944             }
04945             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04946          } else {
04947             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04948          }
04949          ast = ast_channel_unref(ast);
04950       } else {
04951          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04952       }
04953    } else {
04954       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04955    }
04956 
04957    if (check_mime(vmu->fullname)) {
04958       int first_line = 1;
04959       char *ptr;
04960       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04961       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04962          *ptr = '\0';
04963          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04964          first_line = 0;
04965          /* Substring is smaller, so this will never grow */
04966          ast_str_set(&str2, 0, "%s", ptr + 1);
04967       }
04968       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04969    } else {
04970       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04971    }
04972 
04973    if (!ast_strlen_zero(pagersubject)) {
04974       struct ast_channel *ast;
04975       if ((ast = ast_dummy_channel_alloc())) {
04976          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04977          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04978          if (check_mime(ast_str_buffer(str1))) {
04979             int first_line = 1;
04980             char *ptr;
04981             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04982             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04983                *ptr = '\0';
04984                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04985                first_line = 0;
04986                /* Substring is smaller, so this will never grow */
04987                ast_str_set(&str2, 0, "%s", ptr + 1);
04988             }
04989             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04990          } else {
04991             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04992          }
04993          ast = ast_channel_unref(ast);
04994       } else {
04995          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04996       }
04997    } else {
04998       if (ast_strlen_zero(flag)) {
04999          fprintf(p, "Subject: New VM\n\n");
05000       } else {
05001          fprintf(p, "Subject: New %s VM\n\n", flag);
05002       }
05003    }
05004 
05005    if (pagerbody) {
05006       struct ast_channel *ast;
05007       if ((ast = ast_dummy_channel_alloc())) {
05008          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05009          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05010          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05011          ast = ast_channel_unref(ast);
05012       } else {
05013          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05014       }
05015    } else {
05016       fprintf(p, "New %s long %s msg in box %s\n"
05017             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05018    }
05019 
05020    fclose(p);
05021    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05022    ast_safe_system(tmp2);
05023    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05024    ast_free(str1);
05025    ast_free(str2);
05026    return 0;
05027 }
05028 
05029 /*!
05030  * \brief Gets the current date and time, as formatted string.
05031  * \param s The buffer to hold the output formatted date.
05032  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05033  * 
05034  * The date format string used is "%a %b %e %r UTC %Y".
05035  * 
05036  * \return zero on success, -1 on error.
05037  */
05038 static int get_date(char *s, int len)
05039 {
05040    struct ast_tm tm;
05041    struct timeval t = ast_tvnow();
05042    
05043    ast_localtime(&t, &tm, "UTC");
05044 
05045    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05046 }
05047 
05048 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05049 {
05050    int res;
05051    char fn[PATH_MAX];
05052    char dest[PATH_MAX];
05053 
05054    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05055 
05056    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05057       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05058       return -1;
05059    }
05060 
05061    RETRIEVE(fn, -1, ext, context);
05062    if (ast_fileexists(fn, NULL, NULL) > 0) {
05063       res = ast_stream_and_wait(chan, fn, ecodes);
05064       if (res) {
05065          DISPOSE(fn, -1);
05066          return res;
05067       }
05068    } else {
05069       /* Dispose just in case */
05070       DISPOSE(fn, -1);
05071       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05072       if (res)
05073          return res;
05074       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05075       if (res)
05076          return res;
05077    }
05078    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05079    return res;
05080 }
05081 
05082 static void free_zone(struct vm_zone *z)
05083 {
05084    ast_free(z);
05085 }
05086 
05087 #ifdef ODBC_STORAGE
05088 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05089 {
05090    int x = -1;
05091    int res;
05092    SQLHSTMT stmt = NULL;
05093    char sql[PATH_MAX];
05094    char rowdata[20];
05095    char tmp[PATH_MAX] = "";
05096    struct odbc_obj *obj = NULL;
05097    char *context;
05098    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05099 
05100    if (newmsgs)
05101       *newmsgs = 0;
05102    if (oldmsgs)
05103       *oldmsgs = 0;
05104    if (urgentmsgs)
05105       *urgentmsgs = 0;
05106 
05107    /* If no mailbox, return immediately */
05108    if (ast_strlen_zero(mailbox))
05109       return 0;
05110 
05111    ast_copy_string(tmp, mailbox, sizeof(tmp));
05112 
05113    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05114       int u, n, o;
05115       char *next, *remaining = tmp;
05116       while ((next = strsep(&remaining, " ,"))) {
05117          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05118             return -1;
05119          }
05120          if (urgentmsgs) {
05121             *urgentmsgs += u;
05122          }
05123          if (newmsgs) {
05124             *newmsgs += n;
05125          }
05126          if (oldmsgs) {
05127             *oldmsgs += o;
05128          }
05129       }
05130       return 0;
05131    }
05132 
05133    context = strchr(tmp, '@');
05134    if (context) {
05135       *context = '\0';
05136       context++;
05137    } else
05138       context = "default";
05139 
05140    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05141       do {
05142          if (newmsgs) {
05143             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05144             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05145                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05146                break;
05147             }
05148             res = SQLFetch(stmt);
05149             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05150                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05151                break;
05152             }
05153             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05154             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05155                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05156                break;
05157             }
05158             *newmsgs = atoi(rowdata);
05159             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05160          }
05161 
05162          if (oldmsgs) {
05163             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05164             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05165                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05166                break;
05167             }
05168             res = SQLFetch(stmt);
05169             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05170                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05171                break;
05172             }
05173             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05174             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05175                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05176                break;
05177             }
05178             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05179             *oldmsgs = atoi(rowdata);
05180          }
05181 
05182          if (urgentmsgs) {
05183             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05184             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05185                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05186                break;
05187             }
05188             res = SQLFetch(stmt);
05189             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05190                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05191                break;
05192             }
05193             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05194             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05195                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05196                break;
05197             }
05198             *urgentmsgs = atoi(rowdata);
05199          }
05200 
05201          x = 0;
05202       } while (0);
05203    } else {
05204       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05205    }
05206 
05207    if (stmt) {
05208       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05209    }
05210    if (obj) {
05211       ast_odbc_release_obj(obj);
05212    }
05213    return x;
05214 }
05215 
05216 /*!
05217  * \brief Gets the number of messages that exist in a mailbox folder.
05218  * \param context
05219  * \param mailbox
05220  * \param folder
05221  * 
05222  * This method is used when ODBC backend is used.
05223  * \return The number of messages in this mailbox folder (zero or more).
05224  */
05225 static int messagecount(const char *context, const char *mailbox, const char *folder)
05226 {
05227    struct odbc_obj *obj = NULL;
05228    int nummsgs = 0;
05229    int res;
05230    SQLHSTMT stmt = NULL;
05231    char sql[PATH_MAX];
05232    char rowdata[20];
05233    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05234    if (!folder)
05235       folder = "INBOX";
05236    /* If no mailbox, return immediately */
05237    if (ast_strlen_zero(mailbox))
05238       return 0;
05239 
05240    obj = ast_odbc_request_obj(odbc_database, 0);
05241    if (obj) {
05242       if (!strcmp(folder, "INBOX")) {
05243          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);
05244       } else {
05245          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05246       }
05247       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05248       if (!stmt) {
05249          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05250          goto yuck;
05251       }
05252       res = SQLFetch(stmt);
05253       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05254          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05255          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05256          goto yuck;
05257       }
05258       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05259       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05260          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05261          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05262          goto yuck;
05263       }
05264       nummsgs = atoi(rowdata);
05265       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05266    } else
05267       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05268 
05269 yuck:
05270    if (obj)
05271       ast_odbc_release_obj(obj);
05272    return nummsgs;
05273 }
05274 
05275 /** 
05276  * \brief Determines if the given folder has messages.
05277  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05278  * 
05279  * This function is used when the mailbox is stored in an ODBC back end.
05280  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05281  * \return 1 if the folder has one or more messages. zero otherwise.
05282  */
05283 static int has_voicemail(const char *mailbox, const char *folder)
05284 {
05285    char tmp[256], *tmp2 = tmp, *box, *context;
05286    ast_copy_string(tmp, mailbox, sizeof(tmp));
05287    while ((context = box = strsep(&tmp2, ",&"))) {
05288       strsep(&context, "@");
05289       if (ast_strlen_zero(context))
05290          context = "default";
05291       if (messagecount(context, box, folder))
05292          return 1;
05293    }
05294    return 0;
05295 }
05296 #endif
05297 #ifndef IMAP_STORAGE
05298 /*! 
05299  * \brief Copies a message from one mailbox to another.
05300  * \param chan
05301  * \param vmu
05302  * \param imbox
05303  * \param msgnum
05304  * \param duration
05305  * \param recip
05306  * \param fmt
05307  * \param dir
05308  * \param flag
05309  *
05310  * This is only used by file storage based mailboxes.
05311  *
05312  * \return zero on success, -1 on error.
05313  */
05314 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)
05315 {
05316    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05317    const char *frombox = mbox(vmu, imbox);
05318    const char *userfolder;
05319    int recipmsgnum;
05320    int res = 0;
05321 
05322    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05323 
05324    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05325       userfolder = "Urgent";
05326    } else {
05327       userfolder = "INBOX";
05328    }
05329 
05330    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05331 
05332    if (!dir)
05333       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05334    else
05335       ast_copy_string(fromdir, dir, sizeof(fromdir));
05336 
05337    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05338    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05339 
05340    if (vm_lock_path(todir))
05341       return ERROR_LOCK_PATH;
05342 
05343    recipmsgnum = last_message_index(recip, todir) + 1;
05344    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05345       make_file(topath, sizeof(topath), todir, recipmsgnum);
05346 #ifndef ODBC_STORAGE
05347       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05348          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05349       } else {
05350 #endif
05351          /* If we are prepending a message for ODBC, then the message already
05352           * exists in the database, but we want to force copying from the
05353           * filesystem (since only the FS contains the prepend). */
05354          copy_plain_file(frompath, topath);
05355          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05356          vm_delete(topath);
05357 #ifndef ODBC_STORAGE
05358       }
05359 #endif
05360    } else {
05361       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05362       res = -1;
05363    }
05364    ast_unlock_path(todir);
05365    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05366       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05367       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05368       flag);
05369    
05370    return res;
05371 }
05372 #endif
05373 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05374 
05375 static int messagecount(const char *context, const char *mailbox, const char *folder)
05376 {
05377    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05378 }
05379 
05380 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05381 {
05382    DIR *dir;
05383    struct dirent *de;
05384    char fn[256];
05385    int ret = 0;
05386 
05387    /* If no mailbox, return immediately */
05388    if (ast_strlen_zero(mailbox))
05389       return 0;
05390 
05391    if (ast_strlen_zero(folder))
05392       folder = "INBOX";
05393    if (ast_strlen_zero(context))
05394       context = "default";
05395 
05396    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05397 
05398    if (!(dir = opendir(fn)))
05399       return 0;
05400 
05401    while ((de = readdir(dir))) {
05402       if (!strncasecmp(de->d_name, "msg", 3)) {
05403          if (shortcircuit) {
05404             ret = 1;
05405             break;
05406          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05407             ret++;
05408          }
05409       }
05410    }
05411 
05412    closedir(dir);
05413 
05414    return ret;
05415 }
05416 
05417 /** 
05418  * \brief Determines if the given folder has messages.
05419  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05420  * \param folder the folder to look in
05421  *
05422  * This function is used when the mailbox is stored in a filesystem back end.
05423  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05424  * \return 1 if the folder has one or more messages. zero otherwise.
05425  */
05426 static int has_voicemail(const char *mailbox, const char *folder)
05427 {
05428    char tmp[256], *tmp2 = tmp, *box, *context;
05429    ast_copy_string(tmp, mailbox, sizeof(tmp));
05430    if (ast_strlen_zero(folder)) {
05431       folder = "INBOX";
05432    }
05433    while ((box = strsep(&tmp2, ",&"))) {
05434       if ((context = strchr(box, '@')))
05435          *context++ = '\0';
05436       else
05437          context = "default";
05438       if (__has_voicemail(context, box, folder, 1))
05439          return 1;
05440       /* If we are checking INBOX, we should check Urgent as well */
05441       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05442          return 1;
05443       }
05444    }
05445    return 0;
05446 }
05447 
05448 
05449 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05450 {
05451    char tmp[256];
05452    char *context;
05453 
05454    /* If no mailbox, return immediately */
05455    if (ast_strlen_zero(mailbox))
05456       return 0;
05457 
05458    if (newmsgs)
05459       *newmsgs = 0;
05460    if (oldmsgs)
05461       *oldmsgs = 0;
05462    if (urgentmsgs)
05463       *urgentmsgs = 0;
05464 
05465    if (strchr(mailbox, ',')) {
05466       int tmpnew, tmpold, tmpurgent;
05467       char *mb, *cur;
05468 
05469       ast_copy_string(tmp, mailbox, sizeof(tmp));
05470       mb = tmp;
05471       while ((cur = strsep(&mb, ", "))) {
05472          if (!ast_strlen_zero(cur)) {
05473             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05474                return -1;
05475             else {
05476                if (newmsgs)
05477                   *newmsgs += tmpnew; 
05478                if (oldmsgs)
05479                   *oldmsgs += tmpold;
05480                if (urgentmsgs)
05481                   *urgentmsgs += tmpurgent;
05482             }
05483          }
05484       }
05485       return 0;
05486    }
05487 
05488    ast_copy_string(tmp, mailbox, sizeof(tmp));
05489    
05490    if ((context = strchr(tmp, '@')))
05491       *context++ = '\0';
05492    else
05493       context = "default";
05494 
05495    if (newmsgs)
05496       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05497    if (oldmsgs)
05498       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05499    if (urgentmsgs)
05500       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05501 
05502    return 0;
05503 }
05504 
05505 #endif
05506 
05507 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05508 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05509 {
05510    int urgentmsgs = 0;
05511    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05512    if (newmsgs) {
05513       *newmsgs += urgentmsgs;
05514    }
05515    return res;
05516 }
05517 
05518 static void run_externnotify(char *context, char *extension, const char *flag)
05519 {
05520    char arguments[255];
05521    char ext_context[256] = "";
05522    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05523    struct ast_smdi_mwi_message *mwi_msg;
05524 
05525    if (!ast_strlen_zero(context))
05526       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05527    else
05528       ast_copy_string(ext_context, extension, sizeof(ext_context));
05529 
05530    if (smdi_iface) {
05531       if (ast_app_has_voicemail(ext_context, NULL)) 
05532          ast_smdi_mwi_set(smdi_iface, extension);
05533       else
05534          ast_smdi_mwi_unset(smdi_iface, extension);
05535 
05536       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05537          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05538          if (!strncmp(mwi_msg->cause, "INV", 3))
05539             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05540          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05541             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05542          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05543          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05544       } else {
05545          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05546       }
05547    }
05548 
05549    if (!ast_strlen_zero(externnotify)) {
05550       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05551          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05552       } else {
05553          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05554          ast_debug(1, "Executing %s\n", arguments);
05555          ast_safe_system(arguments);
05556       }
05557    }
05558 }
05559 
05560 /*!
05561  * \brief Variables used for saving a voicemail.
05562  *
05563  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05564  */
05565 struct leave_vm_options {
05566    unsigned int flags;
05567    signed char record_gain;
05568    char *exitcontext;
05569 };
05570 
05571 /*!
05572  * \brief Prompts the user and records a voicemail to a mailbox.
05573  * \param chan
05574  * \param ext
05575  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05576  * 
05577  * 
05578  * 
05579  * \return zero on success, -1 on error.
05580  */
05581 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05582 {
05583 #ifdef IMAP_STORAGE
05584    int newmsgs, oldmsgs;
05585 #else
05586    char urgdir[PATH_MAX];
05587 #endif
05588    char txtfile[PATH_MAX];
05589    char tmptxtfile[PATH_MAX];
05590    struct vm_state *vms = NULL;
05591    char callerid[256];
05592    FILE *txt;
05593    char date[256];
05594    int txtdes;
05595    int res = 0;
05596    int msgnum;
05597    int duration = 0;
05598    int sound_duration = 0;
05599    int ausemacro = 0;
05600    int ousemacro = 0;
05601    int ouseexten = 0;
05602    char tmpdur[16];
05603    char priority[16];
05604    char origtime[16];
05605    char dir[PATH_MAX];
05606    char tmpdir[PATH_MAX];
05607    char fn[PATH_MAX];
05608    char prefile[PATH_MAX] = "";
05609    char tempfile[PATH_MAX] = "";
05610    char ext_context[256] = "";
05611    char fmt[80];
05612    char *context;
05613    char ecodes[17] = "#";
05614    struct ast_str *tmp = ast_str_create(16);
05615    char *tmpptr;
05616    struct ast_vm_user *vmu;
05617    struct ast_vm_user svm;
05618    const char *category = NULL;
05619    const char *code;
05620    const char *alldtmf = "0123456789ABCD*#";
05621    char flag[80];
05622 
05623    if (!tmp) {
05624       return -1;
05625    }
05626 
05627    ast_str_set(&tmp, 0, "%s", ext);
05628    ext = ast_str_buffer(tmp);
05629    if ((context = strchr(ext, '@'))) {
05630       *context++ = '\0';
05631       tmpptr = strchr(context, '&');
05632    } else {
05633       tmpptr = strchr(ext, '&');
05634    }
05635 
05636    if (tmpptr)
05637       *tmpptr++ = '\0';
05638 
05639    ast_channel_lock(chan);
05640    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05641       category = ast_strdupa(category);
05642    }
05643    ast_channel_unlock(chan);
05644 
05645    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05646       ast_copy_string(flag, "Urgent", sizeof(flag));
05647    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05648       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05649    } else {
05650       flag[0] = '\0';
05651    }
05652 
05653    ast_debug(3, "Before find_user\n");
05654    if (!(vmu = find_user(&svm, context, ext))) {
05655       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05656       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05657       ast_free(tmp);
05658       return res;
05659    }
05660    /* Setup pre-file if appropriate */
05661    if (strcmp(vmu->context, "default"))
05662       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05663    else
05664       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05665 
05666    /* Set the path to the prefile. Will be one of 
05667       VM_SPOOL_DIRcontext/ext/busy
05668       VM_SPOOL_DIRcontext/ext/unavail
05669       Depending on the flag set in options.
05670    */
05671    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05672       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05673    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05674       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05675    }
05676    /* Set the path to the tmpfile as
05677       VM_SPOOL_DIR/context/ext/temp
05678       and attempt to create the folder structure.
05679    */
05680    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05681    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05682       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05683       ast_free(tmp);
05684       return -1;
05685    }
05686    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05687    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05688       ast_copy_string(prefile, tempfile, sizeof(prefile));
05689 
05690    DISPOSE(tempfile, -1);
05691    /* It's easier just to try to make it than to check for its existence */
05692 #ifndef IMAP_STORAGE
05693    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05694 #else
05695    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05696    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05697       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05698    }
05699 #endif
05700 
05701    /* Check current or macro-calling context for special extensions */
05702    if (ast_test_flag(vmu, VM_OPERATOR)) {
05703       if (!ast_strlen_zero(vmu->exit)) {
05704          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05705             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05706             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05707             ouseexten = 1;
05708          }
05709       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05710          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05711          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05712          ouseexten = 1;
05713       } else if (!ast_strlen_zero(chan->macrocontext)
05714          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05715             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05716          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05717          ousemacro = 1;
05718       }
05719    }
05720 
05721    if (!ast_strlen_zero(vmu->exit)) {
05722       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05723          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05724          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05725       }
05726    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05727       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05728       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05729    } else if (!ast_strlen_zero(chan->macrocontext)
05730       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05731          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05732       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05733       ausemacro = 1;
05734    }
05735 
05736    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05737       for (code = alldtmf; *code; code++) {
05738          char e[2] = "";
05739          e[0] = *code;
05740          if (strchr(ecodes, e[0]) == NULL
05741             && ast_canmatch_extension(chan, chan->context, e, 1,
05742                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05743             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05744          }
05745       }
05746    }
05747 
05748    /* Play the beginning intro if desired */
05749    if (!ast_strlen_zero(prefile)) {
05750 #ifdef ODBC_STORAGE
05751       int success = 
05752 #endif
05753          RETRIEVE(prefile, -1, ext, context);
05754       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05755          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05756             res = ast_waitstream(chan, ecodes);
05757 #ifdef ODBC_STORAGE
05758          if (success == -1) {
05759             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05760             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05761             store_file(prefile, vmu->mailbox, vmu->context, -1);
05762          }
05763 #endif
05764       } else {
05765          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05766          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05767       }
05768       DISPOSE(prefile, -1);
05769       if (res < 0) {
05770          ast_debug(1, "Hang up during prefile playback\n");
05771          free_user(vmu);
05772          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05773          ast_free(tmp);
05774          return -1;
05775       }
05776    }
05777    if (res == '#') {
05778       /* On a '#' we skip the instructions */
05779       ast_set_flag(options, OPT_SILENT);
05780       res = 0;
05781    }
05782    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05783    if (vmu->maxmsg == 0) {
05784       if (option_debug > 2)
05785          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05786       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05787       goto leave_vm_out;
05788    }
05789    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05790       res = ast_stream_and_wait(chan, INTRO, ecodes);
05791       if (res == '#') {
05792          ast_set_flag(options, OPT_SILENT);
05793          res = 0;
05794       }
05795    }
05796    if (res > 0)
05797       ast_stopstream(chan);
05798    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05799     other than the operator -- an automated attendant or mailbox login for example */
05800    if (res == '*') {
05801       chan->exten[0] = 'a';
05802       chan->exten[1] = '\0';
05803       if (!ast_strlen_zero(vmu->exit)) {
05804          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05805       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05806          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05807       }
05808       chan->priority = 0;
05809       free_user(vmu);
05810       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05811       ast_free(tmp);
05812       return 0;
05813    }
05814 
05815    /* Check for a '0' here */
05816    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05817    transfer:
05818       if (ouseexten || ousemacro) {
05819          chan->exten[0] = 'o';
05820          chan->exten[1] = '\0';
05821          if (!ast_strlen_zero(vmu->exit)) {
05822             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05823          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05824             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05825          }
05826          ast_play_and_wait(chan, "transfer");
05827          chan->priority = 0;
05828          free_user(vmu);
05829          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05830       }
05831       ast_free(tmp);
05832       return OPERATOR_EXIT;
05833    }
05834 
05835    /* Allow all other digits to exit Voicemail and return to the dialplan */
05836    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05837       if (!ast_strlen_zero(options->exitcontext))
05838          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05839       free_user(vmu);
05840       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05841       ast_free(tmp);
05842       return res;
05843    }
05844 
05845    if (res < 0) {
05846       free_user(vmu);
05847       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05848       ast_free(tmp);
05849       return -1;
05850    }
05851    /* The meat of recording the message...  All the announcements and beeps have been played*/
05852    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05853    if (!ast_strlen_zero(fmt)) {
05854       msgnum = 0;
05855 
05856 #ifdef IMAP_STORAGE
05857       /* Is ext a mailbox? */
05858       /* must open stream for this user to get info! */
05859       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05860       if (res < 0) {
05861          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05862          ast_free(tmp);
05863          return -1;
05864       }
05865       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05866       /* It is possible under certain circumstances that inboxcount did not
05867        * create a vm_state when it was needed. This is a catchall which will
05868        * rarely be used.
05869        */
05870          if (!(vms = create_vm_state_from_user(vmu))) {
05871             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05872             ast_free(tmp);
05873             return -1;
05874          }
05875       }
05876       vms->newmessages++;
05877       
05878       /* here is a big difference! We add one to it later */
05879       msgnum = newmsgs + oldmsgs;
05880       ast_debug(3, "Messagecount set to %d\n", msgnum);
05881       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05882       /* set variable for compatibility */
05883       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05884 
05885       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05886          goto leave_vm_out;
05887       }
05888 #else
05889       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05890          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05891          if (!res)
05892             res = ast_waitstream(chan, "");
05893          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05894          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05895          inprocess_count(vmu->mailbox, vmu->context, -1);
05896          goto leave_vm_out;
05897       }
05898 
05899 #endif
05900       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05901       txtdes = mkstemp(tmptxtfile);
05902       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05903       if (txtdes < 0) {
05904          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05905          if (!res)
05906             res = ast_waitstream(chan, "");
05907          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05908          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05909          inprocess_count(vmu->mailbox, vmu->context, -1);
05910          goto leave_vm_out;
05911       }
05912 
05913       /* Now play the beep once we have the message number for our next message. */
05914       if (res >= 0) {
05915          /* Unless we're *really* silent, try to send the beep */
05916          res = ast_stream_and_wait(chan, "beep", "");
05917       }
05918             
05919       /* Store information in real-time storage */
05920       if (ast_check_realtime("voicemail_data")) {
05921          snprintf(priority, sizeof(priority), "%d", chan->priority);
05922          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05923          get_date(date, sizeof(date));
05924          ast_callerid_merge(callerid, sizeof(callerid),
05925             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05926             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05927             "Unknown");
05928          ast_store_realtime("voicemail_data",
05929             "origmailbox", ext,
05930             "context", chan->context,
05931             "macrocontext", chan->macrocontext,
05932             "exten", chan->exten,
05933             "priority", priority,
05934             "callerchan", chan->name,
05935             "callerid", callerid,
05936             "origdate", date,
05937             "origtime", origtime,
05938             "category", S_OR(category, ""),
05939             "filename", tmptxtfile,
05940             SENTINEL);
05941       }
05942 
05943       /* Store information */
05944       txt = fdopen(txtdes, "w+");
05945       if (txt) {
05946          get_date(date, sizeof(date));
05947          ast_callerid_merge(callerid, sizeof(callerid),
05948             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05949             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05950             "Unknown");
05951          fprintf(txt, 
05952             ";\n"
05953             "; Message Information file\n"
05954             ";\n"
05955             "[message]\n"
05956             "origmailbox=%s\n"
05957             "context=%s\n"
05958             "macrocontext=%s\n"
05959             "exten=%s\n"
05960             "rdnis=%s\n"
05961             "priority=%d\n"
05962             "callerchan=%s\n"
05963             "callerid=%s\n"
05964             "origdate=%s\n"
05965             "origtime=%ld\n"
05966             "category=%s\n",
05967             ext,
05968             chan->context,
05969             chan->macrocontext, 
05970             chan->exten,
05971             S_COR(chan->redirecting.from.number.valid,
05972                chan->redirecting.from.number.str, "unknown"),
05973             chan->priority,
05974             chan->name,
05975             callerid,
05976             date, (long) time(NULL),
05977             category ? category : "");
05978       } else {
05979          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05980          inprocess_count(vmu->mailbox, vmu->context, -1);
05981          if (ast_check_realtime("voicemail_data")) {
05982             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05983          }
05984          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05985          goto leave_vm_out;
05986       }
05987       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
05988 
05989       if (txt) {
05990          fprintf(txt, "flag=%s\n", flag);
05991          if (sound_duration < vmu->minsecs) {
05992             fclose(txt);
05993             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
05994             ast_filedelete(tmptxtfile, NULL);
05995             unlink(tmptxtfile);
05996             if (ast_check_realtime("voicemail_data")) {
05997                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05998             }
05999             inprocess_count(vmu->mailbox, vmu->context, -1);
06000          } else {
06001             fprintf(txt, "duration=%d\n", duration);
06002             fclose(txt);
06003             if (vm_lock_path(dir)) {
06004                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06005                /* Delete files */
06006                ast_filedelete(tmptxtfile, NULL);
06007                unlink(tmptxtfile);
06008                inprocess_count(vmu->mailbox, vmu->context, -1);
06009             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06010                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06011                unlink(tmptxtfile);
06012                ast_unlock_path(dir);
06013                inprocess_count(vmu->mailbox, vmu->context, -1);
06014                if (ast_check_realtime("voicemail_data")) {
06015                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06016                }
06017             } else {
06018 #ifndef IMAP_STORAGE
06019                msgnum = last_message_index(vmu, dir) + 1;
06020 #endif
06021                make_file(fn, sizeof(fn), dir, msgnum);
06022 
06023                /* assign a variable with the name of the voicemail file */ 
06024 #ifndef IMAP_STORAGE
06025                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06026 #else
06027                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06028 #endif
06029 
06030                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06031                ast_filerename(tmptxtfile, fn, NULL);
06032                rename(tmptxtfile, txtfile);
06033                inprocess_count(vmu->mailbox, vmu->context, -1);
06034 
06035                /* Properly set permissions on voicemail text descriptor file.
06036                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06037                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06038                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06039 
06040                ast_unlock_path(dir);
06041                if (ast_check_realtime("voicemail_data")) {
06042                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06043                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06044                }
06045                /* We must store the file first, before copying the message, because
06046                 * ODBC storage does the entire copy with SQL.
06047                 */
06048                if (ast_fileexists(fn, NULL, NULL) > 0) {
06049                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06050                }
06051 
06052                /* Are there to be more recipients of this message? */
06053                while (tmpptr) {
06054                   struct ast_vm_user recipu, *recip;
06055                   char *exten, *cntx;
06056 
06057                   exten = strsep(&tmpptr, "&");
06058                   cntx = strchr(exten, '@');
06059                   if (cntx) {
06060                      *cntx = '\0';
06061                      cntx++;
06062                   }
06063                   if ((recip = find_user(&recipu, cntx, exten))) {
06064                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06065                      free_user(recip);
06066                   }
06067                }
06068 #ifndef IMAP_STORAGE
06069                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06070                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06071                   char sfn[PATH_MAX];
06072                   char dfn[PATH_MAX];
06073                   int x;
06074                   /* It's easier just to try to make it than to check for its existence */
06075                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06076                   x = last_message_index(vmu, urgdir) + 1;
06077                   make_file(sfn, sizeof(sfn), dir, msgnum);
06078                   make_file(dfn, sizeof(dfn), urgdir, x);
06079                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06080                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06081                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06082                   ast_copy_string(fn, dfn, sizeof(fn));
06083                   msgnum = x;
06084                }
06085 #endif
06086                /* Notification needs to happen after the copy, though. */
06087                if (ast_fileexists(fn, NULL, NULL)) {
06088 #ifdef IMAP_STORAGE
06089                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06090                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06091                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06092                      flag);
06093 #else
06094                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06095                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06096                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06097                      flag);
06098 #endif
06099                }
06100 
06101                /* Disposal needs to happen after the optional move and copy */
06102                if (ast_fileexists(fn, NULL, NULL)) {
06103                   DISPOSE(dir, msgnum);
06104                }
06105             }
06106          }
06107       } else {
06108          inprocess_count(vmu->mailbox, vmu->context, -1);
06109       }
06110       if (res == '0') {
06111          goto transfer;
06112       } else if (res > 0 && res != 't')
06113          res = 0;
06114 
06115       if (sound_duration < vmu->minsecs)
06116          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06117          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06118       else
06119          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06120    } else
06121       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06122 leave_vm_out:
06123    free_user(vmu);
06124 
06125 #ifdef IMAP_STORAGE
06126    /* expunge message - use UID Expunge if supported on IMAP server*/
06127    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06128    if (expungeonhangup == 1) {
06129       ast_mutex_lock(&vms->lock);
06130 #ifdef HAVE_IMAP_TK2006
06131       if (LEVELUIDPLUS (vms->mailstream)) {
06132          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06133       } else 
06134 #endif
06135          mail_expunge(vms->mailstream);
06136       ast_mutex_unlock(&vms->lock);
06137    }
06138 #endif
06139 
06140    ast_free(tmp);
06141    return res;
06142 }
06143 
06144 #if !defined(IMAP_STORAGE)
06145 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06146 {
06147    /* we know the actual number of messages, so stop process when number is hit */
06148 
06149    int x, dest;
06150    char sfn[PATH_MAX];
06151    char dfn[PATH_MAX];
06152 
06153    if (vm_lock_path(dir)) {
06154       return ERROR_LOCK_PATH;
06155    }
06156 
06157    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06158       make_file(sfn, sizeof(sfn), dir, x);
06159       if (EXISTS(dir, x, sfn, NULL)) {
06160 
06161          if (x != dest) {
06162             make_file(dfn, sizeof(dfn), dir, dest);
06163             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06164          }
06165 
06166          dest++;
06167       }
06168    }
06169    ast_unlock_path(dir);
06170 
06171    return dest;
06172 }
06173 #endif
06174 
06175 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06176 {
06177    int d;
06178    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06179    return d;
06180 }
06181 
06182 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06183 {
06184 #ifdef IMAP_STORAGE
06185    /* we must use mbox(x) folder names, and copy the message there */
06186    /* simple. huh? */
06187    char sequence[10];
06188    char mailbox[256];
06189    int res;
06190 
06191    /* get the real IMAP message number for this message */
06192    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06193    
06194    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06195    ast_mutex_lock(&vms->lock);
06196    /* if save to Old folder, put in INBOX as read */
06197    if (box == OLD_FOLDER) {
06198       mail_setflag(vms->mailstream, sequence, "\\Seen");
06199       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06200    } else if (box == NEW_FOLDER) {
06201       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06202       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06203    }
06204    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06205       ast_mutex_unlock(&vms->lock);
06206       return 0;
06207    }
06208    /* Create the folder if it don't exist */
06209    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06210    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06211    if (mail_create(vms->mailstream, mailbox) == NIL) 
06212       ast_debug(5, "Folder exists.\n");
06213    else
06214       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06215    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06216    ast_mutex_unlock(&vms->lock);
06217    return res;
06218 #else
06219    char *dir = vms->curdir;
06220    char *username = vms->username;
06221    char *context = vmu->context;
06222    char sfn[PATH_MAX];
06223    char dfn[PATH_MAX];
06224    char ddir[PATH_MAX];
06225    const char *dbox = mbox(vmu, box);
06226    int x, i;
06227    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06228 
06229    if (vm_lock_path(ddir))
06230       return ERROR_LOCK_PATH;
06231 
06232    x = last_message_index(vmu, ddir) + 1;
06233 
06234    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06235       x--;
06236       for (i = 1; i <= x; i++) {
06237          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06238          make_file(sfn, sizeof(sfn), ddir, i);
06239          make_file(dfn, sizeof(dfn), ddir, i - 1);
06240          if (EXISTS(ddir, i, sfn, NULL)) {
06241             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06242          } else
06243             break;
06244       }
06245    } else {
06246       if (x >= vmu->maxmsg) {
06247          ast_unlock_path(ddir);
06248          return -1;
06249       }
06250    }
06251    make_file(sfn, sizeof(sfn), dir, msg);
06252    make_file(dfn, sizeof(dfn), ddir, x);
06253    if (strcmp(sfn, dfn)) {
06254       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06255    }
06256    ast_unlock_path(ddir);
06257 #endif
06258    return 0;
06259 }
06260 
06261 static int adsi_logo(unsigned char *buf)
06262 {
06263    int bytes = 0;
06264    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06265    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06266    return bytes;
06267 }
06268 
06269 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06270 {
06271    unsigned char buf[256];
06272    int bytes = 0;
06273    int x;
06274    char num[5];
06275 
06276    *useadsi = 0;
06277    bytes += ast_adsi_data_mode(buf + bytes);
06278    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06279 
06280    bytes = 0;
06281    bytes += adsi_logo(buf);
06282    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06283 #ifdef DISPLAY
06284    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06285 #endif
06286    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06287    bytes += ast_adsi_data_mode(buf + bytes);
06288    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06289 
06290    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06291       bytes = 0;
06292       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06293       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06294       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06295       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06296       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06297       return 0;
06298    }
06299 
06300 #ifdef DISPLAY
06301    /* Add a dot */
06302    bytes = 0;
06303    bytes += ast_adsi_logo(buf);
06304    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06305    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06306    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06307    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06308 #endif
06309    bytes = 0;
06310    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06311    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06312    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06313    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06314    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06315    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06316    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06317 
06318 #ifdef DISPLAY
06319    /* Add another dot */
06320    bytes = 0;
06321    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06322    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06323 
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 
06328    bytes = 0;
06329    /* These buttons we load but don't use yet */
06330    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06331    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06332    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06333    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06334    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06335    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06336    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06337 
06338 #ifdef DISPLAY
06339    /* Add another dot */
06340    bytes = 0;
06341    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
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    for (x = 0; x < 5; x++) {
06348       snprintf(num, sizeof(num), "%d", x);
06349       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06350    }
06351    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06352    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06353 
06354 #ifdef DISPLAY
06355    /* Add another dot */
06356    bytes = 0;
06357    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06358    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06359    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06360 #endif
06361 
06362    if (ast_adsi_end_download(chan)) {
06363       bytes = 0;
06364       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06365       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06366       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06367       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06368       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06369       return 0;
06370    }
06371    bytes = 0;
06372    bytes += ast_adsi_download_disconnect(buf + bytes);
06373    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06374    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06375 
06376    ast_debug(1, "Done downloading scripts...\n");
06377 
06378 #ifdef DISPLAY
06379    /* Add last dot */
06380    bytes = 0;
06381    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06382    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06383 #endif
06384    ast_debug(1, "Restarting session...\n");
06385 
06386    bytes = 0;
06387    /* Load the session now */
06388    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06389       *useadsi = 1;
06390       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06391    } else
06392       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06393 
06394    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06395    return 0;
06396 }
06397 
06398 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06399 {
06400    int x;
06401    if (!ast_adsi_available(chan))
06402       return;
06403    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06404    if (x < 0)
06405       return;
06406    if (!x) {
06407       if (adsi_load_vmail(chan, useadsi)) {
06408          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06409          return;
06410       }
06411    } else
06412       *useadsi = 1;
06413 }
06414 
06415 static void adsi_login(struct ast_channel *chan)
06416 {
06417    unsigned char buf[256];
06418    int bytes = 0;
06419    unsigned char keys[8];
06420    int x;
06421    if (!ast_adsi_available(chan))
06422       return;
06423 
06424    for (x = 0; x < 8; x++)
06425       keys[x] = 0;
06426    /* Set one key for next */
06427    keys[3] = ADSI_KEY_APPS + 3;
06428 
06429    bytes += adsi_logo(buf + bytes);
06430    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06431    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06432    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06433    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06434    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06435    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06436    bytes += ast_adsi_set_keys(buf + bytes, keys);
06437    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06438    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06439 }
06440 
06441 static void adsi_password(struct ast_channel *chan)
06442 {
06443    unsigned char buf[256];
06444    int bytes = 0;
06445    unsigned char keys[8];
06446    int x;
06447    if (!ast_adsi_available(chan))
06448       return;
06449 
06450    for (x = 0; x < 8; x++)
06451       keys[x] = 0;
06452    /* Set one key for next */
06453    keys[3] = ADSI_KEY_APPS + 3;
06454 
06455    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06456    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06457    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06458    bytes += ast_adsi_set_keys(buf + bytes, keys);
06459    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06460    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06461 }
06462 
06463 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06464 {
06465    unsigned char buf[256];
06466    int bytes = 0;
06467    unsigned char keys[8];
06468    int x, y;
06469 
06470    if (!ast_adsi_available(chan))
06471       return;
06472 
06473    for (x = 0; x < 5; x++) {
06474       y = ADSI_KEY_APPS + 12 + start + x;
06475       if (y > ADSI_KEY_APPS + 12 + 4)
06476          y = 0;
06477       keys[x] = ADSI_KEY_SKT | y;
06478    }
06479    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06480    keys[6] = 0;
06481    keys[7] = 0;
06482 
06483    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06484    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06485    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06486    bytes += ast_adsi_set_keys(buf + bytes, keys);
06487    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06488 
06489    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06490 }
06491 
06492 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06493 {
06494    int bytes = 0;
06495    unsigned char buf[256]; 
06496    char buf1[256], buf2[256];
06497    char fn2[PATH_MAX];
06498 
06499    char cid[256] = "";
06500    char *val;
06501    char *name, *num;
06502    char datetime[21] = "";
06503    FILE *f;
06504 
06505    unsigned char keys[8];
06506 
06507    int x;
06508 
06509    if (!ast_adsi_available(chan))
06510       return;
06511 
06512    /* Retrieve important info */
06513    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06514    f = fopen(fn2, "r");
06515    if (f) {
06516       while (!feof(f)) {   
06517          if (!fgets((char *) buf, sizeof(buf), f)) {
06518             continue;
06519          }
06520          if (!feof(f)) {
06521             char *stringp = NULL;
06522             stringp = (char *) buf;
06523             strsep(&stringp, "=");
06524             val = strsep(&stringp, "=");
06525             if (!ast_strlen_zero(val)) {
06526                if (!strcmp((char *) buf, "callerid"))
06527                   ast_copy_string(cid, val, sizeof(cid));
06528                if (!strcmp((char *) buf, "origdate"))
06529                   ast_copy_string(datetime, val, sizeof(datetime));
06530             }
06531          }
06532       }
06533       fclose(f);
06534    }
06535    /* New meaning for keys */
06536    for (x = 0; x < 5; x++)
06537       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06538    keys[6] = 0x0;
06539    keys[7] = 0x0;
06540 
06541    if (!vms->curmsg) {
06542       /* No prev key, provide "Folder" instead */
06543       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06544    }
06545    if (vms->curmsg >= vms->lastmsg) {
06546       /* If last message ... */
06547       if (vms->curmsg) {
06548          /* but not only message, provide "Folder" instead */
06549          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06550          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06551 
06552       } else {
06553          /* Otherwise if only message, leave blank */
06554          keys[3] = 1;
06555       }
06556    }
06557 
06558    if (!ast_strlen_zero(cid)) {
06559       ast_callerid_parse(cid, &name, &num);
06560       if (!name)
06561          name = num;
06562    } else
06563       name = "Unknown Caller";
06564 
06565    /* If deleted, show "undeleted" */
06566 
06567    if (vms->deleted[vms->curmsg])
06568       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06569 
06570    /* Except "Exit" */
06571    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06572    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06573       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06574    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06575 
06576    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06577    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06578    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06579    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06580    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06581    bytes += ast_adsi_set_keys(buf + bytes, keys);
06582    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06583 
06584    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06585 }
06586 
06587 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06588 {
06589    int bytes = 0;
06590    unsigned char buf[256];
06591    unsigned char keys[8];
06592 
06593    int x;
06594 
06595    if (!ast_adsi_available(chan))
06596       return;
06597 
06598    /* New meaning for keys */
06599    for (x = 0; x < 5; x++)
06600       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06601 
06602    keys[6] = 0x0;
06603    keys[7] = 0x0;
06604 
06605    if (!vms->curmsg) {
06606       /* No prev key, provide "Folder" instead */
06607       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06608    }
06609    if (vms->curmsg >= vms->lastmsg) {
06610       /* If last message ... */
06611       if (vms->curmsg) {
06612          /* but not only message, provide "Folder" instead */
06613          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06614       } else {
06615          /* Otherwise if only message, leave blank */
06616          keys[3] = 1;
06617       }
06618    }
06619 
06620    /* If deleted, show "undeleted" */
06621    if (vms->deleted[vms->curmsg]) 
06622       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06623 
06624    /* Except "Exit" */
06625    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06626    bytes += ast_adsi_set_keys(buf + bytes, keys);
06627    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06628 
06629    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06630 }
06631 
06632 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06633 {
06634    unsigned char buf[256] = "";
06635    char buf1[256] = "", buf2[256] = "";
06636    int bytes = 0;
06637    unsigned char keys[8];
06638    int x;
06639 
06640    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06641    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06642    if (!ast_adsi_available(chan))
06643       return;
06644    if (vms->newmessages) {
06645       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06646       if (vms->oldmessages) {
06647          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06648          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06649       } else {
06650          snprintf(buf2, sizeof(buf2), "%s.", newm);
06651       }
06652    } else if (vms->oldmessages) {
06653       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06654       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06655    } else {
06656       strcpy(buf1, "You have no messages.");
06657       buf2[0] = ' ';
06658       buf2[1] = '\0';
06659    }
06660    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06661    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06662    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06663 
06664    for (x = 0; x < 6; x++)
06665       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06666    keys[6] = 0;
06667    keys[7] = 0;
06668 
06669    /* Don't let them listen if there are none */
06670    if (vms->lastmsg < 0)
06671       keys[0] = 1;
06672    bytes += ast_adsi_set_keys(buf + bytes, keys);
06673 
06674    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06675 
06676    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06677 }
06678 
06679 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06680 {
06681    unsigned char buf[256] = "";
06682    char buf1[256] = "", buf2[256] = "";
06683    int bytes = 0;
06684    unsigned char keys[8];
06685    int x;
06686 
06687    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06688 
06689    if (!ast_adsi_available(chan))
06690       return;
06691 
06692    /* Original command keys */
06693    for (x = 0; x < 6; x++)
06694       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06695 
06696    keys[6] = 0;
06697    keys[7] = 0;
06698 
06699    if ((vms->lastmsg + 1) < 1)
06700       keys[0] = 0;
06701 
06702    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06703       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06704 
06705    if (vms->lastmsg + 1)
06706       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06707    else
06708       strcpy(buf2, "no messages.");
06709    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06710    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06711    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06712    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06713    bytes += ast_adsi_set_keys(buf + bytes, keys);
06714 
06715    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06716 
06717    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06718    
06719 }
06720 
06721 /*
06722 static void adsi_clear(struct ast_channel *chan)
06723 {
06724    char buf[256];
06725    int bytes=0;
06726    if (!ast_adsi_available(chan))
06727       return;
06728    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06729    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06730 
06731    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06732 }
06733 */
06734 
06735 static void adsi_goodbye(struct ast_channel *chan)
06736 {
06737    unsigned char buf[256];
06738    int bytes = 0;
06739 
06740    if (!ast_adsi_available(chan))
06741       return;
06742    bytes += adsi_logo(buf + bytes);
06743    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06744    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06745    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06746    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06747 
06748    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06749 }
06750 
06751 /*!\brief get_folder: Folder menu
06752  * Plays "press 1 for INBOX messages" etc.
06753  * Should possibly be internationalized
06754  */
06755 static int get_folder(struct ast_channel *chan, int start)
06756 {
06757    int x;
06758    int d;
06759    char fn[PATH_MAX];
06760    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06761    if (d)
06762       return d;
06763    for (x = start; x < 5; x++) { /* For all folders */
06764       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06765          return d;
06766       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06767       if (d)
06768          return d;
06769       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06770 
06771       /* The inbox folder can have its name changed under certain conditions
06772        * so this checks if the sound file exists for the inbox folder name and
06773        * if it doesn't, plays the default name instead. */
06774       if (x == 0) {
06775          if (ast_fileexists(fn, NULL, NULL)) {
06776             d = vm_play_folder_name(chan, fn);
06777          } else {
06778             ast_verb(1, "failed to find %s\n", fn);
06779             d = vm_play_folder_name(chan, "vm-INBOX");
06780          }
06781       } else {
06782          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06783          d = vm_play_folder_name(chan, fn);
06784       }
06785 
06786       if (d)
06787          return d;
06788       d = ast_waitfordigit(chan, 500);
06789       if (d)
06790          return d;
06791    }
06792 
06793    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06794    if (d)
06795       return d;
06796    d = ast_waitfordigit(chan, 4000);
06797    return d;
06798 }
06799 
06800 /*!
06801  * \brief plays a prompt and waits for a keypress.
06802  * \param chan
06803  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06804  * \param start Does not appear to be used at this time.
06805  *
06806  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06807  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06808  * prompting for the number inputs that correspond to the available folders.
06809  * 
06810  * \return zero on success, or -1 on error.
06811  */
06812 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06813 {
06814    int res = 0;
06815    int loops = 0;
06816 
06817    res = ast_play_and_wait(chan, fn);  /* Folder name */
06818    while (((res < '0') || (res > '9')) &&
06819          (res != '#') && (res >= 0) &&
06820          loops < 4) {
06821       res = get_folder(chan, 0);
06822       loops++;
06823    }
06824    if (loops == 4) { /* give up */
06825       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06826       return '#';
06827    }
06828    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06829    return res;
06830 }
06831 
06832 /*!
06833  * \brief presents the option to prepend to an existing message when forwarding it.
06834  * \param chan
06835  * \param vmu
06836  * \param curdir
06837  * \param curmsg
06838  * \param vm_fmts
06839  * \param context
06840  * \param record_gain
06841  * \param duration
06842  * \param vms
06843  * \param flag 
06844  *
06845  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06846  *
06847  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06848  * \return zero on success, -1 on error.
06849  */
06850 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06851          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06852 {
06853 #ifdef IMAP_STORAGE
06854    int res;
06855 #endif
06856    int cmd = 0;
06857    int retries = 0, prepend_duration = 0, already_recorded = 0;
06858    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06859    char textfile[PATH_MAX];
06860    struct ast_config *msg_cfg;
06861    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06862 #ifndef IMAP_STORAGE
06863    signed char zero_gain = 0;
06864 #endif
06865    const char *duration_str;
06866 
06867    /* Must always populate duration correctly */
06868    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06869    strcpy(textfile, msgfile);
06870    strcpy(backup, msgfile);
06871    strcpy(backup_textfile, msgfile);
06872    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06873    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06874    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06875 
06876    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06877       *duration = atoi(duration_str);
06878    } else {
06879       *duration = 0;
06880    }
06881 
06882    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06883       if (cmd)
06884          retries = 0;
06885       switch (cmd) {
06886       case '1': 
06887 
06888 #ifdef IMAP_STORAGE
06889          /* Record new intro file */
06890          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06891          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06892          res = ast_play_and_wait(chan, INTRO);
06893          res = ast_play_and_wait(chan, "beep");
06894          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06895          cmd = 't';
06896 #else
06897 
06898          /* prepend a message to the current message, update the metadata and return */
06899 
06900          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06901          strcpy(textfile, msgfile);
06902          strncat(textfile, ".txt", sizeof(textfile) - 1);
06903          *duration = 0;
06904 
06905          /* if we can't read the message metadata, stop now */
06906          if (!msg_cfg) {
06907             cmd = 0;
06908             break;
06909          }
06910          
06911          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06912 #ifndef IMAP_STORAGE
06913          if (already_recorded) {
06914             ast_filecopy(backup, msgfile, NULL);
06915             copy(backup_textfile, textfile);
06916          }
06917          else {
06918             ast_filecopy(msgfile, backup, NULL);
06919             copy(textfile, backup_textfile);
06920          }
06921 #endif
06922          already_recorded = 1;
06923 
06924          if (record_gain)
06925             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06926 
06927          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06928 
06929          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06930             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06931             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06932             ast_filerename(backup, msgfile, NULL);
06933          }
06934 
06935          if (record_gain)
06936             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06937 
06938          
06939          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06940             *duration = atoi(duration_str);
06941 
06942          if (prepend_duration) {
06943             struct ast_category *msg_cat;
06944             /* need enough space for a maximum-length message duration */
06945             char duration_buf[12];
06946 
06947             *duration += prepend_duration;
06948             msg_cat = ast_category_get(msg_cfg, "message");
06949             snprintf(duration_buf, 11, "%ld", *duration);
06950             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06951                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06952             }
06953          }
06954 
06955 #endif
06956          break;
06957       case '2': 
06958          /* NULL out introfile so we know there is no intro! */
06959 #ifdef IMAP_STORAGE
06960          *vms->introfn = '\0';
06961 #endif
06962          cmd = 't';
06963          break;
06964       case '*':
06965          cmd = '*';
06966          break;
06967       default: 
06968          /* If time_out and return to menu, reset already_recorded */
06969          already_recorded = 0;
06970 
06971          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06972             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06973          if (!cmd) {
06974             cmd = ast_play_and_wait(chan, "vm-starmain");
06975             /* "press star to return to the main menu" */
06976          }
06977          if (!cmd) {
06978             cmd = ast_waitfordigit(chan, 6000);
06979          }
06980          if (!cmd) {
06981             retries++;
06982          }
06983          if (retries > 3) {
06984             cmd = '*'; /* Let's cancel this beast */
06985          }
06986          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
06987       }
06988    }
06989 
06990    if (msg_cfg)
06991       ast_config_destroy(msg_cfg);
06992    if (prepend_duration)
06993       *duration = prepend_duration;
06994 
06995    if (already_recorded && cmd == -1) {
06996       /* restore original message if prepention cancelled */
06997       ast_filerename(backup, msgfile, NULL);
06998       rename(backup_textfile, textfile);
06999    }
07000 
07001    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07002       cmd = 0;
07003    return cmd;
07004 }
07005 
07006 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07007 {
07008    struct ast_event *event;
07009    char *mailbox, *context;
07010 
07011    /* Strip off @default */
07012    context = mailbox = ast_strdupa(box);
07013    strsep(&context, "@");
07014    if (ast_strlen_zero(context))
07015       context = "default";
07016 
07017    if (!(event = ast_event_new(AST_EVENT_MWI,
07018          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07019          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07020          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07021          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07022          AST_EVENT_IE_END))) {
07023       return;
07024    }
07025 
07026    ast_event_queue_and_cache(event);
07027 }
07028 
07029 /*!
07030  * \brief Sends email notification that a user has a new voicemail waiting for them.
07031  * \param chan
07032  * \param vmu
07033  * \param vms
07034  * \param msgnum
07035  * \param duration
07036  * \param fmt
07037  * \param cidnum The Caller ID phone number value.
07038  * \param cidname The Caller ID name value.
07039  * \param flag
07040  *
07041  * \return zero on success, -1 on error.
07042  */
07043 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)
07044 {
07045    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07046    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07047    const char *category;
07048    char *myserveremail = serveremail;
07049 
07050    ast_channel_lock(chan);
07051    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07052       category = ast_strdupa(category);
07053    }
07054    ast_channel_unlock(chan);
07055 
07056 #ifndef IMAP_STORAGE
07057    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07058 #else
07059    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07060 #endif
07061    make_file(fn, sizeof(fn), todir, msgnum);
07062    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07063 
07064    if (!ast_strlen_zero(vmu->attachfmt)) {
07065       if (strstr(fmt, vmu->attachfmt))
07066          fmt = vmu->attachfmt;
07067       else
07068          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);
07069    }
07070 
07071    /* Attach only the first format */
07072    fmt = ast_strdupa(fmt);
07073    stringp = fmt;
07074    strsep(&stringp, "|");
07075 
07076    if (!ast_strlen_zero(vmu->serveremail))
07077       myserveremail = vmu->serveremail;
07078 
07079    if (!ast_strlen_zero(vmu->email)) {
07080       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07081 
07082       if (attach_user_voicemail)
07083          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07084 
07085       /* XXX possible imap issue, should category be NULL XXX */
07086       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07087 
07088       if (attach_user_voicemail)
07089          DISPOSE(todir, msgnum);
07090    }
07091 
07092    if (!ast_strlen_zero(vmu->pager)) {
07093       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07094    }
07095 
07096    if (ast_test_flag(vmu, VM_DELETE))
07097       DELETE(todir, msgnum, fn, vmu);
07098 
07099    /* Leave voicemail for someone */
07100    if (ast_app_has_voicemail(ext_context, NULL)) 
07101       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07102 
07103    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07104 
07105    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);
07106    run_externnotify(vmu->context, vmu->mailbox, flag);
07107 
07108 #ifdef IMAP_STORAGE
07109    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07110    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07111       vm_imap_delete(NULL, vms->curmsg, vmu);
07112       vms->newmessages--;  /* Fix new message count */
07113    }
07114 #endif
07115 
07116    return 0;
07117 }
07118 
07119 /*!
07120  * \brief Sends a voicemail message to a mailbox recipient.
07121  * \param chan
07122  * \param context
07123  * \param vms
07124  * \param sender
07125  * \param fmt
07126  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07127  *             Will be 0 when called to forward an existing message (option 8)
07128  *             Will be 1 when called to leave a message (option 3->5)
07129  * \param record_gain 
07130  * \param urgent
07131  *
07132  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07133  * 
07134  * When in the leave message mode (is_new_message == 1):
07135  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07136  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07137  *
07138  * When in the forward message mode (is_new_message == 0):
07139  *   - retreives the current message to be forwarded
07140  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07141  *   - determines the target mailbox and folders
07142  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07143  *
07144  * \return zero on success, -1 on error.
07145  */
07146 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)
07147 {
07148 #ifdef IMAP_STORAGE
07149    int todircount = 0;
07150    struct vm_state *dstvms;
07151 #endif
07152    char username[70]="";
07153    char fn[PATH_MAX]; /* for playback of name greeting */
07154    char ecodes[16] = "#";
07155    int res = 0, cmd = 0;
07156    struct ast_vm_user *receiver = NULL, *vmtmp;
07157    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07158    char *stringp;
07159    const char *s;
07160    int saved_messages = 0;
07161    int valid_extensions = 0;
07162    char *dir;
07163    int curmsg;
07164    char urgent_str[7] = "";
07165    int prompt_played = 0;
07166 #ifndef IMAP_STORAGE
07167    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07168 #endif
07169    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07170       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07171    }
07172 
07173    if (vms == NULL) return -1;
07174    dir = vms->curdir;
07175    curmsg = vms->curmsg;
07176 
07177    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07178    while (!res && !valid_extensions) {
07179       int use_directory = 0;
07180       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07181          int done = 0;
07182          int retries = 0;
07183          cmd = 0;
07184          while ((cmd >= 0) && !done ){
07185             if (cmd)
07186                retries = 0;
07187             switch (cmd) {
07188             case '1': 
07189                use_directory = 0;
07190                done = 1;
07191                break;
07192             case '2': 
07193                use_directory = 1;
07194                done = 1;
07195                break;
07196             case '*': 
07197                cmd = 't';
07198                done = 1;
07199                break;
07200             default: 
07201                /* Press 1 to enter an extension press 2 to use the directory */
07202                cmd = ast_play_and_wait(chan, "vm-forward");
07203                if (!cmd) {
07204                   cmd = ast_waitfordigit(chan, 3000);
07205                }
07206                if (!cmd) {
07207                   retries++;
07208                }
07209                if (retries > 3) {
07210                   cmd = 't';
07211                   done = 1;
07212                }
07213                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07214             }
07215          }
07216          if (cmd < 0 || cmd == 't')
07217             break;
07218       }
07219       
07220       if (use_directory) {
07221          /* use app_directory */
07222          
07223          char old_context[sizeof(chan->context)];
07224          char old_exten[sizeof(chan->exten)];
07225          int old_priority;
07226          struct ast_app* directory_app;
07227 
07228          directory_app = pbx_findapp("Directory");
07229          if (directory_app) {
07230             char vmcontext[256];
07231             /* make backup copies */
07232             memcpy(old_context, chan->context, sizeof(chan->context));
07233             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07234             old_priority = chan->priority;
07235             
07236             /* call the the Directory, changes the channel */
07237             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07238             res = pbx_exec(chan, directory_app, vmcontext);
07239             
07240             ast_copy_string(username, chan->exten, sizeof(username));
07241             
07242             /* restore the old context, exten, and priority */
07243             memcpy(chan->context, old_context, sizeof(chan->context));
07244             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07245             chan->priority = old_priority;
07246          } else {
07247             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07248             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07249          }
07250       } else {
07251          /* Ask for an extension */
07252          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07253          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07254          prompt_played++;
07255          if (res || prompt_played > 4)
07256             break;
07257          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07258             break;
07259       }
07260       
07261       /* start all over if no username */
07262       if (ast_strlen_zero(username))
07263          continue;
07264       stringp = username;
07265       s = strsep(&stringp, "*");
07266       /* start optimistic */
07267       valid_extensions = 1;
07268       while (s) {
07269          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07270             int oldmsgs;
07271             int newmsgs;
07272             int capacity;
07273             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07274                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07275                /* Shouldn't happen, but allow trying another extension if it does */
07276                res = ast_play_and_wait(chan, "pbx-invalid");
07277                valid_extensions = 0;
07278                break;
07279             }
07280             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07281             if ((newmsgs + oldmsgs) >= capacity) {
07282                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07283                res = ast_play_and_wait(chan, "vm-mailboxfull");
07284                valid_extensions = 0;
07285                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07286                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07287                   free_user(vmtmp);
07288                }
07289                inprocess_count(receiver->mailbox, receiver->context, -1);
07290                break;
07291             }
07292             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07293          } else {
07294             /* XXX Optimization for the future.  When we encounter a single bad extension,
07295              * bailing out on all of the extensions may not be the way to go.  We should
07296              * probably just bail on that single extension, then allow the user to enter
07297              * several more. XXX
07298              */
07299             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07300                free_user(receiver);
07301             }
07302             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07303             /* "I am sorry, that's not a valid extension.  Please try again." */
07304             res = ast_play_and_wait(chan, "pbx-invalid");
07305             valid_extensions = 0;
07306             break;
07307          }
07308 
07309          /* play name if available, else play extension number */
07310          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07311          RETRIEVE(fn, -1, s, receiver->context);
07312          if (ast_fileexists(fn, NULL, NULL) > 0) {
07313             res = ast_stream_and_wait(chan, fn, ecodes);
07314             if (res) {
07315                DISPOSE(fn, -1);
07316                return res;
07317             }
07318          } else {
07319             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07320          }
07321          DISPOSE(fn, -1);
07322 
07323          s = strsep(&stringp, "*");
07324       }
07325       /* break from the loop of reading the extensions */
07326       if (valid_extensions)
07327          break;
07328    }
07329    /* check if we're clear to proceed */
07330    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07331       return res;
07332    if (is_new_message == 1) {
07333       struct leave_vm_options leave_options;
07334       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07335       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07336 
07337       /* Send VoiceMail */
07338       memset(&leave_options, 0, sizeof(leave_options));
07339       leave_options.record_gain = record_gain;
07340       cmd = leave_voicemail(chan, mailbox, &leave_options);
07341    } else {
07342       /* Forward VoiceMail */
07343       long duration = 0;
07344       struct vm_state vmstmp;
07345       int copy_msg_result = 0;
07346       memcpy(&vmstmp, vms, sizeof(vmstmp));
07347 
07348       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07349 
07350       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07351       if (!cmd) {
07352          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07353 #ifdef IMAP_STORAGE
07354             int attach_user_voicemail;
07355             char *myserveremail = serveremail;
07356             
07357             /* get destination mailbox */
07358             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07359             if (!dstvms) {
07360                dstvms = create_vm_state_from_user(vmtmp);
07361             }
07362             if (dstvms) {
07363                init_mailstream(dstvms, 0);
07364                if (!dstvms->mailstream) {
07365                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07366                } else {
07367                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07368                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07369                }
07370             } else {
07371                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07372             }
07373             if (!ast_strlen_zero(vmtmp->serveremail))
07374                myserveremail = vmtmp->serveremail;
07375             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07376             /* NULL category for IMAP storage */
07377             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07378                dstvms->curbox,
07379                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07380                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07381                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07382                NULL, urgent_str);
07383 #else
07384             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07385 #endif
07386             saved_messages++;
07387             AST_LIST_REMOVE_CURRENT(list);
07388             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07389             free_user(vmtmp);
07390             if (res)
07391                break;
07392          }
07393          AST_LIST_TRAVERSE_SAFE_END;
07394          if (saved_messages > 0 && !copy_msg_result) {
07395             /* give confirmation that the message was saved */
07396             /* commented out since we can't forward batches yet
07397             if (saved_messages == 1)
07398                res = ast_play_and_wait(chan, "vm-message");
07399             else
07400                res = ast_play_and_wait(chan, "vm-messages");
07401             if (!res)
07402                res = ast_play_and_wait(chan, "vm-saved"); */
07403 #ifdef IMAP_STORAGE
07404             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07405             if (ast_strlen_zero(vmstmp.introfn))
07406 #endif
07407             res = ast_play_and_wait(chan, "vm-msgsaved");
07408          }
07409 #ifndef IMAP_STORAGE
07410          else {
07411             /* with IMAP, mailbox full warning played by imap_check_limits */
07412             res = ast_play_and_wait(chan, "vm-mailboxfull");
07413          }
07414          /* Restore original message without prepended message if backup exists */
07415          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07416          strcpy(textfile, msgfile);
07417          strcpy(backup, msgfile);
07418          strcpy(backup_textfile, msgfile);
07419          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07420          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07421          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07422          if (ast_fileexists(backup, NULL, NULL) > 0) {
07423             ast_filerename(backup, msgfile, NULL);
07424             rename(backup_textfile, textfile);
07425          }
07426 #endif
07427       }
07428       DISPOSE(dir, curmsg);
07429 #ifndef IMAP_STORAGE
07430       if (cmd) { /* assuming hangup, cleanup backup file */
07431          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07432          strcpy(textfile, msgfile);
07433          strcpy(backup_textfile, msgfile);
07434          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07435          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07436          rename(backup_textfile, textfile);
07437       }
07438 #endif
07439    }
07440 
07441    /* If anything failed above, we still have this list to free */
07442    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07443       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07444       free_user(vmtmp);
07445    }
07446    return res ? res : cmd;
07447 }
07448 
07449 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07450 {
07451    int res;
07452    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07453       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07454    return res;
07455 }
07456 
07457 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07458 {
07459    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07460    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);
07461 }
07462 
07463 static int play_message_category(struct ast_channel *chan, const char *category)
07464 {
07465    int res = 0;
07466 
07467    if (!ast_strlen_zero(category))
07468       res = ast_play_and_wait(chan, category);
07469 
07470    if (res) {
07471       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07472       res = 0;
07473    }
07474 
07475    return res;
07476 }
07477 
07478 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07479 {
07480    int res = 0;
07481    struct vm_zone *the_zone = NULL;
07482    time_t t;
07483 
07484    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07485       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07486       return 0;
07487    }
07488 
07489    /* Does this user have a timezone specified? */
07490    if (!ast_strlen_zero(vmu->zonetag)) {
07491       /* Find the zone in the list */
07492       struct vm_zone *z;
07493       AST_LIST_LOCK(&zones);
07494       AST_LIST_TRAVERSE(&zones, z, list) {
07495          if (!strcmp(z->name, vmu->zonetag)) {
07496             the_zone = z;
07497             break;
07498          }
07499       }
07500       AST_LIST_UNLOCK(&zones);
07501    }
07502 
07503 /* No internal variable parsing for now, so we'll comment it out for the time being */
07504 #if 0
07505    /* Set the DIFF_* variables */
07506    ast_localtime(&t, &time_now, NULL);
07507    tv_now = ast_tvnow();
07508    ast_localtime(&tv_now, &time_then, NULL);
07509 
07510    /* Day difference */
07511    if (time_now.tm_year == time_then.tm_year)
07512       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07513    else
07514       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07515    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07516 
07517    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07518 #endif
07519    if (the_zone) {
07520       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07521    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07522       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07523    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07524       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07525    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07526       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);
07527    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07528       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07529    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07530       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07531    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07532       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07533    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07534       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);
07535    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07536       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07537    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07538       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07539    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07540       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);
07541    } else {
07542       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07543    }
07544 #if 0
07545    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07546 #endif
07547    return res;
07548 }
07549 
07550 
07551 
07552 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07553 {
07554    int res = 0;
07555    int i;
07556    char *callerid, *name;
07557    char prefile[PATH_MAX] = "";
07558    
07559 
07560    /* If voicemail cid is not enabled, or we didn't get cid or context from
07561     * the attribute file, leave now.
07562     *
07563     * TODO Still need to change this so that if this function is called by the
07564     * message envelope (and someone is explicitly requesting to hear the CID),
07565     * it does not check to see if CID is enabled in the config file.
07566     */
07567    if ((cid == NULL)||(context == NULL))
07568       return res;
07569 
07570    /* Strip off caller ID number from name */
07571    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07572    ast_callerid_parse(cid, &name, &callerid);
07573    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07574       /* Check for internal contexts and only */
07575       /* say extension when the call didn't come from an internal context in the list */
07576       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07577          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07578          if ((strcmp(cidinternalcontexts[i], context) == 0))
07579             break;
07580       }
07581       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07582          if (!res) {
07583             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07584             if (!ast_strlen_zero(prefile)) {
07585             /* See if we can find a recorded name for this person instead of their extension number */
07586                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07587                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07588                   if (!callback)
07589                      res = wait_file2(chan, vms, "vm-from");
07590                   res = ast_stream_and_wait(chan, prefile, "");
07591                } else {
07592                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07593                   /* Say "from extension" as one saying to sound smoother */
07594                   if (!callback)
07595                      res = wait_file2(chan, vms, "vm-from-extension");
07596                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07597                }
07598             }
07599          }
07600       } else if (!res) {
07601          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07602          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07603          if (!callback)
07604             res = wait_file2(chan, vms, "vm-from-phonenumber");
07605          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07606       }
07607    } else {
07608       /* Number unknown */
07609       ast_debug(1, "VM-CID: From an unknown number\n");
07610       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07611       res = wait_file2(chan, vms, "vm-unknown-caller");
07612    }
07613    return res;
07614 }
07615 
07616 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07617 {
07618    int res = 0;
07619    int durationm;
07620    int durations;
07621    /* Verify that we have a duration for the message */
07622    if (duration == NULL)
07623       return res;
07624 
07625    /* Convert from seconds to minutes */
07626    durations = atoi(duration);
07627    durationm = (durations / 60);
07628 
07629    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07630 
07631    if ((!res) && (durationm >= minduration)) {
07632       res = wait_file2(chan, vms, "vm-duration");
07633 
07634       /* POLISH syntax */
07635       if (!strncasecmp(chan->language, "pl", 2)) {
07636          div_t num = div(durationm, 10);
07637 
07638          if (durationm == 1) {
07639             res = ast_play_and_wait(chan, "digits/1z");
07640             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07641          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07642             if (num.rem == 2) {
07643                if (!num.quot) {
07644                   res = ast_play_and_wait(chan, "digits/2-ie");
07645                } else {
07646                   res = say_and_wait(chan, durationm - 2 , chan->language);
07647                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07648                }
07649             } else {
07650                res = say_and_wait(chan, durationm, chan->language);
07651             }
07652             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07653          } else {
07654             res = say_and_wait(chan, durationm, chan->language);
07655             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07656          }
07657       /* DEFAULT syntax */
07658       } else {
07659          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07660          res = wait_file2(chan, vms, "vm-minutes");
07661       }
07662    }
07663    return res;
07664 }
07665 
07666 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07667 {
07668    int res = 0;
07669    char filename[256], *cid;
07670    const char *origtime, *context, *category, *duration, *flag;
07671    struct ast_config *msg_cfg;
07672    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07673 
07674    vms->starting = 0;
07675    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07676    adsi_message(chan, vms);
07677    if (!vms->curmsg) {
07678       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07679    } else if (vms->curmsg == vms->lastmsg) {
07680       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07681    }
07682 
07683    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07684    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07685    msg_cfg = ast_config_load(filename, config_flags);
07686    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07687       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07688       return 0;
07689    }
07690    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07691 
07692    /* Play the word urgent if we are listening to urgent messages */
07693    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07694       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07695    }
07696 
07697    if (!res) {
07698       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07699       /* POLISH syntax */
07700       if (!strncasecmp(chan->language, "pl", 2)) {
07701          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07702             int ten, one;
07703             char nextmsg[256];
07704             ten = (vms->curmsg + 1) / 10;
07705             one = (vms->curmsg + 1) % 10;
07706 
07707             if (vms->curmsg < 20) {
07708                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07709                res = wait_file2(chan, vms, nextmsg);
07710             } else {
07711                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07712                res = wait_file2(chan, vms, nextmsg);
07713                if (one > 0) {
07714                   if (!res) {
07715                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07716                      res = wait_file2(chan, vms, nextmsg);
07717                   }
07718                }
07719             }
07720          }
07721          if (!res)
07722             res = wait_file2(chan, vms, "vm-message");
07723       /* HEBREW syntax */
07724       } else if (!strncasecmp(chan->language, "he", 2)) {
07725          if (!vms->curmsg) {
07726             res = wait_file2(chan, vms, "vm-message");
07727             res = wait_file2(chan, vms, "vm-first");
07728          } else if (vms->curmsg == vms->lastmsg) {
07729             res = wait_file2(chan, vms, "vm-message");
07730             res = wait_file2(chan, vms, "vm-last");
07731          } else {
07732             res = wait_file2(chan, vms, "vm-message");
07733             res = wait_file2(chan, vms, "vm-number");
07734             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07735          }
07736       /* VIETNAMESE syntax */
07737       } else if (!strncasecmp(chan->language, "vi", 2)) {
07738          if (!vms->curmsg) {
07739             res = wait_file2(chan, vms, "vm-message");
07740             res = wait_file2(chan, vms, "vm-first");
07741          } else if (vms->curmsg == vms->lastmsg) {
07742             res = wait_file2(chan, vms, "vm-message");
07743             res = wait_file2(chan, vms, "vm-last");
07744          } else {
07745             res = wait_file2(chan, vms, "vm-message");
07746             res = wait_file2(chan, vms, "vm-number");
07747             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07748          }
07749       } else {
07750          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07751             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07752          } else { /* DEFAULT syntax */
07753             res = wait_file2(chan, vms, "vm-message");
07754          }
07755          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07756             if (!res) {
07757                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07758                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07759             }
07760          }
07761       }
07762    }
07763 
07764    if (!msg_cfg) {
07765       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07766       return 0;
07767    }
07768 
07769    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07770       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07771       DISPOSE(vms->curdir, vms->curmsg);
07772       ast_config_destroy(msg_cfg);
07773       return 0;
07774    }
07775 
07776    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07777    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07778    category = ast_variable_retrieve(msg_cfg, "message", "category");
07779 
07780    context = ast_variable_retrieve(msg_cfg, "message", "context");
07781    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07782       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07783    if (!res) {
07784       res = play_message_category(chan, category);
07785    }
07786    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07787       res = play_message_datetime(chan, vmu, origtime, filename);
07788    }
07789    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07790       res = play_message_callerid(chan, vms, cid, context, 0);
07791    }
07792    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07793       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07794    }
07795    /* Allow pressing '1' to skip envelope / callerid */
07796    if (res == '1') {
07797       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07798       res = 0;
07799    }
07800    ast_config_destroy(msg_cfg);
07801 
07802    if (!res) {
07803       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07804       vms->heard[vms->curmsg] = 1;
07805 #ifdef IMAP_STORAGE
07806       /*IMAP storage stores any prepended message from a forward
07807        * as a separate file from the rest of the message
07808        */
07809       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07810          wait_file(chan, vms, vms->introfn);
07811       }
07812 #endif
07813       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07814          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07815          res = 0;
07816       }
07817       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07818    }
07819    DISPOSE(vms->curdir, vms->curmsg);
07820    return res;
07821 }
07822 
07823 #ifdef IMAP_STORAGE
07824 static int imap_remove_file(char *dir, int msgnum)
07825 {
07826    char fn[PATH_MAX];
07827    char full_fn[PATH_MAX];
07828    char intro[PATH_MAX] = {0,};
07829    
07830    if (msgnum > -1) {
07831       make_file(fn, sizeof(fn), dir, msgnum);
07832       snprintf(intro, sizeof(intro), "%sintro", fn);
07833    } else
07834       ast_copy_string(fn, dir, sizeof(fn));
07835    
07836    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07837       ast_filedelete(fn, NULL);
07838       if (!ast_strlen_zero(intro)) {
07839          ast_filedelete(intro, NULL);
07840       }
07841       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07842       unlink(full_fn);
07843    }
07844    return 0;
07845 }
07846 
07847 
07848 
07849 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07850 {
07851    char *file, *filename;
07852    char *attachment;
07853    char arg[10];
07854    int i;
07855    BODY* body;
07856 
07857    file = strrchr(ast_strdupa(dir), '/');
07858    if (file) {
07859       *file++ = '\0';
07860    } else {
07861       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07862       return -1;
07863    }
07864 
07865    ast_mutex_lock(&vms->lock);
07866    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07867       mail_fetchstructure(vms->mailstream, i + 1, &body);
07868       /* We have the body, now we extract the file name of the first attachment. */
07869       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07870          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07871       } else {
07872          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07873          ast_mutex_unlock(&vms->lock);
07874          return -1;
07875       }
07876       filename = strsep(&attachment, ".");
07877       if (!strcmp(filename, file)) {
07878          sprintf(arg, "%d", i + 1);
07879          mail_setflag(vms->mailstream, arg, "\\DELETED");
07880       }
07881    }
07882    mail_expunge(vms->mailstream);
07883    ast_mutex_unlock(&vms->lock);
07884    return 0;
07885 }
07886 
07887 #elif !defined(IMAP_STORAGE)
07888 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07889 {
07890    int count_msg, last_msg;
07891 
07892    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07893 
07894    /* Rename the member vmbox HERE so that we don't try to return before
07895     * we know what's going on.
07896     */
07897    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07898 
07899    /* Faster to make the directory than to check if it exists. */
07900    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07901 
07902    /* traverses directory using readdir (or select query for ODBC) */
07903    count_msg = count_messages(vmu, vms->curdir);
07904    if (count_msg < 0) {
07905       return count_msg;
07906    } else {
07907       vms->lastmsg = count_msg - 1;
07908    }
07909 
07910    if (vm_allocate_dh(vms, vmu, count_msg)) {
07911       return -1;
07912    }
07913 
07914    /*
07915    The following test is needed in case sequencing gets messed up.
07916    There appears to be more than one way to mess up sequence, so
07917    we will not try to find all of the root causes--just fix it when
07918    detected.
07919    */
07920 
07921    if (vm_lock_path(vms->curdir)) {
07922       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07923       return ERROR_LOCK_PATH;
07924    }
07925 
07926    /* for local storage, checks directory for messages up to maxmsg limit */
07927    last_msg = last_message_index(vmu, vms->curdir);
07928    ast_unlock_path(vms->curdir);
07929 
07930    if (last_msg < -1) {
07931       return last_msg;
07932    } else if (vms->lastmsg != last_msg) {
07933       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);
07934       resequence_mailbox(vmu, vms->curdir, count_msg);
07935    }
07936 
07937    return 0;
07938 }
07939 #endif
07940 
07941 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07942 {
07943    int x = 0;
07944 
07945 #ifndef IMAP_STORAGE
07946    int last_msg_idx;
07947    int res = 0, nummsg;
07948    char fn2[PATH_MAX];
07949 #endif
07950 
07951    if (vms->lastmsg <= -1) {
07952       goto done;
07953    }
07954 
07955    vms->curmsg = -1;
07956 #ifndef IMAP_STORAGE
07957    /* Get the deleted messages fixed */
07958    if (vm_lock_path(vms->curdir)) {
07959       return ERROR_LOCK_PATH;
07960    }
07961 
07962    /* update count as message may have arrived while we've got mailbox open */
07963    last_msg_idx = last_message_index(vmu, vms->curdir);
07964    if (last_msg_idx != vms->lastmsg) {
07965       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07966    }
07967 
07968    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07969    for (x = 0; x < last_msg_idx + 1; x++) {
07970       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07971          /* Save this message.  It's not in INBOX or hasn't been heard */
07972          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07973          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07974             break;
07975          }
07976          vms->curmsg++;
07977          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07978          if (strcmp(vms->fn, fn2)) {
07979             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07980          }
07981       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07982          /* Move to old folder before deleting */
07983          res = save_to_folder(vmu, vms, x, 1);
07984          if (res == ERROR_LOCK_PATH) {
07985             /* If save failed do not delete the message */
07986             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07987             vms->deleted[x] = 0;
07988             vms->heard[x] = 0;
07989             --x;
07990          }
07991       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07992          /* Move to deleted folder */
07993          res = save_to_folder(vmu, vms, x, 10);
07994          if (res == ERROR_LOCK_PATH) {
07995             /* If save failed do not delete the message */
07996             vms->deleted[x] = 0;
07997             vms->heard[x] = 0;
07998             --x;
07999          }
08000       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08001          /* If realtime storage enabled - we should explicitly delete this message,
08002          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08003          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08004          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08005             DELETE(vms->curdir, x, vms->fn, vmu);
08006          }
08007       }
08008    }
08009 
08010    /* Delete ALL remaining messages */
08011    nummsg = x - 1;
08012    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08013       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08014       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08015          DELETE(vms->curdir, x, vms->fn, vmu);
08016       }
08017    }
08018    ast_unlock_path(vms->curdir);
08019 #else /* defined(IMAP_STORAGE) */
08020    if (vms->deleted) {
08021       /* Since we now expunge after each delete, deleting in reverse order
08022        * ensures that no reordering occurs between each step. */
08023       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
08024          if (vms->deleted[x]) {
08025             ast_debug(3, "IMAP delete of %d\n", x);
08026             DELETE(vms->curdir, x, vms->fn, vmu);
08027          }
08028       }
08029    }
08030 #endif
08031 
08032 done:
08033    if (vms->deleted && vmu->maxmsg) {
08034       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
08035    }
08036    if (vms->heard && vmu->maxmsg) {
08037       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
08038    }
08039 
08040    return 0;
08041 }
08042 
08043 /* In Greek even though we CAN use a syntax like "friends messages"
08044  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08045  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08046  * syntax for the above three categories which is more elegant.
08047  */
08048 
08049 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08050 {
08051    int cmd;
08052    char *buf;
08053 
08054    buf = alloca(strlen(box) + 2);
08055    strcpy(buf, box);
08056    strcat(buf, "s");
08057 
08058    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08059       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08060       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08061    } else {
08062       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08063       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08064    }
08065 }
08066 
08067 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08068 {
08069    int cmd;
08070 
08071    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08072       if (!strcasecmp(box, "vm-INBOX"))
08073          cmd = ast_play_and_wait(chan, "vm-new-e");
08074       else
08075          cmd = ast_play_and_wait(chan, "vm-old-e");
08076       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08077    } else {
08078       cmd = ast_play_and_wait(chan, "vm-messages");
08079       return cmd ? cmd : ast_play_and_wait(chan, box);
08080    }
08081 }
08082 
08083 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08084 {
08085    int cmd;
08086 
08087    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08088       cmd = ast_play_and_wait(chan, "vm-messages");
08089       return cmd ? cmd : ast_play_and_wait(chan, box);
08090    } else {
08091       cmd = ast_play_and_wait(chan, box);
08092       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08093    }
08094 }
08095 
08096 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08097 {
08098    int cmd;
08099 
08100    if (  !strncasecmp(chan->language, "it", 2) ||
08101         !strncasecmp(chan->language, "es", 2) ||
08102         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08103       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08104       return cmd ? cmd : ast_play_and_wait(chan, box);
08105    } else if (!strncasecmp(chan->language, "gr", 2)) {
08106       return vm_play_folder_name_gr(chan, box);
08107    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08108       return ast_play_and_wait(chan, box);
08109    } else if (!strncasecmp(chan->language, "pl", 2)) {
08110       return vm_play_folder_name_pl(chan, box);
08111    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08112       return vm_play_folder_name_ua(chan, box);
08113    } else if (!strncasecmp(chan->language, "vi", 2)) {
08114       return ast_play_and_wait(chan, box);
08115    } else {  /* Default English */
08116       cmd = ast_play_and_wait(chan, box);
08117       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08118    }
08119 }
08120 
08121 /* GREEK SYNTAX
08122    In greek the plural for old/new is
08123    different so we need the following files
08124    We also need vm-denExeteMynhmata because
08125    this syntax is different.
08126 
08127    -> vm-Olds.wav : "Palia"
08128    -> vm-INBOXs.wav : "Nea"
08129    -> vm-denExeteMynhmata : "den exete mynhmata"
08130 */
08131 
08132 
08133 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08134 {
08135    int res = 0;
08136 
08137    if (vms->newmessages) {
08138       res = ast_play_and_wait(chan, "vm-youhave");
08139       if (!res) 
08140          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08141       if (!res) {
08142          if ((vms->newmessages == 1)) {
08143             res = ast_play_and_wait(chan, "vm-INBOX");
08144             if (!res)
08145                res = ast_play_and_wait(chan, "vm-message");
08146          } else {
08147             res = ast_play_and_wait(chan, "vm-INBOXs");
08148             if (!res)
08149                res = ast_play_and_wait(chan, "vm-messages");
08150          }
08151       }
08152    } else if (vms->oldmessages){
08153       res = ast_play_and_wait(chan, "vm-youhave");
08154       if (!res)
08155          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08156       if ((vms->oldmessages == 1)){
08157          res = ast_play_and_wait(chan, "vm-Old");
08158          if (!res)
08159             res = ast_play_and_wait(chan, "vm-message");
08160       } else {
08161          res = ast_play_and_wait(chan, "vm-Olds");
08162          if (!res)
08163             res = ast_play_and_wait(chan, "vm-messages");
08164       }
08165    } else if (!vms->oldmessages && !vms->newmessages) 
08166       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08167    return res;
08168 }
08169 
08170 /* Version of vm_intro() designed to work for many languages.
08171  *
08172  * It is hoped that this function can prevent the proliferation of 
08173  * language-specific vm_intro() functions and in time replace the language-
08174  * specific functions which already exist.  An examination of the language-
08175  * specific functions revealed that they all corrected the same deficiencies
08176  * in vm_intro_en() (which was the default function). Namely:
08177  *
08178  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08179  *     wording of the voicemail greeting hides this problem.  For example,
08180  *     vm-INBOX contains only the word "new".  This means that both of these
08181  *     sequences produce valid utterances:
08182  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08183  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08184  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08185  *     in many languages) the first utterance becomes "you have 1 the new message".
08186  *  2) The function contains hardcoded rules for pluralizing the word "message".
08187  *     These rules are correct for English, but not for many other languages.
08188  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08189  *     required in many languages.
08190  *  4) The gender of the word for "message" is not specified. This is a problem
08191  *     because in many languages the gender of the number in phrases such
08192  *     as "you have one new message" must match the gender of the word
08193  *     meaning "message".
08194  *
08195  * Fixing these problems for each new language has meant duplication of effort.
08196  * This new function solves the problems in the following general ways:
08197  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08198  *     and vm-Old respectively for those languages where it makes sense.
08199  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08200  *     on vm-message.
08201  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08202  *     prefix on vm-new and vm-old (none for English).
08203  *  4) Pass the gender of the language's word for "message" as an agument to
08204  *     this function which is can in turn pass on to the functions which 
08205  *     say numbers and put endings on nounds and adjectives.
08206  *
08207  * All languages require these messages:
08208  *  vm-youhave    "You have..."
08209  *  vm-and     "and"
08210  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08211  *
08212  * To use it for English, you will need these additional sound files:
08213  *  vm-new     "new"
08214  *  vm-message    "message", singular
08215  *  vm-messages      "messages", plural
08216  *
08217  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08218  *
08219  *  vm-newn    "novoye" (singular, neuter)
08220  *  vm-newx    "novikh" (counting plural form, genative plural)
08221  *  vm-message    "sobsheniye" (singular form)
08222  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08223  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08224  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08225  *  digits/2n     "dva" (neuter singular)
08226  */
08227 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08228 {
08229    int res;
08230    int lastnum = 0;
08231 
08232    res = ast_play_and_wait(chan, "vm-youhave");
08233 
08234    if (!res && vms->newmessages) {
08235       lastnum = vms->newmessages;
08236 
08237       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08238          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08239       }
08240 
08241       if (!res && vms->oldmessages) {
08242          res = ast_play_and_wait(chan, "vm-and");
08243       }
08244    }
08245 
08246    if (!res && vms->oldmessages) {
08247       lastnum = vms->oldmessages;
08248 
08249       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08250          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08251       }
08252    }
08253 
08254    if (!res) {
08255       if (lastnum == 0) {
08256          res = ast_play_and_wait(chan, "vm-no");
08257       }
08258       if (!res) {
08259          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08260       }
08261    }
08262 
08263    return res;
08264 }
08265 
08266 /* Default Hebrew syntax */
08267 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08268 {
08269    int res = 0;
08270 
08271    /* Introduce messages they have */
08272    if (!res) {
08273       if ((vms->newmessages) || (vms->oldmessages)) {
08274          res = ast_play_and_wait(chan, "vm-youhave");
08275       }
08276       /*
08277        * The word "shtei" refers to the number 2 in hebrew when performing a count
08278        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08279        * an element, this is one of them.
08280        */
08281       if (vms->newmessages) {
08282          if (!res) {
08283             if (vms->newmessages == 1) {
08284                res = ast_play_and_wait(chan, "vm-INBOX1");
08285             } else {
08286                if (vms->newmessages == 2) {
08287                   res = ast_play_and_wait(chan, "vm-shtei");
08288                } else {
08289                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08290                }
08291                res = ast_play_and_wait(chan, "vm-INBOX");
08292             }
08293          }
08294          if (vms->oldmessages && !res) {
08295             res = ast_play_and_wait(chan, "vm-and");
08296             if (vms->oldmessages == 1) {
08297                res = ast_play_and_wait(chan, "vm-Old1");
08298             } else {
08299                if (vms->oldmessages == 2) {
08300                   res = ast_play_and_wait(chan, "vm-shtei");
08301                } else {
08302                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08303                }
08304                res = ast_play_and_wait(chan, "vm-Old");
08305             }
08306          }
08307       }
08308       if (!res && vms->oldmessages && !vms->newmessages) {
08309          if (!res) {
08310             if (vms->oldmessages == 1) {
08311                res = ast_play_and_wait(chan, "vm-Old1");
08312             } else {
08313                if (vms->oldmessages == 2) {
08314                   res = ast_play_and_wait(chan, "vm-shtei");
08315                } else {
08316                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08317                }
08318                res = ast_play_and_wait(chan, "vm-Old");
08319             }
08320          }
08321       }
08322       if (!res) {
08323          if (!vms->oldmessages && !vms->newmessages) {
08324             if (!res) {
08325                res = ast_play_and_wait(chan, "vm-nomessages");
08326             }
08327          }
08328       }
08329    }
08330    return res;
08331 }
08332    
08333 /* Default English syntax */
08334 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08335 {
08336    int res;
08337 
08338    /* Introduce messages they have */
08339    res = ast_play_and_wait(chan, "vm-youhave");
08340    if (!res) {
08341       if (vms->urgentmessages) {
08342          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08343          if (!res)
08344             res = ast_play_and_wait(chan, "vm-Urgent");
08345          if ((vms->oldmessages || vms->newmessages) && !res) {
08346             res = ast_play_and_wait(chan, "vm-and");
08347          } else if (!res) {
08348             if ((vms->urgentmessages == 1))
08349                res = ast_play_and_wait(chan, "vm-message");
08350             else
08351                res = ast_play_and_wait(chan, "vm-messages");
08352          }
08353       }
08354       if (vms->newmessages) {
08355          res = say_and_wait(chan, vms->newmessages, chan->language);
08356          if (!res)
08357             res = ast_play_and_wait(chan, "vm-INBOX");
08358          if (vms->oldmessages && !res)
08359             res = ast_play_and_wait(chan, "vm-and");
08360          else if (!res) {
08361             if ((vms->newmessages == 1))
08362                res = ast_play_and_wait(chan, "vm-message");
08363             else
08364                res = ast_play_and_wait(chan, "vm-messages");
08365          }
08366             
08367       }
08368       if (!res && vms->oldmessages) {
08369          res = say_and_wait(chan, vms->oldmessages, chan->language);
08370          if (!res)
08371             res = ast_play_and_wait(chan, "vm-Old");
08372          if (!res) {
08373             if (vms->oldmessages == 1)
08374                res = ast_play_and_wait(chan, "vm-message");
08375             else
08376                res = ast_play_and_wait(chan, "vm-messages");
08377          }
08378       }
08379       if (!res) {
08380          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08381             res = ast_play_and_wait(chan, "vm-no");
08382             if (!res)
08383                res = ast_play_and_wait(chan, "vm-messages");
08384          }
08385       }
08386    }
08387    return res;
08388 }
08389 
08390 /* ITALIAN syntax */
08391 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08392 {
08393    /* Introduce messages they have */
08394    int res;
08395    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08396       res = ast_play_and_wait(chan, "vm-no") ||
08397          ast_play_and_wait(chan, "vm-message");
08398    else
08399       res = ast_play_and_wait(chan, "vm-youhave");
08400    if (!res && vms->newmessages) {
08401       res = (vms->newmessages == 1) ?
08402          ast_play_and_wait(chan, "digits/un") ||
08403          ast_play_and_wait(chan, "vm-nuovo") ||
08404          ast_play_and_wait(chan, "vm-message") :
08405          /* 2 or more new messages */
08406          say_and_wait(chan, vms->newmessages, chan->language) ||
08407          ast_play_and_wait(chan, "vm-nuovi") ||
08408          ast_play_and_wait(chan, "vm-messages");
08409       if (!res && vms->oldmessages)
08410          res = ast_play_and_wait(chan, "vm-and");
08411    }
08412    if (!res && vms->oldmessages) {
08413       res = (vms->oldmessages == 1) ?
08414          ast_play_and_wait(chan, "digits/un") ||
08415          ast_play_and_wait(chan, "vm-vecchio") ||
08416          ast_play_and_wait(chan, "vm-message") :
08417          /* 2 or more old messages */
08418          say_and_wait(chan, vms->oldmessages, chan->language) ||
08419          ast_play_and_wait(chan, "vm-vecchi") ||
08420          ast_play_and_wait(chan, "vm-messages");
08421    }
08422    return res;
08423 }
08424 
08425 /* POLISH syntax */
08426 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08427 {
08428    /* Introduce messages they have */
08429    int res;
08430    div_t num;
08431 
08432    if (!vms->oldmessages && !vms->newmessages) {
08433       res = ast_play_and_wait(chan, "vm-no");
08434       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08435       return res;
08436    } else {
08437       res = ast_play_and_wait(chan, "vm-youhave");
08438    }
08439 
08440    if (vms->newmessages) {
08441       num = div(vms->newmessages, 10);
08442       if (vms->newmessages == 1) {
08443          res = ast_play_and_wait(chan, "digits/1-a");
08444          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08445          res = res ? res : ast_play_and_wait(chan, "vm-message");
08446       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08447          if (num.rem == 2) {
08448             if (!num.quot) {
08449                res = ast_play_and_wait(chan, "digits/2-ie");
08450             } else {
08451                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08452                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08453             }
08454          } else {
08455             res = say_and_wait(chan, vms->newmessages, chan->language);
08456          }
08457          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08458          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08459       } else {
08460          res = say_and_wait(chan, vms->newmessages, chan->language);
08461          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08462          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08463       }
08464       if (!res && vms->oldmessages)
08465          res = ast_play_and_wait(chan, "vm-and");
08466    }
08467    if (!res && vms->oldmessages) {
08468       num = div(vms->oldmessages, 10);
08469       if (vms->oldmessages == 1) {
08470          res = ast_play_and_wait(chan, "digits/1-a");
08471          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08472          res = res ? res : ast_play_and_wait(chan, "vm-message");
08473       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08474          if (num.rem == 2) {
08475             if (!num.quot) {
08476                res = ast_play_and_wait(chan, "digits/2-ie");
08477             } else {
08478                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08479                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08480             }
08481          } else {
08482             res = say_and_wait(chan, vms->oldmessages, chan->language);
08483          }
08484          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08485          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08486       } else {
08487          res = say_and_wait(chan, vms->oldmessages, chan->language);
08488          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08489          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08490       }
08491    }
08492 
08493    return res;
08494 }
08495 
08496 /* SWEDISH syntax */
08497 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08498 {
08499    /* Introduce messages they have */
08500    int res;
08501 
08502    res = ast_play_and_wait(chan, "vm-youhave");
08503    if (res)
08504       return res;
08505 
08506    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08507       res = ast_play_and_wait(chan, "vm-no");
08508       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08509       return res;
08510    }
08511 
08512    if (vms->newmessages) {
08513       if ((vms->newmessages == 1)) {
08514          res = ast_play_and_wait(chan, "digits/ett");
08515          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08516          res = res ? res : ast_play_and_wait(chan, "vm-message");
08517       } else {
08518          res = say_and_wait(chan, vms->newmessages, chan->language);
08519          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08520          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08521       }
08522       if (!res && vms->oldmessages)
08523          res = ast_play_and_wait(chan, "vm-and");
08524    }
08525    if (!res && vms->oldmessages) {
08526       if (vms->oldmessages == 1) {
08527          res = ast_play_and_wait(chan, "digits/ett");
08528          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08529          res = res ? res : ast_play_and_wait(chan, "vm-message");
08530       } else {
08531          res = say_and_wait(chan, vms->oldmessages, chan->language);
08532          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08533          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08534       }
08535    }
08536 
08537    return res;
08538 }
08539 
08540 /* NORWEGIAN syntax */
08541 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08542 {
08543    /* Introduce messages they have */
08544    int res;
08545 
08546    res = ast_play_and_wait(chan, "vm-youhave");
08547    if (res)
08548       return res;
08549 
08550    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08551       res = ast_play_and_wait(chan, "vm-no");
08552       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08553       return res;
08554    }
08555 
08556    if (vms->newmessages) {
08557       if ((vms->newmessages == 1)) {
08558          res = ast_play_and_wait(chan, "digits/1");
08559          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08560          res = res ? res : ast_play_and_wait(chan, "vm-message");
08561       } else {
08562          res = say_and_wait(chan, vms->newmessages, chan->language);
08563          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08564          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08565       }
08566       if (!res && vms->oldmessages)
08567          res = ast_play_and_wait(chan, "vm-and");
08568    }
08569    if (!res && vms->oldmessages) {
08570       if (vms->oldmessages == 1) {
08571          res = ast_play_and_wait(chan, "digits/1");
08572          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08573          res = res ? res : ast_play_and_wait(chan, "vm-message");
08574       } else {
08575          res = say_and_wait(chan, vms->oldmessages, chan->language);
08576          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08577          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08578       }
08579    }
08580 
08581    return res;
08582 }
08583 
08584 /* GERMAN syntax */
08585 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08586 {
08587    /* Introduce messages they have */
08588    int res;
08589    res = ast_play_and_wait(chan, "vm-youhave");
08590    if (!res) {
08591       if (vms->newmessages) {
08592          if ((vms->newmessages == 1))
08593             res = ast_play_and_wait(chan, "digits/1F");
08594          else
08595             res = say_and_wait(chan, vms->newmessages, chan->language);
08596          if (!res)
08597             res = ast_play_and_wait(chan, "vm-INBOX");
08598          if (vms->oldmessages && !res)
08599             res = ast_play_and_wait(chan, "vm-and");
08600          else if (!res) {
08601             if ((vms->newmessages == 1))
08602                res = ast_play_and_wait(chan, "vm-message");
08603             else
08604                res = ast_play_and_wait(chan, "vm-messages");
08605          }
08606             
08607       }
08608       if (!res && vms->oldmessages) {
08609          if (vms->oldmessages == 1)
08610             res = ast_play_and_wait(chan, "digits/1F");
08611          else
08612             res = say_and_wait(chan, vms->oldmessages, chan->language);
08613          if (!res)
08614             res = ast_play_and_wait(chan, "vm-Old");
08615          if (!res) {
08616             if (vms->oldmessages == 1)
08617                res = ast_play_and_wait(chan, "vm-message");
08618             else
08619                res = ast_play_and_wait(chan, "vm-messages");
08620          }
08621       }
08622       if (!res) {
08623          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08624             res = ast_play_and_wait(chan, "vm-no");
08625             if (!res)
08626                res = ast_play_and_wait(chan, "vm-messages");
08627          }
08628       }
08629    }
08630    return res;
08631 }
08632 
08633 /* SPANISH syntax */
08634 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08635 {
08636    /* Introduce messages they have */
08637    int res;
08638    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08639       res = ast_play_and_wait(chan, "vm-youhaveno");
08640       if (!res)
08641          res = ast_play_and_wait(chan, "vm-messages");
08642    } else {
08643       res = ast_play_and_wait(chan, "vm-youhave");
08644    }
08645    if (!res) {
08646       if (vms->newmessages) {
08647          if (!res) {
08648             if ((vms->newmessages == 1)) {
08649                res = ast_play_and_wait(chan, "digits/1M");
08650                if (!res)
08651                   res = ast_play_and_wait(chan, "vm-message");
08652                if (!res)
08653                   res = ast_play_and_wait(chan, "vm-INBOXs");
08654             } else {
08655                res = say_and_wait(chan, vms->newmessages, chan->language);
08656                if (!res)
08657                   res = ast_play_and_wait(chan, "vm-messages");
08658                if (!res)
08659                   res = ast_play_and_wait(chan, "vm-INBOX");
08660             }
08661          }
08662          if (vms->oldmessages && !res)
08663             res = ast_play_and_wait(chan, "vm-and");
08664       }
08665       if (vms->oldmessages) {
08666          if (!res) {
08667             if (vms->oldmessages == 1) {
08668                res = ast_play_and_wait(chan, "digits/1M");
08669                if (!res)
08670                   res = ast_play_and_wait(chan, "vm-message");
08671                if (!res)
08672                   res = ast_play_and_wait(chan, "vm-Olds");
08673             } else {
08674                res = say_and_wait(chan, vms->oldmessages, chan->language);
08675                if (!res)
08676                   res = ast_play_and_wait(chan, "vm-messages");
08677                if (!res)
08678                   res = ast_play_and_wait(chan, "vm-Old");
08679             }
08680          }
08681       }
08682    }
08683 return res;
08684 }
08685 
08686 /* BRAZILIAN PORTUGUESE syntax */
08687 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08688    /* Introduce messages they have */
08689    int res;
08690    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08691       res = ast_play_and_wait(chan, "vm-nomessages");
08692       return res;
08693    } else {
08694       res = ast_play_and_wait(chan, "vm-youhave");
08695    }
08696    if (vms->newmessages) {
08697       if (!res)
08698          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08699       if ((vms->newmessages == 1)) {
08700          if (!res)
08701             res = ast_play_and_wait(chan, "vm-message");
08702          if (!res)
08703             res = ast_play_and_wait(chan, "vm-INBOXs");
08704       } else {
08705          if (!res)
08706             res = ast_play_and_wait(chan, "vm-messages");
08707          if (!res)
08708             res = ast_play_and_wait(chan, "vm-INBOX");
08709       }
08710       if (vms->oldmessages && !res)
08711          res = ast_play_and_wait(chan, "vm-and");
08712    }
08713    if (vms->oldmessages) {
08714       if (!res)
08715          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08716       if (vms->oldmessages == 1) {
08717          if (!res)
08718             res = ast_play_and_wait(chan, "vm-message");
08719          if (!res)
08720             res = ast_play_and_wait(chan, "vm-Olds");
08721       } else {
08722          if (!res)
08723             res = ast_play_and_wait(chan, "vm-messages");
08724          if (!res)
08725             res = ast_play_and_wait(chan, "vm-Old");
08726       }
08727    }
08728    return res;
08729 }
08730 
08731 /* FRENCH syntax */
08732 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08733 {
08734    /* Introduce messages they have */
08735    int res;
08736    res = ast_play_and_wait(chan, "vm-youhave");
08737    if (!res) {
08738       if (vms->newmessages) {
08739          res = say_and_wait(chan, vms->newmessages, chan->language);
08740          if (!res)
08741             res = ast_play_and_wait(chan, "vm-INBOX");
08742          if (vms->oldmessages && !res)
08743             res = ast_play_and_wait(chan, "vm-and");
08744          else if (!res) {
08745             if ((vms->newmessages == 1))
08746                res = ast_play_and_wait(chan, "vm-message");
08747             else
08748                res = ast_play_and_wait(chan, "vm-messages");
08749          }
08750             
08751       }
08752       if (!res && vms->oldmessages) {
08753          res = say_and_wait(chan, vms->oldmessages, chan->language);
08754          if (!res)
08755             res = ast_play_and_wait(chan, "vm-Old");
08756          if (!res) {
08757             if (vms->oldmessages == 1)
08758                res = ast_play_and_wait(chan, "vm-message");
08759             else
08760                res = ast_play_and_wait(chan, "vm-messages");
08761          }
08762       }
08763       if (!res) {
08764          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08765             res = ast_play_and_wait(chan, "vm-no");
08766             if (!res)
08767                res = ast_play_and_wait(chan, "vm-messages");
08768          }
08769       }
08770    }
08771    return res;
08772 }
08773 
08774 /* DUTCH syntax */
08775 static int vm_intro_nl(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             if (vms->newmessages == 1)
08785                res = ast_play_and_wait(chan, "vm-INBOXs");
08786             else
08787                res = ast_play_and_wait(chan, "vm-INBOX");
08788          }
08789          if (vms->oldmessages && !res)
08790             res = ast_play_and_wait(chan, "vm-and");
08791          else if (!res) {
08792             if ((vms->newmessages == 1))
08793                res = ast_play_and_wait(chan, "vm-message");
08794             else
08795                res = ast_play_and_wait(chan, "vm-messages");
08796          }
08797             
08798       }
08799       if (!res && vms->oldmessages) {
08800          res = say_and_wait(chan, vms->oldmessages, chan->language);
08801          if (!res) {
08802             if (vms->oldmessages == 1)
08803                res = ast_play_and_wait(chan, "vm-Olds");
08804             else
08805                res = ast_play_and_wait(chan, "vm-Old");
08806          }
08807          if (!res) {
08808             if (vms->oldmessages == 1)
08809                res = ast_play_and_wait(chan, "vm-message");
08810             else
08811                res = ast_play_and_wait(chan, "vm-messages");
08812          }
08813       }
08814       if (!res) {
08815          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08816             res = ast_play_and_wait(chan, "vm-no");
08817             if (!res)
08818                res = ast_play_and_wait(chan, "vm-messages");
08819          }
08820       }
08821    }
08822    return res;
08823 }
08824 
08825 /* PORTUGUESE syntax */
08826 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08827 {
08828    /* Introduce messages they have */
08829    int res;
08830    res = ast_play_and_wait(chan, "vm-youhave");
08831    if (!res) {
08832       if (vms->newmessages) {
08833          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08834          if (!res) {
08835             if ((vms->newmessages == 1)) {
08836                res = ast_play_and_wait(chan, "vm-message");
08837                if (!res)
08838                   res = ast_play_and_wait(chan, "vm-INBOXs");
08839             } else {
08840                res = ast_play_and_wait(chan, "vm-messages");
08841                if (!res)
08842                   res = ast_play_and_wait(chan, "vm-INBOX");
08843             }
08844          }
08845          if (vms->oldmessages && !res)
08846             res = ast_play_and_wait(chan, "vm-and");
08847       }
08848       if (!res && vms->oldmessages) {
08849          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08850          if (!res) {
08851             if (vms->oldmessages == 1) {
08852                res = ast_play_and_wait(chan, "vm-message");
08853                if (!res)
08854                   res = ast_play_and_wait(chan, "vm-Olds");
08855             } else {
08856                res = ast_play_and_wait(chan, "vm-messages");
08857                if (!res)
08858                   res = ast_play_and_wait(chan, "vm-Old");
08859             }
08860          }
08861       }
08862       if (!res) {
08863          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08864             res = ast_play_and_wait(chan, "vm-no");
08865             if (!res)
08866                res = ast_play_and_wait(chan, "vm-messages");
08867          }
08868       }
08869    }
08870    return res;
08871 }
08872 
08873 
08874 /* CZECH syntax */
08875 /* in czech there must be declension of word new and message
08876  * czech        : english        : czech      : english
08877  * --------------------------------------------------------
08878  * vm-youhave   : you have 
08879  * vm-novou     : one new        : vm-zpravu  : message
08880  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08881  * vm-novych    : 5-infinite new : vm-zprav   : messages
08882  * vm-starou   : one old
08883  * vm-stare     : 2-4 old 
08884  * vm-starych   : 5-infinite old
08885  * jednu        : one   - falling 4. 
08886  * vm-no        : no  ( no messages )
08887  */
08888 
08889 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08890 {
08891    int res;
08892    res = ast_play_and_wait(chan, "vm-youhave");
08893    if (!res) {
08894       if (vms->newmessages) {
08895          if (vms->newmessages == 1) {
08896             res = ast_play_and_wait(chan, "digits/jednu");
08897          } else {
08898             res = say_and_wait(chan, vms->newmessages, chan->language);
08899          }
08900          if (!res) {
08901             if ((vms->newmessages == 1))
08902                res = ast_play_and_wait(chan, "vm-novou");
08903             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08904                res = ast_play_and_wait(chan, "vm-nove");
08905             if (vms->newmessages > 4)
08906                res = ast_play_and_wait(chan, "vm-novych");
08907          }
08908          if (vms->oldmessages && !res)
08909             res = ast_play_and_wait(chan, "vm-and");
08910          else if (!res) {
08911             if ((vms->newmessages == 1))
08912                res = ast_play_and_wait(chan, "vm-zpravu");
08913             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08914                res = ast_play_and_wait(chan, "vm-zpravy");
08915             if (vms->newmessages > 4)
08916                res = ast_play_and_wait(chan, "vm-zprav");
08917          }
08918       }
08919       if (!res && vms->oldmessages) {
08920          res = say_and_wait(chan, vms->oldmessages, chan->language);
08921          if (!res) {
08922             if ((vms->oldmessages == 1))
08923                res = ast_play_and_wait(chan, "vm-starou");
08924             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08925                res = ast_play_and_wait(chan, "vm-stare");
08926             if (vms->oldmessages > 4)
08927                res = ast_play_and_wait(chan, "vm-starych");
08928          }
08929          if (!res) {
08930             if ((vms->oldmessages == 1))
08931                res = ast_play_and_wait(chan, "vm-zpravu");
08932             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08933                res = ast_play_and_wait(chan, "vm-zpravy");
08934             if (vms->oldmessages > 4)
08935                res = ast_play_and_wait(chan, "vm-zprav");
08936          }
08937       }
08938       if (!res) {
08939          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08940             res = ast_play_and_wait(chan, "vm-no");
08941             if (!res)
08942                res = ast_play_and_wait(chan, "vm-zpravy");
08943          }
08944       }
08945    }
08946    return res;
08947 }
08948 
08949 /* CHINESE (Taiwan) syntax */
08950 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08951 {
08952    int res;
08953    /* Introduce messages they have */
08954    res = ast_play_and_wait(chan, "vm-you");
08955 
08956    if (!res && vms->newmessages) {
08957       res = ast_play_and_wait(chan, "vm-have");
08958       if (!res)
08959          res = say_and_wait(chan, vms->newmessages, chan->language);
08960       if (!res)
08961          res = ast_play_and_wait(chan, "vm-tong");
08962       if (!res)
08963          res = ast_play_and_wait(chan, "vm-INBOX");
08964       if (vms->oldmessages && !res)
08965          res = ast_play_and_wait(chan, "vm-and");
08966       else if (!res) 
08967          res = ast_play_and_wait(chan, "vm-messages");
08968    }
08969    if (!res && vms->oldmessages) {
08970       res = ast_play_and_wait(chan, "vm-have");
08971       if (!res)
08972          res = say_and_wait(chan, vms->oldmessages, chan->language);
08973       if (!res)
08974          res = ast_play_and_wait(chan, "vm-tong");
08975       if (!res)
08976          res = ast_play_and_wait(chan, "vm-Old");
08977       if (!res)
08978          res = ast_play_and_wait(chan, "vm-messages");
08979    }
08980    if (!res && !vms->oldmessages && !vms->newmessages) {
08981       res = ast_play_and_wait(chan, "vm-haveno");
08982       if (!res)
08983          res = ast_play_and_wait(chan, "vm-messages");
08984    }
08985    return res;
08986 }
08987 
08988 /* Vietnamese syntax */
08989 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08990 {
08991    int res;
08992 
08993    /* Introduce messages they have */
08994    res = ast_play_and_wait(chan, "vm-youhave");
08995    if (!res) {
08996       if (vms->newmessages) {
08997          res = say_and_wait(chan, vms->newmessages, chan->language);
08998          if (!res)
08999             res = ast_play_and_wait(chan, "vm-INBOX");
09000          if (vms->oldmessages && !res)
09001             res = ast_play_and_wait(chan, "vm-and");
09002       }
09003       if (!res && vms->oldmessages) {
09004          res = say_and_wait(chan, vms->oldmessages, chan->language);
09005          if (!res)
09006             res = ast_play_and_wait(chan, "vm-Old");        
09007       }
09008       if (!res) {
09009          if (!vms->oldmessages && !vms->newmessages) {
09010             res = ast_play_and_wait(chan, "vm-no");
09011             if (!res)
09012                res = ast_play_and_wait(chan, "vm-message");
09013          }
09014       }
09015    }
09016    return res;
09017 }
09018 
09019 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09020 {
09021    char prefile[256];
09022    
09023    /* Notify the user that the temp greeting is set and give them the option to remove it */
09024    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09025    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09026       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09027       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09028          ast_play_and_wait(chan, "vm-tempgreetactive");
09029       }
09030       DISPOSE(prefile, -1);
09031    }
09032 
09033    /* Play voicemail intro - syntax is different for different languages */
09034    if (0) {
09035       return 0;
09036    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09037       return vm_intro_cs(chan, vms);
09038    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09039       static int deprecation_warning = 0;
09040       if (deprecation_warning++ % 10 == 0) {
09041          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09042       }
09043       return vm_intro_cs(chan, vms);
09044    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09045       return vm_intro_de(chan, vms);
09046    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09047       return vm_intro_es(chan, vms);
09048    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09049       return vm_intro_fr(chan, vms);
09050    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09051       return vm_intro_gr(chan, vms);
09052    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09053       return vm_intro_he(chan, vms);
09054    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09055       return vm_intro_it(chan, vms);
09056    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09057       return vm_intro_nl(chan, vms);
09058    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09059       return vm_intro_no(chan, vms);
09060    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09061       return vm_intro_pl(chan, vms);
09062    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09063       return vm_intro_pt_BR(chan, vms);
09064    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09065       return vm_intro_pt(chan, vms);
09066    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09067       return vm_intro_multilang(chan, vms, "n");
09068    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09069       return vm_intro_se(chan, vms);
09070    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09071       return vm_intro_multilang(chan, vms, "n");
09072    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09073       return vm_intro_vi(chan, vms);
09074    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09075       return vm_intro_zh(chan, vms);
09076    } else {                                             /* Default to ENGLISH */
09077       return vm_intro_en(chan, vms);
09078    }
09079 }
09080 
09081 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09082 {
09083    int res = 0;
09084    /* Play instructions and wait for new command */
09085    while (!res) {
09086       if (vms->starting) {
09087          if (vms->lastmsg > -1) {
09088             if (skipadvanced)
09089                res = ast_play_and_wait(chan, "vm-onefor-full");
09090             else
09091                res = ast_play_and_wait(chan, "vm-onefor");
09092             if (!res)
09093                res = vm_play_folder_name(chan, vms->vmbox);
09094          }
09095          if (!res) {
09096             if (skipadvanced)
09097                res = ast_play_and_wait(chan, "vm-opts-full");
09098             else
09099                res = ast_play_and_wait(chan, "vm-opts");
09100          }
09101       } else {
09102          /* Added for additional help */
09103          if (skipadvanced) {
09104             res = ast_play_and_wait(chan, "vm-onefor-full");
09105             if (!res)
09106                res = vm_play_folder_name(chan, vms->vmbox);
09107             res = ast_play_and_wait(chan, "vm-opts-full");
09108          }
09109          /* Logic:
09110           * If the current message is not the first OR
09111           * if we're listening to the first new message and there are
09112           * also urgent messages, then prompt for navigation to the
09113           * previous message
09114           */
09115          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09116             res = ast_play_and_wait(chan, "vm-prev");
09117          }
09118          if (!res && !skipadvanced)
09119             res = ast_play_and_wait(chan, "vm-advopts");
09120          if (!res)
09121             res = ast_play_and_wait(chan, "vm-repeat");
09122          /* Logic:
09123           * If we're not listening to the last message OR
09124           * we're listening to the last urgent message and there are
09125           * also new non-urgent messages, then prompt for navigation
09126           * to the next message
09127           */
09128          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09129             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09130             res = ast_play_and_wait(chan, "vm-next");
09131          }
09132          if (!res) {
09133             if (!vms->deleted[vms->curmsg])
09134                res = ast_play_and_wait(chan, "vm-delete");
09135             else
09136                res = ast_play_and_wait(chan, "vm-undelete");
09137             if (!res)
09138                res = ast_play_and_wait(chan, "vm-toforward");
09139             if (!res)
09140                res = ast_play_and_wait(chan, "vm-savemessage");
09141          }
09142       }
09143       if (!res) {
09144          res = ast_play_and_wait(chan, "vm-helpexit");
09145       }
09146       if (!res)
09147          res = ast_waitfordigit(chan, 6000);
09148       if (!res) {
09149          vms->repeats++;
09150          if (vms->repeats > 2) {
09151             res = 't';
09152          }
09153       }
09154    }
09155    return res;
09156 }
09157 
09158 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09159 {
09160    int res = 0;
09161    /* Play instructions and wait for new command */
09162    while (!res) {
09163       if (vms->lastmsg > -1) {
09164          res = ast_play_and_wait(chan, "vm-listen");
09165          if (!res)
09166             res = vm_play_folder_name(chan, vms->vmbox);
09167          if (!res)
09168             res = ast_play_and_wait(chan, "press");
09169          if (!res)
09170             res = ast_play_and_wait(chan, "digits/1");
09171       }
09172       if (!res)
09173          res = ast_play_and_wait(chan, "vm-opts");
09174       if (!res) {
09175          vms->starting = 0;
09176          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09177       }
09178    }
09179    return res;
09180 }
09181 
09182 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09183 {
09184    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09185       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09186    } else {             /* Default to ENGLISH */
09187       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09188    }
09189 }
09190 
09191 
09192 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09193 {
09194    int cmd = 0;
09195    int duration = 0;
09196    int tries = 0;
09197    char newpassword[80] = "";
09198    char newpassword2[80] = "";
09199    char prefile[PATH_MAX] = "";
09200    unsigned char buf[256];
09201    int bytes = 0;
09202 
09203    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09204    if (ast_adsi_available(chan)) {
09205       bytes += adsi_logo(buf + bytes);
09206       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09207       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09208       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09209       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09210       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09211    }
09212 
09213    /* If forcename is set, have the user record their name */
09214    if (ast_test_flag(vmu, VM_FORCENAME)) {
09215       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09216       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09217          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09218          if (cmd < 0 || cmd == 't' || cmd == '#')
09219             return cmd;
09220       }
09221    }
09222 
09223    /* If forcegreetings is set, have the user record their greetings */
09224    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09225       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09226       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09227          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09228          if (cmd < 0 || cmd == 't' || cmd == '#')
09229             return cmd;
09230       }
09231 
09232       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09233       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09234          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09235          if (cmd < 0 || cmd == 't' || cmd == '#')
09236             return cmd;
09237       }
09238    }
09239 
09240    /*
09241     * Change the password last since new users will be able to skip over any steps this one comes before
09242     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09243     */
09244    for (;;) {
09245       newpassword[1] = '\0';
09246       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09247       if (cmd == '#')
09248          newpassword[0] = '\0';
09249       if (cmd < 0 || cmd == 't' || cmd == '#')
09250          return cmd;
09251       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09252       if (cmd < 0 || cmd == 't' || cmd == '#')
09253          return cmd;
09254       cmd = check_password(vmu, newpassword); /* perform password validation */
09255       if (cmd != 0) {
09256          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09257          cmd = ast_play_and_wait(chan, vm_invalid_password);
09258       } else {
09259          newpassword2[1] = '\0';
09260          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09261          if (cmd == '#')
09262             newpassword2[0] = '\0';
09263          if (cmd < 0 || cmd == 't' || cmd == '#')
09264             return cmd;
09265          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09266          if (cmd < 0 || cmd == 't' || cmd == '#')
09267             return cmd;
09268          if (!strcmp(newpassword, newpassword2))
09269             break;
09270          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09271          cmd = ast_play_and_wait(chan, vm_mismatch);
09272       }
09273       if (++tries == 3)
09274          return -1;
09275       if (cmd != 0) {
09276          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09277       }
09278    }
09279    if (pwdchange & PWDCHANGE_INTERNAL)
09280       vm_change_password(vmu, newpassword);
09281    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09282       vm_change_password_shell(vmu, newpassword);
09283 
09284    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09285    cmd = ast_play_and_wait(chan, vm_passchanged);
09286 
09287    return cmd;
09288 }
09289 
09290 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09291 {
09292    int cmd = 0;
09293    int retries = 0;
09294    int duration = 0;
09295    char newpassword[80] = "";
09296    char newpassword2[80] = "";
09297    char prefile[PATH_MAX] = "";
09298    unsigned char buf[256];
09299    int bytes = 0;
09300 
09301    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09302    if (ast_adsi_available(chan)) {
09303       bytes += adsi_logo(buf + bytes);
09304       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09305       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09306       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09307       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09308       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09309    }
09310    while ((cmd >= 0) && (cmd != 't')) {
09311       if (cmd)
09312          retries = 0;
09313       switch (cmd) {
09314       case '1': /* Record your unavailable message */
09315          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09316          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09317          break;
09318       case '2':  /* Record your busy message */
09319          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09320          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09321          break;
09322       case '3': /* Record greeting */
09323          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09324          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09325          break;
09326       case '4':  /* manage the temporary greeting */
09327          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09328          break;
09329       case '5': /* change password */
09330          if (vmu->password[0] == '-') {
09331             cmd = ast_play_and_wait(chan, "vm-no");
09332             break;
09333          }
09334          newpassword[1] = '\0';
09335          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09336          if (cmd == '#')
09337             newpassword[0] = '\0';
09338          else {
09339             if (cmd < 0)
09340                break;
09341             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09342                break;
09343             }
09344          }
09345          cmd = check_password(vmu, newpassword); /* perform password validation */
09346          if (cmd != 0) {
09347             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09348             cmd = ast_play_and_wait(chan, vm_invalid_password);
09349             if (!cmd) {
09350                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09351             }
09352             break;
09353          }
09354          newpassword2[1] = '\0';
09355          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09356          if (cmd == '#')
09357             newpassword2[0] = '\0';
09358          else {
09359             if (cmd < 0)
09360                break;
09361 
09362             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09363                break;
09364             }
09365          }
09366          if (strcmp(newpassword, newpassword2)) {
09367             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09368             cmd = ast_play_and_wait(chan, vm_mismatch);
09369             if (!cmd) {
09370                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09371             }
09372             break;
09373          }
09374 
09375          if (pwdchange & PWDCHANGE_INTERNAL) {
09376             vm_change_password(vmu, newpassword);
09377          }
09378          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09379             vm_change_password_shell(vmu, newpassword);
09380          }
09381 
09382          ast_debug(1, "User %s set password to %s of length %d\n",
09383             vms->username, newpassword, (int) strlen(newpassword));
09384          cmd = ast_play_and_wait(chan, vm_passchanged);
09385          break;
09386       case '*': 
09387          cmd = 't';
09388          break;
09389       default: 
09390          cmd = 0;
09391          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09392          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09393          if (ast_fileexists(prefile, NULL, NULL)) {
09394             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09395          }
09396          DISPOSE(prefile, -1);
09397          if (!cmd) {
09398             cmd = ast_play_and_wait(chan, "vm-options");
09399          }
09400          if (!cmd) {
09401             cmd = ast_waitfordigit(chan, 6000);
09402          }
09403          if (!cmd) {
09404             retries++;
09405          }
09406          if (retries > 3) {
09407             cmd = 't';
09408          }
09409          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09410       }
09411    }
09412    if (cmd == 't')
09413       cmd = 0;
09414    return cmd;
09415 }
09416 
09417 /*!
09418  * \brief The handler for 'record a temporary greeting'. 
09419  * \param chan
09420  * \param vmu
09421  * \param vms
09422  * \param fmtc
09423  * \param record_gain
09424  *
09425  * This is option 4 from the mailbox options menu.
09426  * This function manages the following promptings:
09427  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09428  * 2: remove (delete) the temporary greeting.
09429  * *: return to the main menu.
09430  *
09431  * \return zero on success, -1 on error.
09432  */
09433 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09434 {
09435    int cmd = 0;
09436    int retries = 0;
09437    int duration = 0;
09438    char prefile[PATH_MAX] = "";
09439    unsigned char buf[256];
09440    int bytes = 0;
09441 
09442    if (ast_adsi_available(chan)) {
09443       bytes += adsi_logo(buf + bytes);
09444       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09445       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09446       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09447       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09448       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09449    }
09450 
09451    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09452    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09453    while ((cmd >= 0) && (cmd != 't')) {
09454       if (cmd)
09455          retries = 0;
09456       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09457       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09458          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09459          cmd = 't';  
09460       } else {
09461          switch (cmd) {
09462          case '1':
09463             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09464             break;
09465          case '2':
09466             DELETE(prefile, -1, prefile, vmu);
09467             ast_play_and_wait(chan, "vm-tempremoved");
09468             cmd = 't';  
09469             break;
09470          case '*': 
09471             cmd = 't';
09472             break;
09473          default:
09474             cmd = ast_play_and_wait(chan,
09475                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09476                   "vm-tempgreeting2" : "vm-tempgreeting");
09477             if (!cmd) {
09478                cmd = ast_waitfordigit(chan, 6000);
09479             }
09480             if (!cmd) {
09481                retries++;
09482             }
09483             if (retries > 3) {
09484                cmd = 't';
09485             }
09486             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09487          }
09488       }
09489       DISPOSE(prefile, -1);
09490    }
09491    if (cmd == 't')
09492       cmd = 0;
09493    return cmd;
09494 }
09495 
09496 /*!
09497  * \brief Greek syntax for 'You have N messages' greeting.
09498  * \param chan
09499  * \param vms
09500  * \param vmu
09501  *
09502  * \return zero on success, -1 on error.
09503  */   
09504 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09505 {
09506    int cmd = 0;
09507 
09508    if (vms->lastmsg > -1) {
09509       cmd = play_message(chan, vmu, vms);
09510    } else {
09511       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09512       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09513          if (!cmd) {
09514             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09515             cmd = ast_play_and_wait(chan, vms->fn);
09516          }
09517          if (!cmd)
09518             cmd = ast_play_and_wait(chan, "vm-messages");
09519       } else {
09520          if (!cmd)
09521             cmd = ast_play_and_wait(chan, "vm-messages");
09522          if (!cmd) {
09523             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09524             cmd = ast_play_and_wait(chan, vms->fn);
09525          }
09526       }
09527    } 
09528    return cmd;
09529 }
09530 
09531 /* Hebrew Syntax */
09532 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09533 {
09534    int cmd = 0;
09535 
09536    if (vms->lastmsg > -1) {
09537       cmd = play_message(chan, vmu, vms);
09538    } else {
09539       if (!strcasecmp(vms->fn, "INBOX")) {
09540          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09541       } else {
09542          cmd = ast_play_and_wait(chan, "vm-nomessages");
09543       }
09544    }
09545    return cmd;
09546 }
09547 
09548 /*! 
09549  * \brief Default English syntax for 'You have N messages' greeting.
09550  * \param chan
09551  * \param vms
09552  * \param vmu
09553  *
09554  * \return zero on success, -1 on error.
09555  */
09556 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09557 {
09558    int cmd = 0;
09559 
09560    if (vms->lastmsg > -1) {
09561       cmd = play_message(chan, vmu, vms);
09562    } else {
09563       cmd = ast_play_and_wait(chan, "vm-youhave");
09564       if (!cmd) 
09565          cmd = ast_play_and_wait(chan, "vm-no");
09566       if (!cmd) {
09567          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09568          cmd = ast_play_and_wait(chan, vms->fn);
09569       }
09570       if (!cmd)
09571          cmd = ast_play_and_wait(chan, "vm-messages");
09572    }
09573    return cmd;
09574 }
09575 
09576 /*! 
09577  *\brief Italian syntax for 'You have N messages' greeting.
09578  * \param chan
09579  * \param vms
09580  * \param vmu
09581  *
09582  * \return zero on success, -1 on error.
09583  */
09584 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09585 {
09586    int cmd;
09587 
09588    if (vms->lastmsg > -1) {
09589       cmd = play_message(chan, vmu, vms);
09590    } else {
09591       cmd = ast_play_and_wait(chan, "vm-no");
09592       if (!cmd)
09593          cmd = ast_play_and_wait(chan, "vm-message");
09594       if (!cmd) {
09595          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09596          cmd = ast_play_and_wait(chan, vms->fn);
09597       }
09598    }
09599    return cmd;
09600 }
09601 
09602 /*! 
09603  * \brief Spanish syntax for 'You have N messages' greeting.
09604  * \param chan
09605  * \param vms
09606  * \param vmu
09607  *
09608  * \return zero on success, -1 on error.
09609  */
09610 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09611 {
09612    int cmd;
09613 
09614    if (vms->lastmsg > -1) {
09615       cmd = play_message(chan, vmu, vms);
09616    } else {
09617       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09618       if (!cmd)
09619          cmd = ast_play_and_wait(chan, "vm-messages");
09620       if (!cmd) {
09621          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09622          cmd = ast_play_and_wait(chan, vms->fn);
09623       }
09624    }
09625    return cmd;
09626 }
09627 
09628 /*! 
09629  * \brief Portuguese syntax for 'You have N messages' greeting.
09630  * \param chan
09631  * \param vms
09632  * \param vmu
09633  *
09634  * \return zero on success, -1 on error.
09635  */
09636 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09637 {
09638    int cmd;
09639 
09640    if (vms->lastmsg > -1) {
09641       cmd = play_message(chan, vmu, vms);
09642    } else {
09643       cmd = ast_play_and_wait(chan, "vm-no");
09644       if (!cmd) {
09645          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09646          cmd = ast_play_and_wait(chan, vms->fn);
09647       }
09648       if (!cmd)
09649          cmd = ast_play_and_wait(chan, "vm-messages");
09650    }
09651    return cmd;
09652 }
09653 
09654 /*! 
09655  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09656  * \param chan
09657  * \param vms
09658  * \param vmu
09659  *
09660  * \return zero on success, -1 on error.
09661  */
09662 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09663 {
09664    int cmd;
09665 
09666    if (vms->lastmsg > -1) {
09667       cmd = play_message(chan, vmu, vms);
09668    } else {
09669       cmd = ast_play_and_wait(chan, "vm-you");
09670       if (!cmd) 
09671          cmd = ast_play_and_wait(chan, "vm-haveno");
09672       if (!cmd)
09673          cmd = ast_play_and_wait(chan, "vm-messages");
09674       if (!cmd) {
09675          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09676          cmd = ast_play_and_wait(chan, vms->fn);
09677       }
09678    }
09679    return cmd;
09680 }
09681 
09682 /*! 
09683  * \brief Vietnamese syntax for 'You have N messages' greeting.
09684  * \param chan
09685  * \param vms
09686  * \param vmu
09687  *
09688  * \return zero on success, -1 on error.
09689  */
09690 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09691 {
09692    int cmd = 0;
09693 
09694    if (vms->lastmsg > -1) {
09695       cmd = play_message(chan, vmu, vms);
09696    } else {
09697       cmd = ast_play_and_wait(chan, "vm-no");
09698       if (!cmd) {
09699          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09700          cmd = ast_play_and_wait(chan, vms->fn);
09701       }
09702    }
09703    return cmd;
09704 }
09705 
09706 /*!
09707  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09708  * \param chan The channel for the current user. We read the language property from this.
09709  * \param vms passed into the language-specific vm_browse_messages function.
09710  * \param vmu passed into the language-specific vm_browse_messages function.
09711  * 
09712  * The method to be invoked is determined by the value of language code property in the user's channel.
09713  * The default (when unable to match) is to use english.
09714  *
09715  * \return zero on success, -1 on error.
09716  */
09717 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09718 {
09719    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09720       return vm_browse_messages_es(chan, vms, vmu);
09721    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09722       return vm_browse_messages_gr(chan, vms, vmu);
09723    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09724       return vm_browse_messages_he(chan, vms, vmu);
09725    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09726       return vm_browse_messages_it(chan, vms, vmu);
09727    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09728       return vm_browse_messages_pt(chan, vms, vmu);
09729    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09730       return vm_browse_messages_vi(chan, vms, vmu);
09731    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09732       return vm_browse_messages_zh(chan, vms, vmu);
09733    } else {                                             /* Default to English syntax */
09734       return vm_browse_messages_en(chan, vms, vmu);
09735    }
09736 }
09737 
09738 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09739          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09740          int skipuser, int max_logins, int silent)
09741 {
09742    int useadsi = 0, valid = 0, logretries = 0;
09743    char password[AST_MAX_EXTENSION]="", *passptr;
09744    struct ast_vm_user vmus, *vmu = NULL;
09745 
09746    /* If ADSI is supported, setup login screen */
09747    adsi_begin(chan, &useadsi);
09748    if (!skipuser && useadsi)
09749       adsi_login(chan);
09750    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09751    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09752       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09753       return -1;
09754    }
09755 
09756    /* Authenticate them and get their mailbox/password */
09757 
09758    while (!valid && (logretries < max_logins)) {
09759       /* Prompt for, and read in the username */
09760       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09761          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09762          return -1;
09763       }
09764       if (ast_strlen_zero(mailbox)) {
09765          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09766             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09767          } else {
09768             ast_verb(3, "Username not entered\n"); 
09769             return -1;
09770          }
09771       } else if (mailbox[0] == '*') {
09772          /* user entered '*' */
09773          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09774          if (ast_exists_extension(chan, chan->context, "a", 1,
09775             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09776             return -1;
09777          }
09778          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09779          mailbox[0] = '\0';
09780       }
09781 
09782       if (useadsi)
09783          adsi_password(chan);
09784 
09785       if (!ast_strlen_zero(prefix)) {
09786          char fullusername[80] = "";
09787          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09788          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09789          ast_copy_string(mailbox, fullusername, mailbox_size);
09790       }
09791 
09792       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09793       vmu = find_user(&vmus, context, mailbox);
09794       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09795          /* saved password is blank, so don't bother asking */
09796          password[0] = '\0';
09797       } else {
09798          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09799          if (ast_streamfile(chan, vm_password, chan->language)) {
09800             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09801             return -1;
09802          }
09803          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09804             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09805             return -1;
09806          } else if (password[0] == '*') {
09807             /* user entered '*' */
09808             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09809             if (ast_exists_extension(chan, chan->context, "a", 1,
09810                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09811                mailbox[0] = '*';
09812                return -1;
09813             }
09814             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09815             mailbox[0] = '\0';
09816             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09817             vmu = NULL;
09818          }
09819       }
09820 
09821       if (vmu) {
09822          passptr = vmu->password;
09823          if (passptr[0] == '-') passptr++;
09824       }
09825       if (vmu && !strcmp(passptr, password))
09826          valid++;
09827       else {
09828          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09829          if (!ast_strlen_zero(prefix))
09830             mailbox[0] = '\0';
09831       }
09832       logretries++;
09833       if (!valid) {
09834          if (skipuser || logretries >= max_logins) {
09835             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09836             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09837                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09838                return -1;
09839             }
09840          } else {
09841             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09842             if (useadsi)
09843                adsi_login(chan);
09844             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09845                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09846                return -1;
09847             }
09848          }
09849          if (ast_waitstream(chan, "")) /* Channel is hung up */
09850             return -1;
09851       }
09852    }
09853    if (!valid && (logretries >= max_logins)) {
09854       ast_stopstream(chan);
09855       ast_play_and_wait(chan, "vm-goodbye");
09856       return -1;
09857    }
09858    if (vmu && !skipuser) {
09859       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09860    }
09861    return 0;
09862 }
09863 
09864 static int vm_execmain(struct ast_channel *chan, const char *data)
09865 {
09866    /* XXX This is, admittedly, some pretty horrendous code.  For some
09867       reason it just seemed a lot easier to do with GOTO's.  I feel
09868       like I'm back in my GWBASIC days. XXX */
09869    int res = -1;
09870    int cmd = 0;
09871    int valid = 0;
09872    char prefixstr[80] ="";
09873    char ext_context[256]="";
09874    int box;
09875    int useadsi = 0;
09876    int skipuser = 0;
09877    struct vm_state vms;
09878    struct ast_vm_user *vmu = NULL, vmus;
09879    char *context = NULL;
09880    int silentexit = 0;
09881    struct ast_flags flags = { 0 };
09882    signed char record_gain = 0;
09883    int play_auto = 0;
09884    int play_folder = 0;
09885    int in_urgent = 0;
09886 #ifdef IMAP_STORAGE
09887    int deleted = 0;
09888 #endif
09889 
09890    /* Add the vm_state to the active list and keep it active */
09891    memset(&vms, 0, sizeof(vms));
09892 
09893    vms.lastmsg = -1;
09894 
09895    memset(&vmus, 0, sizeof(vmus));
09896 
09897    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09898    if (chan->_state != AST_STATE_UP) {
09899       ast_debug(1, "Before ast_answer\n");
09900       ast_answer(chan);
09901    }
09902 
09903    if (!ast_strlen_zero(data)) {
09904       char *opts[OPT_ARG_ARRAY_SIZE];
09905       char *parse;
09906       AST_DECLARE_APP_ARGS(args,
09907          AST_APP_ARG(argv0);
09908          AST_APP_ARG(argv1);
09909       );
09910 
09911       parse = ast_strdupa(data);
09912 
09913       AST_STANDARD_APP_ARGS(args, parse);
09914 
09915       if (args.argc == 2) {
09916          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09917             return -1;
09918          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09919             int gain;
09920             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09921                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09922                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09923                   return -1;
09924                } else {
09925                   record_gain = (signed char) gain;
09926                }
09927             } else {
09928                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09929             }
09930          }
09931          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09932             play_auto = 1;
09933             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09934                /* See if it is a folder name first */
09935                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09936                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09937                      play_folder = -1;
09938                   }
09939                } else {
09940                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09941                }
09942             } else {
09943                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09944             }
09945             if (play_folder > 9 || play_folder < 0) {
09946                ast_log(AST_LOG_WARNING,
09947                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09948                   opts[OPT_ARG_PLAYFOLDER]);
09949                play_folder = 0;
09950             }
09951          }
09952       } else {
09953          /* old style options parsing */
09954          while (*(args.argv0)) {
09955             if (*(args.argv0) == 's')
09956                ast_set_flag(&flags, OPT_SILENT);
09957             else if (*(args.argv0) == 'p')
09958                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09959             else 
09960                break;
09961             (args.argv0)++;
09962          }
09963 
09964       }
09965 
09966       valid = ast_test_flag(&flags, OPT_SILENT);
09967 
09968       if ((context = strchr(args.argv0, '@')))
09969          *context++ = '\0';
09970 
09971       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09972          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09973       else
09974          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09975 
09976       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09977          skipuser++;
09978       else
09979          valid = 0;
09980    }
09981 
09982    if (!valid)
09983       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09984 
09985    ast_debug(1, "After vm_authenticate\n");
09986 
09987    if (vms.username[0] == '*') {
09988       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09989 
09990       /* user entered '*' */
09991       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09992          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
09993          res = 0; /* prevent hangup */
09994          goto out;
09995       }
09996    }
09997 
09998    if (!res) {
09999       valid = 1;
10000       if (!skipuser)
10001          vmu = &vmus;
10002    } else {
10003       res = 0;
10004    }
10005 
10006    /* If ADSI is supported, setup login screen */
10007    adsi_begin(chan, &useadsi);
10008 
10009    ast_test_suite_assert(valid);
10010    if (!valid) {
10011       goto out;
10012    }
10013    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10014 
10015 #ifdef IMAP_STORAGE
10016    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10017    pthread_setspecific(ts_vmstate.key, &vms);
10018 
10019    vms.interactive = 1;
10020    vms.updated = 1;
10021    if (vmu)
10022       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10023    vmstate_insert(&vms);
10024    init_vm_state(&vms);
10025 #endif
10026    /* Avoid allocating a buffer of 0 bytes, because some platforms really don't like that. */
10027    if (!(vms.deleted = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
10028       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
10029       cmd = ast_play_and_wait(chan, "an-error-has-occured");
10030       return -1;
10031    }
10032    if (!(vms.heard = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
10033       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
10034       cmd = ast_play_and_wait(chan, "an-error-has-occured");
10035       return -1;
10036    }
10037    
10038    /* Set language from config to override channel language */
10039    if (!ast_strlen_zero(vmu->language))
10040       ast_string_field_set(chan, language, vmu->language);
10041 
10042    /* Retrieve urgent, old and new message counts */
10043    ast_debug(1, "Before open_mailbox\n");
10044    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10045    if (res < 0)
10046       goto out;
10047    vms.oldmessages = vms.lastmsg + 1;
10048    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10049    /* check INBOX */
10050    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10051    if (res < 0)
10052       goto out;
10053    vms.newmessages = vms.lastmsg + 1;
10054    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10055    /* Start in Urgent */
10056    in_urgent = 1;
10057    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10058    if (res < 0)
10059       goto out;
10060    vms.urgentmessages = vms.lastmsg + 1;
10061    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10062 
10063    /* Select proper mailbox FIRST!! */
10064    if (play_auto) {
10065       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10066       if (vms.urgentmessages) {
10067          in_urgent = 1;
10068          res = open_mailbox(&vms, vmu, 11);
10069       } else {
10070          in_urgent = 0;
10071          res = open_mailbox(&vms, vmu, play_folder);
10072       }
10073       if (res < 0)
10074          goto out;
10075 
10076       /* If there are no new messages, inform the user and hangup */
10077       if (vms.lastmsg == -1) {
10078          in_urgent = 0;
10079          cmd = vm_browse_messages(chan, &vms, vmu);
10080          res = 0;
10081          goto out;
10082       }
10083    } else {
10084       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10085          /* If we only have old messages start here */
10086          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10087          in_urgent = 0;
10088          play_folder = 1;
10089          if (res < 0)
10090             goto out;
10091       } else if (!vms.urgentmessages && vms.newmessages) {
10092          /* If we have new messages but none are urgent */
10093          in_urgent = 0;
10094          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10095          if (res < 0)
10096             goto out;
10097       }
10098    }
10099 
10100    if (useadsi)
10101       adsi_status(chan, &vms);
10102    res = 0;
10103 
10104    /* Check to see if this is a new user */
10105    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10106       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10107       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10108          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10109       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10110       if ((cmd == 't') || (cmd == '#')) {
10111          /* Timeout */
10112          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10113          res = 0;
10114          goto out;
10115       } else if (cmd < 0) {
10116          /* Hangup */
10117          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10118          res = -1;
10119          goto out;
10120       }
10121    }
10122 #ifdef IMAP_STORAGE
10123       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10124       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10125          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10126          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10127       }
10128       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10129       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10130          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10131          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10132       }
10133 #endif
10134 
10135    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10136    if (play_auto) {
10137       cmd = '1';
10138    } else {
10139       cmd = vm_intro(chan, vmu, &vms);
10140    }
10141    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10142 
10143    vms.repeats = 0;
10144    vms.starting = 1;
10145    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10146       /* Run main menu */
10147       switch (cmd) {
10148       case '1': /* First message */
10149          vms.curmsg = 0;
10150          /* Fall through */
10151       case '5': /* Play current message */
10152          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10153          cmd = vm_browse_messages(chan, &vms, vmu);
10154          break;
10155       case '2': /* Change folders */
10156          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10157          if (useadsi)
10158             adsi_folders(chan, 0, "Change to folder...");
10159 
10160          cmd = get_folder2(chan, "vm-changeto", 0);
10161          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10162          if (cmd == '#') {
10163             cmd = 0;
10164          } else if (cmd > 0) {
10165             cmd = cmd - '0';
10166             res = close_mailbox(&vms, vmu);
10167             if (res == ERROR_LOCK_PATH)
10168                goto out;
10169             /* If folder is not urgent, set in_urgent to zero! */
10170             if (cmd != 11) in_urgent = 0;
10171             res = open_mailbox(&vms, vmu, cmd);
10172             if (res < 0)
10173                goto out;
10174             play_folder = cmd;
10175             cmd = 0;
10176          }
10177          if (useadsi)
10178             adsi_status2(chan, &vms);
10179 
10180          if (!cmd) {
10181             cmd = vm_play_folder_name(chan, vms.vmbox);
10182          }
10183 
10184          vms.starting = 1;
10185          break;
10186       case '3': /* Advanced options */
10187          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10188          cmd = 0;
10189          vms.repeats = 0;
10190          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10191             switch (cmd) {
10192             case '1': /* Reply */
10193                if (vms.lastmsg > -1 && !vms.starting) {
10194                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10195                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10196                      res = cmd;
10197                      goto out;
10198                   }
10199                } else {
10200                   cmd = ast_play_and_wait(chan, "vm-sorry");
10201                }
10202                cmd = 't';
10203                break;
10204             case '2': /* Callback */
10205                if (!vms.starting)
10206                   ast_verb(3, "Callback Requested\n");
10207                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10208                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10209                   if (cmd == 9) {
10210                      silentexit = 1;
10211                      goto out;
10212                   } else if (cmd == ERROR_LOCK_PATH) {
10213                      res = cmd;
10214                      goto out;
10215                   }
10216                } else {
10217                   cmd = ast_play_and_wait(chan, "vm-sorry");
10218                }
10219                cmd = 't';
10220                break;
10221             case '3': /* Envelope */
10222                if (vms.lastmsg > -1 && !vms.starting) {
10223                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10224                   if (cmd == ERROR_LOCK_PATH) {
10225                      res = cmd;
10226                      goto out;
10227                   }
10228                } else {
10229                   cmd = ast_play_and_wait(chan, "vm-sorry");
10230                }
10231                cmd = 't';
10232                break;
10233             case '4': /* Dialout */
10234                if (!ast_strlen_zero(vmu->dialout)) {
10235                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10236                   if (cmd == 9) {
10237                      silentexit = 1;
10238                      goto out;
10239                   }
10240                } else {
10241                   cmd = ast_play_and_wait(chan, "vm-sorry");
10242                }
10243                cmd = 't';
10244                break;
10245 
10246             case '5': /* Leave VoiceMail */
10247                if (ast_test_flag(vmu, VM_SVMAIL)) {
10248                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10249                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10250                      res = cmd;
10251                      goto out;
10252                   }
10253                } else {
10254                   cmd = ast_play_and_wait(chan, "vm-sorry");
10255                }
10256                cmd = 't';
10257                break;
10258 
10259             case '*': /* Return to main menu */
10260                cmd = 't';
10261                break;
10262 
10263             default:
10264                cmd = 0;
10265                if (!vms.starting) {
10266                   cmd = ast_play_and_wait(chan, "vm-toreply");
10267                }
10268                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10269                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10270                }
10271                if (!cmd && !vms.starting) {
10272                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10273                }
10274                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10275                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10276                }
10277                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10278                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10279                }
10280                if (!cmd) {
10281                   cmd = ast_play_and_wait(chan, "vm-starmain");
10282                }
10283                if (!cmd) {
10284                   cmd = ast_waitfordigit(chan, 6000);
10285                }
10286                if (!cmd) {
10287                   vms.repeats++;
10288                }
10289                if (vms.repeats > 3) {
10290                   cmd = 't';
10291                }
10292                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10293             }
10294          }
10295          if (cmd == 't') {
10296             cmd = 0;
10297             vms.repeats = 0;
10298          }
10299          break;
10300       case '4': /* Go to the previous message */
10301          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10302          if (vms.curmsg > 0) {
10303             vms.curmsg--;
10304             cmd = play_message(chan, vmu, &vms);
10305          } else {
10306             /* Check if we were listening to new
10307                messages.  If so, go to Urgent messages
10308                instead of saying "no more messages"
10309             */
10310             if (in_urgent == 0 && vms.urgentmessages > 0) {
10311                /* Check for Urgent messages */
10312                in_urgent = 1;
10313                res = close_mailbox(&vms, vmu);
10314                if (res == ERROR_LOCK_PATH)
10315                   goto out;
10316                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10317                if (res < 0)
10318                   goto out;
10319                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10320                vms.curmsg = vms.lastmsg;
10321                if (vms.lastmsg < 0) {
10322                   cmd = ast_play_and_wait(chan, "vm-nomore");
10323                }
10324             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10325                vms.curmsg = vms.lastmsg;
10326                cmd = play_message(chan, vmu, &vms);
10327             } else {
10328                cmd = ast_play_and_wait(chan, "vm-nomore");
10329             }
10330          }
10331          break;
10332       case '6': /* Go to the next message */
10333          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10334          if (vms.curmsg < vms.lastmsg) {
10335             vms.curmsg++;
10336             cmd = play_message(chan, vmu, &vms);
10337          } else {
10338             if (in_urgent && vms.newmessages > 0) {
10339                /* Check if we were listening to urgent
10340                 * messages.  If so, go to regular new messages
10341                 * instead of saying "no more messages"
10342                 */
10343                in_urgent = 0;
10344                res = close_mailbox(&vms, vmu);
10345                if (res == ERROR_LOCK_PATH)
10346                   goto out;
10347                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10348                if (res < 0)
10349                   goto out;
10350                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10351                vms.curmsg = -1;
10352                if (vms.lastmsg < 0) {
10353                   cmd = ast_play_and_wait(chan, "vm-nomore");
10354                }
10355             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10356                vms.curmsg = 0;
10357                cmd = play_message(chan, vmu, &vms);
10358             } else {
10359                cmd = ast_play_and_wait(chan, "vm-nomore");
10360             }
10361          }
10362          break;
10363       case '7': /* Delete the current message */
10364          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10365             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10366             if (useadsi)
10367                adsi_delete(chan, &vms);
10368             if (vms.deleted[vms.curmsg]) {
10369                if (play_folder == 0) {
10370                   if (in_urgent) {
10371                      vms.urgentmessages--;
10372                   } else {
10373                      vms.newmessages--;
10374                   }
10375                }
10376                else if (play_folder == 1)
10377                   vms.oldmessages--;
10378                cmd = ast_play_and_wait(chan, "vm-deleted");
10379             } else {
10380                if (play_folder == 0) {
10381                   if (in_urgent) {
10382                      vms.urgentmessages++;
10383                   } else {
10384                      vms.newmessages++;
10385                   }
10386                }
10387                else if (play_folder == 1)
10388                   vms.oldmessages++;
10389                cmd = ast_play_and_wait(chan, "vm-undeleted");
10390             }
10391             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10392                if (vms.curmsg < vms.lastmsg) {
10393                   vms.curmsg++;
10394                   cmd = play_message(chan, vmu, &vms);
10395                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10396                   vms.curmsg = 0;
10397                   cmd = play_message(chan, vmu, &vms);
10398                } else {
10399                   /* Check if we were listening to urgent
10400                      messages.  If so, go to regular new messages
10401                      instead of saying "no more messages"
10402                   */
10403                   if (in_urgent == 1) {
10404                      /* Check for new messages */
10405                      in_urgent = 0;
10406                      res = close_mailbox(&vms, vmu);
10407                      if (res == ERROR_LOCK_PATH)
10408                         goto out;
10409                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10410                      if (res < 0)
10411                         goto out;
10412                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10413                      vms.curmsg = -1;
10414                      if (vms.lastmsg < 0) {
10415                         cmd = ast_play_and_wait(chan, "vm-nomore");
10416                      }
10417                   } else {
10418                      cmd = ast_play_and_wait(chan, "vm-nomore");
10419                   }
10420                }
10421             }
10422          } else /* Delete not valid if we haven't selected a message */
10423             cmd = 0;
10424 #ifdef IMAP_STORAGE
10425          deleted = 1;
10426 #endif
10427          break;
10428    
10429       case '8': /* Forward the current message */
10430          if (vms.lastmsg > -1) {
10431             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10432             if (cmd == ERROR_LOCK_PATH) {
10433                res = cmd;
10434                goto out;
10435             }
10436          } else {
10437             /* Check if we were listening to urgent
10438                messages.  If so, go to regular new messages
10439                instead of saying "no more messages"
10440             */
10441             if (in_urgent == 1 && vms.newmessages > 0) {
10442                /* Check for new messages */
10443                in_urgent = 0;
10444                res = close_mailbox(&vms, vmu);
10445                if (res == ERROR_LOCK_PATH)
10446                   goto out;
10447                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10448                if (res < 0)
10449                   goto out;
10450                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10451                vms.curmsg = -1;
10452                if (vms.lastmsg < 0) {
10453                   cmd = ast_play_and_wait(chan, "vm-nomore");
10454                }
10455             } else {
10456                cmd = ast_play_and_wait(chan, "vm-nomore");
10457             }
10458          }
10459          break;
10460       case '9': /* Save message to folder */
10461          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10462          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10463             /* No message selected */
10464             cmd = 0;
10465             break;
10466          }
10467          if (useadsi)
10468             adsi_folders(chan, 1, "Save to folder...");
10469          cmd = get_folder2(chan, "vm-savefolder", 1);
10470          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10471          box = 0; /* Shut up compiler */
10472          if (cmd == '#') {
10473             cmd = 0;
10474             break;
10475          } else if (cmd > 0) {
10476             box = cmd = cmd - '0';
10477             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10478             if (cmd == ERROR_LOCK_PATH) {
10479                res = cmd;
10480                goto out;
10481 #ifndef IMAP_STORAGE
10482             } else if (!cmd) {
10483                vms.deleted[vms.curmsg] = 1;
10484 #endif
10485             } else {
10486                vms.deleted[vms.curmsg] = 0;
10487                vms.heard[vms.curmsg] = 0;
10488             }
10489          }
10490          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10491          if (useadsi)
10492             adsi_message(chan, &vms);
10493          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10494          if (!cmd) {
10495             cmd = ast_play_and_wait(chan, "vm-message");
10496             if (!cmd) 
10497                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10498             if (!cmd)
10499                cmd = ast_play_and_wait(chan, "vm-savedto");
10500             if (!cmd)
10501                cmd = vm_play_folder_name(chan, vms.fn);
10502          } else {
10503             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10504          }
10505          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10506             if (vms.curmsg < vms.lastmsg) {
10507                vms.curmsg++;
10508                cmd = play_message(chan, vmu, &vms);
10509             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10510                vms.curmsg = 0;
10511                cmd = play_message(chan, vmu, &vms);
10512             } else {
10513                /* Check if we were listening to urgent
10514                   messages.  If so, go to regular new messages
10515                   instead of saying "no more messages"
10516                */
10517                if (in_urgent == 1 && vms.newmessages > 0) {
10518                   /* Check for new messages */
10519                   in_urgent = 0;
10520                   res = close_mailbox(&vms, vmu);
10521                   if (res == ERROR_LOCK_PATH)
10522                      goto out;
10523                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10524                   if (res < 0)
10525                      goto out;
10526                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10527                   vms.curmsg = -1;
10528                   if (vms.lastmsg < 0) {
10529                      cmd = ast_play_and_wait(chan, "vm-nomore");
10530                   }
10531                } else {
10532                   cmd = ast_play_and_wait(chan, "vm-nomore");
10533                }
10534             }
10535          }
10536          break;
10537       case '*': /* Help */
10538          if (!vms.starting) {
10539             cmd = ast_play_and_wait(chan, "vm-onefor");
10540             if (!strncasecmp(chan->language, "he", 2)) {
10541                cmd = ast_play_and_wait(chan, "vm-for");
10542             }
10543             if (!cmd)
10544                cmd = vm_play_folder_name(chan, vms.vmbox);
10545             if (!cmd)
10546                cmd = ast_play_and_wait(chan, "vm-opts");
10547             if (!cmd)
10548                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10549          } else
10550             cmd = 0;
10551          break;
10552       case '0': /* Mailbox options */
10553          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10554          if (useadsi)
10555             adsi_status(chan, &vms);
10556          break;
10557       default: /* Nothing */
10558          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10559          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10560          break;
10561       }
10562    }
10563    if ((cmd == 't') || (cmd == '#')) {
10564       /* Timeout */
10565       res = 0;
10566    } else {
10567       /* Hangup */
10568       res = -1;
10569    }
10570 
10571 out:
10572    if (res > -1) {
10573       ast_stopstream(chan);
10574       adsi_goodbye(chan);
10575       if (valid && res != OPERATOR_EXIT) {
10576          if (silentexit)
10577             res = ast_play_and_wait(chan, "vm-dialout");
10578          else 
10579             res = ast_play_and_wait(chan, "vm-goodbye");
10580       }
10581       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10582          res = 0;
10583       }
10584       if (useadsi)
10585          ast_adsi_unload_session(chan);
10586    }
10587    if (vmu)
10588       close_mailbox(&vms, vmu);
10589    if (valid) {
10590       int new = 0, old = 0, urgent = 0;
10591       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10592       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10593       /* Urgent flag not passwd to externnotify here */
10594       run_externnotify(vmu->context, vmu->mailbox, NULL);
10595       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10596       queue_mwi_event(ext_context, urgent, new, old);
10597    }
10598 #ifdef IMAP_STORAGE
10599    /* expunge message - use UID Expunge if supported on IMAP server*/
10600    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10601    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10602       ast_mutex_lock(&vms.lock);
10603 #ifdef HAVE_IMAP_TK2006
10604       if (LEVELUIDPLUS (vms.mailstream)) {
10605          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10606       } else 
10607 #endif
10608          mail_expunge(vms.mailstream);
10609       ast_mutex_unlock(&vms.lock);
10610    }
10611    /*  before we delete the state, we should copy pertinent info
10612     *  back to the persistent model */
10613    if (vmu) {
10614       vmstate_delete(&vms);
10615    }
10616 #endif
10617    if (vmu)
10618       free_user(vmu);
10619    if (vms.deleted)
10620       ast_free(vms.deleted);
10621    if (vms.heard)
10622       ast_free(vms.heard);
10623 
10624 #ifdef IMAP_STORAGE
10625    pthread_setspecific(ts_vmstate.key, NULL);
10626 #endif
10627    return res;
10628 }
10629 
10630 static int vm_exec(struct ast_channel *chan, const char *data)
10631 {
10632    int res = 0;
10633    char *tmp;
10634    struct leave_vm_options leave_options;
10635    struct ast_flags flags = { 0 };
10636    char *opts[OPT_ARG_ARRAY_SIZE];
10637    AST_DECLARE_APP_ARGS(args,
10638       AST_APP_ARG(argv0);
10639       AST_APP_ARG(argv1);
10640    );
10641    
10642    memset(&leave_options, 0, sizeof(leave_options));
10643 
10644    if (chan->_state != AST_STATE_UP)
10645       ast_answer(chan);
10646 
10647    if (!ast_strlen_zero(data)) {
10648       tmp = ast_strdupa(data);
10649       AST_STANDARD_APP_ARGS(args, tmp);
10650       if (args.argc == 2) {
10651          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10652             return -1;
10653          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10654          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10655             int gain;
10656 
10657             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10658                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10659                return -1;
10660             } else {
10661                leave_options.record_gain = (signed char) gain;
10662             }
10663          }
10664          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10665             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10666                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10667          }
10668       }
10669    } else {
10670       char temp[256];
10671       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10672       if (res < 0)
10673          return res;
10674       if (ast_strlen_zero(temp))
10675          return 0;
10676       args.argv0 = ast_strdupa(temp);
10677    }
10678 
10679    res = leave_voicemail(chan, args.argv0, &leave_options);
10680    if (res == 't') {
10681       ast_play_and_wait(chan, "vm-goodbye");
10682       res = 0;
10683    }
10684 
10685    if (res == OPERATOR_EXIT) {
10686       res = 0;
10687    }
10688 
10689    if (res == ERROR_LOCK_PATH) {
10690       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10691       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10692       res = 0;
10693    }
10694 
10695    return res;
10696 }
10697 
10698 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10699 {
10700    struct ast_vm_user *vmu;
10701 
10702    if (!ast_strlen_zero(box) && box[0] == '*') {
10703       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10704             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10705             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10706             "\n\tand will be ignored.\n", box, context);
10707       return NULL;
10708    }
10709 
10710    AST_LIST_TRAVERSE(&users, vmu, list) {
10711       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10712          if (strcasecmp(vmu->context, context)) {
10713             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10714                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10715                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10716                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10717          }
10718          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10719          return NULL;
10720       }
10721       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10722          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10723          return NULL;
10724       }
10725    }
10726    
10727    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10728       return NULL;
10729    
10730    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10731    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10732 
10733    AST_LIST_INSERT_TAIL(&users, vmu, list);
10734    
10735    return vmu;
10736 }
10737 
10738 static int append_mailbox(const char *context, const char *box, const char *data)
10739 {
10740    /* Assumes lock is already held */
10741    char *tmp;
10742    char *stringp;
10743    char *s;
10744    struct ast_vm_user *vmu;
10745    char *mailbox_full;
10746    int new = 0, old = 0, urgent = 0;
10747    char secretfn[PATH_MAX] = "";
10748 
10749    tmp = ast_strdupa(data);
10750 
10751    if (!(vmu = find_or_create(context, box)))
10752       return -1;
10753 
10754    populate_defaults(vmu);
10755 
10756    stringp = tmp;
10757    if ((s = strsep(&stringp, ","))) {
10758       if (!ast_strlen_zero(s) && s[0] == '*') {
10759          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10760             "\n\tmust be reset in voicemail.conf.\n", box);
10761       }
10762       /* assign password regardless of validity to prevent NULL password from being assigned */
10763       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10764    }
10765    if (stringp && (s = strsep(&stringp, ","))) {
10766       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10767    }
10768    if (stringp && (s = strsep(&stringp, ","))) {
10769       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10770    }
10771    if (stringp && (s = strsep(&stringp, ","))) {
10772       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10773    }
10774    if (stringp && (s = strsep(&stringp, ","))) {
10775       apply_options(vmu, s);
10776    }
10777 
10778    switch (vmu->passwordlocation) {
10779    case OPT_PWLOC_SPOOLDIR:
10780       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10781       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10782    }
10783 
10784    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10785    strcpy(mailbox_full, box);
10786    strcat(mailbox_full, "@");
10787    strcat(mailbox_full, context);
10788 
10789    inboxcount2(mailbox_full, &urgent, &new, &old);
10790    queue_mwi_event(mailbox_full, urgent, new, old);
10791 
10792    return 0;
10793 }
10794 
10795 AST_TEST_DEFINE(test_voicemail_vmuser)
10796 {
10797    int res = 0;
10798    struct ast_vm_user *vmu;
10799    /* language parameter seems to only be used for display in manager action */
10800    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10801       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10802       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10803       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10804       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10805       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10806       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10807       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10808       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10809 #ifdef IMAP_STORAGE
10810    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10811       "imapfolder=INBOX|imapvmshareid=6000";
10812 #endif
10813 
10814    switch (cmd) {
10815    case TEST_INIT:
10816       info->name = "vmuser";
10817       info->category = "/apps/app_voicemail/";
10818       info->summary = "Vmuser unit test";
10819       info->description =
10820          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10821       return AST_TEST_NOT_RUN;
10822    case TEST_EXECUTE:
10823       break;
10824    }
10825 
10826    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10827       return AST_TEST_NOT_RUN;
10828    }
10829    ast_set_flag(vmu, VM_ALLOCED);
10830    populate_defaults(vmu);
10831 
10832    apply_options(vmu, options_string);
10833 
10834    if (!ast_test_flag(vmu, VM_ATTACH)) {
10835       ast_test_status_update(test, "Parse failure for attach option\n");
10836       res = 1;
10837    }
10838    if (strcasecmp(vmu->attachfmt, "wav49")) {
10839       ast_test_status_update(test, "Parse failure for attachftm option\n");
10840       res = 1;
10841    }
10842    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10843       ast_test_status_update(test, "Parse failure for serveremail option\n");
10844       res = 1;
10845    }
10846    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10847       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10848       res = 1;
10849    }
10850    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10851       ast_test_status_update(test, "Parse failure for emailbody option\n");
10852       res = 1;
10853    }
10854    if (strcasecmp(vmu->zonetag, "central")) {
10855       ast_test_status_update(test, "Parse failure for tz option\n");
10856       res = 1;
10857    }
10858    if (!ast_test_flag(vmu, VM_DELETE)) {
10859       ast_test_status_update(test, "Parse failure for delete option\n");
10860       res = 1;
10861    }
10862    if (!ast_test_flag(vmu, VM_SAYCID)) {
10863       ast_test_status_update(test, "Parse failure for saycid option\n");
10864       res = 1;
10865    }
10866    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10867       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10868       res = 1;
10869    }
10870    if (!ast_test_flag(vmu, VM_REVIEW)) {
10871       ast_test_status_update(test, "Parse failure for review option\n");
10872       res = 1;
10873    }
10874    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10875       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10876       res = 1;
10877    }
10878    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10879       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10880       res = 1;
10881    }
10882    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10883       ast_test_status_update(test, "Parse failure for operator option\n");
10884       res = 1;
10885    }
10886    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10887       ast_test_status_update(test, "Parse failure for envelope option\n");
10888       res = 1;
10889    }
10890    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10891       ast_test_status_update(test, "Parse failure for moveheard option\n");
10892       res = 1;
10893    }
10894    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10895       ast_test_status_update(test, "Parse failure for sayduration option\n");
10896       res = 1;
10897    }
10898    if (vmu->saydurationm != 5) {
10899       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10900       res = 1;
10901    }
10902    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10903       ast_test_status_update(test, "Parse failure for forcename option\n");
10904       res = 1;
10905    }
10906    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10907       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10908       res = 1;
10909    }
10910    if (strcasecmp(vmu->callback, "somecontext")) {
10911       ast_test_status_update(test, "Parse failure for callbacks option\n");
10912       res = 1;
10913    }
10914    if (strcasecmp(vmu->dialout, "somecontext2")) {
10915       ast_test_status_update(test, "Parse failure for dialout option\n");
10916       res = 1;
10917    }
10918    if (strcasecmp(vmu->exit, "somecontext3")) {
10919       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10920       res = 1;
10921    }
10922    if (vmu->minsecs != 10) {
10923       ast_test_status_update(test, "Parse failure for minsecs option\n");
10924       res = 1;
10925    }
10926    if (vmu->maxsecs != 100) {
10927       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10928       res = 1;
10929    }
10930    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10931       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10932       res = 1;
10933    }
10934    if (vmu->maxdeletedmsg != 50) {
10935       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10936       res = 1;
10937    }
10938    if (vmu->volgain != 1.3) {
10939       ast_test_status_update(test, "Parse failure for volgain option\n");
10940       res = 1;
10941    }
10942    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10943       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10944       res = 1;
10945    }
10946 #ifdef IMAP_STORAGE
10947    apply_options(vmu, option_string2);
10948 
10949    if (strcasecmp(vmu->imapuser, "imapuser")) {
10950       ast_test_status_update(test, "Parse failure for imapuser option\n");
10951       res = 1;
10952    }
10953    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10954       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10955       res = 1;
10956    }
10957    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10958       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10959       res = 1;
10960    }
10961    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10962       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10963       res = 1;
10964    }
10965 #endif
10966 
10967    free_user(vmu);
10968    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10969 }
10970 
10971 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10972 {
10973    struct ast_vm_user svm;
10974    char *context, *box;
10975    AST_DECLARE_APP_ARGS(args,
10976       AST_APP_ARG(mbox);
10977       AST_APP_ARG(options);
10978    );
10979    static int dep_warning = 0;
10980 
10981    if (ast_strlen_zero(data)) {
10982       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10983       return -1;
10984    }
10985 
10986    if (!dep_warning) {
10987       dep_warning = 1;
10988       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10989    }
10990 
10991    box = ast_strdupa(data);
10992 
10993    AST_STANDARD_APP_ARGS(args, box);
10994 
10995    if (args.options) {
10996    }
10997 
10998    if ((context = strchr(args.mbox, '@'))) {
10999       *context = '\0';
11000       context++;
11001    }
11002 
11003    if (find_user(&svm, context, args.mbox)) {
11004       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11005    } else
11006       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11007 
11008    return 0;
11009 }
11010 
11011 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11012 {
11013    struct ast_vm_user svm;
11014    AST_DECLARE_APP_ARGS(arg,
11015       AST_APP_ARG(mbox);
11016       AST_APP_ARG(context);
11017    );
11018 
11019    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11020 
11021    if (ast_strlen_zero(arg.mbox)) {
11022       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11023       return -1;
11024    }
11025 
11026    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11027    return 0;
11028 }
11029 
11030 static struct ast_custom_function mailbox_exists_acf = {
11031    .name = "MAILBOX_EXISTS",
11032    .read = acf_mailbox_exists,
11033 };
11034 
11035 static int vmauthenticate(struct ast_channel *chan, const char *data)
11036 {
11037    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11038    struct ast_vm_user vmus;
11039    char *options = NULL;
11040    int silent = 0, skipuser = 0;
11041    int res = -1;
11042    
11043    if (data) {
11044       s = ast_strdupa(data);
11045       user = strsep(&s, ",");
11046       options = strsep(&s, ",");
11047       if (user) {
11048          s = user;
11049          user = strsep(&s, "@");
11050          context = strsep(&s, "");
11051          if (!ast_strlen_zero(user))
11052             skipuser++;
11053          ast_copy_string(mailbox, user, sizeof(mailbox));
11054       }
11055    }
11056 
11057    if (options) {
11058       silent = (strchr(options, 's')) != NULL;
11059    }
11060 
11061    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11062       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11063       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11064       ast_play_and_wait(chan, "auth-thankyou");
11065       res = 0;
11066    } else if (mailbox[0] == '*') {
11067       /* user entered '*' */
11068       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11069          res = 0; /* prevent hangup */
11070       }
11071    }
11072 
11073    return res;
11074 }
11075 
11076 static char *show_users_realtime(int fd, const char *context)
11077 {
11078    struct ast_config *cfg;
11079    const char *cat = NULL;
11080 
11081    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11082       "context", context, SENTINEL))) {
11083       return CLI_FAILURE;
11084    }
11085 
11086    ast_cli(fd,
11087       "\n"
11088       "=============================================================\n"
11089       "=== Configured Voicemail Users ==============================\n"
11090       "=============================================================\n"
11091       "===\n");
11092 
11093    while ((cat = ast_category_browse(cfg, cat))) {
11094       struct ast_variable *var = NULL;
11095       ast_cli(fd,
11096          "=== Mailbox ...\n"
11097          "===\n");
11098       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11099          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11100       ast_cli(fd,
11101          "===\n"
11102          "=== ---------------------------------------------------------\n"
11103          "===\n");
11104    }
11105 
11106    ast_cli(fd,
11107       "=============================================================\n"
11108       "\n");
11109 
11110    ast_config_destroy(cfg);
11111 
11112    return CLI_SUCCESS;
11113 }
11114 
11115 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11116 {
11117    int which = 0;
11118    int wordlen;
11119    struct ast_vm_user *vmu;
11120    const char *context = "";
11121 
11122    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11123    if (pos > 4)
11124       return NULL;
11125    if (pos == 3)
11126       return (state == 0) ? ast_strdup("for") : NULL;
11127    wordlen = strlen(word);
11128    AST_LIST_TRAVERSE(&users, vmu, list) {
11129       if (!strncasecmp(word, vmu->context, wordlen)) {
11130          if (context && strcmp(context, vmu->context) && ++which > state)
11131             return ast_strdup(vmu->context);
11132          /* ignore repeated contexts ? */
11133          context = vmu->context;
11134       }
11135    }
11136    return NULL;
11137 }
11138 
11139 /*! \brief Show a list of voicemail users in the CLI */
11140 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11141 {
11142    struct ast_vm_user *vmu;
11143 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11144    const char *context = NULL;
11145    int users_counter = 0;
11146 
11147    switch (cmd) {
11148    case CLI_INIT:
11149       e->command = "voicemail show users";
11150       e->usage =
11151          "Usage: voicemail show users [for <context>]\n"
11152          "       Lists all mailboxes currently set up\n";
11153       return NULL;
11154    case CLI_GENERATE:
11155       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11156    }  
11157 
11158    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11159       return CLI_SHOWUSAGE;
11160    if (a->argc == 5) {
11161       if (strcmp(a->argv[3],"for"))
11162          return CLI_SHOWUSAGE;
11163       context = a->argv[4];
11164    }
11165 
11166    if (ast_check_realtime("voicemail")) {
11167       if (!context) {
11168          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11169          return CLI_SHOWUSAGE;
11170       }
11171       return show_users_realtime(a->fd, context);
11172    }
11173 
11174    AST_LIST_LOCK(&users);
11175    if (AST_LIST_EMPTY(&users)) {
11176       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11177       AST_LIST_UNLOCK(&users);
11178       return CLI_FAILURE;
11179    }
11180    if (a->argc == 3)
11181       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11182    else {
11183       int count = 0;
11184       AST_LIST_TRAVERSE(&users, vmu, list) {
11185          if (!strcmp(context, vmu->context))
11186             count++;
11187       }
11188       if (count) {
11189          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11190       } else {
11191          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11192          AST_LIST_UNLOCK(&users);
11193          return CLI_FAILURE;
11194       }
11195    }
11196    AST_LIST_TRAVERSE(&users, vmu, list) {
11197       int newmsgs = 0, oldmsgs = 0;
11198       char count[12], tmp[256] = "";
11199 
11200       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
11201          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11202          inboxcount(tmp, &newmsgs, &oldmsgs);
11203          snprintf(count, sizeof(count), "%d", newmsgs);
11204          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11205          users_counter++;
11206       }
11207    }
11208    AST_LIST_UNLOCK(&users);
11209    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11210    return CLI_SUCCESS;
11211 }
11212 
11213 /*! \brief Show a list of voicemail zones in the CLI */
11214 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11215 {
11216    struct vm_zone *zone;
11217 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11218    char *res = CLI_SUCCESS;
11219 
11220    switch (cmd) {
11221    case CLI_INIT:
11222       e->command = "voicemail show zones";
11223       e->usage =
11224          "Usage: voicemail show zones\n"
11225          "       Lists zone message formats\n";
11226       return NULL;
11227    case CLI_GENERATE:
11228       return NULL;
11229    }
11230 
11231    if (a->argc != 3)
11232       return CLI_SHOWUSAGE;
11233 
11234    AST_LIST_LOCK(&zones);
11235    if (!AST_LIST_EMPTY(&zones)) {
11236       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11237       AST_LIST_TRAVERSE(&zones, zone, list) {
11238          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11239       }
11240    } else {
11241       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11242       res = CLI_FAILURE;
11243    }
11244    AST_LIST_UNLOCK(&zones);
11245 
11246    return res;
11247 }
11248 
11249 /*! \brief Reload voicemail configuration from the CLI */
11250 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11251 {
11252    switch (cmd) {
11253    case CLI_INIT:
11254       e->command = "voicemail reload";
11255       e->usage =
11256          "Usage: voicemail reload\n"
11257          "       Reload voicemail configuration\n";
11258       return NULL;
11259    case CLI_GENERATE:
11260       return NULL;
11261    }
11262 
11263    if (a->argc != 2)
11264       return CLI_SHOWUSAGE;
11265 
11266    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11267    load_config(1);
11268    
11269    return CLI_SUCCESS;
11270 }
11271 
11272 static struct ast_cli_entry cli_voicemail[] = {
11273    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11274    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11275    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11276 };
11277 
11278 #ifdef IMAP_STORAGE
11279    #define DATA_EXPORT_VM_USERS(USER)              \
11280       USER(ast_vm_user, context, AST_DATA_STRING)        \
11281       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11282       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11283       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11284       USER(ast_vm_user, email, AST_DATA_STRING)       \
11285       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11286       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11287       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11288       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11289       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11290       USER(ast_vm_user, language, AST_DATA_STRING)       \
11291       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11292       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11293       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11294       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11295       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11296       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11297       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11298       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11299       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11300       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11301       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11302       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11303       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11304       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11305       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11306 #else
11307    #define DATA_EXPORT_VM_USERS(USER)              \
11308       USER(ast_vm_user, context, AST_DATA_STRING)        \
11309       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11310       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11311       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11312       USER(ast_vm_user, email, AST_DATA_STRING)       \
11313       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11314       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11315       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11316       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11317       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11318       USER(ast_vm_user, language, AST_DATA_STRING)       \
11319       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11320       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11321       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11322       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11323       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11324       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11325       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11326       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11327       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11328       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11329       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11330       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11331 #endif
11332 
11333 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11334 
11335 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11336    ZONE(vm_zone, name, AST_DATA_STRING)      \
11337    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11338    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11339 
11340 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11341 
11342 /*!
11343  * \internal
11344  * \brief Add voicemail user to the data_root.
11345  * \param[in] search The search tree.
11346  * \param[in] data_root The main result node.
11347  * \param[in] user The voicemail user.
11348  */
11349 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11350     struct ast_data *data_root, struct ast_vm_user *user)
11351 {
11352    struct ast_data *data_user, *data_zone;
11353    struct ast_data *data_state;
11354    struct vm_zone *zone = NULL;
11355    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11356    char ext_context[256] = "";
11357 
11358    data_user = ast_data_add_node(data_root, "user");
11359    if (!data_user) {
11360       return -1;
11361    }
11362 
11363    ast_data_add_structure(ast_vm_user, data_user, user);
11364 
11365    AST_LIST_LOCK(&zones);
11366    AST_LIST_TRAVERSE(&zones, zone, list) {
11367       if (!strcmp(zone->name, user->zonetag)) {
11368          break;
11369       }
11370    }
11371    AST_LIST_UNLOCK(&zones);
11372 
11373    /* state */
11374    data_state = ast_data_add_node(data_user, "state");
11375    if (!data_state) {
11376       return -1;
11377    }
11378    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11379    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11380    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11381    ast_data_add_int(data_state, "newmsg", newmsg);
11382    ast_data_add_int(data_state, "oldmsg", oldmsg);
11383 
11384    if (zone) {
11385       data_zone = ast_data_add_node(data_user, "zone");
11386       ast_data_add_structure(vm_zone, data_zone, zone);
11387    }
11388 
11389    if (!ast_data_search_match(search, data_user)) {
11390       ast_data_remove_node(data_root, data_user);
11391    }
11392 
11393    return 0;
11394 }
11395 
11396 static int vm_users_data_provider_get(const struct ast_data_search *search,
11397    struct ast_data *data_root)
11398 {
11399    struct ast_vm_user *user;
11400 
11401    AST_LIST_LOCK(&users);
11402    AST_LIST_TRAVERSE(&users, user, list) {
11403       vm_users_data_provider_get_helper(search, data_root, user);
11404    }
11405    AST_LIST_UNLOCK(&users);
11406 
11407    return 0;
11408 }
11409 
11410 static const struct ast_data_handler vm_users_data_provider = {
11411    .version = AST_DATA_HANDLER_VERSION,
11412    .get = vm_users_data_provider_get
11413 };
11414 
11415 static const struct ast_data_entry vm_data_providers[] = {
11416    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11417 };
11418 
11419 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11420 {
11421    int new = 0, old = 0, urgent = 0;
11422 
11423    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11424 
11425    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11426       mwi_sub->old_urgent = urgent;
11427       mwi_sub->old_new = new;
11428       mwi_sub->old_old = old;
11429       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11430       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11431    }
11432 }
11433 
11434 static void poll_subscribed_mailboxes(void)
11435 {
11436    struct mwi_sub *mwi_sub;
11437 
11438    AST_RWLIST_RDLOCK(&mwi_subs);
11439    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11440       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11441          poll_subscribed_mailbox(mwi_sub);
11442       }
11443    }
11444    AST_RWLIST_UNLOCK(&mwi_subs);
11445 }
11446 
11447 static void *mb_poll_thread(void *data)
11448 {
11449    while (poll_thread_run) {
11450       struct timespec ts = { 0, };
11451       struct timeval wait;
11452 
11453       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11454       ts.tv_sec = wait.tv_sec;
11455       ts.tv_nsec = wait.tv_usec * 1000;
11456 
11457       ast_mutex_lock(&poll_lock);
11458       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11459       ast_mutex_unlock(&poll_lock);
11460 
11461       if (!poll_thread_run)
11462          break;
11463 
11464       poll_subscribed_mailboxes();
11465    }
11466 
11467    return NULL;
11468 }
11469 
11470 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11471 {
11472    ast_free(mwi_sub);
11473 }
11474 
11475 static int handle_unsubscribe(void *datap)
11476 {
11477    struct mwi_sub *mwi_sub;
11478    uint32_t *uniqueid = datap;
11479    
11480    AST_RWLIST_WRLOCK(&mwi_subs);
11481    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11482       if (mwi_sub->uniqueid == *uniqueid) {
11483          AST_LIST_REMOVE_CURRENT(entry);
11484          break;
11485       }
11486    }
11487    AST_RWLIST_TRAVERSE_SAFE_END
11488    AST_RWLIST_UNLOCK(&mwi_subs);
11489 
11490    if (mwi_sub)
11491       mwi_sub_destroy(mwi_sub);
11492 
11493    ast_free(uniqueid);  
11494    return 0;
11495 }
11496 
11497 static int handle_subscribe(void *datap)
11498 {
11499    unsigned int len;
11500    struct mwi_sub *mwi_sub;
11501    struct mwi_sub_task *p = datap;
11502 
11503    len = sizeof(*mwi_sub);
11504    if (!ast_strlen_zero(p->mailbox))
11505       len += strlen(p->mailbox);
11506 
11507    if (!ast_strlen_zero(p->context))
11508       len += strlen(p->context) + 1; /* Allow for seperator */
11509 
11510    if (!(mwi_sub = ast_calloc(1, len)))
11511       return -1;
11512 
11513    mwi_sub->uniqueid = p->uniqueid;
11514    if (!ast_strlen_zero(p->mailbox))
11515       strcpy(mwi_sub->mailbox, p->mailbox);
11516 
11517    if (!ast_strlen_zero(p->context)) {
11518       strcat(mwi_sub->mailbox, "@");
11519       strcat(mwi_sub->mailbox, p->context);
11520    }
11521 
11522    AST_RWLIST_WRLOCK(&mwi_subs);
11523    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11524    AST_RWLIST_UNLOCK(&mwi_subs);
11525    ast_free((void *) p->mailbox);
11526    ast_free((void *) p->context);
11527    ast_free(p);
11528    poll_subscribed_mailbox(mwi_sub);
11529    return 0;
11530 }
11531 
11532 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11533 {
11534    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11535    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11536       return;
11537 
11538    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11539       return;
11540 
11541    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11542    *uniqueid = u;
11543    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11544       ast_free(uniqueid);
11545    }
11546 }
11547 
11548 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11549 {
11550    struct mwi_sub_task *mwist;
11551    
11552    if (ast_event_get_type(event) != AST_EVENT_SUB)
11553       return;
11554 
11555    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11556       return;
11557 
11558    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11559       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11560       return;
11561    }
11562    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11563    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11564    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11565    
11566    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11567       ast_free(mwist);
11568    }
11569 }
11570 
11571 static void start_poll_thread(void)
11572 {
11573    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11574       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11575       AST_EVENT_IE_END);
11576 
11577    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11578       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11579       AST_EVENT_IE_END);
11580 
11581    if (mwi_sub_sub)
11582       ast_event_report_subs(mwi_sub_sub);
11583 
11584    poll_thread_run = 1;
11585 
11586    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11587 }
11588 
11589 static void stop_poll_thread(void)
11590 {
11591    poll_thread_run = 0;
11592 
11593    if (mwi_sub_sub) {
11594       ast_event_unsubscribe(mwi_sub_sub);
11595       mwi_sub_sub = NULL;
11596    }
11597 
11598    if (mwi_unsub_sub) {
11599       ast_event_unsubscribe(mwi_unsub_sub);
11600       mwi_unsub_sub = NULL;
11601    }
11602 
11603    ast_mutex_lock(&poll_lock);
11604    ast_cond_signal(&poll_cond);
11605    ast_mutex_unlock(&poll_lock);
11606 
11607    pthread_join(poll_thread, NULL);
11608 
11609    poll_thread = AST_PTHREADT_NULL;
11610 }
11611 
11612 /*! \brief Manager list voicemail users command */
11613 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11614 {
11615    struct ast_vm_user *vmu = NULL;
11616    const char *id = astman_get_header(m, "ActionID");
11617    char actionid[128] = "";
11618 
11619    if (!ast_strlen_zero(id))
11620       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11621 
11622    AST_LIST_LOCK(&users);
11623 
11624    if (AST_LIST_EMPTY(&users)) {
11625       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11626       AST_LIST_UNLOCK(&users);
11627       return RESULT_SUCCESS;
11628    }
11629    
11630    astman_send_ack(s, m, "Voicemail user list will follow");
11631    
11632    AST_LIST_TRAVERSE(&users, vmu, list) {
11633       char dirname[256];
11634 
11635 #ifdef IMAP_STORAGE
11636       int new, old;
11637       inboxcount(vmu->mailbox, &new, &old);
11638 #endif
11639       
11640       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11641       astman_append(s,
11642          "%s"
11643          "Event: VoicemailUserEntry\r\n"
11644          "VMContext: %s\r\n"
11645          "VoiceMailbox: %s\r\n"
11646          "Fullname: %s\r\n"
11647          "Email: %s\r\n"
11648          "Pager: %s\r\n"
11649          "ServerEmail: %s\r\n"
11650          "MailCommand: %s\r\n"
11651          "Language: %s\r\n"
11652          "TimeZone: %s\r\n"
11653          "Callback: %s\r\n"
11654          "Dialout: %s\r\n"
11655          "UniqueID: %s\r\n"
11656          "ExitContext: %s\r\n"
11657          "SayDurationMinimum: %d\r\n"
11658          "SayEnvelope: %s\r\n"
11659          "SayCID: %s\r\n"
11660          "AttachMessage: %s\r\n"
11661          "AttachmentFormat: %s\r\n"
11662          "DeleteMessage: %s\r\n"
11663          "VolumeGain: %.2f\r\n"
11664          "CanReview: %s\r\n"
11665          "CallOperator: %s\r\n"
11666          "MaxMessageCount: %d\r\n"
11667          "MaxMessageLength: %d\r\n"
11668          "NewMessageCount: %d\r\n"
11669 #ifdef IMAP_STORAGE
11670          "OldMessageCount: %d\r\n"
11671          "IMAPUser: %s\r\n"
11672 #endif
11673          "\r\n",
11674          actionid,
11675          vmu->context,
11676          vmu->mailbox,
11677          vmu->fullname,
11678          vmu->email,
11679          vmu->pager,
11680          vmu->serveremail,
11681          vmu->mailcmd,
11682          vmu->language,
11683          vmu->zonetag,
11684          vmu->callback,
11685          vmu->dialout,
11686          vmu->uniqueid,
11687          vmu->exit,
11688          vmu->saydurationm,
11689          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11690          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11691          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11692          vmu->attachfmt,
11693          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11694          vmu->volgain,
11695          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11696          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11697          vmu->maxmsg,
11698          vmu->maxsecs,
11699 #ifdef IMAP_STORAGE
11700          new, old, vmu->imapuser
11701 #else
11702          count_messages(vmu, dirname)
11703 #endif
11704          );
11705    }     
11706    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11707 
11708    AST_LIST_UNLOCK(&users);
11709 
11710    return RESULT_SUCCESS;
11711 }
11712 
11713 /*! \brief Free the users structure. */
11714 static void free_vm_users(void) 
11715 {
11716    struct ast_vm_user *current;
11717    AST_LIST_LOCK(&users);
11718    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11719       ast_set_flag(current, VM_ALLOCED);
11720       free_user(current);
11721    }
11722    AST_LIST_UNLOCK(&users);
11723 }
11724 
11725 /*! \brief Free the zones structure. */
11726 static void free_vm_zones(void)
11727 {
11728    struct vm_zone *zcur;
11729    AST_LIST_LOCK(&zones);
11730    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11731       free_zone(zcur);
11732    AST_LIST_UNLOCK(&zones);
11733 }
11734 
11735 static const char *substitute_escapes(const char *value)
11736 {
11737    char *current;
11738 
11739    /* Add 16 for fudge factor */
11740    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11741 
11742    ast_str_reset(str);
11743    
11744    /* Substitute strings \r, \n, and \t into the appropriate characters */
11745    for (current = (char *) value; *current; current++) {
11746       if (*current == '\\') {
11747          current++;
11748          if (!*current) {
11749             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11750             break;
11751          }
11752          switch (*current) {
11753          case '\\':
11754             ast_str_append(&str, 0, "\\");
11755             break;
11756          case 'r':
11757             ast_str_append(&str, 0, "\r");
11758             break;
11759          case 'n':
11760 #ifdef IMAP_STORAGE
11761             if (!str->used || str->str[str->used - 1] != '\r') {
11762                ast_str_append(&str, 0, "\r");
11763             }
11764 #endif
11765             ast_str_append(&str, 0, "\n");
11766             break;
11767          case 't':
11768             ast_str_append(&str, 0, "\t");
11769             break;
11770          default:
11771             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11772             break;
11773          }
11774       } else {
11775          ast_str_append(&str, 0, "%c", *current);
11776       }
11777    }
11778 
11779    return ast_str_buffer(str);
11780 }
11781 
11782 static int load_config(int reload)
11783 {
11784    struct ast_config *cfg, *ucfg;
11785    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11786    int res;
11787 
11788    ast_unload_realtime("voicemail");
11789    ast_unload_realtime("voicemail_data");
11790 
11791    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11792       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11793          return 0;
11794       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11795          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11796          ucfg = NULL;
11797       }
11798       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11799       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11800          ast_config_destroy(ucfg);
11801          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11802          return 0;
11803       }
11804    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11805       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11806       return 0;
11807    } else {
11808       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11809       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11810          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11811          ucfg = NULL;
11812       }
11813    }
11814 
11815    res = actual_load_config(reload, cfg, ucfg);
11816 
11817    ast_config_destroy(cfg);
11818    ast_config_destroy(ucfg);
11819 
11820    return res;
11821 }
11822 
11823 #ifdef TEST_FRAMEWORK
11824 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11825 {
11826    ast_unload_realtime("voicemail");
11827    ast_unload_realtime("voicemail_data");
11828    return actual_load_config(reload, cfg, ucfg);
11829 }
11830 #endif
11831 
11832 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11833 {
11834    struct ast_vm_user *current;
11835    char *cat;
11836    struct ast_variable *var;
11837    const char *val;
11838    char *q, *stringp, *tmp;
11839    int x;
11840    int tmpadsi[4];
11841    char secretfn[PATH_MAX] = "";
11842 
11843 #ifdef IMAP_STORAGE
11844    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11845 #endif
11846    /* set audio control prompts */
11847    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11848    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11849    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11850    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11851    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11852 
11853    /* Free all the users structure */  
11854    free_vm_users();
11855 
11856    /* Free all the zones structure */
11857    free_vm_zones();
11858 
11859    AST_LIST_LOCK(&users);  
11860 
11861    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11862    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11863 
11864    if (cfg) {
11865       /* General settings */
11866 
11867       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11868          val = "default";
11869       ast_copy_string(userscontext, val, sizeof(userscontext));
11870       /* Attach voice message to mail message ? */
11871       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11872          val = "yes";
11873       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11874 
11875       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11876          val = "no";
11877       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11878 
11879       volgain = 0.0;
11880       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11881          sscanf(val, "%30lf", &volgain);
11882 
11883 #ifdef ODBC_STORAGE
11884       strcpy(odbc_database, "asterisk");
11885       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11886          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11887       }
11888       strcpy(odbc_table, "voicemessages");
11889       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11890          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11891       }
11892 #endif      
11893       /* Mail command */
11894       strcpy(mailcmd, SENDMAIL);
11895       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11896          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11897 
11898       maxsilence = 0;
11899       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11900          maxsilence = atoi(val);
11901          if (maxsilence > 0)
11902             maxsilence *= 1000;
11903       }
11904       
11905       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11906          maxmsg = MAXMSG;
11907       } else {
11908          maxmsg = atoi(val);
11909          if (maxmsg < 0) {
11910             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11911             maxmsg = MAXMSG;
11912          } else if (maxmsg > MAXMSGLIMIT) {
11913             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11914             maxmsg = MAXMSGLIMIT;
11915          }
11916       }
11917 
11918       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11919          maxdeletedmsg = 0;
11920       } else {
11921          if (sscanf(val, "%30d", &x) == 1)
11922             maxdeletedmsg = x;
11923          else if (ast_true(val))
11924             maxdeletedmsg = MAXMSG;
11925          else
11926             maxdeletedmsg = 0;
11927 
11928          if (maxdeletedmsg < 0) {
11929             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11930             maxdeletedmsg = MAXMSG;
11931          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11932             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11933             maxdeletedmsg = MAXMSGLIMIT;
11934          }
11935       }
11936 
11937       /* Load date format config for voicemail mail */
11938       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11939          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11940       }
11941 
11942       /* Load date format config for voicemail pager mail */
11943       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11944          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11945       }
11946 
11947       /* External password changing command */
11948       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11949          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11950          pwdchange = PWDCHANGE_EXTERNAL;
11951       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11952          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11953          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11954       }
11955  
11956       /* External password validation command */
11957       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11958          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11959          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11960       }
11961 
11962 #ifdef IMAP_STORAGE
11963       /* IMAP server address */
11964       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11965          ast_copy_string(imapserver, val, sizeof(imapserver));
11966       } else {
11967          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11968       }
11969       /* IMAP server port */
11970       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11971          ast_copy_string(imapport, val, sizeof(imapport));
11972       } else {
11973          ast_copy_string(imapport, "143", sizeof(imapport));
11974       }
11975       /* IMAP server flags */
11976       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11977          ast_copy_string(imapflags, val, sizeof(imapflags));
11978       }
11979       /* IMAP server master username */
11980       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11981          ast_copy_string(authuser, val, sizeof(authuser));
11982       }
11983       /* IMAP server master password */
11984       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11985          ast_copy_string(authpassword, val, sizeof(authpassword));
11986       }
11987       /* Expunge on exit */
11988       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11989          if (ast_false(val))
11990             expungeonhangup = 0;
11991          else
11992             expungeonhangup = 1;
11993       } else {
11994          expungeonhangup = 1;
11995       }
11996       /* IMAP voicemail folder */
11997       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11998          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11999       } else {
12000          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12001       }
12002       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12003          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12004       }
12005       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12006          imapgreetings = ast_true(val);
12007       } else {
12008          imapgreetings = 0;
12009       }
12010       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12011          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12012       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12013          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12014          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12015       } else {
12016          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12017       }
12018 
12019       /* There is some very unorthodox casting done here. This is due
12020        * to the way c-client handles the argument passed in. It expects a 
12021        * void pointer and casts the pointer directly to a long without
12022        * first dereferencing it. */
12023       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12024          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12025       } else {
12026          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12027       }
12028 
12029       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12030          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12031       } else {
12032          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12033       }
12034 
12035       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12036          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12037       } else {
12038          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12039       }
12040 
12041       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12042          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12043       } else {
12044          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12045       }
12046 
12047       /* Increment configuration version */
12048       imapversion++;
12049 #endif
12050       /* External voicemail notify application */
12051       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12052          ast_copy_string(externnotify, val, sizeof(externnotify));
12053          ast_debug(1, "found externnotify: %s\n", externnotify);
12054       } else {
12055          externnotify[0] = '\0';
12056       }
12057 
12058       /* SMDI voicemail notification */
12059       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12060          ast_debug(1, "Enabled SMDI voicemail notification\n");
12061          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12062             smdi_iface = ast_smdi_interface_find(val);
12063          } else {
12064             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12065             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12066          }
12067          if (!smdi_iface) {
12068             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12069          } 
12070       }
12071 
12072       /* Silence treshold */
12073       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12074       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12075          silencethreshold = atoi(val);
12076       
12077       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12078          val = ASTERISK_USERNAME;
12079       ast_copy_string(serveremail, val, sizeof(serveremail));
12080       
12081       vmmaxsecs = 0;
12082       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12083          if (sscanf(val, "%30d", &x) == 1) {
12084             vmmaxsecs = x;
12085          } else {
12086             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12087          }
12088       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12089          static int maxmessage_deprecate = 0;
12090          if (maxmessage_deprecate == 0) {
12091             maxmessage_deprecate = 1;
12092             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12093          }
12094          if (sscanf(val, "%30d", &x) == 1) {
12095             vmmaxsecs = x;
12096          } else {
12097             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12098          }
12099       }
12100 
12101       vmminsecs = 0;
12102       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12103          if (sscanf(val, "%30d", &x) == 1) {
12104             vmminsecs = x;
12105             if (maxsilence / 1000 >= vmminsecs) {
12106                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12107             }
12108          } else {
12109             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12110          }
12111       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12112          static int maxmessage_deprecate = 0;
12113          if (maxmessage_deprecate == 0) {
12114             maxmessage_deprecate = 1;
12115             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12116          }
12117          if (sscanf(val, "%30d", &x) == 1) {
12118             vmminsecs = x;
12119             if (maxsilence / 1000 >= vmminsecs) {
12120                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12121             }
12122          } else {
12123             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12124          }
12125       }
12126 
12127       val = ast_variable_retrieve(cfg, "general", "format");
12128       if (!val) {
12129          val = "wav";   
12130       } else {
12131          tmp = ast_strdupa(val);
12132          val = ast_format_str_reduce(tmp);
12133          if (!val) {
12134             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12135             val = "wav";
12136          }
12137       }
12138       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12139 
12140       skipms = 3000;
12141       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12142          if (sscanf(val, "%30d", &x) == 1) {
12143             maxgreet = x;
12144          } else {
12145             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12146          }
12147       }
12148 
12149       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12150          if (sscanf(val, "%30d", &x) == 1) {
12151             skipms = x;
12152          } else {
12153             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12154          }
12155       }
12156 
12157       maxlogins = 3;
12158       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12159          if (sscanf(val, "%30d", &x) == 1) {
12160             maxlogins = x;
12161          } else {
12162             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12163          }
12164       }
12165 
12166       minpassword = MINPASSWORD;
12167       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12168          if (sscanf(val, "%30d", &x) == 1) {
12169             minpassword = x;
12170          } else {
12171             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12172          }
12173       }
12174 
12175       /* Force new user to record name ? */
12176       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12177          val = "no";
12178       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12179 
12180       /* Force new user to record greetings ? */
12181       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12182          val = "no";
12183       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12184 
12185       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12186          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12187          stringp = ast_strdupa(val);
12188          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12189             if (!ast_strlen_zero(stringp)) {
12190                q = strsep(&stringp, ",");
12191                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12192                   q++;
12193                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12194                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12195             } else {
12196                cidinternalcontexts[x][0] = '\0';
12197             }
12198          }
12199       }
12200       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12201          ast_debug(1, "VM Review Option disabled globally\n");
12202          val = "no";
12203       }
12204       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12205 
12206       /* Temporary greeting reminder */
12207       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12208          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12209          val = "no";
12210       } else {
12211          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12212       }
12213       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12214       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12215          ast_debug(1, "VM next message wrap disabled globally\n");
12216          val = "no";
12217       }
12218       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12219 
12220       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12221          ast_debug(1, "VM Operator break disabled globally\n");
12222          val = "no";
12223       }
12224       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12225 
12226       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12227          ast_debug(1, "VM CID Info before msg disabled globally\n");
12228          val = "no";
12229       } 
12230       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12231 
12232       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12233          ast_debug(1, "Send Voicemail msg disabled globally\n");
12234          val = "no";
12235       }
12236       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12237    
12238       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12239          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12240          val = "yes";
12241       }
12242       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12243 
12244       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12245          ast_debug(1, "Move Heard enabled globally\n");
12246          val = "yes";
12247       }
12248       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12249 
12250       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12251          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12252          val = "no";
12253       }
12254       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12255 
12256       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12257          ast_debug(1, "Duration info before msg enabled globally\n");
12258          val = "yes";
12259       }
12260       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12261 
12262       saydurationminfo = 2;
12263       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12264          if (sscanf(val, "%30d", &x) == 1) {
12265             saydurationminfo = x;
12266          } else {
12267             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12268          }
12269       }
12270 
12271       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12272          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12273          val = "no";
12274       }
12275       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12276 
12277       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12278          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12279          ast_debug(1, "found dialout context: %s\n", dialcontext);
12280       } else {
12281          dialcontext[0] = '\0';  
12282       }
12283       
12284       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12285          ast_copy_string(callcontext, val, sizeof(callcontext));
12286          ast_debug(1, "found callback context: %s\n", callcontext);
12287       } else {
12288          callcontext[0] = '\0';
12289       }
12290 
12291       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12292          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12293          ast_debug(1, "found operator context: %s\n", exitcontext);
12294       } else {
12295          exitcontext[0] = '\0';
12296       }
12297       
12298       /* load password sounds configuration */
12299       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12300          ast_copy_string(vm_password, val, sizeof(vm_password));
12301       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12302          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12303       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12304          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12305       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12306          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12307       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12308          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12309       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12310          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12311       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12312          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12313       }
12314       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12315          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12316       }
12317       /* load configurable audio prompts */
12318       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12319          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12320       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12321          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12322       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12323          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12324       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12325          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12326       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12327          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12328 
12329       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12330          val = "no";
12331       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12332 
12333       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12334          val = "voicemail.conf";
12335       }
12336       if (!(strcmp(val, "spooldir"))) {
12337          passwordlocation = OPT_PWLOC_SPOOLDIR;
12338       } else {
12339          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12340       }
12341 
12342       poll_freq = DEFAULT_POLL_FREQ;
12343       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12344          if (sscanf(val, "%30u", &poll_freq) != 1) {
12345             poll_freq = DEFAULT_POLL_FREQ;
12346             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12347          }
12348       }
12349 
12350       poll_mailboxes = 0;
12351       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12352          poll_mailboxes = ast_true(val);
12353 
12354       memset(fromstring, 0, sizeof(fromstring));
12355       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12356       strcpy(charset, "ISO-8859-1");
12357       if (emailbody) {
12358          ast_free(emailbody);
12359          emailbody = NULL;
12360       }
12361       if (emailsubject) {
12362          ast_free(emailsubject);
12363          emailsubject = NULL;
12364       }
12365       if (pagerbody) {
12366          ast_free(pagerbody);
12367          pagerbody = NULL;
12368       }
12369       if (pagersubject) {
12370          ast_free(pagersubject);
12371          pagersubject = NULL;
12372       }
12373       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12374          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12375       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12376          ast_copy_string(fromstring, val, sizeof(fromstring));
12377       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12378          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12379       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12380          ast_copy_string(charset, val, sizeof(charset));
12381       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12382          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12383          for (x = 0; x < 4; x++) {
12384             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12385          }
12386       }
12387       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12388          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12389          for (x = 0; x < 4; x++) {
12390             memcpy(&adsisec[x], &tmpadsi[x], 1);
12391          }
12392       }
12393       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12394          if (atoi(val)) {
12395             adsiver = atoi(val);
12396          }
12397       }
12398       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12399          ast_copy_string(zonetag, val, sizeof(zonetag));
12400       }
12401       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12402          ast_copy_string(locale, val, sizeof(locale));
12403       }
12404       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12405          emailsubject = ast_strdup(substitute_escapes(val));
12406       }
12407       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12408          emailbody = ast_strdup(substitute_escapes(val));
12409       }
12410       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12411          pagersubject = ast_strdup(substitute_escapes(val));
12412       }
12413       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12414          pagerbody = ast_strdup(substitute_escapes(val));
12415       }
12416 
12417       /* load mailboxes from users.conf */
12418       if (ucfg) { 
12419          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12420             if (!strcasecmp(cat, "general")) {
12421                continue;
12422             }
12423             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12424                continue;
12425             if ((current = find_or_create(userscontext, cat))) {
12426                populate_defaults(current);
12427                apply_options_full(current, ast_variable_browse(ucfg, cat));
12428                ast_copy_string(current->context, userscontext, sizeof(current->context));
12429                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12430                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12431                }
12432 
12433                switch (current->passwordlocation) {
12434                case OPT_PWLOC_SPOOLDIR:
12435                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12436                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12437                }
12438             }
12439          }
12440       }
12441 
12442       /* load mailboxes from voicemail.conf */
12443       cat = ast_category_browse(cfg, NULL);
12444       while (cat) {
12445          if (strcasecmp(cat, "general")) {
12446             var = ast_variable_browse(cfg, cat);
12447             if (strcasecmp(cat, "zonemessages")) {
12448                /* Process mailboxes in this context */
12449                while (var) {
12450                   append_mailbox(cat, var->name, var->value);
12451                   var = var->next;
12452                }
12453             } else {
12454                /* Timezones in this context */
12455                while (var) {
12456                   struct vm_zone *z;
12457                   if ((z = ast_malloc(sizeof(*z)))) {
12458                      char *msg_format, *tzone;
12459                      msg_format = ast_strdupa(var->value);
12460                      tzone = strsep(&msg_format, "|,");
12461                      if (msg_format) {
12462                         ast_copy_string(z->name, var->name, sizeof(z->name));
12463                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12464                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12465                         AST_LIST_LOCK(&zones);
12466                         AST_LIST_INSERT_HEAD(&zones, z, list);
12467                         AST_LIST_UNLOCK(&zones);
12468                      } else {
12469                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12470                         ast_free(z);
12471                      }
12472                   } else {
12473                      AST_LIST_UNLOCK(&users);
12474                      return -1;
12475                   }
12476                   var = var->next;
12477                }
12478             }
12479          }
12480          cat = ast_category_browse(cfg, cat);
12481       }
12482 
12483       AST_LIST_UNLOCK(&users);
12484 
12485       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12486          start_poll_thread();
12487       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12488          stop_poll_thread();;
12489 
12490       return 0;
12491    } else {
12492       AST_LIST_UNLOCK(&users);
12493       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12494       return 0;
12495    }
12496 }
12497 
12498 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12499 {
12500    int res = -1;
12501    char dir[PATH_MAX];
12502    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12503    ast_debug(2, "About to try retrieving name file %s\n", dir);
12504    RETRIEVE(dir, -1, mailbox, context);
12505    if (ast_fileexists(dir, NULL, NULL)) {
12506       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12507    }
12508    DISPOSE(dir, -1);
12509    return res;
12510 }
12511 
12512 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12513    struct ast_config *pwconf;
12514    struct ast_flags config_flags = { 0 };
12515 
12516    pwconf = ast_config_load(secretfn, config_flags);
12517    if (pwconf) {
12518       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12519       if (val) {
12520          ast_copy_string(password, val, passwordlen);
12521          return;
12522       }
12523    }
12524    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12525 }
12526 
12527 static int write_password_to_file(const char *secretfn, const char *password) {
12528    struct ast_config *conf;
12529    struct ast_category *cat;
12530    struct ast_variable *var;
12531 
12532    if (!(conf=ast_config_new())) {
12533       ast_log(LOG_ERROR, "Error creating new config structure\n");
12534       return -1;
12535    }
12536    if (!(cat=ast_category_new("general","",1))) {
12537       ast_log(LOG_ERROR, "Error creating new category structure\n");
12538       return -1;
12539    }
12540    if (!(var=ast_variable_new("password",password,""))) {
12541       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12542       return -1;
12543    }
12544    ast_category_append(conf,cat);
12545    ast_variable_append(cat,var);
12546    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12547       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12548       return -1;
12549    }
12550    return 0;
12551 }
12552 
12553 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12554 {
12555    char *context;
12556    char *args_copy;
12557    int res;
12558 
12559    if (ast_strlen_zero(data)) {
12560       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12561       return -1;
12562    }
12563 
12564    args_copy = ast_strdupa(data);
12565    if ((context = strchr(args_copy, '@'))) {
12566       *context++ = '\0';
12567    } else {
12568       context = "default";
12569    }
12570 
12571    if ((res = sayname(chan, args_copy, context) < 0)) {
12572       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12573       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12574       if (!res) {
12575          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12576       }
12577    }
12578 
12579    return res;
12580 }
12581 
12582 #ifdef TEST_FRAMEWORK
12583 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12584 {
12585    return 0;
12586 }
12587 
12588 static struct ast_frame *fake_read(struct ast_channel *ast)
12589 {
12590    return &ast_null_frame;
12591 }
12592 
12593 AST_TEST_DEFINE(test_voicemail_vmsayname)
12594 {
12595    char dir[PATH_MAX];
12596    char dir2[PATH_MAX];
12597    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12598    static const char TEST_EXTENSION[] = "1234";
12599 
12600    struct ast_channel *test_channel1 = NULL;
12601    int res = -1;
12602 
12603    static const struct ast_channel_tech fake_tech = {
12604       .write = fake_write,
12605       .read = fake_read,
12606    };
12607 
12608    switch (cmd) {
12609    case TEST_INIT:
12610       info->name = "vmsayname_exec";
12611       info->category = "/apps/app_voicemail/";
12612       info->summary = "Vmsayname unit test";
12613       info->description =
12614          "This tests passing various parameters to vmsayname";
12615       return AST_TEST_NOT_RUN;
12616    case TEST_EXECUTE:
12617       break;
12618    }
12619 
12620    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12621         NULL, NULL, 0, 0, "TestChannel1"))) {
12622       goto exit_vmsayname_test;
12623    }
12624 
12625    /* normally this is done in the channel driver */
12626    test_channel1->nativeformats = AST_FORMAT_GSM;
12627    test_channel1->writeformat = AST_FORMAT_GSM;
12628    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12629    test_channel1->readformat = AST_FORMAT_GSM;
12630    test_channel1->rawreadformat = AST_FORMAT_GSM;
12631    test_channel1->tech = &fake_tech;
12632 
12633    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12634    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12635    if (!(res = vmsayname_exec(test_channel1, dir))) {
12636       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12637       if (ast_fileexists(dir, NULL, NULL)) {
12638          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12639          res = -1;
12640          goto exit_vmsayname_test;
12641       } else {
12642          /* no greeting already exists as expected, let's create one to fully test sayname */
12643          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12644             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12645             goto exit_vmsayname_test;
12646          }
12647          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12648          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12649          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12650          if ((res = symlink(dir, dir2))) {
12651             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12652             goto exit_vmsayname_test;
12653          }
12654          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12655          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12656          res = vmsayname_exec(test_channel1, dir);
12657 
12658          /* TODO: there may be a better way to do this */
12659          unlink(dir2);
12660          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12661          rmdir(dir2);
12662          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12663          rmdir(dir2);
12664       }
12665    }
12666 
12667 exit_vmsayname_test:
12668 
12669    if (test_channel1) {
12670       ast_hangup(test_channel1);
12671    }
12672 
12673    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12674 }
12675 
12676 AST_TEST_DEFINE(test_voicemail_msgcount)
12677 {
12678    int i, j, res = AST_TEST_PASS, syserr;
12679    struct ast_vm_user *vmu;
12680    struct vm_state vms;
12681 #ifdef IMAP_STORAGE
12682    struct ast_channel *chan = NULL;
12683 #endif
12684    struct {
12685       char dir[256];
12686       char file[256];
12687       char txtfile[256];
12688    } tmp[3];
12689    char syscmd[256];
12690    const char origweasels[] = "tt-weasels";
12691    const char testcontext[] = "test";
12692    const char testmailbox[] = "00000000";
12693    const char testspec[] = "00000000@test";
12694    FILE *txt;
12695    int new, old, urgent;
12696    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12697    const int folder2mbox[3] = { 1, 11, 0 };
12698    const int expected_results[3][12] = {
12699       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12700       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12701       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12702       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12703    };
12704 
12705    switch (cmd) {
12706    case TEST_INIT:
12707       info->name = "test_voicemail_msgcount";
12708       info->category = "/apps/app_voicemail/";
12709       info->summary = "Test Voicemail status checks";
12710       info->description =
12711          "Verify that message counts are correct when retrieved through the public API";
12712       return AST_TEST_NOT_RUN;
12713    case TEST_EXECUTE:
12714       break;
12715    }
12716 
12717    /* Make sure the original path was completely empty */
12718    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12719    if ((syserr = ast_safe_system(syscmd))) {
12720       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12721          syserr > 0 ? strerror(syserr) : "unable to fork()");
12722       return AST_TEST_FAIL;
12723    }
12724 
12725 #ifdef IMAP_STORAGE
12726    if (!(chan = ast_dummy_channel_alloc())) {
12727       ast_test_status_update(test, "Unable to create dummy channel\n");
12728       return AST_TEST_FAIL;
12729    }
12730 #endif
12731 
12732    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12733       !(vmu = find_or_create(testcontext, testmailbox))) {
12734       ast_test_status_update(test, "Cannot create vmu structure\n");
12735       ast_unreplace_sigchld();
12736 #ifdef IMAP_STORAGE
12737       chan = ast_channel_unref(chan);
12738 #endif
12739       return AST_TEST_FAIL;
12740    }
12741 
12742    populate_defaults(vmu);
12743    memset(&vms, 0, sizeof(vms));
12744 
12745    /* Create temporary voicemail */
12746    for (i = 0; i < 3; i++) {
12747       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12748       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12749       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12750 
12751       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12752          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12753             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12754          if ((syserr = ast_safe_system(syscmd))) {
12755             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12756                syserr > 0 ? strerror(syserr) : "unable to fork()");
12757             ast_unreplace_sigchld();
12758 #ifdef IMAP_STORAGE
12759             chan = ast_channel_unref(chan);
12760 #endif
12761             return AST_TEST_FAIL;
12762          }
12763       }
12764 
12765       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12766          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12767          fclose(txt);
12768       } else {
12769          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12770          res = AST_TEST_FAIL;
12771          break;
12772       }
12773       open_mailbox(&vms, vmu, folder2mbox[i]);
12774       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12775 
12776       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12777       for (j = 0; j < 3; j++) {
12778          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12779          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12780             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12781                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12782             res = AST_TEST_FAIL;
12783          }
12784       }
12785 
12786       new = old = urgent = 0;
12787       if (ast_app_inboxcount(testspec, &new, &old)) {
12788          ast_test_status_update(test, "inboxcount returned failure\n");
12789          res = AST_TEST_FAIL;
12790       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12791          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12792             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12793          res = AST_TEST_FAIL;
12794       }
12795 
12796       new = old = urgent = 0;
12797       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12798          ast_test_status_update(test, "inboxcount2 returned failure\n");
12799          res = AST_TEST_FAIL;
12800       } else if (old != expected_results[i][6 + 0] ||
12801             urgent != expected_results[i][6 + 1] ||
12802                new != expected_results[i][6 + 2]    ) {
12803          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12804             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12805          res = AST_TEST_FAIL;
12806       }
12807 
12808       new = old = urgent = 0;
12809       for (j = 0; j < 3; j++) {
12810          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12811             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12812                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12813             res = AST_TEST_FAIL;
12814          }
12815       }
12816    }
12817 
12818    for (i = 0; i < 3; i++) {
12819       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12820        * server, in which case, the rm below will not affect the
12821        * voicemails. */
12822       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12823       DISPOSE(tmp[i].dir, 0);
12824    }
12825 
12826    if (vms.deleted) {
12827       ast_free(vms.deleted);
12828    }
12829    if (vms.heard) {
12830       ast_free(vms.heard);
12831    }
12832 
12833 #ifdef IMAP_STORAGE
12834    chan = ast_channel_unref(chan);
12835 #endif
12836 
12837    /* And remove test directory */
12838    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12839    if ((syserr = ast_safe_system(syscmd))) {
12840       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12841          syserr > 0 ? strerror(syserr) : "unable to fork()");
12842    }
12843 
12844    return res;
12845 }
12846 
12847 AST_TEST_DEFINE(test_voicemail_notify_endl)
12848 {
12849    int res = AST_TEST_PASS;
12850    char testcontext[] = "test";
12851    char testmailbox[] = "00000000";
12852    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12853    char attach[256], attach2[256];
12854    char buf[256] = ""; /* No line should actually be longer than 80 */
12855    struct ast_channel *chan = NULL;
12856    struct ast_vm_user *vmu, vmus = {
12857       .flags = 0,
12858    };
12859    FILE *file;
12860    struct {
12861       char *name;
12862       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12863       void *location;
12864       union {
12865          int intval;
12866          char *strval;
12867       } u;
12868    } test_items[] = {
12869       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12870       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12871       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12872       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12873       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12874       { "attach2", STRPTR, attach2, .u.strval = "" },
12875       { "attach", STRPTR, attach, .u.strval = "" },
12876    };
12877    int which;
12878 
12879    switch (cmd) {
12880    case TEST_INIT:
12881       info->name = "test_voicemail_notify_endl";
12882       info->category = "/apps/app_voicemail/";
12883       info->summary = "Test Voicemail notification end-of-line";
12884       info->description =
12885          "Verify that notification emails use a consistent end-of-line character";
12886       return AST_TEST_NOT_RUN;
12887    case TEST_EXECUTE:
12888       break;
12889    }
12890 
12891    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12892    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12893 
12894    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12895       !(vmu = find_or_create(testcontext, testmailbox))) {
12896       ast_test_status_update(test, "Cannot create vmu structure\n");
12897       return AST_TEST_NOT_RUN;
12898    }
12899 
12900    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12901       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12902       return AST_TEST_NOT_RUN;
12903    }
12904 
12905    populate_defaults(vmu);
12906    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12907 #ifdef IMAP_STORAGE
12908    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12909 #endif
12910 
12911    file = tmpfile();
12912    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12913       /* Kill previous test, if any */
12914       rewind(file);
12915       if (ftruncate(fileno(file), 0)) {
12916          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12917          res = AST_TEST_FAIL;
12918          break;
12919       }
12920 
12921       /* Make each change, in order, to the test mailbox */
12922       if (test_items[which].type == INT) {
12923          *((int *) test_items[which].location) = test_items[which].u.intval;
12924       } else if (test_items[which].type == FLAGVAL) {
12925          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12926             ast_clear_flag(vmu, test_items[which].u.intval);
12927          } else {
12928             ast_set_flag(vmu, test_items[which].u.intval);
12929          }
12930       } else if (test_items[which].type == STATIC) {
12931          strcpy(test_items[which].location, test_items[which].u.strval);
12932       } else if (test_items[which].type == STRPTR) {
12933          test_items[which].location = test_items[which].u.strval;
12934       }
12935 
12936       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12937       rewind(file);
12938       while (fgets(buf, sizeof(buf), file)) {
12939          if (
12940 #ifdef IMAP_STORAGE
12941          buf[strlen(buf) - 2] != '\r'
12942 #else
12943          buf[strlen(buf) - 2] == '\r'
12944 #endif
12945          || buf[strlen(buf) - 1] != '\n') {
12946             res = AST_TEST_FAIL;
12947          }
12948       }
12949    }
12950    fclose(file);
12951    return res;
12952 }
12953 
12954 AST_TEST_DEFINE(test_voicemail_load_config)
12955 {
12956    int res = AST_TEST_PASS;
12957    struct ast_vm_user *vmu;
12958    struct ast_config *cfg;
12959    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
12960    int fd;
12961    FILE *file;
12962    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
12963 
12964    switch (cmd) {
12965    case TEST_INIT:
12966       info->name = "test_voicemail_load_config";
12967       info->category = "/apps/app_voicemail/";
12968       info->summary = "Test loading Voicemail config";
12969       info->description =
12970          "Verify that configuration is loaded consistently. "
12971          "This is to test regressions of ASTERISK-18838 where it was noticed that "
12972          "some options were loaded after the mailboxes were instantiated, causing "
12973          "those options not to be set correctly.";
12974       return AST_TEST_NOT_RUN;
12975    case TEST_EXECUTE:
12976       break;
12977    }
12978 
12979    /* build a config file by hand... */
12980    if ((fd = mkstemp(config_filename)) < 0) {
12981       return AST_TEST_FAIL;
12982    }
12983    if (!(file = fdopen(fd, "w"))) {
12984       close(fd);
12985       unlink(config_filename);
12986       return AST_TEST_FAIL;
12987    }
12988    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
12989    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
12990    fputs("00000002 => 9999,Mrs. Test\n", file);
12991    fclose(file);
12992 
12993    if (!(cfg = ast_config_load(config_filename, config_flags))) {
12994       res = AST_TEST_FAIL;
12995       goto cleanup;
12996    }
12997 
12998    load_config_from_memory(1, cfg, NULL);
12999    ast_config_destroy(cfg);
13000 
13001 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13002    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13003    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13004 
13005    AST_LIST_LOCK(&users);
13006    AST_LIST_TRAVERSE(&users, vmu, list) {
13007       if (!strcmp(vmu->mailbox, "00000001")) {
13008          if (0); /* trick to get CHECK to work */
13009          CHECK(vmu, callback, "othercontext")
13010          CHECK(vmu, locale, "nl_NL.UTF-8")
13011          CHECK(vmu, zonetag, "central")
13012       } else if (!strcmp(vmu->mailbox, "00000002")) {
13013          if (0); /* trick to get CHECK to work */
13014          CHECK(vmu, callback, "somecontext")
13015          CHECK(vmu, locale, "de_DE.UTF-8")
13016          CHECK(vmu, zonetag, "european")
13017       }
13018    }
13019    AST_LIST_UNLOCK(&users);
13020 
13021 #undef CHECK
13022 
13023    /* restore config */
13024    load_config(1); /* this might say "Failed to load configuration file." */
13025 
13026 cleanup:
13027    unlink(config_filename);
13028    return res;
13029 }
13030 
13031 #endif /* defined(TEST_FRAMEWORK) */
13032 
13033 static int reload(void)
13034 {
13035    return load_config(1);
13036 }
13037 
13038 static int unload_module(void)
13039 {
13040    int res;
13041 
13042    res = ast_unregister_application(app);
13043    res |= ast_unregister_application(app2);
13044    res |= ast_unregister_application(app3);
13045    res |= ast_unregister_application(app4);
13046    res |= ast_unregister_application(sayname_app);
13047    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13048    res |= ast_manager_unregister("VoicemailUsersList");
13049    res |= ast_data_unregister(NULL);
13050 #ifdef TEST_FRAMEWORK
13051    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13052    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13053    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13054    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13055    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13056 #endif
13057    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13058    ast_uninstall_vm_functions();
13059    ao2_ref(inprocess_container, -1);
13060 
13061    if (poll_thread != AST_PTHREADT_NULL)
13062       stop_poll_thread();
13063 
13064    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13065    ast_unload_realtime("voicemail");
13066    ast_unload_realtime("voicemail_data");
13067 
13068    free_vm_users();
13069    free_vm_zones();
13070    return res;
13071 }
13072 
13073 static int load_module(void)
13074 {
13075    int res;
13076    my_umask = umask(0);
13077    umask(my_umask);
13078 
13079    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13080       return AST_MODULE_LOAD_DECLINE;
13081    }
13082 
13083    /* compute the location of the voicemail spool directory */
13084    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13085    
13086    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13087       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13088    }
13089 
13090    if ((res = load_config(0)))
13091       return res;
13092 
13093    res = ast_register_application_xml(app, vm_exec);
13094    res |= ast_register_application_xml(app2, vm_execmain);
13095    res |= ast_register_application_xml(app3, vm_box_exists);
13096    res |= ast_register_application_xml(app4, vmauthenticate);
13097    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13098    res |= ast_custom_function_register(&mailbox_exists_acf);
13099    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13100 #ifdef TEST_FRAMEWORK
13101    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13102    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13103    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13104    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13105    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13106 #endif
13107 
13108    if (res)
13109       return res;
13110 
13111    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13112    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13113 
13114    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13115    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13116    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13117 
13118    return res;
13119 }
13120 
13121 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13122 {
13123    int cmd = 0;
13124    char destination[80] = "";
13125    int retries = 0;
13126 
13127    if (!num) {
13128       ast_verb(3, "Destination number will be entered manually\n");
13129       while (retries < 3 && cmd != 't') {
13130          destination[1] = '\0';
13131          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13132          if (!cmd)
13133             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13134          if (!cmd)
13135             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13136          if (!cmd) {
13137             cmd = ast_waitfordigit(chan, 6000);
13138             if (cmd)
13139                destination[0] = cmd;
13140          }
13141          if (!cmd) {
13142             retries++;
13143          } else {
13144 
13145             if (cmd < 0)
13146                return 0;
13147             if (cmd == '*') {
13148                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13149                return 0;
13150             }
13151             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13152                retries++;
13153             else
13154                cmd = 't';
13155          }
13156          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13157       }
13158       if (retries >= 3) {
13159          return 0;
13160       }
13161       
13162    } else {
13163       if (option_verbose > 2)
13164          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13165       ast_copy_string(destination, num, sizeof(destination));
13166    }
13167 
13168    if (!ast_strlen_zero(destination)) {
13169       if (destination[strlen(destination) -1 ] == '*')
13170          return 0; 
13171       if (option_verbose > 2)
13172          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13173       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13174       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13175       chan->priority = 0;
13176       return 9;
13177    }
13178    return 0;
13179 }
13180 
13181 /*!
13182  * \brief The advanced options within a message.
13183  * \param chan
13184  * \param vmu 
13185  * \param vms
13186  * \param msg
13187  * \param option
13188  * \param record_gain
13189  *
13190  * Provides handling for the play message envelope, call the person back, or reply to message. 
13191  *
13192  * \return zero on success, -1 on error.
13193  */
13194 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)
13195 {
13196    int res = 0;
13197    char filename[PATH_MAX];
13198    struct ast_config *msg_cfg = NULL;
13199    const char *origtime, *context;
13200    char *name, *num;
13201    int retries = 0;
13202    char *cid;
13203    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13204 
13205    vms->starting = 0; 
13206 
13207    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13208 
13209    /* Retrieve info from VM attribute file */
13210    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13211    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13212    msg_cfg = ast_config_load(filename, config_flags);
13213    DISPOSE(vms->curdir, vms->curmsg);
13214    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13215       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13216       return 0;
13217    }
13218 
13219    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13220       ast_config_destroy(msg_cfg);
13221       return 0;
13222    }
13223 
13224    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13225 
13226    context = ast_variable_retrieve(msg_cfg, "message", "context");
13227    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13228       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13229    switch (option) {
13230    case 3: /* Play message envelope */
13231       if (!res)
13232          res = play_message_datetime(chan, vmu, origtime, filename);
13233       if (!res)
13234          res = play_message_callerid(chan, vms, cid, context, 0);
13235 
13236       res = 't';
13237       break;
13238 
13239    case 2:  /* Call back */
13240 
13241       if (ast_strlen_zero(cid))
13242          break;
13243 
13244       ast_callerid_parse(cid, &name, &num);
13245       while ((res > -1) && (res != 't')) {
13246          switch (res) {
13247          case '1':
13248             if (num) {
13249                /* Dial the CID number */
13250                res = dialout(chan, vmu, num, vmu->callback);
13251                if (res) {
13252                   ast_config_destroy(msg_cfg);
13253                   return 9;
13254                }
13255             } else {
13256                res = '2';
13257             }
13258             break;
13259 
13260          case '2':
13261             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13262             if (!ast_strlen_zero(vmu->dialout)) {
13263                res = dialout(chan, vmu, NULL, vmu->dialout);
13264                if (res) {
13265                   ast_config_destroy(msg_cfg);
13266                   return 9;
13267                }
13268             } else {
13269                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13270                res = ast_play_and_wait(chan, "vm-sorry");
13271             }
13272             ast_config_destroy(msg_cfg);
13273             return res;
13274          case '*':
13275             res = 't';
13276             break;
13277          case '3':
13278          case '4':
13279          case '5':
13280          case '6':
13281          case '7':
13282          case '8':
13283          case '9':
13284          case '0':
13285 
13286             res = ast_play_and_wait(chan, "vm-sorry");
13287             retries++;
13288             break;
13289          default:
13290             if (num) {
13291                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13292                res = ast_play_and_wait(chan, "vm-num-i-have");
13293                if (!res)
13294                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13295                if (!res)
13296                   res = ast_play_and_wait(chan, "vm-tocallnum");
13297                /* Only prompt for a caller-specified number if there is a dialout context specified */
13298                if (!ast_strlen_zero(vmu->dialout)) {
13299                   if (!res)
13300                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13301                }
13302             } else {
13303                res = ast_play_and_wait(chan, "vm-nonumber");
13304                if (!ast_strlen_zero(vmu->dialout)) {
13305                   if (!res)
13306                      res = ast_play_and_wait(chan, "vm-toenternumber");
13307                }
13308             }
13309             if (!res) {
13310                res = ast_play_and_wait(chan, "vm-star-cancel");
13311             }
13312             if (!res) {
13313                res = ast_waitfordigit(chan, 6000);
13314             }
13315             if (!res) {
13316                retries++;
13317                if (retries > 3) {
13318                   res = 't';
13319                }
13320             }
13321             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13322             break; 
13323             
13324          }
13325          if (res == 't')
13326             res = 0;
13327          else if (res == '*')
13328             res = -1;
13329       }
13330       break;
13331       
13332    case 1:  /* Reply */
13333       /* Send reply directly to sender */
13334       if (ast_strlen_zero(cid))
13335          break;
13336 
13337       ast_callerid_parse(cid, &name, &num);
13338       if (!num) {
13339          ast_verb(3, "No CID number available, no reply sent\n");
13340          if (!res)
13341             res = ast_play_and_wait(chan, "vm-nonumber");
13342          ast_config_destroy(msg_cfg);
13343          return res;
13344       } else {
13345          struct ast_vm_user vmu2;
13346          if (find_user(&vmu2, vmu->context, num)) {
13347             struct leave_vm_options leave_options;
13348             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13349             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13350 
13351             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13352             
13353             memset(&leave_options, 0, sizeof(leave_options));
13354             leave_options.record_gain = record_gain;
13355             res = leave_voicemail(chan, mailbox, &leave_options);
13356             if (!res)
13357                res = 't';
13358             ast_config_destroy(msg_cfg);
13359             return res;
13360          } else {
13361             /* Sender has no mailbox, can't reply */
13362             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13363             ast_play_and_wait(chan, "vm-nobox");
13364             res = 't';
13365             ast_config_destroy(msg_cfg);
13366             return res;
13367          }
13368       } 
13369       res = 0;
13370 
13371       break;
13372    }
13373 
13374 #ifndef IMAP_STORAGE
13375    ast_config_destroy(msg_cfg);
13376 
13377    if (!res) {
13378       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13379       vms->heard[msg] = 1;
13380       res = wait_file(chan, vms, vms->fn);
13381    }
13382 #endif
13383    return res;
13384 }
13385 
13386 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13387          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13388          signed char record_gain, struct vm_state *vms, char *flag)
13389 {
13390    /* Record message & let caller review or re-record it, or set options if applicable */
13391    int res = 0;
13392    int cmd = 0;
13393    int max_attempts = 3;
13394    int attempts = 0;
13395    int recorded = 0;
13396    int msg_exists = 0;
13397    signed char zero_gain = 0;
13398    char tempfile[PATH_MAX];
13399    char *acceptdtmf = "#";
13400    char *canceldtmf = "";
13401    int canceleddtmf = 0;
13402 
13403    /* Note that urgent and private are for flagging messages as such in the future */
13404 
13405    /* barf if no pointer passed to store duration in */
13406    if (duration == NULL) {
13407       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13408       return -1;
13409    }
13410 
13411    if (!outsidecaller)
13412       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13413    else
13414       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13415 
13416    cmd = '3';  /* Want to start by recording */
13417 
13418    while ((cmd >= 0) && (cmd != 't')) {
13419       switch (cmd) {
13420       case '1':
13421          if (!msg_exists) {
13422             /* In this case, 1 is to record a message */
13423             cmd = '3';
13424             break;
13425          } else {
13426             /* Otherwise 1 is to save the existing message */
13427             ast_verb(3, "Saving message as is\n");
13428             if (!outsidecaller) 
13429                ast_filerename(tempfile, recordfile, NULL);
13430             ast_stream_and_wait(chan, "vm-msgsaved", "");
13431             if (!outsidecaller) {
13432                /* Saves to IMAP server only if imapgreeting=yes */
13433                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13434                DISPOSE(recordfile, -1);
13435             }
13436             cmd = 't';
13437             return res;
13438          }
13439       case '2':
13440          /* Review */
13441          ast_verb(3, "Reviewing the message\n");
13442          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13443          break;
13444       case '3':
13445          msg_exists = 0;
13446          /* Record */
13447          if (recorded == 1) 
13448             ast_verb(3, "Re-recording the message\n");
13449          else  
13450             ast_verb(3, "Recording the message\n");
13451          
13452          if (recorded && outsidecaller) {
13453             cmd = ast_play_and_wait(chan, INTRO);
13454             cmd = ast_play_and_wait(chan, "beep");
13455          }
13456          recorded = 1;
13457          /* 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 */
13458          if (record_gain)
13459             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13460          if (ast_test_flag(vmu, VM_OPERATOR))
13461             canceldtmf = "0";
13462          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13463          if (strchr(canceldtmf, cmd)) {
13464          /* need this flag here to distinguish between pressing '0' during message recording or after */
13465             canceleddtmf = 1;
13466          }
13467          if (record_gain)
13468             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13469          if (cmd == -1) {
13470             /* User has hung up, no options to give */
13471             if (!outsidecaller) {
13472                /* user was recording a greeting and they hung up, so let's delete the recording. */
13473                ast_filedelete(tempfile, NULL);
13474             }     
13475             return cmd;
13476          }
13477          if (cmd == '0') {
13478             break;
13479          } else if (cmd == '*') {
13480             break;
13481 #if 0
13482          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13483             /* Message is too short */
13484             ast_verb(3, "Message too short\n");
13485             cmd = ast_play_and_wait(chan, "vm-tooshort");
13486             cmd = ast_filedelete(tempfile, NULL);
13487             break;
13488          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13489             /* Message is all silence */
13490             ast_verb(3, "Nothing recorded\n");
13491             cmd = ast_filedelete(tempfile, NULL);
13492             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13493             if (!cmd)
13494                cmd = ast_play_and_wait(chan, "vm-speakup");
13495             break;
13496 #endif
13497          } else {
13498             /* If all is well, a message exists */
13499             msg_exists = 1;
13500             cmd = 0;
13501          }
13502          break;
13503       case '4':
13504          if (outsidecaller) {  /* only mark vm messages */
13505             /* Mark Urgent */
13506             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13507                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13508                res = ast_play_and_wait(chan, "vm-marked-urgent");
13509                strcpy(flag, "Urgent");
13510             } else if (flag) {
13511                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13512                res = ast_play_and_wait(chan, "vm-urgent-removed");
13513                strcpy(flag, "");
13514             } else {
13515                ast_play_and_wait(chan, "vm-sorry");
13516             }
13517             cmd = 0;
13518          } else {
13519             cmd = ast_play_and_wait(chan, "vm-sorry");
13520          }
13521          break;
13522       case '5':
13523       case '6':
13524       case '7':
13525       case '8':
13526       case '9':
13527       case '*':
13528       case '#':
13529          cmd = ast_play_and_wait(chan, "vm-sorry");
13530          break;
13531 #if 0 
13532 /*  XXX Commented out for the moment because of the dangers of deleting
13533     a message while recording (can put the message numbers out of sync) */
13534       case '*':
13535          /* Cancel recording, delete message, offer to take another message*/
13536          cmd = ast_play_and_wait(chan, "vm-deleted");
13537          cmd = ast_filedelete(tempfile, NULL);
13538          if (outsidecaller) {
13539             res = vm_exec(chan, NULL);
13540             return res;
13541          }
13542          else
13543             return 1;
13544 #endif
13545       case '0':
13546          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13547             cmd = ast_play_and_wait(chan, "vm-sorry");
13548             break;
13549          }
13550          if (msg_exists || recorded) {
13551             cmd = ast_play_and_wait(chan, "vm-saveoper");
13552             if (!cmd)
13553                cmd = ast_waitfordigit(chan, 3000);
13554             if (cmd == '1') {
13555                ast_filerename(tempfile, recordfile, NULL);
13556                ast_play_and_wait(chan, "vm-msgsaved");
13557                cmd = '0';
13558             } else if (cmd == '4') {
13559                if (flag) {
13560                   ast_play_and_wait(chan, "vm-marked-urgent");
13561                   strcpy(flag, "Urgent");
13562                }
13563                ast_play_and_wait(chan, "vm-msgsaved");
13564                cmd = '0';
13565             } else {
13566                ast_play_and_wait(chan, "vm-deleted");
13567                DELETE(tempfile, -1, tempfile, vmu);
13568                cmd = '0';
13569             }
13570          }
13571          return cmd;
13572       default:
13573          /* If the caller is an ouside caller, and the review option is enabled,
13574             allow them to review the message, but let the owner of the box review
13575             their OGM's */
13576          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13577             return cmd;
13578          if (msg_exists) {
13579             cmd = ast_play_and_wait(chan, "vm-review");
13580             if (!cmd && outsidecaller) {
13581                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13582                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13583                } else if (flag) {
13584                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13585                }
13586             }
13587          } else {
13588             cmd = ast_play_and_wait(chan, "vm-torerecord");
13589             if (!cmd)
13590                cmd = ast_waitfordigit(chan, 600);
13591          }
13592          
13593          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13594             cmd = ast_play_and_wait(chan, "vm-reachoper");
13595             if (!cmd)
13596                cmd = ast_waitfordigit(chan, 600);
13597          }
13598 #if 0
13599          if (!cmd)
13600             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13601 #endif
13602          if (!cmd)
13603             cmd = ast_waitfordigit(chan, 6000);
13604          if (!cmd) {
13605             attempts++;
13606          }
13607          if (attempts > max_attempts) {
13608             cmd = 't';
13609          }
13610       }
13611    }
13612    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13613       /* Hang up or timeout, so delete the recording. */
13614       ast_filedelete(tempfile, NULL);
13615    }
13616 
13617    if (cmd != 't' && outsidecaller)
13618       ast_play_and_wait(chan, "vm-goodbye");
13619 
13620    return cmd;
13621 }
13622 
13623 /* This is a workaround so that menuselect displays a proper description
13624  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13625  */
13626 
13627 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13628       .load = load_module,
13629       .unload = unload_module,
13630       .reload = reload,
13631       .nonoptreq = "res_adsi,res_smdi",
13632       );