Mon Jun 18 2012 03:55:21

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: 356797 $")
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 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    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    int cmd = 0;
06854    int retries = 0, prepend_duration = 0, already_recorded = 0;
06855    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06856    char textfile[PATH_MAX];
06857    struct ast_config *msg_cfg;
06858    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06859 #ifndef IMAP_STORAGE
06860    signed char zero_gain = 0;
06861 #endif
06862    const char *duration_str;
06863 
06864    /* Must always populate duration correctly */
06865    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06866    strcpy(textfile, msgfile);
06867    strcpy(backup, msgfile);
06868    strcpy(backup_textfile, msgfile);
06869    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06870    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06871    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06872 
06873    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06874       *duration = atoi(duration_str);
06875    } else {
06876       *duration = 0;
06877    }
06878 
06879    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06880       if (cmd)
06881          retries = 0;
06882       switch (cmd) {
06883       case '1': 
06884 
06885 #ifdef IMAP_STORAGE
06886          /* Record new intro file */
06887          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06888          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06889          ast_play_and_wait(chan, INTRO);
06890          ast_play_and_wait(chan, "beep");
06891          play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06892          cmd = 't';
06893 #else
06894 
06895          /* prepend a message to the current message, update the metadata and return */
06896 
06897          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06898          strcpy(textfile, msgfile);
06899          strncat(textfile, ".txt", sizeof(textfile) - 1);
06900          *duration = 0;
06901 
06902          /* if we can't read the message metadata, stop now */
06903          if (!msg_cfg) {
06904             cmd = 0;
06905             break;
06906          }
06907 
06908          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06909 #ifndef IMAP_STORAGE
06910          if (already_recorded) {
06911             ast_filecopy(backup, msgfile, NULL);
06912             copy(backup_textfile, textfile);
06913          }
06914          else {
06915             ast_filecopy(msgfile, backup, NULL);
06916             copy(textfile, backup_textfile);
06917          }
06918 #endif
06919          already_recorded = 1;
06920 
06921          if (record_gain)
06922             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06923 
06924          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06925 
06926          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06927             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06928             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06929             ast_filerename(backup, msgfile, NULL);
06930          }
06931 
06932          if (record_gain)
06933             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06934 
06935          
06936          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06937             *duration = atoi(duration_str);
06938 
06939          if (prepend_duration) {
06940             struct ast_category *msg_cat;
06941             /* need enough space for a maximum-length message duration */
06942             char duration_buf[12];
06943 
06944             *duration += prepend_duration;
06945             msg_cat = ast_category_get(msg_cfg, "message");
06946             snprintf(duration_buf, 11, "%ld", *duration);
06947             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06948                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06949             }
06950          }
06951 
06952 #endif
06953          break;
06954       case '2': 
06955          /* NULL out introfile so we know there is no intro! */
06956 #ifdef IMAP_STORAGE
06957          *vms->introfn = '\0';
06958 #endif
06959          cmd = 't';
06960          break;
06961       case '*':
06962          cmd = '*';
06963          break;
06964       default: 
06965          /* If time_out and return to menu, reset already_recorded */
06966          already_recorded = 0;
06967 
06968          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06969             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06970          if (!cmd) {
06971             cmd = ast_play_and_wait(chan, "vm-starmain");
06972             /* "press star to return to the main menu" */
06973          }
06974          if (!cmd) {
06975             cmd = ast_waitfordigit(chan, 6000);
06976          }
06977          if (!cmd) {
06978             retries++;
06979          }
06980          if (retries > 3) {
06981             cmd = '*'; /* Let's cancel this beast */
06982          }
06983          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
06984       }
06985    }
06986 
06987    if (msg_cfg)
06988       ast_config_destroy(msg_cfg);
06989    if (prepend_duration)
06990       *duration = prepend_duration;
06991 
06992    if (already_recorded && cmd == -1) {
06993       /* restore original message if prepention cancelled */
06994       ast_filerename(backup, msgfile, NULL);
06995       rename(backup_textfile, textfile);
06996    }
06997 
06998    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
06999       cmd = 0;
07000    return cmd;
07001 }
07002 
07003 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07004 {
07005    struct ast_event *event;
07006    char *mailbox, *context;
07007 
07008    /* Strip off @default */
07009    context = mailbox = ast_strdupa(box);
07010    strsep(&context, "@");
07011    if (ast_strlen_zero(context))
07012       context = "default";
07013 
07014    if (!(event = ast_event_new(AST_EVENT_MWI,
07015          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07016          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07017          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07018          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07019          AST_EVENT_IE_END))) {
07020       return;
07021    }
07022 
07023    ast_event_queue_and_cache(event);
07024 }
07025 
07026 /*!
07027  * \brief Sends email notification that a user has a new voicemail waiting for them.
07028  * \param chan
07029  * \param vmu
07030  * \param vms
07031  * \param msgnum
07032  * \param duration
07033  * \param fmt
07034  * \param cidnum The Caller ID phone number value.
07035  * \param cidname The Caller ID name value.
07036  * \param flag
07037  *
07038  * \return zero on success, -1 on error.
07039  */
07040 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)
07041 {
07042    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07043    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07044    const char *category;
07045    char *myserveremail = serveremail;
07046 
07047    ast_channel_lock(chan);
07048    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07049       category = ast_strdupa(category);
07050    }
07051    ast_channel_unlock(chan);
07052 
07053 #ifndef IMAP_STORAGE
07054    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07055 #else
07056    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07057 #endif
07058    make_file(fn, sizeof(fn), todir, msgnum);
07059    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07060 
07061    if (!ast_strlen_zero(vmu->attachfmt)) {
07062       if (strstr(fmt, vmu->attachfmt))
07063          fmt = vmu->attachfmt;
07064       else
07065          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);
07066    }
07067 
07068    /* Attach only the first format */
07069    fmt = ast_strdupa(fmt);
07070    stringp = fmt;
07071    strsep(&stringp, "|");
07072 
07073    if (!ast_strlen_zero(vmu->serveremail))
07074       myserveremail = vmu->serveremail;
07075 
07076    if (!ast_strlen_zero(vmu->email)) {
07077       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07078 
07079       if (attach_user_voicemail)
07080          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07081 
07082       /* XXX possible imap issue, should category be NULL XXX */
07083       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07084 
07085       if (attach_user_voicemail)
07086          DISPOSE(todir, msgnum);
07087    }
07088 
07089    if (!ast_strlen_zero(vmu->pager)) {
07090       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07091    }
07092 
07093    if (ast_test_flag(vmu, VM_DELETE))
07094       DELETE(todir, msgnum, fn, vmu);
07095 
07096    /* Leave voicemail for someone */
07097    if (ast_app_has_voicemail(ext_context, NULL)) 
07098       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07099 
07100    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07101 
07102    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);
07103    run_externnotify(vmu->context, vmu->mailbox, flag);
07104 
07105 #ifdef IMAP_STORAGE
07106    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07107    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07108       vm_imap_delete(NULL, vms->curmsg, vmu);
07109       vms->newmessages--;  /* Fix new message count */
07110    }
07111 #endif
07112 
07113    return 0;
07114 }
07115 
07116 /*!
07117  * \brief Sends a voicemail message to a mailbox recipient.
07118  * \param chan
07119  * \param context
07120  * \param vms
07121  * \param sender
07122  * \param fmt
07123  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07124  *             Will be 0 when called to forward an existing message (option 8)
07125  *             Will be 1 when called to leave a message (option 3->5)
07126  * \param record_gain 
07127  * \param urgent
07128  *
07129  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07130  * 
07131  * When in the leave message mode (is_new_message == 1):
07132  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07133  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07134  *
07135  * When in the forward message mode (is_new_message == 0):
07136  *   - retreives the current message to be forwarded
07137  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07138  *   - determines the target mailbox and folders
07139  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07140  *
07141  * \return zero on success, -1 on error.
07142  */
07143 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)
07144 {
07145 #ifdef IMAP_STORAGE
07146    int todircount = 0;
07147    struct vm_state *dstvms;
07148 #endif
07149    char username[70]="";
07150    char fn[PATH_MAX]; /* for playback of name greeting */
07151    char ecodes[16] = "#";
07152    int res = 0, cmd = 0;
07153    struct ast_vm_user *receiver = NULL, *vmtmp;
07154    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07155    char *stringp;
07156    const char *s;
07157    int saved_messages = 0;
07158    int valid_extensions = 0;
07159    char *dir;
07160    int curmsg;
07161    char urgent_str[7] = "";
07162    int prompt_played = 0;
07163 #ifndef IMAP_STORAGE
07164    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07165 #endif
07166    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07167       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07168    }
07169 
07170    if (vms == NULL) return -1;
07171    dir = vms->curdir;
07172    curmsg = vms->curmsg;
07173 
07174    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07175    while (!res && !valid_extensions) {
07176       int use_directory = 0;
07177       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07178          int done = 0;
07179          int retries = 0;
07180          cmd = 0;
07181          while ((cmd >= 0) && !done ){
07182             if (cmd)
07183                retries = 0;
07184             switch (cmd) {
07185             case '1': 
07186                use_directory = 0;
07187                done = 1;
07188                break;
07189             case '2': 
07190                use_directory = 1;
07191                done = 1;
07192                break;
07193             case '*': 
07194                cmd = 't';
07195                done = 1;
07196                break;
07197             default: 
07198                /* Press 1 to enter an extension press 2 to use the directory */
07199                cmd = ast_play_and_wait(chan, "vm-forward");
07200                if (!cmd) {
07201                   cmd = ast_waitfordigit(chan, 3000);
07202                }
07203                if (!cmd) {
07204                   retries++;
07205                }
07206                if (retries > 3) {
07207                   cmd = 't';
07208                   done = 1;
07209                }
07210                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07211             }
07212          }
07213          if (cmd < 0 || cmd == 't')
07214             break;
07215       }
07216       
07217       if (use_directory) {
07218          /* use app_directory */
07219          
07220          char old_context[sizeof(chan->context)];
07221          char old_exten[sizeof(chan->exten)];
07222          int old_priority;
07223          struct ast_app* directory_app;
07224 
07225          directory_app = pbx_findapp("Directory");
07226          if (directory_app) {
07227             char vmcontext[256];
07228             /* make backup copies */
07229             memcpy(old_context, chan->context, sizeof(chan->context));
07230             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07231             old_priority = chan->priority;
07232             
07233             /* call the the Directory, changes the channel */
07234             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07235             res = pbx_exec(chan, directory_app, vmcontext);
07236             
07237             ast_copy_string(username, chan->exten, sizeof(username));
07238             
07239             /* restore the old context, exten, and priority */
07240             memcpy(chan->context, old_context, sizeof(chan->context));
07241             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07242             chan->priority = old_priority;
07243          } else {
07244             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07245             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07246          }
07247       } else {
07248          /* Ask for an extension */
07249          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07250          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07251          prompt_played++;
07252          if (res || prompt_played > 4)
07253             break;
07254          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07255             break;
07256       }
07257       
07258       /* start all over if no username */
07259       if (ast_strlen_zero(username))
07260          continue;
07261       stringp = username;
07262       s = strsep(&stringp, "*");
07263       /* start optimistic */
07264       valid_extensions = 1;
07265       while (s) {
07266          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07267             int oldmsgs;
07268             int newmsgs;
07269             int capacity;
07270             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07271                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07272                /* Shouldn't happen, but allow trying another extension if it does */
07273                res = ast_play_and_wait(chan, "pbx-invalid");
07274                valid_extensions = 0;
07275                break;
07276             }
07277             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07278             if ((newmsgs + oldmsgs) >= capacity) {
07279                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07280                res = ast_play_and_wait(chan, "vm-mailboxfull");
07281                valid_extensions = 0;
07282                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07283                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07284                   free_user(vmtmp);
07285                }
07286                inprocess_count(receiver->mailbox, receiver->context, -1);
07287                break;
07288             }
07289             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07290          } else {
07291             /* XXX Optimization for the future.  When we encounter a single bad extension,
07292              * bailing out on all of the extensions may not be the way to go.  We should
07293              * probably just bail on that single extension, then allow the user to enter
07294              * several more. XXX
07295              */
07296             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07297                free_user(receiver);
07298             }
07299             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07300             /* "I am sorry, that's not a valid extension.  Please try again." */
07301             res = ast_play_and_wait(chan, "pbx-invalid");
07302             valid_extensions = 0;
07303             break;
07304          }
07305 
07306          /* play name if available, else play extension number */
07307          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07308          RETRIEVE(fn, -1, s, receiver->context);
07309          if (ast_fileexists(fn, NULL, NULL) > 0) {
07310             res = ast_stream_and_wait(chan, fn, ecodes);
07311             if (res) {
07312                DISPOSE(fn, -1);
07313                return res;
07314             }
07315          } else {
07316             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07317          }
07318          DISPOSE(fn, -1);
07319 
07320          s = strsep(&stringp, "*");
07321       }
07322       /* break from the loop of reading the extensions */
07323       if (valid_extensions)
07324          break;
07325    }
07326    /* check if we're clear to proceed */
07327    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07328       return res;
07329    if (is_new_message == 1) {
07330       struct leave_vm_options leave_options;
07331       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07332       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07333 
07334       /* Send VoiceMail */
07335       memset(&leave_options, 0, sizeof(leave_options));
07336       leave_options.record_gain = record_gain;
07337       cmd = leave_voicemail(chan, mailbox, &leave_options);
07338    } else {
07339       /* Forward VoiceMail */
07340       long duration = 0;
07341       struct vm_state vmstmp;
07342       int copy_msg_result = 0;
07343       memcpy(&vmstmp, vms, sizeof(vmstmp));
07344 
07345       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07346 
07347       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07348       if (!cmd) {
07349          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07350 #ifdef IMAP_STORAGE
07351             int attach_user_voicemail;
07352             char *myserveremail = serveremail;
07353             
07354             /* get destination mailbox */
07355             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07356             if (!dstvms) {
07357                dstvms = create_vm_state_from_user(vmtmp);
07358             }
07359             if (dstvms) {
07360                init_mailstream(dstvms, 0);
07361                if (!dstvms->mailstream) {
07362                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07363                } else {
07364                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07365                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07366                }
07367             } else {
07368                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07369             }
07370             if (!ast_strlen_zero(vmtmp->serveremail))
07371                myserveremail = vmtmp->serveremail;
07372             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07373             /* NULL category for IMAP storage */
07374             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07375                dstvms->curbox,
07376                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07377                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07378                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07379                NULL, urgent_str);
07380 #else
07381             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07382 #endif
07383             saved_messages++;
07384             AST_LIST_REMOVE_CURRENT(list);
07385             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07386             free_user(vmtmp);
07387             if (res)
07388                break;
07389          }
07390          AST_LIST_TRAVERSE_SAFE_END;
07391          if (saved_messages > 0 && !copy_msg_result) {
07392             /* give confirmation that the message was saved */
07393             /* commented out since we can't forward batches yet
07394             if (saved_messages == 1)
07395                res = ast_play_and_wait(chan, "vm-message");
07396             else
07397                res = ast_play_and_wait(chan, "vm-messages");
07398             if (!res)
07399                res = ast_play_and_wait(chan, "vm-saved"); */
07400 #ifdef IMAP_STORAGE
07401             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07402             if (ast_strlen_zero(vmstmp.introfn))
07403 #endif
07404             res = ast_play_and_wait(chan, "vm-msgsaved");
07405          }
07406 #ifndef IMAP_STORAGE
07407          else {
07408             /* with IMAP, mailbox full warning played by imap_check_limits */
07409             res = ast_play_and_wait(chan, "vm-mailboxfull");
07410          }
07411          /* Restore original message without prepended message if backup exists */
07412          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07413          strcpy(textfile, msgfile);
07414          strcpy(backup, msgfile);
07415          strcpy(backup_textfile, msgfile);
07416          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07417          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07418          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07419          if (ast_fileexists(backup, NULL, NULL) > 0) {
07420             ast_filerename(backup, msgfile, NULL);
07421             rename(backup_textfile, textfile);
07422          }
07423 #endif
07424       }
07425       DISPOSE(dir, curmsg);
07426 #ifndef IMAP_STORAGE
07427       if (cmd) { /* assuming hangup, cleanup backup file */
07428          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07429          strcpy(textfile, msgfile);
07430          strcpy(backup_textfile, msgfile);
07431          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07432          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07433          rename(backup_textfile, textfile);
07434       }
07435 #endif
07436    }
07437 
07438    /* If anything failed above, we still have this list to free */
07439    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07440       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07441       free_user(vmtmp);
07442    }
07443    return res ? res : cmd;
07444 }
07445 
07446 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07447 {
07448    int res;
07449    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07450       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07451    return res;
07452 }
07453 
07454 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07455 {
07456    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07457    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);
07458 }
07459 
07460 static int play_message_category(struct ast_channel *chan, const char *category)
07461 {
07462    int res = 0;
07463 
07464    if (!ast_strlen_zero(category))
07465       res = ast_play_and_wait(chan, category);
07466 
07467    if (res) {
07468       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07469       res = 0;
07470    }
07471 
07472    return res;
07473 }
07474 
07475 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07476 {
07477    int res = 0;
07478    struct vm_zone *the_zone = NULL;
07479    time_t t;
07480 
07481    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07482       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07483       return 0;
07484    }
07485 
07486    /* Does this user have a timezone specified? */
07487    if (!ast_strlen_zero(vmu->zonetag)) {
07488       /* Find the zone in the list */
07489       struct vm_zone *z;
07490       AST_LIST_LOCK(&zones);
07491       AST_LIST_TRAVERSE(&zones, z, list) {
07492          if (!strcmp(z->name, vmu->zonetag)) {
07493             the_zone = z;
07494             break;
07495          }
07496       }
07497       AST_LIST_UNLOCK(&zones);
07498    }
07499 
07500 /* No internal variable parsing for now, so we'll comment it out for the time being */
07501 #if 0
07502    /* Set the DIFF_* variables */
07503    ast_localtime(&t, &time_now, NULL);
07504    tv_now = ast_tvnow();
07505    ast_localtime(&tv_now, &time_then, NULL);
07506 
07507    /* Day difference */
07508    if (time_now.tm_year == time_then.tm_year)
07509       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07510    else
07511       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07512    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07513 
07514    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07515 #endif
07516    if (the_zone) {
07517       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07518    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07519       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07520    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07521       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07522    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07523       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);
07524    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07525       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07526    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07527       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07528    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07529       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07530    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07531       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);
07532    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07533       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07534    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07535       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07536    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07537       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);
07538    } else {
07539       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07540    }
07541 #if 0
07542    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07543 #endif
07544    return res;
07545 }
07546 
07547 
07548 
07549 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07550 {
07551    int res = 0;
07552    int i;
07553    char *callerid, *name;
07554    char prefile[PATH_MAX] = "";
07555    
07556 
07557    /* If voicemail cid is not enabled, or we didn't get cid or context from
07558     * the attribute file, leave now.
07559     *
07560     * TODO Still need to change this so that if this function is called by the
07561     * message envelope (and someone is explicitly requesting to hear the CID),
07562     * it does not check to see if CID is enabled in the config file.
07563     */
07564    if ((cid == NULL)||(context == NULL))
07565       return res;
07566 
07567    /* Strip off caller ID number from name */
07568    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07569    ast_callerid_parse(cid, &name, &callerid);
07570    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07571       /* Check for internal contexts and only */
07572       /* say extension when the call didn't come from an internal context in the list */
07573       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07574          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07575          if ((strcmp(cidinternalcontexts[i], context) == 0))
07576             break;
07577       }
07578       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07579          if (!res) {
07580             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07581             if (!ast_strlen_zero(prefile)) {
07582             /* See if we can find a recorded name for this person instead of their extension number */
07583                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07584                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07585                   if (!callback)
07586                      res = wait_file2(chan, vms, "vm-from");
07587                   res = ast_stream_and_wait(chan, prefile, "");
07588                } else {
07589                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07590                   /* Say "from extension" as one saying to sound smoother */
07591                   if (!callback)
07592                      res = wait_file2(chan, vms, "vm-from-extension");
07593                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07594                }
07595             }
07596          }
07597       } else if (!res) {
07598          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07599          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07600          if (!callback)
07601             res = wait_file2(chan, vms, "vm-from-phonenumber");
07602          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07603       }
07604    } else {
07605       /* Number unknown */
07606       ast_debug(1, "VM-CID: From an unknown number\n");
07607       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07608       res = wait_file2(chan, vms, "vm-unknown-caller");
07609    }
07610    return res;
07611 }
07612 
07613 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07614 {
07615    int res = 0;
07616    int durationm;
07617    int durations;
07618    /* Verify that we have a duration for the message */
07619    if (duration == NULL)
07620       return res;
07621 
07622    /* Convert from seconds to minutes */
07623    durations = atoi(duration);
07624    durationm = (durations / 60);
07625 
07626    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07627 
07628    if ((!res) && (durationm >= minduration)) {
07629       res = wait_file2(chan, vms, "vm-duration");
07630 
07631       /* POLISH syntax */
07632       if (!strncasecmp(chan->language, "pl", 2)) {
07633          div_t num = div(durationm, 10);
07634 
07635          if (durationm == 1) {
07636             res = ast_play_and_wait(chan, "digits/1z");
07637             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07638          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07639             if (num.rem == 2) {
07640                if (!num.quot) {
07641                   res = ast_play_and_wait(chan, "digits/2-ie");
07642                } else {
07643                   res = say_and_wait(chan, durationm - 2 , chan->language);
07644                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07645                }
07646             } else {
07647                res = say_and_wait(chan, durationm, chan->language);
07648             }
07649             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07650          } else {
07651             res = say_and_wait(chan, durationm, chan->language);
07652             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07653          }
07654       /* DEFAULT syntax */
07655       } else {
07656          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07657          res = wait_file2(chan, vms, "vm-minutes");
07658       }
07659    }
07660    return res;
07661 }
07662 
07663 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07664 {
07665    int res = 0;
07666    char filename[256], *cid;
07667    const char *origtime, *context, *category, *duration, *flag;
07668    struct ast_config *msg_cfg;
07669    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07670 
07671    vms->starting = 0;
07672    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07673    adsi_message(chan, vms);
07674    if (!vms->curmsg) {
07675       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07676    } else if (vms->curmsg == vms->lastmsg) {
07677       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07678    }
07679 
07680    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07681    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07682    msg_cfg = ast_config_load(filename, config_flags);
07683    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07684       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07685       return 0;
07686    }
07687    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07688 
07689    /* Play the word urgent if we are listening to urgent messages */
07690    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07691       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07692    }
07693 
07694    if (!res) {
07695       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07696       /* POLISH syntax */
07697       if (!strncasecmp(chan->language, "pl", 2)) {
07698          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07699             int ten, one;
07700             char nextmsg[256];
07701             ten = (vms->curmsg + 1) / 10;
07702             one = (vms->curmsg + 1) % 10;
07703 
07704             if (vms->curmsg < 20) {
07705                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07706                res = wait_file2(chan, vms, nextmsg);
07707             } else {
07708                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07709                res = wait_file2(chan, vms, nextmsg);
07710                if (one > 0) {
07711                   if (!res) {
07712                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07713                      res = wait_file2(chan, vms, nextmsg);
07714                   }
07715                }
07716             }
07717          }
07718          if (!res)
07719             res = wait_file2(chan, vms, "vm-message");
07720       /* HEBREW syntax */
07721       } else if (!strncasecmp(chan->language, "he", 2)) {
07722          if (!vms->curmsg) {
07723             res = wait_file2(chan, vms, "vm-message");
07724             res = wait_file2(chan, vms, "vm-first");
07725          } else if (vms->curmsg == vms->lastmsg) {
07726             res = wait_file2(chan, vms, "vm-message");
07727             res = wait_file2(chan, vms, "vm-last");
07728          } else {
07729             res = wait_file2(chan, vms, "vm-message");
07730             res = wait_file2(chan, vms, "vm-number");
07731             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07732          }
07733       /* VIETNAMESE syntax */
07734       } else if (!strncasecmp(chan->language, "vi", 2)) {
07735          if (!vms->curmsg) {
07736             res = wait_file2(chan, vms, "vm-message");
07737             res = wait_file2(chan, vms, "vm-first");
07738          } else if (vms->curmsg == vms->lastmsg) {
07739             res = wait_file2(chan, vms, "vm-message");
07740             res = wait_file2(chan, vms, "vm-last");
07741          } else {
07742             res = wait_file2(chan, vms, "vm-message");
07743             res = wait_file2(chan, vms, "vm-number");
07744             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07745          }
07746       } else {
07747          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07748             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07749          } else { /* DEFAULT syntax */
07750             res = wait_file2(chan, vms, "vm-message");
07751          }
07752          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07753             if (!res) {
07754                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07755                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07756             }
07757          }
07758       }
07759    }
07760 
07761    if (!msg_cfg) {
07762       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07763       return 0;
07764    }
07765 
07766    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07767       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07768       DISPOSE(vms->curdir, vms->curmsg);
07769       ast_config_destroy(msg_cfg);
07770       return 0;
07771    }
07772 
07773    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07774    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07775    category = ast_variable_retrieve(msg_cfg, "message", "category");
07776 
07777    context = ast_variable_retrieve(msg_cfg, "message", "context");
07778    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07779       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07780    if (!res) {
07781       res = play_message_category(chan, category);
07782    }
07783    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07784       res = play_message_datetime(chan, vmu, origtime, filename);
07785    }
07786    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07787       res = play_message_callerid(chan, vms, cid, context, 0);
07788    }
07789    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07790       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07791    }
07792    /* Allow pressing '1' to skip envelope / callerid */
07793    if (res == '1') {
07794       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07795       res = 0;
07796    }
07797    ast_config_destroy(msg_cfg);
07798 
07799    if (!res) {
07800       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07801       vms->heard[vms->curmsg] = 1;
07802 #ifdef IMAP_STORAGE
07803       /*IMAP storage stores any prepended message from a forward
07804        * as a separate file from the rest of the message
07805        */
07806       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07807          wait_file(chan, vms, vms->introfn);
07808       }
07809 #endif
07810       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07811          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07812          res = 0;
07813       }
07814       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07815    }
07816    DISPOSE(vms->curdir, vms->curmsg);
07817    return res;
07818 }
07819 
07820 #ifdef IMAP_STORAGE
07821 static int imap_remove_file(char *dir, int msgnum)
07822 {
07823    char fn[PATH_MAX];
07824    char full_fn[PATH_MAX];
07825    char intro[PATH_MAX] = {0,};
07826    
07827    if (msgnum > -1) {
07828       make_file(fn, sizeof(fn), dir, msgnum);
07829       snprintf(intro, sizeof(intro), "%sintro", fn);
07830    } else
07831       ast_copy_string(fn, dir, sizeof(fn));
07832    
07833    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07834       ast_filedelete(fn, NULL);
07835       if (!ast_strlen_zero(intro)) {
07836          ast_filedelete(intro, NULL);
07837       }
07838       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07839       unlink(full_fn);
07840    }
07841    return 0;
07842 }
07843 
07844 
07845 
07846 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07847 {
07848    char *file, *filename;
07849    char *attachment;
07850    char arg[10];
07851    int i;
07852    BODY* body;
07853 
07854    file = strrchr(ast_strdupa(dir), '/');
07855    if (file) {
07856       *file++ = '\0';
07857    } else {
07858       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07859       return -1;
07860    }
07861 
07862    ast_mutex_lock(&vms->lock);
07863    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07864       mail_fetchstructure(vms->mailstream, i + 1, &body);
07865       /* We have the body, now we extract the file name of the first attachment. */
07866       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07867          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07868       } else {
07869          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07870          ast_mutex_unlock(&vms->lock);
07871          return -1;
07872       }
07873       filename = strsep(&attachment, ".");
07874       if (!strcmp(filename, file)) {
07875          sprintf(arg, "%d", i + 1);
07876          mail_setflag(vms->mailstream, arg, "\\DELETED");
07877       }
07878    }
07879    mail_expunge(vms->mailstream);
07880    ast_mutex_unlock(&vms->lock);
07881    return 0;
07882 }
07883 
07884 #elif !defined(IMAP_STORAGE)
07885 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07886 {
07887    int count_msg, last_msg;
07888 
07889    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07890 
07891    /* Rename the member vmbox HERE so that we don't try to return before
07892     * we know what's going on.
07893     */
07894    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07895 
07896    /* Faster to make the directory than to check if it exists. */
07897    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07898 
07899    /* traverses directory using readdir (or select query for ODBC) */
07900    count_msg = count_messages(vmu, vms->curdir);
07901    if (count_msg < 0) {
07902       return count_msg;
07903    } else {
07904       vms->lastmsg = count_msg - 1;
07905    }
07906 
07907    if (vm_allocate_dh(vms, vmu, count_msg)) {
07908       return -1;
07909    }
07910 
07911    /*
07912    The following test is needed in case sequencing gets messed up.
07913    There appears to be more than one way to mess up sequence, so
07914    we will not try to find all of the root causes--just fix it when
07915    detected.
07916    */
07917 
07918    if (vm_lock_path(vms->curdir)) {
07919       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07920       return ERROR_LOCK_PATH;
07921    }
07922 
07923    /* for local storage, checks directory for messages up to maxmsg limit */
07924    last_msg = last_message_index(vmu, vms->curdir);
07925    ast_unlock_path(vms->curdir);
07926 
07927    if (last_msg < -1) {
07928       return last_msg;
07929    } else if (vms->lastmsg != last_msg) {
07930       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);
07931       resequence_mailbox(vmu, vms->curdir, count_msg);
07932    }
07933 
07934    return 0;
07935 }
07936 #endif
07937 
07938 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07939 {
07940    int x = 0;
07941    int last_msg_idx = 0;
07942 
07943 #ifndef IMAP_STORAGE
07944    int res = 0, nummsg;
07945    char fn2[PATH_MAX];
07946 #endif
07947 
07948    if (vms->lastmsg <= -1) {
07949       goto done;
07950    }
07951 
07952    vms->curmsg = -1;
07953 #ifndef IMAP_STORAGE
07954    /* Get the deleted messages fixed */
07955    if (vm_lock_path(vms->curdir)) {
07956       return ERROR_LOCK_PATH;
07957    }
07958 
07959    /* update count as message may have arrived while we've got mailbox open */
07960    last_msg_idx = last_message_index(vmu, vms->curdir);
07961    if (last_msg_idx != vms->lastmsg) {
07962       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07963    }
07964 
07965    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07966    for (x = 0; x < last_msg_idx + 1; x++) {
07967       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07968          /* Save this message.  It's not in INBOX or hasn't been heard */
07969          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07970          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07971             break;
07972          }
07973          vms->curmsg++;
07974          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07975          if (strcmp(vms->fn, fn2)) {
07976             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07977          }
07978       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07979          /* Move to old folder before deleting */
07980          res = save_to_folder(vmu, vms, x, 1);
07981          if (res == ERROR_LOCK_PATH) {
07982             /* If save failed do not delete the message */
07983             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07984             vms->deleted[x] = 0;
07985             vms->heard[x] = 0;
07986             --x;
07987          }
07988       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07989          /* Move to deleted folder */
07990          res = save_to_folder(vmu, vms, x, 10);
07991          if (res == ERROR_LOCK_PATH) {
07992             /* If save failed do not delete the message */
07993             vms->deleted[x] = 0;
07994             vms->heard[x] = 0;
07995             --x;
07996          }
07997       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07998          /* If realtime storage enabled - we should explicitly delete this message,
07999          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08000          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08001          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08002             DELETE(vms->curdir, x, vms->fn, vmu);
08003          }
08004       }
08005    }
08006 
08007    /* Delete ALL remaining messages */
08008    nummsg = x - 1;
08009    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08010       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08011       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08012          DELETE(vms->curdir, x, vms->fn, vmu);
08013       }
08014    }
08015    ast_unlock_path(vms->curdir);
08016 #else /* defined(IMAP_STORAGE) */
08017    if (vms->deleted) {
08018       /* Since we now expunge after each delete, deleting in reverse order
08019        * ensures that no reordering occurs between each step. */
08020       last_msg_idx = vms->dh_arraysize;
08021       for (x = last_msg_idx - 1; x >= 0; x--) {
08022          if (vms->deleted[x]) {
08023             ast_debug(3, "IMAP delete of %d\n", x);
08024             DELETE(vms->curdir, x, vms->fn, vmu);
08025          }
08026       }
08027    }
08028 #endif
08029 
08030 done:
08031    if (vms->deleted && last_msg_idx) {
08032       ast_free(vms->deleted);
08033    }
08034    if (vms->heard && last_msg_idx) {
08035       ast_free(vms->heard);
08036    }
08037 
08038    return 0;
08039 }
08040 
08041 /* In Greek even though we CAN use a syntax like "friends messages"
08042  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08043  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08044  * syntax for the above three categories which is more elegant.
08045  */
08046 
08047 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08048 {
08049    int cmd;
08050    char *buf;
08051 
08052    buf = alloca(strlen(box) + 2);
08053    strcpy(buf, box);
08054    strcat(buf, "s");
08055 
08056    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08057       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08058       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08059    } else {
08060       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08061       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08062    }
08063 }
08064 
08065 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08066 {
08067    int cmd;
08068 
08069    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08070       if (!strcasecmp(box, "vm-INBOX"))
08071          cmd = ast_play_and_wait(chan, "vm-new-e");
08072       else
08073          cmd = ast_play_and_wait(chan, "vm-old-e");
08074       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08075    } else {
08076       cmd = ast_play_and_wait(chan, "vm-messages");
08077       return cmd ? cmd : ast_play_and_wait(chan, box);
08078    }
08079 }
08080 
08081 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08082 {
08083    int cmd;
08084 
08085    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08086       cmd = ast_play_and_wait(chan, "vm-messages");
08087       return cmd ? cmd : ast_play_and_wait(chan, box);
08088    } else {
08089       cmd = ast_play_and_wait(chan, box);
08090       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08091    }
08092 }
08093 
08094 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08095 {
08096    int cmd;
08097 
08098    if (  !strncasecmp(chan->language, "it", 2) ||
08099         !strncasecmp(chan->language, "es", 2) ||
08100         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08101       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08102       return cmd ? cmd : ast_play_and_wait(chan, box);
08103    } else if (!strncasecmp(chan->language, "gr", 2)) {
08104       return vm_play_folder_name_gr(chan, box);
08105    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08106       return ast_play_and_wait(chan, box);
08107    } else if (!strncasecmp(chan->language, "pl", 2)) {
08108       return vm_play_folder_name_pl(chan, box);
08109    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08110       return vm_play_folder_name_ua(chan, box);
08111    } else if (!strncasecmp(chan->language, "vi", 2)) {
08112       return ast_play_and_wait(chan, box);
08113    } else {  /* Default English */
08114       cmd = ast_play_and_wait(chan, box);
08115       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08116    }
08117 }
08118 
08119 /* GREEK SYNTAX
08120    In greek the plural for old/new is
08121    different so we need the following files
08122    We also need vm-denExeteMynhmata because
08123    this syntax is different.
08124 
08125    -> vm-Olds.wav : "Palia"
08126    -> vm-INBOXs.wav : "Nea"
08127    -> vm-denExeteMynhmata : "den exete mynhmata"
08128 */
08129 
08130 
08131 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08132 {
08133    int res = 0;
08134 
08135    if (vms->newmessages) {
08136       res = ast_play_and_wait(chan, "vm-youhave");
08137       if (!res) 
08138          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08139       if (!res) {
08140          if ((vms->newmessages == 1)) {
08141             res = ast_play_and_wait(chan, "vm-INBOX");
08142             if (!res)
08143                res = ast_play_and_wait(chan, "vm-message");
08144          } else {
08145             res = ast_play_and_wait(chan, "vm-INBOXs");
08146             if (!res)
08147                res = ast_play_and_wait(chan, "vm-messages");
08148          }
08149       }
08150    } else if (vms->oldmessages){
08151       res = ast_play_and_wait(chan, "vm-youhave");
08152       if (!res)
08153          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08154       if ((vms->oldmessages == 1)){
08155          res = ast_play_and_wait(chan, "vm-Old");
08156          if (!res)
08157             res = ast_play_and_wait(chan, "vm-message");
08158       } else {
08159          res = ast_play_and_wait(chan, "vm-Olds");
08160          if (!res)
08161             res = ast_play_and_wait(chan, "vm-messages");
08162       }
08163    } else if (!vms->oldmessages && !vms->newmessages) 
08164       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08165    return res;
08166 }
08167 
08168 /* Version of vm_intro() designed to work for many languages.
08169  *
08170  * It is hoped that this function can prevent the proliferation of 
08171  * language-specific vm_intro() functions and in time replace the language-
08172  * specific functions which already exist.  An examination of the language-
08173  * specific functions revealed that they all corrected the same deficiencies
08174  * in vm_intro_en() (which was the default function). Namely:
08175  *
08176  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08177  *     wording of the voicemail greeting hides this problem.  For example,
08178  *     vm-INBOX contains only the word "new".  This means that both of these
08179  *     sequences produce valid utterances:
08180  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08181  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08182  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08183  *     in many languages) the first utterance becomes "you have 1 the new message".
08184  *  2) The function contains hardcoded rules for pluralizing the word "message".
08185  *     These rules are correct for English, but not for many other languages.
08186  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08187  *     required in many languages.
08188  *  4) The gender of the word for "message" is not specified. This is a problem
08189  *     because in many languages the gender of the number in phrases such
08190  *     as "you have one new message" must match the gender of the word
08191  *     meaning "message".
08192  *
08193  * Fixing these problems for each new language has meant duplication of effort.
08194  * This new function solves the problems in the following general ways:
08195  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08196  *     and vm-Old respectively for those languages where it makes sense.
08197  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08198  *     on vm-message.
08199  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08200  *     prefix on vm-new and vm-old (none for English).
08201  *  4) Pass the gender of the language's word for "message" as an agument to
08202  *     this function which is can in turn pass on to the functions which 
08203  *     say numbers and put endings on nounds and adjectives.
08204  *
08205  * All languages require these messages:
08206  *  vm-youhave    "You have..."
08207  *  vm-and     "and"
08208  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08209  *
08210  * To use it for English, you will need these additional sound files:
08211  *  vm-new     "new"
08212  *  vm-message    "message", singular
08213  *  vm-messages      "messages", plural
08214  *
08215  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08216  *
08217  *  vm-newn    "novoye" (singular, neuter)
08218  *  vm-newx    "novikh" (counting plural form, genative plural)
08219  *  vm-message    "sobsheniye" (singular form)
08220  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08221  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08222  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08223  *  digits/2n     "dva" (neuter singular)
08224  */
08225 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08226 {
08227    int res;
08228    int lastnum = 0;
08229 
08230    res = ast_play_and_wait(chan, "vm-youhave");
08231 
08232    if (!res && vms->newmessages) {
08233       lastnum = vms->newmessages;
08234 
08235       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08236          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08237       }
08238 
08239       if (!res && vms->oldmessages) {
08240          res = ast_play_and_wait(chan, "vm-and");
08241       }
08242    }
08243 
08244    if (!res && vms->oldmessages) {
08245       lastnum = vms->oldmessages;
08246 
08247       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08248          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08249       }
08250    }
08251 
08252    if (!res) {
08253       if (lastnum == 0) {
08254          res = ast_play_and_wait(chan, "vm-no");
08255       }
08256       if (!res) {
08257          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08258       }
08259    }
08260 
08261    return res;
08262 }
08263 
08264 /* Default Hebrew syntax */
08265 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08266 {
08267    int res = 0;
08268 
08269    /* Introduce messages they have */
08270    if (!res) {
08271       if ((vms->newmessages) || (vms->oldmessages)) {
08272          res = ast_play_and_wait(chan, "vm-youhave");
08273       }
08274       /*
08275        * The word "shtei" refers to the number 2 in hebrew when performing a count
08276        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08277        * an element, this is one of them.
08278        */
08279       if (vms->newmessages) {
08280          if (!res) {
08281             if (vms->newmessages == 1) {
08282                res = ast_play_and_wait(chan, "vm-INBOX1");
08283             } else {
08284                if (vms->newmessages == 2) {
08285                   res = ast_play_and_wait(chan, "vm-shtei");
08286                } else {
08287                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08288                }
08289                res = ast_play_and_wait(chan, "vm-INBOX");
08290             }
08291          }
08292          if (vms->oldmessages && !res) {
08293             res = ast_play_and_wait(chan, "vm-and");
08294             if (vms->oldmessages == 1) {
08295                res = ast_play_and_wait(chan, "vm-Old1");
08296             } else {
08297                if (vms->oldmessages == 2) {
08298                   res = ast_play_and_wait(chan, "vm-shtei");
08299                } else {
08300                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08301                }
08302                res = ast_play_and_wait(chan, "vm-Old");
08303             }
08304          }
08305       }
08306       if (!res && vms->oldmessages && !vms->newmessages) {
08307          if (!res) {
08308             if (vms->oldmessages == 1) {
08309                res = ast_play_and_wait(chan, "vm-Old1");
08310             } else {
08311                if (vms->oldmessages == 2) {
08312                   res = ast_play_and_wait(chan, "vm-shtei");
08313                } else {
08314                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08315                }
08316                res = ast_play_and_wait(chan, "vm-Old");
08317             }
08318          }
08319       }
08320       if (!res) {
08321          if (!vms->oldmessages && !vms->newmessages) {
08322             if (!res) {
08323                res = ast_play_and_wait(chan, "vm-nomessages");
08324             }
08325          }
08326       }
08327    }
08328    return res;
08329 }
08330    
08331 /* Default English syntax */
08332 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08333 {
08334    int res;
08335 
08336    /* Introduce messages they have */
08337    res = ast_play_and_wait(chan, "vm-youhave");
08338    if (!res) {
08339       if (vms->urgentmessages) {
08340          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08341          if (!res)
08342             res = ast_play_and_wait(chan, "vm-Urgent");
08343          if ((vms->oldmessages || vms->newmessages) && !res) {
08344             res = ast_play_and_wait(chan, "vm-and");
08345          } else if (!res) {
08346             if ((vms->urgentmessages == 1))
08347                res = ast_play_and_wait(chan, "vm-message");
08348             else
08349                res = ast_play_and_wait(chan, "vm-messages");
08350          }
08351       }
08352       if (vms->newmessages) {
08353          res = say_and_wait(chan, vms->newmessages, chan->language);
08354          if (!res)
08355             res = ast_play_and_wait(chan, "vm-INBOX");
08356          if (vms->oldmessages && !res)
08357             res = ast_play_and_wait(chan, "vm-and");
08358          else if (!res) {
08359             if ((vms->newmessages == 1))
08360                res = ast_play_and_wait(chan, "vm-message");
08361             else
08362                res = ast_play_and_wait(chan, "vm-messages");
08363          }
08364             
08365       }
08366       if (!res && vms->oldmessages) {
08367          res = say_and_wait(chan, vms->oldmessages, chan->language);
08368          if (!res)
08369             res = ast_play_and_wait(chan, "vm-Old");
08370          if (!res) {
08371             if (vms->oldmessages == 1)
08372                res = ast_play_and_wait(chan, "vm-message");
08373             else
08374                res = ast_play_and_wait(chan, "vm-messages");
08375          }
08376       }
08377       if (!res) {
08378          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08379             res = ast_play_and_wait(chan, "vm-no");
08380             if (!res)
08381                res = ast_play_and_wait(chan, "vm-messages");
08382          }
08383       }
08384    }
08385    return res;
08386 }
08387 
08388 /* ITALIAN syntax */
08389 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08390 {
08391    /* Introduce messages they have */
08392    int res;
08393    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08394       res = ast_play_and_wait(chan, "vm-no") ||
08395          ast_play_and_wait(chan, "vm-message");
08396    else
08397       res = ast_play_and_wait(chan, "vm-youhave");
08398    if (!res && vms->newmessages) {
08399       res = (vms->newmessages == 1) ?
08400          ast_play_and_wait(chan, "digits/un") ||
08401          ast_play_and_wait(chan, "vm-nuovo") ||
08402          ast_play_and_wait(chan, "vm-message") :
08403          /* 2 or more new messages */
08404          say_and_wait(chan, vms->newmessages, chan->language) ||
08405          ast_play_and_wait(chan, "vm-nuovi") ||
08406          ast_play_and_wait(chan, "vm-messages");
08407       if (!res && vms->oldmessages)
08408          res = ast_play_and_wait(chan, "vm-and");
08409    }
08410    if (!res && vms->oldmessages) {
08411       res = (vms->oldmessages == 1) ?
08412          ast_play_and_wait(chan, "digits/un") ||
08413          ast_play_and_wait(chan, "vm-vecchio") ||
08414          ast_play_and_wait(chan, "vm-message") :
08415          /* 2 or more old messages */
08416          say_and_wait(chan, vms->oldmessages, chan->language) ||
08417          ast_play_and_wait(chan, "vm-vecchi") ||
08418          ast_play_and_wait(chan, "vm-messages");
08419    }
08420    return res;
08421 }
08422 
08423 /* POLISH syntax */
08424 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08425 {
08426    /* Introduce messages they have */
08427    int res;
08428    div_t num;
08429 
08430    if (!vms->oldmessages && !vms->newmessages) {
08431       res = ast_play_and_wait(chan, "vm-no");
08432       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08433       return res;
08434    } else {
08435       res = ast_play_and_wait(chan, "vm-youhave");
08436    }
08437 
08438    if (vms->newmessages) {
08439       num = div(vms->newmessages, 10);
08440       if (vms->newmessages == 1) {
08441          res = ast_play_and_wait(chan, "digits/1-a");
08442          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08443          res = res ? res : ast_play_and_wait(chan, "vm-message");
08444       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08445          if (num.rem == 2) {
08446             if (!num.quot) {
08447                res = ast_play_and_wait(chan, "digits/2-ie");
08448             } else {
08449                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08450                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08451             }
08452          } else {
08453             res = say_and_wait(chan, vms->newmessages, chan->language);
08454          }
08455          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08456          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08457       } else {
08458          res = say_and_wait(chan, vms->newmessages, chan->language);
08459          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08460          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08461       }
08462       if (!res && vms->oldmessages)
08463          res = ast_play_and_wait(chan, "vm-and");
08464    }
08465    if (!res && vms->oldmessages) {
08466       num = div(vms->oldmessages, 10);
08467       if (vms->oldmessages == 1) {
08468          res = ast_play_and_wait(chan, "digits/1-a");
08469          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08470          res = res ? res : ast_play_and_wait(chan, "vm-message");
08471       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08472          if (num.rem == 2) {
08473             if (!num.quot) {
08474                res = ast_play_and_wait(chan, "digits/2-ie");
08475             } else {
08476                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08477                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08478             }
08479          } else {
08480             res = say_and_wait(chan, vms->oldmessages, chan->language);
08481          }
08482          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08483          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08484       } else {
08485          res = say_and_wait(chan, vms->oldmessages, chan->language);
08486          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08487          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08488       }
08489    }
08490 
08491    return res;
08492 }
08493 
08494 /* SWEDISH syntax */
08495 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08496 {
08497    /* Introduce messages they have */
08498    int res;
08499 
08500    res = ast_play_and_wait(chan, "vm-youhave");
08501    if (res)
08502       return res;
08503 
08504    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08505       res = ast_play_and_wait(chan, "vm-no");
08506       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08507       return res;
08508    }
08509 
08510    if (vms->newmessages) {
08511       if ((vms->newmessages == 1)) {
08512          res = ast_play_and_wait(chan, "digits/ett");
08513          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08514          res = res ? res : ast_play_and_wait(chan, "vm-message");
08515       } else {
08516          res = say_and_wait(chan, vms->newmessages, chan->language);
08517          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08518          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08519       }
08520       if (!res && vms->oldmessages)
08521          res = ast_play_and_wait(chan, "vm-and");
08522    }
08523    if (!res && vms->oldmessages) {
08524       if (vms->oldmessages == 1) {
08525          res = ast_play_and_wait(chan, "digits/ett");
08526          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08527          res = res ? res : ast_play_and_wait(chan, "vm-message");
08528       } else {
08529          res = say_and_wait(chan, vms->oldmessages, chan->language);
08530          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08531          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08532       }
08533    }
08534 
08535    return res;
08536 }
08537 
08538 /* NORWEGIAN syntax */
08539 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08540 {
08541    /* Introduce messages they have */
08542    int res;
08543 
08544    res = ast_play_and_wait(chan, "vm-youhave");
08545    if (res)
08546       return res;
08547 
08548    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08549       res = ast_play_and_wait(chan, "vm-no");
08550       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08551       return res;
08552    }
08553 
08554    if (vms->newmessages) {
08555       if ((vms->newmessages == 1)) {
08556          res = ast_play_and_wait(chan, "digits/1");
08557          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08558          res = res ? res : ast_play_and_wait(chan, "vm-message");
08559       } else {
08560          res = say_and_wait(chan, vms->newmessages, chan->language);
08561          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08562          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08563       }
08564       if (!res && vms->oldmessages)
08565          res = ast_play_and_wait(chan, "vm-and");
08566    }
08567    if (!res && vms->oldmessages) {
08568       if (vms->oldmessages == 1) {
08569          res = ast_play_and_wait(chan, "digits/1");
08570          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08571          res = res ? res : ast_play_and_wait(chan, "vm-message");
08572       } else {
08573          res = say_and_wait(chan, vms->oldmessages, chan->language);
08574          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08575          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08576       }
08577    }
08578 
08579    return res;
08580 }
08581 
08582 /* GERMAN syntax */
08583 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08584 {
08585    /* Introduce messages they have */
08586    int res;
08587    res = ast_play_and_wait(chan, "vm-youhave");
08588    if (!res) {
08589       if (vms->newmessages) {
08590          if ((vms->newmessages == 1))
08591             res = ast_play_and_wait(chan, "digits/1F");
08592          else
08593             res = say_and_wait(chan, vms->newmessages, chan->language);
08594          if (!res)
08595             res = ast_play_and_wait(chan, "vm-INBOX");
08596          if (vms->oldmessages && !res)
08597             res = ast_play_and_wait(chan, "vm-and");
08598          else if (!res) {
08599             if ((vms->newmessages == 1))
08600                res = ast_play_and_wait(chan, "vm-message");
08601             else
08602                res = ast_play_and_wait(chan, "vm-messages");
08603          }
08604             
08605       }
08606       if (!res && vms->oldmessages) {
08607          if (vms->oldmessages == 1)
08608             res = ast_play_and_wait(chan, "digits/1F");
08609          else
08610             res = say_and_wait(chan, vms->oldmessages, chan->language);
08611          if (!res)
08612             res = ast_play_and_wait(chan, "vm-Old");
08613          if (!res) {
08614             if (vms->oldmessages == 1)
08615                res = ast_play_and_wait(chan, "vm-message");
08616             else
08617                res = ast_play_and_wait(chan, "vm-messages");
08618          }
08619       }
08620       if (!res) {
08621          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08622             res = ast_play_and_wait(chan, "vm-no");
08623             if (!res)
08624                res = ast_play_and_wait(chan, "vm-messages");
08625          }
08626       }
08627    }
08628    return res;
08629 }
08630 
08631 /* SPANISH syntax */
08632 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08633 {
08634    /* Introduce messages they have */
08635    int res;
08636    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08637       res = ast_play_and_wait(chan, "vm-youhaveno");
08638       if (!res)
08639          res = ast_play_and_wait(chan, "vm-messages");
08640    } else {
08641       res = ast_play_and_wait(chan, "vm-youhave");
08642    }
08643    if (!res) {
08644       if (vms->newmessages) {
08645          if (!res) {
08646             if ((vms->newmessages == 1)) {
08647                res = ast_play_and_wait(chan, "digits/1M");
08648                if (!res)
08649                   res = ast_play_and_wait(chan, "vm-message");
08650                if (!res)
08651                   res = ast_play_and_wait(chan, "vm-INBOXs");
08652             } else {
08653                res = say_and_wait(chan, vms->newmessages, chan->language);
08654                if (!res)
08655                   res = ast_play_and_wait(chan, "vm-messages");
08656                if (!res)
08657                   res = ast_play_and_wait(chan, "vm-INBOX");
08658             }
08659          }
08660          if (vms->oldmessages && !res)
08661             res = ast_play_and_wait(chan, "vm-and");
08662       }
08663       if (vms->oldmessages) {
08664          if (!res) {
08665             if (vms->oldmessages == 1) {
08666                res = ast_play_and_wait(chan, "digits/1M");
08667                if (!res)
08668                   res = ast_play_and_wait(chan, "vm-message");
08669                if (!res)
08670                   res = ast_play_and_wait(chan, "vm-Olds");
08671             } else {
08672                res = say_and_wait(chan, vms->oldmessages, chan->language);
08673                if (!res)
08674                   res = ast_play_and_wait(chan, "vm-messages");
08675                if (!res)
08676                   res = ast_play_and_wait(chan, "vm-Old");
08677             }
08678          }
08679       }
08680    }
08681 return res;
08682 }
08683 
08684 /* BRAZILIAN PORTUGUESE syntax */
08685 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08686    /* Introduce messages they have */
08687    int res;
08688    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08689       res = ast_play_and_wait(chan, "vm-nomessages");
08690       return res;
08691    } else {
08692       res = ast_play_and_wait(chan, "vm-youhave");
08693    }
08694    if (vms->newmessages) {
08695       if (!res)
08696          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08697       if ((vms->newmessages == 1)) {
08698          if (!res)
08699             res = ast_play_and_wait(chan, "vm-message");
08700          if (!res)
08701             res = ast_play_and_wait(chan, "vm-INBOXs");
08702       } else {
08703          if (!res)
08704             res = ast_play_and_wait(chan, "vm-messages");
08705          if (!res)
08706             res = ast_play_and_wait(chan, "vm-INBOX");
08707       }
08708       if (vms->oldmessages && !res)
08709          res = ast_play_and_wait(chan, "vm-and");
08710    }
08711    if (vms->oldmessages) {
08712       if (!res)
08713          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08714       if (vms->oldmessages == 1) {
08715          if (!res)
08716             res = ast_play_and_wait(chan, "vm-message");
08717          if (!res)
08718             res = ast_play_and_wait(chan, "vm-Olds");
08719       } else {
08720          if (!res)
08721             res = ast_play_and_wait(chan, "vm-messages");
08722          if (!res)
08723             res = ast_play_and_wait(chan, "vm-Old");
08724       }
08725    }
08726    return res;
08727 }
08728 
08729 /* FRENCH syntax */
08730 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08731 {
08732    /* Introduce messages they have */
08733    int res;
08734    res = ast_play_and_wait(chan, "vm-youhave");
08735    if (!res) {
08736       if (vms->newmessages) {
08737          res = say_and_wait(chan, vms->newmessages, chan->language);
08738          if (!res)
08739             res = ast_play_and_wait(chan, "vm-INBOX");
08740          if (vms->oldmessages && !res)
08741             res = ast_play_and_wait(chan, "vm-and");
08742          else if (!res) {
08743             if ((vms->newmessages == 1))
08744                res = ast_play_and_wait(chan, "vm-message");
08745             else
08746                res = ast_play_and_wait(chan, "vm-messages");
08747          }
08748             
08749       }
08750       if (!res && vms->oldmessages) {
08751          res = say_and_wait(chan, vms->oldmessages, chan->language);
08752          if (!res)
08753             res = ast_play_and_wait(chan, "vm-Old");
08754          if (!res) {
08755             if (vms->oldmessages == 1)
08756                res = ast_play_and_wait(chan, "vm-message");
08757             else
08758                res = ast_play_and_wait(chan, "vm-messages");
08759          }
08760       }
08761       if (!res) {
08762          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08763             res = ast_play_and_wait(chan, "vm-no");
08764             if (!res)
08765                res = ast_play_and_wait(chan, "vm-messages");
08766          }
08767       }
08768    }
08769    return res;
08770 }
08771 
08772 /* DUTCH syntax */
08773 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08774 {
08775    /* Introduce messages they have */
08776    int res;
08777    res = ast_play_and_wait(chan, "vm-youhave");
08778    if (!res) {
08779       if (vms->newmessages) {
08780          res = say_and_wait(chan, vms->newmessages, chan->language);
08781          if (!res) {
08782             if (vms->newmessages == 1)
08783                res = ast_play_and_wait(chan, "vm-INBOXs");
08784             else
08785                res = ast_play_and_wait(chan, "vm-INBOX");
08786          }
08787          if (vms->oldmessages && !res)
08788             res = ast_play_and_wait(chan, "vm-and");
08789          else if (!res) {
08790             if ((vms->newmessages == 1))
08791                res = ast_play_and_wait(chan, "vm-message");
08792             else
08793                res = ast_play_and_wait(chan, "vm-messages");
08794          }
08795             
08796       }
08797       if (!res && vms->oldmessages) {
08798          res = say_and_wait(chan, vms->oldmessages, chan->language);
08799          if (!res) {
08800             if (vms->oldmessages == 1)
08801                res = ast_play_and_wait(chan, "vm-Olds");
08802             else
08803                res = ast_play_and_wait(chan, "vm-Old");
08804          }
08805          if (!res) {
08806             if (vms->oldmessages == 1)
08807                res = ast_play_and_wait(chan, "vm-message");
08808             else
08809                res = ast_play_and_wait(chan, "vm-messages");
08810          }
08811       }
08812       if (!res) {
08813          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08814             res = ast_play_and_wait(chan, "vm-no");
08815             if (!res)
08816                res = ast_play_and_wait(chan, "vm-messages");
08817          }
08818       }
08819    }
08820    return res;
08821 }
08822 
08823 /* PORTUGUESE syntax */
08824 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08825 {
08826    /* Introduce messages they have */
08827    int res;
08828    res = ast_play_and_wait(chan, "vm-youhave");
08829    if (!res) {
08830       if (vms->newmessages) {
08831          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08832          if (!res) {
08833             if ((vms->newmessages == 1)) {
08834                res = ast_play_and_wait(chan, "vm-message");
08835                if (!res)
08836                   res = ast_play_and_wait(chan, "vm-INBOXs");
08837             } else {
08838                res = ast_play_and_wait(chan, "vm-messages");
08839                if (!res)
08840                   res = ast_play_and_wait(chan, "vm-INBOX");
08841             }
08842          }
08843          if (vms->oldmessages && !res)
08844             res = ast_play_and_wait(chan, "vm-and");
08845       }
08846       if (!res && vms->oldmessages) {
08847          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08848          if (!res) {
08849             if (vms->oldmessages == 1) {
08850                res = ast_play_and_wait(chan, "vm-message");
08851                if (!res)
08852                   res = ast_play_and_wait(chan, "vm-Olds");
08853             } else {
08854                res = ast_play_and_wait(chan, "vm-messages");
08855                if (!res)
08856                   res = ast_play_and_wait(chan, "vm-Old");
08857             }
08858          }
08859       }
08860       if (!res) {
08861          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08862             res = ast_play_and_wait(chan, "vm-no");
08863             if (!res)
08864                res = ast_play_and_wait(chan, "vm-messages");
08865          }
08866       }
08867    }
08868    return res;
08869 }
08870 
08871 
08872 /* CZECH syntax */
08873 /* in czech there must be declension of word new and message
08874  * czech        : english        : czech      : english
08875  * --------------------------------------------------------
08876  * vm-youhave   : you have 
08877  * vm-novou     : one new        : vm-zpravu  : message
08878  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08879  * vm-novych    : 5-infinite new : vm-zprav   : messages
08880  * vm-starou   : one old
08881  * vm-stare     : 2-4 old 
08882  * vm-starych   : 5-infinite old
08883  * jednu        : one   - falling 4. 
08884  * vm-no        : no  ( no messages )
08885  */
08886 
08887 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08888 {
08889    int res;
08890    res = ast_play_and_wait(chan, "vm-youhave");
08891    if (!res) {
08892       if (vms->newmessages) {
08893          if (vms->newmessages == 1) {
08894             res = ast_play_and_wait(chan, "digits/jednu");
08895          } else {
08896             res = say_and_wait(chan, vms->newmessages, chan->language);
08897          }
08898          if (!res) {
08899             if ((vms->newmessages == 1))
08900                res = ast_play_and_wait(chan, "vm-novou");
08901             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08902                res = ast_play_and_wait(chan, "vm-nove");
08903             if (vms->newmessages > 4)
08904                res = ast_play_and_wait(chan, "vm-novych");
08905          }
08906          if (vms->oldmessages && !res)
08907             res = ast_play_and_wait(chan, "vm-and");
08908          else if (!res) {
08909             if ((vms->newmessages == 1))
08910                res = ast_play_and_wait(chan, "vm-zpravu");
08911             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08912                res = ast_play_and_wait(chan, "vm-zpravy");
08913             if (vms->newmessages > 4)
08914                res = ast_play_and_wait(chan, "vm-zprav");
08915          }
08916       }
08917       if (!res && vms->oldmessages) {
08918          res = say_and_wait(chan, vms->oldmessages, chan->language);
08919          if (!res) {
08920             if ((vms->oldmessages == 1))
08921                res = ast_play_and_wait(chan, "vm-starou");
08922             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08923                res = ast_play_and_wait(chan, "vm-stare");
08924             if (vms->oldmessages > 4)
08925                res = ast_play_and_wait(chan, "vm-starych");
08926          }
08927          if (!res) {
08928             if ((vms->oldmessages == 1))
08929                res = ast_play_and_wait(chan, "vm-zpravu");
08930             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08931                res = ast_play_and_wait(chan, "vm-zpravy");
08932             if (vms->oldmessages > 4)
08933                res = ast_play_and_wait(chan, "vm-zprav");
08934          }
08935       }
08936       if (!res) {
08937          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08938             res = ast_play_and_wait(chan, "vm-no");
08939             if (!res)
08940                res = ast_play_and_wait(chan, "vm-zpravy");
08941          }
08942       }
08943    }
08944    return res;
08945 }
08946 
08947 /* CHINESE (Taiwan) syntax */
08948 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08949 {
08950    int res;
08951    /* Introduce messages they have */
08952    res = ast_play_and_wait(chan, "vm-you");
08953 
08954    if (!res && vms->newmessages) {
08955       res = ast_play_and_wait(chan, "vm-have");
08956       if (!res)
08957          res = say_and_wait(chan, vms->newmessages, chan->language);
08958       if (!res)
08959          res = ast_play_and_wait(chan, "vm-tong");
08960       if (!res)
08961          res = ast_play_and_wait(chan, "vm-INBOX");
08962       if (vms->oldmessages && !res)
08963          res = ast_play_and_wait(chan, "vm-and");
08964       else if (!res) 
08965          res = ast_play_and_wait(chan, "vm-messages");
08966    }
08967    if (!res && vms->oldmessages) {
08968       res = ast_play_and_wait(chan, "vm-have");
08969       if (!res)
08970          res = say_and_wait(chan, vms->oldmessages, chan->language);
08971       if (!res)
08972          res = ast_play_and_wait(chan, "vm-tong");
08973       if (!res)
08974          res = ast_play_and_wait(chan, "vm-Old");
08975       if (!res)
08976          res = ast_play_and_wait(chan, "vm-messages");
08977    }
08978    if (!res && !vms->oldmessages && !vms->newmessages) {
08979       res = ast_play_and_wait(chan, "vm-haveno");
08980       if (!res)
08981          res = ast_play_and_wait(chan, "vm-messages");
08982    }
08983    return res;
08984 }
08985 
08986 /* Vietnamese syntax */
08987 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08988 {
08989    int res;
08990 
08991    /* Introduce messages they have */
08992    res = ast_play_and_wait(chan, "vm-youhave");
08993    if (!res) {
08994       if (vms->newmessages) {
08995          res = say_and_wait(chan, vms->newmessages, chan->language);
08996          if (!res)
08997             res = ast_play_and_wait(chan, "vm-INBOX");
08998          if (vms->oldmessages && !res)
08999             res = ast_play_and_wait(chan, "vm-and");
09000       }
09001       if (!res && vms->oldmessages) {
09002          res = say_and_wait(chan, vms->oldmessages, chan->language);
09003          if (!res)
09004             res = ast_play_and_wait(chan, "vm-Old");        
09005       }
09006       if (!res) {
09007          if (!vms->oldmessages && !vms->newmessages) {
09008             res = ast_play_and_wait(chan, "vm-no");
09009             if (!res)
09010                res = ast_play_and_wait(chan, "vm-message");
09011          }
09012       }
09013    }
09014    return res;
09015 }
09016 
09017 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09018 {
09019    char prefile[256];
09020    
09021    /* Notify the user that the temp greeting is set and give them the option to remove it */
09022    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09023    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09024       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09025       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09026          ast_play_and_wait(chan, "vm-tempgreetactive");
09027       }
09028       DISPOSE(prefile, -1);
09029    }
09030 
09031    /* Play voicemail intro - syntax is different for different languages */
09032    if (0) {
09033       return 0;
09034    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09035       return vm_intro_cs(chan, vms);
09036    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09037       static int deprecation_warning = 0;
09038       if (deprecation_warning++ % 10 == 0) {
09039          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09040       }
09041       return vm_intro_cs(chan, vms);
09042    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09043       return vm_intro_de(chan, vms);
09044    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09045       return vm_intro_es(chan, vms);
09046    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09047       return vm_intro_fr(chan, vms);
09048    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09049       return vm_intro_gr(chan, vms);
09050    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09051       return vm_intro_he(chan, vms);
09052    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09053       return vm_intro_it(chan, vms);
09054    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09055       return vm_intro_nl(chan, vms);
09056    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09057       return vm_intro_no(chan, vms);
09058    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09059       return vm_intro_pl(chan, vms);
09060    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09061       return vm_intro_pt_BR(chan, vms);
09062    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09063       return vm_intro_pt(chan, vms);
09064    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09065       return vm_intro_multilang(chan, vms, "n");
09066    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09067       return vm_intro_se(chan, vms);
09068    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09069       return vm_intro_multilang(chan, vms, "n");
09070    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09071       return vm_intro_vi(chan, vms);
09072    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09073       return vm_intro_zh(chan, vms);
09074    } else {                                             /* Default to ENGLISH */
09075       return vm_intro_en(chan, vms);
09076    }
09077 }
09078 
09079 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09080 {
09081    int res = 0;
09082    /* Play instructions and wait for new command */
09083    while (!res) {
09084       if (vms->starting) {
09085          if (vms->lastmsg > -1) {
09086             if (skipadvanced)
09087                res = ast_play_and_wait(chan, "vm-onefor-full");
09088             else
09089                res = ast_play_and_wait(chan, "vm-onefor");
09090             if (!res)
09091                res = vm_play_folder_name(chan, vms->vmbox);
09092          }
09093          if (!res) {
09094             if (skipadvanced)
09095                res = ast_play_and_wait(chan, "vm-opts-full");
09096             else
09097                res = ast_play_and_wait(chan, "vm-opts");
09098          }
09099       } else {
09100          /* Added for additional help */
09101          if (skipadvanced) {
09102             res = ast_play_and_wait(chan, "vm-onefor-full");
09103             if (!res)
09104                res = vm_play_folder_name(chan, vms->vmbox);
09105             res = ast_play_and_wait(chan, "vm-opts-full");
09106          }
09107          /* Logic:
09108           * If the current message is not the first OR
09109           * if we're listening to the first new message and there are
09110           * also urgent messages, then prompt for navigation to the
09111           * previous message
09112           */
09113          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09114             res = ast_play_and_wait(chan, "vm-prev");
09115          }
09116          if (!res && !skipadvanced)
09117             res = ast_play_and_wait(chan, "vm-advopts");
09118          if (!res)
09119             res = ast_play_and_wait(chan, "vm-repeat");
09120          /* Logic:
09121           * If we're not listening to the last message OR
09122           * we're listening to the last urgent message and there are
09123           * also new non-urgent messages, then prompt for navigation
09124           * to the next message
09125           */
09126          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09127             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09128             res = ast_play_and_wait(chan, "vm-next");
09129          }
09130          if (!res) {
09131             if (!vms->deleted[vms->curmsg])
09132                res = ast_play_and_wait(chan, "vm-delete");
09133             else
09134                res = ast_play_and_wait(chan, "vm-undelete");
09135             if (!res)
09136                res = ast_play_and_wait(chan, "vm-toforward");
09137             if (!res)
09138                res = ast_play_and_wait(chan, "vm-savemessage");
09139          }
09140       }
09141       if (!res) {
09142          res = ast_play_and_wait(chan, "vm-helpexit");
09143       }
09144       if (!res)
09145          res = ast_waitfordigit(chan, 6000);
09146       if (!res) {
09147          vms->repeats++;
09148          if (vms->repeats > 2) {
09149             res = 't';
09150          }
09151       }
09152    }
09153    return res;
09154 }
09155 
09156 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09157 {
09158    int res = 0;
09159    /* Play instructions and wait for new command */
09160    while (!res) {
09161       if (vms->lastmsg > -1) {
09162          res = ast_play_and_wait(chan, "vm-listen");
09163          if (!res)
09164             res = vm_play_folder_name(chan, vms->vmbox);
09165          if (!res)
09166             res = ast_play_and_wait(chan, "press");
09167          if (!res)
09168             res = ast_play_and_wait(chan, "digits/1");
09169       }
09170       if (!res)
09171          res = ast_play_and_wait(chan, "vm-opts");
09172       if (!res) {
09173          vms->starting = 0;
09174          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09175       }
09176    }
09177    return res;
09178 }
09179 
09180 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09181 {
09182    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09183       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09184    } else {             /* Default to ENGLISH */
09185       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09186    }
09187 }
09188 
09189 
09190 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09191 {
09192    int cmd = 0;
09193    int duration = 0;
09194    int tries = 0;
09195    char newpassword[80] = "";
09196    char newpassword2[80] = "";
09197    char prefile[PATH_MAX] = "";
09198    unsigned char buf[256];
09199    int bytes = 0;
09200 
09201    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09202    if (ast_adsi_available(chan)) {
09203       bytes += adsi_logo(buf + bytes);
09204       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09205       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09206       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09207       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09208       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09209    }
09210 
09211    /* If forcename is set, have the user record their name */
09212    if (ast_test_flag(vmu, VM_FORCENAME)) {
09213       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09214       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09215          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09216          if (cmd < 0 || cmd == 't' || cmd == '#')
09217             return cmd;
09218       }
09219    }
09220 
09221    /* If forcegreetings is set, have the user record their greetings */
09222    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09223       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09224       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09225          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09226          if (cmd < 0 || cmd == 't' || cmd == '#')
09227             return cmd;
09228       }
09229 
09230       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09231       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09232          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09233          if (cmd < 0 || cmd == 't' || cmd == '#')
09234             return cmd;
09235       }
09236    }
09237 
09238    /*
09239     * Change the password last since new users will be able to skip over any steps this one comes before
09240     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09241     */
09242    for (;;) {
09243       newpassword[1] = '\0';
09244       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09245       if (cmd == '#')
09246          newpassword[0] = '\0';
09247       if (cmd < 0 || cmd == 't' || cmd == '#')
09248          return cmd;
09249       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09250       if (cmd < 0 || cmd == 't' || cmd == '#')
09251          return cmd;
09252       cmd = check_password(vmu, newpassword); /* perform password validation */
09253       if (cmd != 0) {
09254          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09255          cmd = ast_play_and_wait(chan, vm_invalid_password);
09256       } else {
09257          newpassword2[1] = '\0';
09258          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09259          if (cmd == '#')
09260             newpassword2[0] = '\0';
09261          if (cmd < 0 || cmd == 't' || cmd == '#')
09262             return cmd;
09263          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09264          if (cmd < 0 || cmd == 't' || cmd == '#')
09265             return cmd;
09266          if (!strcmp(newpassword, newpassword2))
09267             break;
09268          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09269          cmd = ast_play_and_wait(chan, vm_mismatch);
09270       }
09271       if (++tries == 3)
09272          return -1;
09273       if (cmd != 0) {
09274          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09275       }
09276    }
09277    if (pwdchange & PWDCHANGE_INTERNAL)
09278       vm_change_password(vmu, newpassword);
09279    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09280       vm_change_password_shell(vmu, newpassword);
09281 
09282    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09283    cmd = ast_play_and_wait(chan, vm_passchanged);
09284 
09285    return cmd;
09286 }
09287 
09288 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09289 {
09290    int cmd = 0;
09291    int retries = 0;
09292    int duration = 0;
09293    char newpassword[80] = "";
09294    char newpassword2[80] = "";
09295    char prefile[PATH_MAX] = "";
09296    unsigned char buf[256];
09297    int bytes = 0;
09298 
09299    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09300    if (ast_adsi_available(chan)) {
09301       bytes += adsi_logo(buf + bytes);
09302       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09303       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09304       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09305       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09306       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09307    }
09308    while ((cmd >= 0) && (cmd != 't')) {
09309       if (cmd)
09310          retries = 0;
09311       switch (cmd) {
09312       case '1': /* Record your unavailable message */
09313          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09314          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09315          break;
09316       case '2':  /* Record your busy message */
09317          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09318          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09319          break;
09320       case '3': /* Record greeting */
09321          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09322          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09323          break;
09324       case '4':  /* manage the temporary greeting */
09325          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09326          break;
09327       case '5': /* change password */
09328          if (vmu->password[0] == '-') {
09329             cmd = ast_play_and_wait(chan, "vm-no");
09330             break;
09331          }
09332          newpassword[1] = '\0';
09333          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09334          if (cmd == '#')
09335             newpassword[0] = '\0';
09336          else {
09337             if (cmd < 0)
09338                break;
09339             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09340                break;
09341             }
09342          }
09343          cmd = check_password(vmu, newpassword); /* perform password validation */
09344          if (cmd != 0) {
09345             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09346             cmd = ast_play_and_wait(chan, vm_invalid_password);
09347             if (!cmd) {
09348                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09349             }
09350             break;
09351          }
09352          newpassword2[1] = '\0';
09353          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09354          if (cmd == '#')
09355             newpassword2[0] = '\0';
09356          else {
09357             if (cmd < 0)
09358                break;
09359 
09360             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09361                break;
09362             }
09363          }
09364          if (strcmp(newpassword, newpassword2)) {
09365             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09366             cmd = ast_play_and_wait(chan, vm_mismatch);
09367             if (!cmd) {
09368                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09369             }
09370             break;
09371          }
09372 
09373          if (pwdchange & PWDCHANGE_INTERNAL) {
09374             vm_change_password(vmu, newpassword);
09375          }
09376          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09377             vm_change_password_shell(vmu, newpassword);
09378          }
09379 
09380          ast_debug(1, "User %s set password to %s of length %d\n",
09381             vms->username, newpassword, (int) strlen(newpassword));
09382          cmd = ast_play_and_wait(chan, vm_passchanged);
09383          break;
09384       case '*': 
09385          cmd = 't';
09386          break;
09387       default: 
09388          cmd = 0;
09389          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09390          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09391          if (ast_fileexists(prefile, NULL, NULL)) {
09392             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09393          }
09394          DISPOSE(prefile, -1);
09395          if (!cmd) {
09396             cmd = ast_play_and_wait(chan, "vm-options");
09397          }
09398          if (!cmd) {
09399             cmd = ast_waitfordigit(chan, 6000);
09400          }
09401          if (!cmd) {
09402             retries++;
09403          }
09404          if (retries > 3) {
09405             cmd = 't';
09406          }
09407          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09408       }
09409    }
09410    if (cmd == 't')
09411       cmd = 0;
09412    return cmd;
09413 }
09414 
09415 /*!
09416  * \brief The handler for 'record a temporary greeting'. 
09417  * \param chan
09418  * \param vmu
09419  * \param vms
09420  * \param fmtc
09421  * \param record_gain
09422  *
09423  * This is option 4 from the mailbox options menu.
09424  * This function manages the following promptings:
09425  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09426  * 2: remove (delete) the temporary greeting.
09427  * *: return to the main menu.
09428  *
09429  * \return zero on success, -1 on error.
09430  */
09431 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09432 {
09433    int cmd = 0;
09434    int retries = 0;
09435    int duration = 0;
09436    char prefile[PATH_MAX] = "";
09437    unsigned char buf[256];
09438    int bytes = 0;
09439 
09440    if (ast_adsi_available(chan)) {
09441       bytes += adsi_logo(buf + bytes);
09442       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09443       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09444       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09445       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09446       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09447    }
09448 
09449    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09450    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09451    while ((cmd >= 0) && (cmd != 't')) {
09452       if (cmd)
09453          retries = 0;
09454       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09455       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09456          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09457          cmd = 't';  
09458       } else {
09459          switch (cmd) {
09460          case '1':
09461             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09462             break;
09463          case '2':
09464             DELETE(prefile, -1, prefile, vmu);
09465             ast_play_and_wait(chan, "vm-tempremoved");
09466             cmd = 't';  
09467             break;
09468          case '*': 
09469             cmd = 't';
09470             break;
09471          default:
09472             cmd = ast_play_and_wait(chan,
09473                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09474                   "vm-tempgreeting2" : "vm-tempgreeting");
09475             if (!cmd) {
09476                cmd = ast_waitfordigit(chan, 6000);
09477             }
09478             if (!cmd) {
09479                retries++;
09480             }
09481             if (retries > 3) {
09482                cmd = 't';
09483             }
09484             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09485          }
09486       }
09487       DISPOSE(prefile, -1);
09488    }
09489    if (cmd == 't')
09490       cmd = 0;
09491    return cmd;
09492 }
09493 
09494 /*!
09495  * \brief Greek syntax for 'You have N messages' greeting.
09496  * \param chan
09497  * \param vms
09498  * \param vmu
09499  *
09500  * \return zero on success, -1 on error.
09501  */   
09502 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09503 {
09504    int cmd = 0;
09505 
09506    if (vms->lastmsg > -1) {
09507       cmd = play_message(chan, vmu, vms);
09508    } else {
09509       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09510       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09511          if (!cmd) {
09512             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09513             cmd = ast_play_and_wait(chan, vms->fn);
09514          }
09515          if (!cmd)
09516             cmd = ast_play_and_wait(chan, "vm-messages");
09517       } else {
09518          if (!cmd)
09519             cmd = ast_play_and_wait(chan, "vm-messages");
09520          if (!cmd) {
09521             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09522             cmd = ast_play_and_wait(chan, vms->fn);
09523          }
09524       }
09525    } 
09526    return cmd;
09527 }
09528 
09529 /* Hebrew Syntax */
09530 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09531 {
09532    int cmd = 0;
09533 
09534    if (vms->lastmsg > -1) {
09535       cmd = play_message(chan, vmu, vms);
09536    } else {
09537       if (!strcasecmp(vms->fn, "INBOX")) {
09538          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09539       } else {
09540          cmd = ast_play_and_wait(chan, "vm-nomessages");
09541       }
09542    }
09543    return cmd;
09544 }
09545 
09546 /*! 
09547  * \brief Default English syntax for 'You have N messages' greeting.
09548  * \param chan
09549  * \param vms
09550  * \param vmu
09551  *
09552  * \return zero on success, -1 on error.
09553  */
09554 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09555 {
09556    int cmd = 0;
09557 
09558    if (vms->lastmsg > -1) {
09559       cmd = play_message(chan, vmu, vms);
09560    } else {
09561       cmd = ast_play_and_wait(chan, "vm-youhave");
09562       if (!cmd) 
09563          cmd = ast_play_and_wait(chan, "vm-no");
09564       if (!cmd) {
09565          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09566          cmd = ast_play_and_wait(chan, vms->fn);
09567       }
09568       if (!cmd)
09569          cmd = ast_play_and_wait(chan, "vm-messages");
09570    }
09571    return cmd;
09572 }
09573 
09574 /*! 
09575  *\brief Italian syntax for 'You have N messages' greeting.
09576  * \param chan
09577  * \param vms
09578  * \param vmu
09579  *
09580  * \return zero on success, -1 on error.
09581  */
09582 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09583 {
09584    int cmd;
09585 
09586    if (vms->lastmsg > -1) {
09587       cmd = play_message(chan, vmu, vms);
09588    } else {
09589       cmd = ast_play_and_wait(chan, "vm-no");
09590       if (!cmd)
09591          cmd = ast_play_and_wait(chan, "vm-message");
09592       if (!cmd) {
09593          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09594          cmd = ast_play_and_wait(chan, vms->fn);
09595       }
09596    }
09597    return cmd;
09598 }
09599 
09600 /*! 
09601  * \brief Spanish syntax for 'You have N messages' greeting.
09602  * \param chan
09603  * \param vms
09604  * \param vmu
09605  *
09606  * \return zero on success, -1 on error.
09607  */
09608 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09609 {
09610    int cmd;
09611 
09612    if (vms->lastmsg > -1) {
09613       cmd = play_message(chan, vmu, vms);
09614    } else {
09615       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09616       if (!cmd)
09617          cmd = ast_play_and_wait(chan, "vm-messages");
09618       if (!cmd) {
09619          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09620          cmd = ast_play_and_wait(chan, vms->fn);
09621       }
09622    }
09623    return cmd;
09624 }
09625 
09626 /*! 
09627  * \brief Portuguese syntax for 'You have N messages' greeting.
09628  * \param chan
09629  * \param vms
09630  * \param vmu
09631  *
09632  * \return zero on success, -1 on error.
09633  */
09634 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09635 {
09636    int cmd;
09637 
09638    if (vms->lastmsg > -1) {
09639       cmd = play_message(chan, vmu, vms);
09640    } else {
09641       cmd = ast_play_and_wait(chan, "vm-no");
09642       if (!cmd) {
09643          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09644          cmd = ast_play_and_wait(chan, vms->fn);
09645       }
09646       if (!cmd)
09647          cmd = ast_play_and_wait(chan, "vm-messages");
09648    }
09649    return cmd;
09650 }
09651 
09652 /*! 
09653  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09654  * \param chan
09655  * \param vms
09656  * \param vmu
09657  *
09658  * \return zero on success, -1 on error.
09659  */
09660 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09661 {
09662    int cmd;
09663 
09664    if (vms->lastmsg > -1) {
09665       cmd = play_message(chan, vmu, vms);
09666    } else {
09667       cmd = ast_play_and_wait(chan, "vm-you");
09668       if (!cmd) 
09669          cmd = ast_play_and_wait(chan, "vm-haveno");
09670       if (!cmd)
09671          cmd = ast_play_and_wait(chan, "vm-messages");
09672       if (!cmd) {
09673          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09674          cmd = ast_play_and_wait(chan, vms->fn);
09675       }
09676    }
09677    return cmd;
09678 }
09679 
09680 /*! 
09681  * \brief Vietnamese syntax for 'You have N messages' greeting.
09682  * \param chan
09683  * \param vms
09684  * \param vmu
09685  *
09686  * \return zero on success, -1 on error.
09687  */
09688 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09689 {
09690    int cmd = 0;
09691 
09692    if (vms->lastmsg > -1) {
09693       cmd = play_message(chan, vmu, vms);
09694    } else {
09695       cmd = ast_play_and_wait(chan, "vm-no");
09696       if (!cmd) {
09697          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09698          cmd = ast_play_and_wait(chan, vms->fn);
09699       }
09700    }
09701    return cmd;
09702 }
09703 
09704 /*!
09705  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09706  * \param chan The channel for the current user. We read the language property from this.
09707  * \param vms passed into the language-specific vm_browse_messages function.
09708  * \param vmu passed into the language-specific vm_browse_messages function.
09709  * 
09710  * The method to be invoked is determined by the value of language code property in the user's channel.
09711  * The default (when unable to match) is to use english.
09712  *
09713  * \return zero on success, -1 on error.
09714  */
09715 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09716 {
09717    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09718       return vm_browse_messages_es(chan, vms, vmu);
09719    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09720       return vm_browse_messages_gr(chan, vms, vmu);
09721    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09722       return vm_browse_messages_he(chan, vms, vmu);
09723    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09724       return vm_browse_messages_it(chan, vms, vmu);
09725    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09726       return vm_browse_messages_pt(chan, vms, vmu);
09727    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09728       return vm_browse_messages_vi(chan, vms, vmu);
09729    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09730       return vm_browse_messages_zh(chan, vms, vmu);
09731    } else {                                             /* Default to English syntax */
09732       return vm_browse_messages_en(chan, vms, vmu);
09733    }
09734 }
09735 
09736 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09737          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09738          int skipuser, int max_logins, int silent)
09739 {
09740    int useadsi = 0, valid = 0, logretries = 0;
09741    char password[AST_MAX_EXTENSION]="", *passptr;
09742    struct ast_vm_user vmus, *vmu = NULL;
09743 
09744    /* If ADSI is supported, setup login screen */
09745    adsi_begin(chan, &useadsi);
09746    if (!skipuser && useadsi)
09747       adsi_login(chan);
09748    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09749    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09750       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09751       return -1;
09752    }
09753 
09754    /* Authenticate them and get their mailbox/password */
09755 
09756    while (!valid && (logretries < max_logins)) {
09757       /* Prompt for, and read in the username */
09758       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09759          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09760          return -1;
09761       }
09762       if (ast_strlen_zero(mailbox)) {
09763          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09764             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09765          } else {
09766             ast_verb(3, "Username not entered\n"); 
09767             return -1;
09768          }
09769       } else if (mailbox[0] == '*') {
09770          /* user entered '*' */
09771          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09772          if (ast_exists_extension(chan, chan->context, "a", 1,
09773             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09774             return -1;
09775          }
09776          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09777          mailbox[0] = '\0';
09778       }
09779 
09780       if (useadsi)
09781          adsi_password(chan);
09782 
09783       if (!ast_strlen_zero(prefix)) {
09784          char fullusername[80] = "";
09785          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09786          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09787          ast_copy_string(mailbox, fullusername, mailbox_size);
09788       }
09789 
09790       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09791       vmu = find_user(&vmus, context, mailbox);
09792       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09793          /* saved password is blank, so don't bother asking */
09794          password[0] = '\0';
09795       } else {
09796          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09797          if (ast_streamfile(chan, vm_password, chan->language)) {
09798             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09799             return -1;
09800          }
09801          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09802             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09803             return -1;
09804          } else if (password[0] == '*') {
09805             /* user entered '*' */
09806             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09807             if (ast_exists_extension(chan, chan->context, "a", 1,
09808                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09809                mailbox[0] = '*';
09810                return -1;
09811             }
09812             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09813             mailbox[0] = '\0';
09814             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09815             vmu = NULL;
09816          }
09817       }
09818 
09819       if (vmu) {
09820          passptr = vmu->password;
09821          if (passptr[0] == '-') passptr++;
09822       }
09823       if (vmu && !strcmp(passptr, password))
09824          valid++;
09825       else {
09826          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09827          if (!ast_strlen_zero(prefix))
09828             mailbox[0] = '\0';
09829       }
09830       logretries++;
09831       if (!valid) {
09832          if (skipuser || logretries >= max_logins) {
09833             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09834             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09835                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09836                return -1;
09837             }
09838          } else {
09839             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09840             if (useadsi)
09841                adsi_login(chan);
09842             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09843                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09844                return -1;
09845             }
09846          }
09847          if (ast_waitstream(chan, "")) /* Channel is hung up */
09848             return -1;
09849       }
09850    }
09851    if (!valid && (logretries >= max_logins)) {
09852       ast_stopstream(chan);
09853       ast_play_and_wait(chan, "vm-goodbye");
09854       return -1;
09855    }
09856    if (vmu && !skipuser) {
09857       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09858    }
09859    return 0;
09860 }
09861 
09862 static int vm_execmain(struct ast_channel *chan, const char *data)
09863 {
09864    /* XXX This is, admittedly, some pretty horrendous code.  For some
09865       reason it just seemed a lot easier to do with GOTO's.  I feel
09866       like I'm back in my GWBASIC days. XXX */
09867    int res = -1;
09868    int cmd = 0;
09869    int valid = 0;
09870    char prefixstr[80] ="";
09871    char ext_context[256]="";
09872    int box;
09873    int useadsi = 0;
09874    int skipuser = 0;
09875    struct vm_state vms;
09876    struct ast_vm_user *vmu = NULL, vmus;
09877    char *context = NULL;
09878    int silentexit = 0;
09879    struct ast_flags flags = { 0 };
09880    signed char record_gain = 0;
09881    int play_auto = 0;
09882    int play_folder = 0;
09883    int in_urgent = 0;
09884 #ifdef IMAP_STORAGE
09885    int deleted = 0;
09886 #endif
09887 
09888    /* Add the vm_state to the active list and keep it active */
09889    memset(&vms, 0, sizeof(vms));
09890 
09891    vms.lastmsg = -1;
09892 
09893    memset(&vmus, 0, sizeof(vmus));
09894 
09895    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09896    if (chan->_state != AST_STATE_UP) {
09897       ast_debug(1, "Before ast_answer\n");
09898       ast_answer(chan);
09899    }
09900 
09901    if (!ast_strlen_zero(data)) {
09902       char *opts[OPT_ARG_ARRAY_SIZE];
09903       char *parse;
09904       AST_DECLARE_APP_ARGS(args,
09905          AST_APP_ARG(argv0);
09906          AST_APP_ARG(argv1);
09907       );
09908 
09909       parse = ast_strdupa(data);
09910 
09911       AST_STANDARD_APP_ARGS(args, parse);
09912 
09913       if (args.argc == 2) {
09914          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09915             return -1;
09916          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09917             int gain;
09918             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09919                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09920                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09921                   return -1;
09922                } else {
09923                   record_gain = (signed char) gain;
09924                }
09925             } else {
09926                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09927             }
09928          }
09929          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09930             play_auto = 1;
09931             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09932                /* See if it is a folder name first */
09933                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09934                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09935                      play_folder = -1;
09936                   }
09937                } else {
09938                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09939                }
09940             } else {
09941                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09942             }
09943             if (play_folder > 9 || play_folder < 0) {
09944                ast_log(AST_LOG_WARNING,
09945                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09946                   opts[OPT_ARG_PLAYFOLDER]);
09947                play_folder = 0;
09948             }
09949          }
09950       } else {
09951          /* old style options parsing */
09952          while (*(args.argv0)) {
09953             if (*(args.argv0) == 's')
09954                ast_set_flag(&flags, OPT_SILENT);
09955             else if (*(args.argv0) == 'p')
09956                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09957             else 
09958                break;
09959             (args.argv0)++;
09960          }
09961 
09962       }
09963 
09964       valid = ast_test_flag(&flags, OPT_SILENT);
09965 
09966       if ((context = strchr(args.argv0, '@')))
09967          *context++ = '\0';
09968 
09969       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09970          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09971       else
09972          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09973 
09974       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09975          skipuser++;
09976       else
09977          valid = 0;
09978    }
09979 
09980    if (!valid)
09981       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09982 
09983    ast_debug(1, "After vm_authenticate\n");
09984 
09985    if (vms.username[0] == '*') {
09986       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09987 
09988       /* user entered '*' */
09989       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09990          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
09991          res = 0; /* prevent hangup */
09992          goto out;
09993       }
09994    }
09995 
09996    if (!res) {
09997       valid = 1;
09998       if (!skipuser)
09999          vmu = &vmus;
10000    } else {
10001       res = 0;
10002    }
10003 
10004    /* If ADSI is supported, setup login screen */
10005    adsi_begin(chan, &useadsi);
10006 
10007    ast_test_suite_assert(valid);
10008    if (!valid) {
10009       goto out;
10010    }
10011    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10012 
10013 #ifdef IMAP_STORAGE
10014    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10015    pthread_setspecific(ts_vmstate.key, &vms);
10016 
10017    vms.interactive = 1;
10018    vms.updated = 1;
10019    if (vmu)
10020       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10021    vmstate_insert(&vms);
10022    init_vm_state(&vms);
10023 #endif
10024    
10025    /* Set language from config to override channel language */
10026    if (!ast_strlen_zero(vmu->language))
10027       ast_string_field_set(chan, language, vmu->language);
10028 
10029    /* Retrieve urgent, old and new message counts */
10030    ast_debug(1, "Before open_mailbox\n");
10031    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10032    if (res < 0)
10033       goto out;
10034    vms.oldmessages = vms.lastmsg + 1;
10035    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10036    /* check INBOX */
10037    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10038    if (res < 0)
10039       goto out;
10040    vms.newmessages = vms.lastmsg + 1;
10041    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10042    /* Start in Urgent */
10043    in_urgent = 1;
10044    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10045    if (res < 0)
10046       goto out;
10047    vms.urgentmessages = vms.lastmsg + 1;
10048    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10049 
10050    /* Select proper mailbox FIRST!! */
10051    if (play_auto) {
10052       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10053       if (vms.urgentmessages) {
10054          in_urgent = 1;
10055          res = open_mailbox(&vms, vmu, 11);
10056       } else {
10057          in_urgent = 0;
10058          res = open_mailbox(&vms, vmu, play_folder);
10059       }
10060       if (res < 0)
10061          goto out;
10062 
10063       /* If there are no new messages, inform the user and hangup */
10064       if (vms.lastmsg == -1) {
10065          in_urgent = 0;
10066          cmd = vm_browse_messages(chan, &vms, vmu);
10067          res = 0;
10068          goto out;
10069       }
10070    } else {
10071       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10072          /* If we only have old messages start here */
10073          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10074          in_urgent = 0;
10075          play_folder = 1;
10076          if (res < 0)
10077             goto out;
10078       } else if (!vms.urgentmessages && vms.newmessages) {
10079          /* If we have new messages but none are urgent */
10080          in_urgent = 0;
10081          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10082          if (res < 0)
10083             goto out;
10084       }
10085    }
10086 
10087    if (useadsi)
10088       adsi_status(chan, &vms);
10089    res = 0;
10090 
10091    /* Check to see if this is a new user */
10092    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10093       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10094       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10095          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10096       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10097       if ((cmd == 't') || (cmd == '#')) {
10098          /* Timeout */
10099          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10100          res = 0;
10101          goto out;
10102       } else if (cmd < 0) {
10103          /* Hangup */
10104          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10105          res = -1;
10106          goto out;
10107       }
10108    }
10109 #ifdef IMAP_STORAGE
10110       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10111       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10112          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10113          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10114       }
10115       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10116       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10117          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10118          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10119       }
10120 #endif
10121 
10122    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10123    if (play_auto) {
10124       cmd = '1';
10125    } else {
10126       cmd = vm_intro(chan, vmu, &vms);
10127    }
10128    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10129 
10130    vms.repeats = 0;
10131    vms.starting = 1;
10132    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10133       /* Run main menu */
10134       switch (cmd) {
10135       case '1': /* First message */
10136          vms.curmsg = 0;
10137          /* Fall through */
10138       case '5': /* Play current message */
10139          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10140          cmd = vm_browse_messages(chan, &vms, vmu);
10141          break;
10142       case '2': /* Change folders */
10143          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10144          if (useadsi)
10145             adsi_folders(chan, 0, "Change to folder...");
10146 
10147          cmd = get_folder2(chan, "vm-changeto", 0);
10148          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10149          if (cmd == '#') {
10150             cmd = 0;
10151          } else if (cmd > 0) {
10152             cmd = cmd - '0';
10153             res = close_mailbox(&vms, vmu);
10154             if (res == ERROR_LOCK_PATH)
10155                goto out;
10156             /* If folder is not urgent, set in_urgent to zero! */
10157             if (cmd != 11) in_urgent = 0;
10158             res = open_mailbox(&vms, vmu, cmd);
10159             if (res < 0)
10160                goto out;
10161             play_folder = cmd;
10162             cmd = 0;
10163          }
10164          if (useadsi)
10165             adsi_status2(chan, &vms);
10166 
10167          if (!cmd) {
10168             cmd = vm_play_folder_name(chan, vms.vmbox);
10169          }
10170 
10171          vms.starting = 1;
10172          break;
10173       case '3': /* Advanced options */
10174          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10175          cmd = 0;
10176          vms.repeats = 0;
10177          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10178             switch (cmd) {
10179             case '1': /* Reply */
10180                if (vms.lastmsg > -1 && !vms.starting) {
10181                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10182                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10183                      res = cmd;
10184                      goto out;
10185                   }
10186                } else {
10187                   cmd = ast_play_and_wait(chan, "vm-sorry");
10188                }
10189                cmd = 't';
10190                break;
10191             case '2': /* Callback */
10192                if (!vms.starting)
10193                   ast_verb(3, "Callback Requested\n");
10194                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10195                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10196                   if (cmd == 9) {
10197                      silentexit = 1;
10198                      goto out;
10199                   } else if (cmd == ERROR_LOCK_PATH) {
10200                      res = cmd;
10201                      goto out;
10202                   }
10203                } else {
10204                   cmd = ast_play_and_wait(chan, "vm-sorry");
10205                }
10206                cmd = 't';
10207                break;
10208             case '3': /* Envelope */
10209                if (vms.lastmsg > -1 && !vms.starting) {
10210                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10211                   if (cmd == ERROR_LOCK_PATH) {
10212                      res = cmd;
10213                      goto out;
10214                   }
10215                } else {
10216                   cmd = ast_play_and_wait(chan, "vm-sorry");
10217                }
10218                cmd = 't';
10219                break;
10220             case '4': /* Dialout */
10221                if (!ast_strlen_zero(vmu->dialout)) {
10222                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10223                   if (cmd == 9) {
10224                      silentexit = 1;
10225                      goto out;
10226                   }
10227                } else {
10228                   cmd = ast_play_and_wait(chan, "vm-sorry");
10229                }
10230                cmd = 't';
10231                break;
10232 
10233             case '5': /* Leave VoiceMail */
10234                if (ast_test_flag(vmu, VM_SVMAIL)) {
10235                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10236                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10237                      res = cmd;
10238                      goto out;
10239                   }
10240                } else {
10241                   cmd = ast_play_and_wait(chan, "vm-sorry");
10242                }
10243                cmd = 't';
10244                break;
10245 
10246             case '*': /* Return to main menu */
10247                cmd = 't';
10248                break;
10249 
10250             default:
10251                cmd = 0;
10252                if (!vms.starting) {
10253                   cmd = ast_play_and_wait(chan, "vm-toreply");
10254                }
10255                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10256                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10257                }
10258                if (!cmd && !vms.starting) {
10259                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10260                }
10261                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10262                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10263                }
10264                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10265                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10266                }
10267                if (!cmd) {
10268                   cmd = ast_play_and_wait(chan, "vm-starmain");
10269                }
10270                if (!cmd) {
10271                   cmd = ast_waitfordigit(chan, 6000);
10272                }
10273                if (!cmd) {
10274                   vms.repeats++;
10275                }
10276                if (vms.repeats > 3) {
10277                   cmd = 't';
10278                }
10279                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10280             }
10281          }
10282          if (cmd == 't') {
10283             cmd = 0;
10284             vms.repeats = 0;
10285          }
10286          break;
10287       case '4': /* Go to the previous message */
10288          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10289          if (vms.curmsg > 0) {
10290             vms.curmsg--;
10291             cmd = play_message(chan, vmu, &vms);
10292          } else {
10293             /* Check if we were listening to new
10294                messages.  If so, go to Urgent messages
10295                instead of saying "no more messages"
10296             */
10297             if (in_urgent == 0 && vms.urgentmessages > 0) {
10298                /* Check for Urgent messages */
10299                in_urgent = 1;
10300                res = close_mailbox(&vms, vmu);
10301                if (res == ERROR_LOCK_PATH)
10302                   goto out;
10303                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10304                if (res < 0)
10305                   goto out;
10306                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10307                vms.curmsg = vms.lastmsg;
10308                if (vms.lastmsg < 0) {
10309                   cmd = ast_play_and_wait(chan, "vm-nomore");
10310                }
10311             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10312                vms.curmsg = vms.lastmsg;
10313                cmd = play_message(chan, vmu, &vms);
10314             } else {
10315                cmd = ast_play_and_wait(chan, "vm-nomore");
10316             }
10317          }
10318          break;
10319       case '6': /* Go to the next message */
10320          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10321          if (vms.curmsg < vms.lastmsg) {
10322             vms.curmsg++;
10323             cmd = play_message(chan, vmu, &vms);
10324          } else {
10325             if (in_urgent && vms.newmessages > 0) {
10326                /* Check if we were listening to urgent
10327                 * messages.  If so, go to regular new messages
10328                 * instead of saying "no more messages"
10329                 */
10330                in_urgent = 0;
10331                res = close_mailbox(&vms, vmu);
10332                if (res == ERROR_LOCK_PATH)
10333                   goto out;
10334                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10335                if (res < 0)
10336                   goto out;
10337                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10338                vms.curmsg = -1;
10339                if (vms.lastmsg < 0) {
10340                   cmd = ast_play_and_wait(chan, "vm-nomore");
10341                }
10342             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10343                vms.curmsg = 0;
10344                cmd = play_message(chan, vmu, &vms);
10345             } else {
10346                cmd = ast_play_and_wait(chan, "vm-nomore");
10347             }
10348          }
10349          break;
10350       case '7': /* Delete the current message */
10351          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10352             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10353             if (useadsi)
10354                adsi_delete(chan, &vms);
10355             if (vms.deleted[vms.curmsg]) {
10356                if (play_folder == 0) {
10357                   if (in_urgent) {
10358                      vms.urgentmessages--;
10359                   } else {
10360                      vms.newmessages--;
10361                   }
10362                }
10363                else if (play_folder == 1)
10364                   vms.oldmessages--;
10365                cmd = ast_play_and_wait(chan, "vm-deleted");
10366             } else {
10367                if (play_folder == 0) {
10368                   if (in_urgent) {
10369                      vms.urgentmessages++;
10370                   } else {
10371                      vms.newmessages++;
10372                   }
10373                }
10374                else if (play_folder == 1)
10375                   vms.oldmessages++;
10376                cmd = ast_play_and_wait(chan, "vm-undeleted");
10377             }
10378             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10379                if (vms.curmsg < vms.lastmsg) {
10380                   vms.curmsg++;
10381                   cmd = play_message(chan, vmu, &vms);
10382                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10383                   vms.curmsg = 0;
10384                   cmd = play_message(chan, vmu, &vms);
10385                } else {
10386                   /* Check if we were listening to urgent
10387                      messages.  If so, go to regular new messages
10388                      instead of saying "no more messages"
10389                   */
10390                   if (in_urgent == 1) {
10391                      /* Check for new messages */
10392                      in_urgent = 0;
10393                      res = close_mailbox(&vms, vmu);
10394                      if (res == ERROR_LOCK_PATH)
10395                         goto out;
10396                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10397                      if (res < 0)
10398                         goto out;
10399                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10400                      vms.curmsg = -1;
10401                      if (vms.lastmsg < 0) {
10402                         cmd = ast_play_and_wait(chan, "vm-nomore");
10403                      }
10404                   } else {
10405                      cmd = ast_play_and_wait(chan, "vm-nomore");
10406                   }
10407                }
10408             }
10409          } else /* Delete not valid if we haven't selected a message */
10410             cmd = 0;
10411 #ifdef IMAP_STORAGE
10412          deleted = 1;
10413 #endif
10414          break;
10415    
10416       case '8': /* Forward the current message */
10417          if (vms.lastmsg > -1) {
10418             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10419             if (cmd == ERROR_LOCK_PATH) {
10420                res = cmd;
10421                goto out;
10422             }
10423          } else {
10424             /* Check if we were listening to urgent
10425                messages.  If so, go to regular new messages
10426                instead of saying "no more messages"
10427             */
10428             if (in_urgent == 1 && vms.newmessages > 0) {
10429                /* Check for new messages */
10430                in_urgent = 0;
10431                res = close_mailbox(&vms, vmu);
10432                if (res == ERROR_LOCK_PATH)
10433                   goto out;
10434                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10435                if (res < 0)
10436                   goto out;
10437                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10438                vms.curmsg = -1;
10439                if (vms.lastmsg < 0) {
10440                   cmd = ast_play_and_wait(chan, "vm-nomore");
10441                }
10442             } else {
10443                cmd = ast_play_and_wait(chan, "vm-nomore");
10444             }
10445          }
10446          break;
10447       case '9': /* Save message to folder */
10448          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10449          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10450             /* No message selected */
10451             cmd = 0;
10452             break;
10453          }
10454          if (useadsi)
10455             adsi_folders(chan, 1, "Save to folder...");
10456          cmd = get_folder2(chan, "vm-savefolder", 1);
10457          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10458          box = 0; /* Shut up compiler */
10459          if (cmd == '#') {
10460             cmd = 0;
10461             break;
10462          } else if (cmd > 0) {
10463             box = cmd = cmd - '0';
10464             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10465             if (cmd == ERROR_LOCK_PATH) {
10466                res = cmd;
10467                goto out;
10468 #ifndef IMAP_STORAGE
10469             } else if (!cmd) {
10470                vms.deleted[vms.curmsg] = 1;
10471 #endif
10472             } else {
10473                vms.deleted[vms.curmsg] = 0;
10474                vms.heard[vms.curmsg] = 0;
10475             }
10476          }
10477          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10478          if (useadsi)
10479             adsi_message(chan, &vms);
10480          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10481          if (!cmd) {
10482             cmd = ast_play_and_wait(chan, "vm-message");
10483             if (!cmd) 
10484                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10485             if (!cmd)
10486                cmd = ast_play_and_wait(chan, "vm-savedto");
10487             if (!cmd)
10488                cmd = vm_play_folder_name(chan, vms.fn);
10489          } else {
10490             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10491          }
10492          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10493             if (vms.curmsg < vms.lastmsg) {
10494                vms.curmsg++;
10495                cmd = play_message(chan, vmu, &vms);
10496             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10497                vms.curmsg = 0;
10498                cmd = play_message(chan, vmu, &vms);
10499             } else {
10500                /* Check if we were listening to urgent
10501                   messages.  If so, go to regular new messages
10502                   instead of saying "no more messages"
10503                */
10504                if (in_urgent == 1 && vms.newmessages > 0) {
10505                   /* Check for new messages */
10506                   in_urgent = 0;
10507                   res = close_mailbox(&vms, vmu);
10508                   if (res == ERROR_LOCK_PATH)
10509                      goto out;
10510                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10511                   if (res < 0)
10512                      goto out;
10513                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10514                   vms.curmsg = -1;
10515                   if (vms.lastmsg < 0) {
10516                      cmd = ast_play_and_wait(chan, "vm-nomore");
10517                   }
10518                } else {
10519                   cmd = ast_play_and_wait(chan, "vm-nomore");
10520                }
10521             }
10522          }
10523          break;
10524       case '*': /* Help */
10525          if (!vms.starting) {
10526             cmd = ast_play_and_wait(chan, "vm-onefor");
10527             if (!strncasecmp(chan->language, "he", 2)) {
10528                cmd = ast_play_and_wait(chan, "vm-for");
10529             }
10530             if (!cmd)
10531                cmd = vm_play_folder_name(chan, vms.vmbox);
10532             if (!cmd)
10533                cmd = ast_play_and_wait(chan, "vm-opts");
10534             if (!cmd)
10535                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10536          } else
10537             cmd = 0;
10538          break;
10539       case '0': /* Mailbox options */
10540          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10541          if (useadsi)
10542             adsi_status(chan, &vms);
10543          break;
10544       default: /* Nothing */
10545          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10546          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10547          break;
10548       }
10549    }
10550    if ((cmd == 't') || (cmd == '#')) {
10551       /* Timeout */
10552       res = 0;
10553    } else {
10554       /* Hangup */
10555       res = -1;
10556    }
10557 
10558 out:
10559    if (res > -1) {
10560       ast_stopstream(chan);
10561       adsi_goodbye(chan);
10562       if (valid && res != OPERATOR_EXIT) {
10563          if (silentexit)
10564             res = ast_play_and_wait(chan, "vm-dialout");
10565          else 
10566             res = ast_play_and_wait(chan, "vm-goodbye");
10567       }
10568       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10569          res = 0;
10570       }
10571       if (useadsi)
10572          ast_adsi_unload_session(chan);
10573    }
10574    if (vmu)
10575       close_mailbox(&vms, vmu);
10576    if (valid) {
10577       int new = 0, old = 0, urgent = 0;
10578       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10579       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10580       /* Urgent flag not passwd to externnotify here */
10581       run_externnotify(vmu->context, vmu->mailbox, NULL);
10582       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10583       queue_mwi_event(ext_context, urgent, new, old);
10584    }
10585 #ifdef IMAP_STORAGE
10586    /* expunge message - use UID Expunge if supported on IMAP server*/
10587    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10588    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10589       ast_mutex_lock(&vms.lock);
10590 #ifdef HAVE_IMAP_TK2006
10591       if (LEVELUIDPLUS (vms.mailstream)) {
10592          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10593       } else 
10594 #endif
10595          mail_expunge(vms.mailstream);
10596       ast_mutex_unlock(&vms.lock);
10597    }
10598    /*  before we delete the state, we should copy pertinent info
10599     *  back to the persistent model */
10600    if (vmu) {
10601       vmstate_delete(&vms);
10602    }
10603 #endif
10604    if (vmu)
10605       free_user(vmu);
10606 
10607 #ifdef IMAP_STORAGE
10608    pthread_setspecific(ts_vmstate.key, NULL);
10609 #endif
10610    return res;
10611 }
10612 
10613 static int vm_exec(struct ast_channel *chan, const char *data)
10614 {
10615    int res = 0;
10616    char *tmp;
10617    struct leave_vm_options leave_options;
10618    struct ast_flags flags = { 0 };
10619    char *opts[OPT_ARG_ARRAY_SIZE];
10620    AST_DECLARE_APP_ARGS(args,
10621       AST_APP_ARG(argv0);
10622       AST_APP_ARG(argv1);
10623    );
10624    
10625    memset(&leave_options, 0, sizeof(leave_options));
10626 
10627    if (chan->_state != AST_STATE_UP)
10628       ast_answer(chan);
10629 
10630    if (!ast_strlen_zero(data)) {
10631       tmp = ast_strdupa(data);
10632       AST_STANDARD_APP_ARGS(args, tmp);
10633       if (args.argc == 2) {
10634          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10635             return -1;
10636          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10637          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10638             int gain;
10639 
10640             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10641                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10642                return -1;
10643             } else {
10644                leave_options.record_gain = (signed char) gain;
10645             }
10646          }
10647          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10648             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10649                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10650          }
10651       }
10652    } else {
10653       char temp[256];
10654       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10655       if (res < 0)
10656          return res;
10657       if (ast_strlen_zero(temp))
10658          return 0;
10659       args.argv0 = ast_strdupa(temp);
10660    }
10661 
10662    res = leave_voicemail(chan, args.argv0, &leave_options);
10663    if (res == 't') {
10664       ast_play_and_wait(chan, "vm-goodbye");
10665       res = 0;
10666    }
10667 
10668    if (res == OPERATOR_EXIT) {
10669       res = 0;
10670    }
10671 
10672    if (res == ERROR_LOCK_PATH) {
10673       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10674       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10675       res = 0;
10676    }
10677 
10678    return res;
10679 }
10680 
10681 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10682 {
10683    struct ast_vm_user *vmu;
10684 
10685    if (!ast_strlen_zero(box) && box[0] == '*') {
10686       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10687             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10688             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10689             "\n\tand will be ignored.\n", box, context);
10690       return NULL;
10691    }
10692 
10693    AST_LIST_TRAVERSE(&users, vmu, list) {
10694       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10695          if (strcasecmp(vmu->context, context)) {
10696             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10697                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10698                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10699                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10700          }
10701          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10702          return NULL;
10703       }
10704       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10705          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10706          return NULL;
10707       }
10708    }
10709    
10710    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10711       return NULL;
10712    
10713    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10714    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10715 
10716    AST_LIST_INSERT_TAIL(&users, vmu, list);
10717    
10718    return vmu;
10719 }
10720 
10721 static int append_mailbox(const char *context, const char *box, const char *data)
10722 {
10723    /* Assumes lock is already held */
10724    char *tmp;
10725    char *stringp;
10726    char *s;
10727    struct ast_vm_user *vmu;
10728    char *mailbox_full;
10729    int new = 0, old = 0, urgent = 0;
10730    char secretfn[PATH_MAX] = "";
10731 
10732    tmp = ast_strdupa(data);
10733 
10734    if (!(vmu = find_or_create(context, box)))
10735       return -1;
10736 
10737    populate_defaults(vmu);
10738 
10739    stringp = tmp;
10740    if ((s = strsep(&stringp, ","))) {
10741       if (!ast_strlen_zero(s) && s[0] == '*') {
10742          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10743             "\n\tmust be reset in voicemail.conf.\n", box);
10744       }
10745       /* assign password regardless of validity to prevent NULL password from being assigned */
10746       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10747    }
10748    if (stringp && (s = strsep(&stringp, ","))) {
10749       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10750    }
10751    if (stringp && (s = strsep(&stringp, ","))) {
10752       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10753    }
10754    if (stringp && (s = strsep(&stringp, ","))) {
10755       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10756    }
10757    if (stringp && (s = strsep(&stringp, ","))) {
10758       apply_options(vmu, s);
10759    }
10760 
10761    switch (vmu->passwordlocation) {
10762    case OPT_PWLOC_SPOOLDIR:
10763       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10764       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10765    }
10766 
10767    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10768    strcpy(mailbox_full, box);
10769    strcat(mailbox_full, "@");
10770    strcat(mailbox_full, context);
10771 
10772    inboxcount2(mailbox_full, &urgent, &new, &old);
10773    queue_mwi_event(mailbox_full, urgent, new, old);
10774 
10775    return 0;
10776 }
10777 
10778 AST_TEST_DEFINE(test_voicemail_vmuser)
10779 {
10780    int res = 0;
10781    struct ast_vm_user *vmu;
10782    /* language parameter seems to only be used for display in manager action */
10783    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10784       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10785       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10786       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10787       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10788       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10789       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10790       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10791       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10792 #ifdef IMAP_STORAGE
10793    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10794       "imapfolder=INBOX|imapvmshareid=6000";
10795 #endif
10796 
10797    switch (cmd) {
10798    case TEST_INIT:
10799       info->name = "vmuser";
10800       info->category = "/apps/app_voicemail/";
10801       info->summary = "Vmuser unit test";
10802       info->description =
10803          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10804       return AST_TEST_NOT_RUN;
10805    case TEST_EXECUTE:
10806       break;
10807    }
10808 
10809    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10810       return AST_TEST_NOT_RUN;
10811    }
10812    ast_set_flag(vmu, VM_ALLOCED);
10813    populate_defaults(vmu);
10814 
10815    apply_options(vmu, options_string);
10816 
10817    if (!ast_test_flag(vmu, VM_ATTACH)) {
10818       ast_test_status_update(test, "Parse failure for attach option\n");
10819       res = 1;
10820    }
10821    if (strcasecmp(vmu->attachfmt, "wav49")) {
10822       ast_test_status_update(test, "Parse failure for attachftm option\n");
10823       res = 1;
10824    }
10825    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10826       ast_test_status_update(test, "Parse failure for serveremail option\n");
10827       res = 1;
10828    }
10829    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10830       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10831       res = 1;
10832    }
10833    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10834       ast_test_status_update(test, "Parse failure for emailbody option\n");
10835       res = 1;
10836    }
10837    if (strcasecmp(vmu->zonetag, "central")) {
10838       ast_test_status_update(test, "Parse failure for tz option\n");
10839       res = 1;
10840    }
10841    if (!ast_test_flag(vmu, VM_DELETE)) {
10842       ast_test_status_update(test, "Parse failure for delete option\n");
10843       res = 1;
10844    }
10845    if (!ast_test_flag(vmu, VM_SAYCID)) {
10846       ast_test_status_update(test, "Parse failure for saycid option\n");
10847       res = 1;
10848    }
10849    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10850       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10851       res = 1;
10852    }
10853    if (!ast_test_flag(vmu, VM_REVIEW)) {
10854       ast_test_status_update(test, "Parse failure for review option\n");
10855       res = 1;
10856    }
10857    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10858       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10859       res = 1;
10860    }
10861    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10862       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10863       res = 1;
10864    }
10865    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10866       ast_test_status_update(test, "Parse failure for operator option\n");
10867       res = 1;
10868    }
10869    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10870       ast_test_status_update(test, "Parse failure for envelope option\n");
10871       res = 1;
10872    }
10873    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10874       ast_test_status_update(test, "Parse failure for moveheard option\n");
10875       res = 1;
10876    }
10877    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10878       ast_test_status_update(test, "Parse failure for sayduration option\n");
10879       res = 1;
10880    }
10881    if (vmu->saydurationm != 5) {
10882       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10883       res = 1;
10884    }
10885    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10886       ast_test_status_update(test, "Parse failure for forcename option\n");
10887       res = 1;
10888    }
10889    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10890       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10891       res = 1;
10892    }
10893    if (strcasecmp(vmu->callback, "somecontext")) {
10894       ast_test_status_update(test, "Parse failure for callbacks option\n");
10895       res = 1;
10896    }
10897    if (strcasecmp(vmu->dialout, "somecontext2")) {
10898       ast_test_status_update(test, "Parse failure for dialout option\n");
10899       res = 1;
10900    }
10901    if (strcasecmp(vmu->exit, "somecontext3")) {
10902       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10903       res = 1;
10904    }
10905    if (vmu->minsecs != 10) {
10906       ast_test_status_update(test, "Parse failure for minsecs option\n");
10907       res = 1;
10908    }
10909    if (vmu->maxsecs != 100) {
10910       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10911       res = 1;
10912    }
10913    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10914       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10915       res = 1;
10916    }
10917    if (vmu->maxdeletedmsg != 50) {
10918       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10919       res = 1;
10920    }
10921    if (vmu->volgain != 1.3) {
10922       ast_test_status_update(test, "Parse failure for volgain option\n");
10923       res = 1;
10924    }
10925    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10926       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10927       res = 1;
10928    }
10929 #ifdef IMAP_STORAGE
10930    apply_options(vmu, option_string2);
10931 
10932    if (strcasecmp(vmu->imapuser, "imapuser")) {
10933       ast_test_status_update(test, "Parse failure for imapuser option\n");
10934       res = 1;
10935    }
10936    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10937       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10938       res = 1;
10939    }
10940    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10941       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10942       res = 1;
10943    }
10944    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10945       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10946       res = 1;
10947    }
10948 #endif
10949 
10950    free_user(vmu);
10951    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10952 }
10953 
10954 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10955 {
10956    struct ast_vm_user svm;
10957    char *context, *box;
10958    AST_DECLARE_APP_ARGS(args,
10959       AST_APP_ARG(mbox);
10960       AST_APP_ARG(options);
10961    );
10962    static int dep_warning = 0;
10963 
10964    if (ast_strlen_zero(data)) {
10965       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10966       return -1;
10967    }
10968 
10969    if (!dep_warning) {
10970       dep_warning = 1;
10971       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10972    }
10973 
10974    box = ast_strdupa(data);
10975 
10976    AST_STANDARD_APP_ARGS(args, box);
10977 
10978    if (args.options) {
10979    }
10980 
10981    if ((context = strchr(args.mbox, '@'))) {
10982       *context = '\0';
10983       context++;
10984    }
10985 
10986    if (find_user(&svm, context, args.mbox)) {
10987       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10988    } else
10989       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10990 
10991    return 0;
10992 }
10993 
10994 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10995 {
10996    struct ast_vm_user svm;
10997    AST_DECLARE_APP_ARGS(arg,
10998       AST_APP_ARG(mbox);
10999       AST_APP_ARG(context);
11000    );
11001 
11002    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11003 
11004    if (ast_strlen_zero(arg.mbox)) {
11005       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11006       return -1;
11007    }
11008 
11009    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11010    return 0;
11011 }
11012 
11013 static struct ast_custom_function mailbox_exists_acf = {
11014    .name = "MAILBOX_EXISTS",
11015    .read = acf_mailbox_exists,
11016 };
11017 
11018 static int vmauthenticate(struct ast_channel *chan, const char *data)
11019 {
11020    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11021    struct ast_vm_user vmus;
11022    char *options = NULL;
11023    int silent = 0, skipuser = 0;
11024    int res = -1;
11025    
11026    if (data) {
11027       s = ast_strdupa(data);
11028       user = strsep(&s, ",");
11029       options = strsep(&s, ",");
11030       if (user) {
11031          s = user;
11032          user = strsep(&s, "@");
11033          context = strsep(&s, "");
11034          if (!ast_strlen_zero(user))
11035             skipuser++;
11036          ast_copy_string(mailbox, user, sizeof(mailbox));
11037       }
11038    }
11039 
11040    if (options) {
11041       silent = (strchr(options, 's')) != NULL;
11042    }
11043 
11044    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11045       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11046       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11047       ast_play_and_wait(chan, "auth-thankyou");
11048       res = 0;
11049    } else if (mailbox[0] == '*') {
11050       /* user entered '*' */
11051       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11052          res = 0; /* prevent hangup */
11053       }
11054    }
11055 
11056    return res;
11057 }
11058 
11059 static char *show_users_realtime(int fd, const char *context)
11060 {
11061    struct ast_config *cfg;
11062    const char *cat = NULL;
11063 
11064    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11065       "context", context, SENTINEL))) {
11066       return CLI_FAILURE;
11067    }
11068 
11069    ast_cli(fd,
11070       "\n"
11071       "=============================================================\n"
11072       "=== Configured Voicemail Users ==============================\n"
11073       "=============================================================\n"
11074       "===\n");
11075 
11076    while ((cat = ast_category_browse(cfg, cat))) {
11077       struct ast_variable *var = NULL;
11078       ast_cli(fd,
11079          "=== Mailbox ...\n"
11080          "===\n");
11081       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11082          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11083       ast_cli(fd,
11084          "===\n"
11085          "=== ---------------------------------------------------------\n"
11086          "===\n");
11087    }
11088 
11089    ast_cli(fd,
11090       "=============================================================\n"
11091       "\n");
11092 
11093    ast_config_destroy(cfg);
11094 
11095    return CLI_SUCCESS;
11096 }
11097 
11098 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11099 {
11100    int which = 0;
11101    int wordlen;
11102    struct ast_vm_user *vmu;
11103    const char *context = "";
11104 
11105    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11106    if (pos > 4)
11107       return NULL;
11108    if (pos == 3)
11109       return (state == 0) ? ast_strdup("for") : NULL;
11110    wordlen = strlen(word);
11111    AST_LIST_TRAVERSE(&users, vmu, list) {
11112       if (!strncasecmp(word, vmu->context, wordlen)) {
11113          if (context && strcmp(context, vmu->context) && ++which > state)
11114             return ast_strdup(vmu->context);
11115          /* ignore repeated contexts ? */
11116          context = vmu->context;
11117       }
11118    }
11119    return NULL;
11120 }
11121 
11122 /*! \brief Show a list of voicemail users in the CLI */
11123 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11124 {
11125    struct ast_vm_user *vmu;
11126 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11127    const char *context = NULL;
11128    int users_counter = 0;
11129 
11130    switch (cmd) {
11131    case CLI_INIT:
11132       e->command = "voicemail show users";
11133       e->usage =
11134          "Usage: voicemail show users [for <context>]\n"
11135          "       Lists all mailboxes currently set up\n";
11136       return NULL;
11137    case CLI_GENERATE:
11138       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11139    }  
11140 
11141    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11142       return CLI_SHOWUSAGE;
11143    if (a->argc == 5) {
11144       if (strcmp(a->argv[3],"for"))
11145          return CLI_SHOWUSAGE;
11146       context = a->argv[4];
11147    }
11148 
11149    if (ast_check_realtime("voicemail")) {
11150       if (!context) {
11151          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11152          return CLI_SHOWUSAGE;
11153       }
11154       return show_users_realtime(a->fd, context);
11155    }
11156 
11157    AST_LIST_LOCK(&users);
11158    if (AST_LIST_EMPTY(&users)) {
11159       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11160       AST_LIST_UNLOCK(&users);
11161       return CLI_FAILURE;
11162    }
11163    if (a->argc == 3)
11164       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11165    else {
11166       int count = 0;
11167       AST_LIST_TRAVERSE(&users, vmu, list) {
11168          if (!strcmp(context, vmu->context))
11169             count++;
11170       }
11171       if (count) {
11172          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11173       } else {
11174          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11175          AST_LIST_UNLOCK(&users);
11176          return CLI_FAILURE;
11177       }
11178    }
11179    AST_LIST_TRAVERSE(&users, vmu, list) {
11180       int newmsgs = 0, oldmsgs = 0;
11181       char count[12], tmp[256] = "";
11182 
11183       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
11184          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11185          inboxcount(tmp, &newmsgs, &oldmsgs);
11186          snprintf(count, sizeof(count), "%d", newmsgs);
11187          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11188          users_counter++;
11189       }
11190    }
11191    AST_LIST_UNLOCK(&users);
11192    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11193    return CLI_SUCCESS;
11194 }
11195 
11196 /*! \brief Show a list of voicemail zones in the CLI */
11197 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11198 {
11199    struct vm_zone *zone;
11200 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11201    char *res = CLI_SUCCESS;
11202 
11203    switch (cmd) {
11204    case CLI_INIT:
11205       e->command = "voicemail show zones";
11206       e->usage =
11207          "Usage: voicemail show zones\n"
11208          "       Lists zone message formats\n";
11209       return NULL;
11210    case CLI_GENERATE:
11211       return NULL;
11212    }
11213 
11214    if (a->argc != 3)
11215       return CLI_SHOWUSAGE;
11216 
11217    AST_LIST_LOCK(&zones);
11218    if (!AST_LIST_EMPTY(&zones)) {
11219       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11220       AST_LIST_TRAVERSE(&zones, zone, list) {
11221          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11222       }
11223    } else {
11224       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11225       res = CLI_FAILURE;
11226    }
11227    AST_LIST_UNLOCK(&zones);
11228 
11229    return res;
11230 }
11231 
11232 /*! \brief Reload voicemail configuration from the CLI */
11233 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11234 {
11235    switch (cmd) {
11236    case CLI_INIT:
11237       e->command = "voicemail reload";
11238       e->usage =
11239          "Usage: voicemail reload\n"
11240          "       Reload voicemail configuration\n";
11241       return NULL;
11242    case CLI_GENERATE:
11243       return NULL;
11244    }
11245 
11246    if (a->argc != 2)
11247       return CLI_SHOWUSAGE;
11248 
11249    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11250    load_config(1);
11251    
11252    return CLI_SUCCESS;
11253 }
11254 
11255 static struct ast_cli_entry cli_voicemail[] = {
11256    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11257    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11258    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11259 };
11260 
11261 #ifdef IMAP_STORAGE
11262    #define DATA_EXPORT_VM_USERS(USER)              \
11263       USER(ast_vm_user, context, AST_DATA_STRING)        \
11264       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11265       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11266       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11267       USER(ast_vm_user, email, AST_DATA_STRING)       \
11268       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11269       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11270       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11271       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11272       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11273       USER(ast_vm_user, language, AST_DATA_STRING)       \
11274       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11275       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11276       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11277       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11278       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11279       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11280       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11281       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11282       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11283       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11284       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11285       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11286       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11287       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11288       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11289 #else
11290    #define DATA_EXPORT_VM_USERS(USER)              \
11291       USER(ast_vm_user, context, AST_DATA_STRING)        \
11292       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11293       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11294       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11295       USER(ast_vm_user, email, AST_DATA_STRING)       \
11296       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11297       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11298       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11299       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11300       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11301       USER(ast_vm_user, language, AST_DATA_STRING)       \
11302       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11303       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11304       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11305       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11306       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11307       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11308       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11309       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11310       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11311       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11312       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11313       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11314 #endif
11315 
11316 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11317 
11318 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11319    ZONE(vm_zone, name, AST_DATA_STRING)      \
11320    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11321    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11322 
11323 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11324 
11325 /*!
11326  * \internal
11327  * \brief Add voicemail user to the data_root.
11328  * \param[in] search The search tree.
11329  * \param[in] data_root The main result node.
11330  * \param[in] user The voicemail user.
11331  */
11332 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11333     struct ast_data *data_root, struct ast_vm_user *user)
11334 {
11335    struct ast_data *data_user, *data_zone;
11336    struct ast_data *data_state;
11337    struct vm_zone *zone = NULL;
11338    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11339    char ext_context[256] = "";
11340 
11341    data_user = ast_data_add_node(data_root, "user");
11342    if (!data_user) {
11343       return -1;
11344    }
11345 
11346    ast_data_add_structure(ast_vm_user, data_user, user);
11347 
11348    AST_LIST_LOCK(&zones);
11349    AST_LIST_TRAVERSE(&zones, zone, list) {
11350       if (!strcmp(zone->name, user->zonetag)) {
11351          break;
11352       }
11353    }
11354    AST_LIST_UNLOCK(&zones);
11355 
11356    /* state */
11357    data_state = ast_data_add_node(data_user, "state");
11358    if (!data_state) {
11359       return -1;
11360    }
11361    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11362    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11363    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11364    ast_data_add_int(data_state, "newmsg", newmsg);
11365    ast_data_add_int(data_state, "oldmsg", oldmsg);
11366 
11367    if (zone) {
11368       data_zone = ast_data_add_node(data_user, "zone");
11369       ast_data_add_structure(vm_zone, data_zone, zone);
11370    }
11371 
11372    if (!ast_data_search_match(search, data_user)) {
11373       ast_data_remove_node(data_root, data_user);
11374    }
11375 
11376    return 0;
11377 }
11378 
11379 static int vm_users_data_provider_get(const struct ast_data_search *search,
11380    struct ast_data *data_root)
11381 {
11382    struct ast_vm_user *user;
11383 
11384    AST_LIST_LOCK(&users);
11385    AST_LIST_TRAVERSE(&users, user, list) {
11386       vm_users_data_provider_get_helper(search, data_root, user);
11387    }
11388    AST_LIST_UNLOCK(&users);
11389 
11390    return 0;
11391 }
11392 
11393 static const struct ast_data_handler vm_users_data_provider = {
11394    .version = AST_DATA_HANDLER_VERSION,
11395    .get = vm_users_data_provider_get
11396 };
11397 
11398 static const struct ast_data_entry vm_data_providers[] = {
11399    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11400 };
11401 
11402 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11403 {
11404    int new = 0, old = 0, urgent = 0;
11405 
11406    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11407 
11408    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11409       mwi_sub->old_urgent = urgent;
11410       mwi_sub->old_new = new;
11411       mwi_sub->old_old = old;
11412       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11413       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11414    }
11415 }
11416 
11417 static void poll_subscribed_mailboxes(void)
11418 {
11419    struct mwi_sub *mwi_sub;
11420 
11421    AST_RWLIST_RDLOCK(&mwi_subs);
11422    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11423       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11424          poll_subscribed_mailbox(mwi_sub);
11425       }
11426    }
11427    AST_RWLIST_UNLOCK(&mwi_subs);
11428 }
11429 
11430 static void *mb_poll_thread(void *data)
11431 {
11432    while (poll_thread_run) {
11433       struct timespec ts = { 0, };
11434       struct timeval wait;
11435 
11436       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11437       ts.tv_sec = wait.tv_sec;
11438       ts.tv_nsec = wait.tv_usec * 1000;
11439 
11440       ast_mutex_lock(&poll_lock);
11441       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11442       ast_mutex_unlock(&poll_lock);
11443 
11444       if (!poll_thread_run)
11445          break;
11446 
11447       poll_subscribed_mailboxes();
11448    }
11449 
11450    return NULL;
11451 }
11452 
11453 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11454 {
11455    ast_free(mwi_sub);
11456 }
11457 
11458 static int handle_unsubscribe(void *datap)
11459 {
11460    struct mwi_sub *mwi_sub;
11461    uint32_t *uniqueid = datap;
11462    
11463    AST_RWLIST_WRLOCK(&mwi_subs);
11464    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11465       if (mwi_sub->uniqueid == *uniqueid) {
11466          AST_LIST_REMOVE_CURRENT(entry);
11467          break;
11468       }
11469    }
11470    AST_RWLIST_TRAVERSE_SAFE_END
11471    AST_RWLIST_UNLOCK(&mwi_subs);
11472 
11473    if (mwi_sub)
11474       mwi_sub_destroy(mwi_sub);
11475 
11476    ast_free(uniqueid);  
11477    return 0;
11478 }
11479 
11480 static int handle_subscribe(void *datap)
11481 {
11482    unsigned int len;
11483    struct mwi_sub *mwi_sub;
11484    struct mwi_sub_task *p = datap;
11485 
11486    len = sizeof(*mwi_sub);
11487    if (!ast_strlen_zero(p->mailbox))
11488       len += strlen(p->mailbox);
11489 
11490    if (!ast_strlen_zero(p->context))
11491       len += strlen(p->context) + 1; /* Allow for seperator */
11492 
11493    if (!(mwi_sub = ast_calloc(1, len)))
11494       return -1;
11495 
11496    mwi_sub->uniqueid = p->uniqueid;
11497    if (!ast_strlen_zero(p->mailbox))
11498       strcpy(mwi_sub->mailbox, p->mailbox);
11499 
11500    if (!ast_strlen_zero(p->context)) {
11501       strcat(mwi_sub->mailbox, "@");
11502       strcat(mwi_sub->mailbox, p->context);
11503    }
11504 
11505    AST_RWLIST_WRLOCK(&mwi_subs);
11506    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11507    AST_RWLIST_UNLOCK(&mwi_subs);
11508    ast_free((void *) p->mailbox);
11509    ast_free((void *) p->context);
11510    ast_free(p);
11511    poll_subscribed_mailbox(mwi_sub);
11512    return 0;
11513 }
11514 
11515 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11516 {
11517    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11518    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11519       return;
11520 
11521    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11522       return;
11523 
11524    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11525    *uniqueid = u;
11526    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11527       ast_free(uniqueid);
11528    }
11529 }
11530 
11531 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11532 {
11533    struct mwi_sub_task *mwist;
11534    
11535    if (ast_event_get_type(event) != AST_EVENT_SUB)
11536       return;
11537 
11538    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11539       return;
11540 
11541    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11542       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11543       return;
11544    }
11545    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11546    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11547    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11548    
11549    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11550       ast_free(mwist);
11551    }
11552 }
11553 
11554 static void start_poll_thread(void)
11555 {
11556    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11557       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11558       AST_EVENT_IE_END);
11559 
11560    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11561       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11562       AST_EVENT_IE_END);
11563 
11564    if (mwi_sub_sub)
11565       ast_event_report_subs(mwi_sub_sub);
11566 
11567    poll_thread_run = 1;
11568 
11569    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11570 }
11571 
11572 static void stop_poll_thread(void)
11573 {
11574    poll_thread_run = 0;
11575 
11576    if (mwi_sub_sub) {
11577       ast_event_unsubscribe(mwi_sub_sub);
11578       mwi_sub_sub = NULL;
11579    }
11580 
11581    if (mwi_unsub_sub) {
11582       ast_event_unsubscribe(mwi_unsub_sub);
11583       mwi_unsub_sub = NULL;
11584    }
11585 
11586    ast_mutex_lock(&poll_lock);
11587    ast_cond_signal(&poll_cond);
11588    ast_mutex_unlock(&poll_lock);
11589 
11590    pthread_join(poll_thread, NULL);
11591 
11592    poll_thread = AST_PTHREADT_NULL;
11593 }
11594 
11595 /*! \brief Manager list voicemail users command */
11596 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11597 {
11598    struct ast_vm_user *vmu = NULL;
11599    const char *id = astman_get_header(m, "ActionID");
11600    char actionid[128] = "";
11601 
11602    if (!ast_strlen_zero(id))
11603       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11604 
11605    AST_LIST_LOCK(&users);
11606 
11607    if (AST_LIST_EMPTY(&users)) {
11608       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11609       AST_LIST_UNLOCK(&users);
11610       return RESULT_SUCCESS;
11611    }
11612    
11613    astman_send_ack(s, m, "Voicemail user list will follow");
11614    
11615    AST_LIST_TRAVERSE(&users, vmu, list) {
11616       char dirname[256];
11617 
11618 #ifdef IMAP_STORAGE
11619       int new, old;
11620       inboxcount(vmu->mailbox, &new, &old);
11621 #endif
11622       
11623       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11624       astman_append(s,
11625          "%s"
11626          "Event: VoicemailUserEntry\r\n"
11627          "VMContext: %s\r\n"
11628          "VoiceMailbox: %s\r\n"
11629          "Fullname: %s\r\n"
11630          "Email: %s\r\n"
11631          "Pager: %s\r\n"
11632          "ServerEmail: %s\r\n"
11633          "MailCommand: %s\r\n"
11634          "Language: %s\r\n"
11635          "TimeZone: %s\r\n"
11636          "Callback: %s\r\n"
11637          "Dialout: %s\r\n"
11638          "UniqueID: %s\r\n"
11639          "ExitContext: %s\r\n"
11640          "SayDurationMinimum: %d\r\n"
11641          "SayEnvelope: %s\r\n"
11642          "SayCID: %s\r\n"
11643          "AttachMessage: %s\r\n"
11644          "AttachmentFormat: %s\r\n"
11645          "DeleteMessage: %s\r\n"
11646          "VolumeGain: %.2f\r\n"
11647          "CanReview: %s\r\n"
11648          "CallOperator: %s\r\n"
11649          "MaxMessageCount: %d\r\n"
11650          "MaxMessageLength: %d\r\n"
11651          "NewMessageCount: %d\r\n"
11652 #ifdef IMAP_STORAGE
11653          "OldMessageCount: %d\r\n"
11654          "IMAPUser: %s\r\n"
11655 #endif
11656          "\r\n",
11657          actionid,
11658          vmu->context,
11659          vmu->mailbox,
11660          vmu->fullname,
11661          vmu->email,
11662          vmu->pager,
11663          vmu->serveremail,
11664          vmu->mailcmd,
11665          vmu->language,
11666          vmu->zonetag,
11667          vmu->callback,
11668          vmu->dialout,
11669          vmu->uniqueid,
11670          vmu->exit,
11671          vmu->saydurationm,
11672          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11673          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11674          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11675          vmu->attachfmt,
11676          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11677          vmu->volgain,
11678          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11679          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11680          vmu->maxmsg,
11681          vmu->maxsecs,
11682 #ifdef IMAP_STORAGE
11683          new, old, vmu->imapuser
11684 #else
11685          count_messages(vmu, dirname)
11686 #endif
11687          );
11688    }     
11689    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11690 
11691    AST_LIST_UNLOCK(&users);
11692 
11693    return RESULT_SUCCESS;
11694 }
11695 
11696 /*! \brief Free the users structure. */
11697 static void free_vm_users(void) 
11698 {
11699    struct ast_vm_user *current;
11700    AST_LIST_LOCK(&users);
11701    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11702       ast_set_flag(current, VM_ALLOCED);
11703       free_user(current);
11704    }
11705    AST_LIST_UNLOCK(&users);
11706 }
11707 
11708 /*! \brief Free the zones structure. */
11709 static void free_vm_zones(void)
11710 {
11711    struct vm_zone *zcur;
11712    AST_LIST_LOCK(&zones);
11713    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11714       free_zone(zcur);
11715    AST_LIST_UNLOCK(&zones);
11716 }
11717 
11718 static const char *substitute_escapes(const char *value)
11719 {
11720    char *current;
11721 
11722    /* Add 16 for fudge factor */
11723    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11724 
11725    ast_str_reset(str);
11726    
11727    /* Substitute strings \r, \n, and \t into the appropriate characters */
11728    for (current = (char *) value; *current; current++) {
11729       if (*current == '\\') {
11730          current++;
11731          if (!*current) {
11732             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11733             break;
11734          }
11735          switch (*current) {
11736          case '\\':
11737             ast_str_append(&str, 0, "\\");
11738             break;
11739          case 'r':
11740             ast_str_append(&str, 0, "\r");
11741             break;
11742          case 'n':
11743 #ifdef IMAP_STORAGE
11744             if (!str->used || str->str[str->used - 1] != '\r') {
11745                ast_str_append(&str, 0, "\r");
11746             }
11747 #endif
11748             ast_str_append(&str, 0, "\n");
11749             break;
11750          case 't':
11751             ast_str_append(&str, 0, "\t");
11752             break;
11753          default:
11754             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11755             break;
11756          }
11757       } else {
11758          ast_str_append(&str, 0, "%c", *current);
11759       }
11760    }
11761 
11762    return ast_str_buffer(str);
11763 }
11764 
11765 static int load_config(int reload)
11766 {
11767    struct ast_config *cfg, *ucfg;
11768    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11769    int res;
11770 
11771    ast_unload_realtime("voicemail");
11772    ast_unload_realtime("voicemail_data");
11773 
11774    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11775       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11776          return 0;
11777       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11778          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11779          ucfg = NULL;
11780       }
11781       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11782       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11783          ast_config_destroy(ucfg);
11784          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11785          return 0;
11786       }
11787    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11788       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11789       return 0;
11790    } else {
11791       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11792       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11793          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11794          ucfg = NULL;
11795       }
11796    }
11797 
11798    res = actual_load_config(reload, cfg, ucfg);
11799 
11800    ast_config_destroy(cfg);
11801    ast_config_destroy(ucfg);
11802 
11803    return res;
11804 }
11805 
11806 #ifdef TEST_FRAMEWORK
11807 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11808 {
11809    ast_unload_realtime("voicemail");
11810    ast_unload_realtime("voicemail_data");
11811    return actual_load_config(reload, cfg, ucfg);
11812 }
11813 #endif
11814 
11815 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11816 {
11817    struct ast_vm_user *current;
11818    char *cat;
11819    struct ast_variable *var;
11820    const char *val;
11821    char *q, *stringp, *tmp;
11822    int x;
11823    int tmpadsi[4];
11824    char secretfn[PATH_MAX] = "";
11825 
11826 #ifdef IMAP_STORAGE
11827    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11828 #endif
11829    /* set audio control prompts */
11830    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11831    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11832    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11833    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11834    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11835 
11836    /* Free all the users structure */  
11837    free_vm_users();
11838 
11839    /* Free all the zones structure */
11840    free_vm_zones();
11841 
11842    AST_LIST_LOCK(&users);  
11843 
11844    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11845    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11846 
11847    if (cfg) {
11848       /* General settings */
11849 
11850       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11851          val = "default";
11852       ast_copy_string(userscontext, val, sizeof(userscontext));
11853       /* Attach voice message to mail message ? */
11854       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11855          val = "yes";
11856       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11857 
11858       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11859          val = "no";
11860       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11861 
11862       volgain = 0.0;
11863       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11864          sscanf(val, "%30lf", &volgain);
11865 
11866 #ifdef ODBC_STORAGE
11867       strcpy(odbc_database, "asterisk");
11868       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11869          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11870       }
11871       strcpy(odbc_table, "voicemessages");
11872       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11873          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11874       }
11875 #endif      
11876       /* Mail command */
11877       strcpy(mailcmd, SENDMAIL);
11878       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11879          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11880 
11881       maxsilence = 0;
11882       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11883          maxsilence = atoi(val);
11884          if (maxsilence > 0)
11885             maxsilence *= 1000;
11886       }
11887       
11888       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11889          maxmsg = MAXMSG;
11890       } else {
11891          maxmsg = atoi(val);
11892          if (maxmsg < 0) {
11893             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11894             maxmsg = MAXMSG;
11895          } else if (maxmsg > MAXMSGLIMIT) {
11896             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11897             maxmsg = MAXMSGLIMIT;
11898          }
11899       }
11900 
11901       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11902          maxdeletedmsg = 0;
11903       } else {
11904          if (sscanf(val, "%30d", &x) == 1)
11905             maxdeletedmsg = x;
11906          else if (ast_true(val))
11907             maxdeletedmsg = MAXMSG;
11908          else
11909             maxdeletedmsg = 0;
11910 
11911          if (maxdeletedmsg < 0) {
11912             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11913             maxdeletedmsg = MAXMSG;
11914          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11915             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11916             maxdeletedmsg = MAXMSGLIMIT;
11917          }
11918       }
11919 
11920       /* Load date format config for voicemail mail */
11921       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11922          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11923       }
11924 
11925       /* Load date format config for voicemail pager mail */
11926       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11927          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11928       }
11929 
11930       /* External password changing command */
11931       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11932          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11933          pwdchange = PWDCHANGE_EXTERNAL;
11934       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11935          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11936          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11937       }
11938  
11939       /* External password validation command */
11940       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11941          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11942          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11943       }
11944 
11945 #ifdef IMAP_STORAGE
11946       /* IMAP server address */
11947       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11948          ast_copy_string(imapserver, val, sizeof(imapserver));
11949       } else {
11950          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11951       }
11952       /* IMAP server port */
11953       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11954          ast_copy_string(imapport, val, sizeof(imapport));
11955       } else {
11956          ast_copy_string(imapport, "143", sizeof(imapport));
11957       }
11958       /* IMAP server flags */
11959       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11960          ast_copy_string(imapflags, val, sizeof(imapflags));
11961       }
11962       /* IMAP server master username */
11963       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11964          ast_copy_string(authuser, val, sizeof(authuser));
11965       }
11966       /* IMAP server master password */
11967       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11968          ast_copy_string(authpassword, val, sizeof(authpassword));
11969       }
11970       /* Expunge on exit */
11971       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11972          if (ast_false(val))
11973             expungeonhangup = 0;
11974          else
11975             expungeonhangup = 1;
11976       } else {
11977          expungeonhangup = 1;
11978       }
11979       /* IMAP voicemail folder */
11980       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11981          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11982       } else {
11983          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
11984       }
11985       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11986          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11987       }
11988       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11989          imapgreetings = ast_true(val);
11990       } else {
11991          imapgreetings = 0;
11992       }
11993       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11994          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11995       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
11996          /* Also support greetingsfolder as documented in voicemail.conf.sample */
11997          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11998       } else {
11999          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12000       }
12001 
12002       /* There is some very unorthodox casting done here. This is due
12003        * to the way c-client handles the argument passed in. It expects a 
12004        * void pointer and casts the pointer directly to a long without
12005        * first dereferencing it. */
12006       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12007          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12008       } else {
12009          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12010       }
12011 
12012       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12013          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12014       } else {
12015          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12016       }
12017 
12018       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12019          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12020       } else {
12021          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12022       }
12023 
12024       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12025          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12026       } else {
12027          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12028       }
12029 
12030       /* Increment configuration version */
12031       imapversion++;
12032 #endif
12033       /* External voicemail notify application */
12034       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12035          ast_copy_string(externnotify, val, sizeof(externnotify));
12036          ast_debug(1, "found externnotify: %s\n", externnotify);
12037       } else {
12038          externnotify[0] = '\0';
12039       }
12040 
12041       /* SMDI voicemail notification */
12042       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12043          ast_debug(1, "Enabled SMDI voicemail notification\n");
12044          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12045             smdi_iface = ast_smdi_interface_find(val);
12046          } else {
12047             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12048             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12049          }
12050          if (!smdi_iface) {
12051             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12052          } 
12053       }
12054 
12055       /* Silence treshold */
12056       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12057       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12058          silencethreshold = atoi(val);
12059       
12060       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12061          val = ASTERISK_USERNAME;
12062       ast_copy_string(serveremail, val, sizeof(serveremail));
12063       
12064       vmmaxsecs = 0;
12065       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12066          if (sscanf(val, "%30d", &x) == 1) {
12067             vmmaxsecs = x;
12068          } else {
12069             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12070          }
12071       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12072          static int maxmessage_deprecate = 0;
12073          if (maxmessage_deprecate == 0) {
12074             maxmessage_deprecate = 1;
12075             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12076          }
12077          if (sscanf(val, "%30d", &x) == 1) {
12078             vmmaxsecs = x;
12079          } else {
12080             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12081          }
12082       }
12083 
12084       vmminsecs = 0;
12085       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12086          if (sscanf(val, "%30d", &x) == 1) {
12087             vmminsecs = x;
12088             if (maxsilence / 1000 >= vmminsecs) {
12089                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12090             }
12091          } else {
12092             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12093          }
12094       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12095          static int maxmessage_deprecate = 0;
12096          if (maxmessage_deprecate == 0) {
12097             maxmessage_deprecate = 1;
12098             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12099          }
12100          if (sscanf(val, "%30d", &x) == 1) {
12101             vmminsecs = x;
12102             if (maxsilence / 1000 >= vmminsecs) {
12103                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12104             }
12105          } else {
12106             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12107          }
12108       }
12109 
12110       val = ast_variable_retrieve(cfg, "general", "format");
12111       if (!val) {
12112          val = "wav";   
12113       } else {
12114          tmp = ast_strdupa(val);
12115          val = ast_format_str_reduce(tmp);
12116          if (!val) {
12117             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12118             val = "wav";
12119          }
12120       }
12121       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12122 
12123       skipms = 3000;
12124       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12125          if (sscanf(val, "%30d", &x) == 1) {
12126             maxgreet = x;
12127          } else {
12128             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12129          }
12130       }
12131 
12132       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12133          if (sscanf(val, "%30d", &x) == 1) {
12134             skipms = x;
12135          } else {
12136             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12137          }
12138       }
12139 
12140       maxlogins = 3;
12141       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12142          if (sscanf(val, "%30d", &x) == 1) {
12143             maxlogins = x;
12144          } else {
12145             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12146          }
12147       }
12148 
12149       minpassword = MINPASSWORD;
12150       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12151          if (sscanf(val, "%30d", &x) == 1) {
12152             minpassword = x;
12153          } else {
12154             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12155          }
12156       }
12157 
12158       /* Force new user to record name ? */
12159       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12160          val = "no";
12161       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12162 
12163       /* Force new user to record greetings ? */
12164       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12165          val = "no";
12166       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12167 
12168       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12169          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12170          stringp = ast_strdupa(val);
12171          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12172             if (!ast_strlen_zero(stringp)) {
12173                q = strsep(&stringp, ",");
12174                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12175                   q++;
12176                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12177                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12178             } else {
12179                cidinternalcontexts[x][0] = '\0';
12180             }
12181          }
12182       }
12183       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12184          ast_debug(1, "VM Review Option disabled globally\n");
12185          val = "no";
12186       }
12187       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12188 
12189       /* Temporary greeting reminder */
12190       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12191          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12192          val = "no";
12193       } else {
12194          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12195       }
12196       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12197       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12198          ast_debug(1, "VM next message wrap disabled globally\n");
12199          val = "no";
12200       }
12201       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12202 
12203       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12204          ast_debug(1, "VM Operator break disabled globally\n");
12205          val = "no";
12206       }
12207       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12208 
12209       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12210          ast_debug(1, "VM CID Info before msg disabled globally\n");
12211          val = "no";
12212       } 
12213       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12214 
12215       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12216          ast_debug(1, "Send Voicemail msg disabled globally\n");
12217          val = "no";
12218       }
12219       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12220    
12221       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12222          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12223          val = "yes";
12224       }
12225       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12226 
12227       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12228          ast_debug(1, "Move Heard enabled globally\n");
12229          val = "yes";
12230       }
12231       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12232 
12233       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12234          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12235          val = "no";
12236       }
12237       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12238 
12239       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12240          ast_debug(1, "Duration info before msg enabled globally\n");
12241          val = "yes";
12242       }
12243       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12244 
12245       saydurationminfo = 2;
12246       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12247          if (sscanf(val, "%30d", &x) == 1) {
12248             saydurationminfo = x;
12249          } else {
12250             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12251          }
12252       }
12253 
12254       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12255          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12256          val = "no";
12257       }
12258       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12259 
12260       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12261          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12262          ast_debug(1, "found dialout context: %s\n", dialcontext);
12263       } else {
12264          dialcontext[0] = '\0';  
12265       }
12266       
12267       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12268          ast_copy_string(callcontext, val, sizeof(callcontext));
12269          ast_debug(1, "found callback context: %s\n", callcontext);
12270       } else {
12271          callcontext[0] = '\0';
12272       }
12273 
12274       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12275          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12276          ast_debug(1, "found operator context: %s\n", exitcontext);
12277       } else {
12278          exitcontext[0] = '\0';
12279       }
12280       
12281       /* load password sounds configuration */
12282       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12283          ast_copy_string(vm_password, val, sizeof(vm_password));
12284       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12285          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12286       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12287          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12288       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12289          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12290       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12291          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12292       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12293          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12294       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12295          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12296       }
12297       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12298          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12299       }
12300       /* load configurable audio prompts */
12301       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12302          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12303       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12304          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12305       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12306          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12307       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12308          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12309       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12310          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12311 
12312       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12313          val = "no";
12314       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12315 
12316       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12317          val = "voicemail.conf";
12318       }
12319       if (!(strcmp(val, "spooldir"))) {
12320          passwordlocation = OPT_PWLOC_SPOOLDIR;
12321       } else {
12322          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12323       }
12324 
12325       poll_freq = DEFAULT_POLL_FREQ;
12326       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12327          if (sscanf(val, "%30u", &poll_freq) != 1) {
12328             poll_freq = DEFAULT_POLL_FREQ;
12329             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12330          }
12331       }
12332 
12333       poll_mailboxes = 0;
12334       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12335          poll_mailboxes = ast_true(val);
12336 
12337       memset(fromstring, 0, sizeof(fromstring));
12338       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12339       strcpy(charset, "ISO-8859-1");
12340       if (emailbody) {
12341          ast_free(emailbody);
12342          emailbody = NULL;
12343       }
12344       if (emailsubject) {
12345          ast_free(emailsubject);
12346          emailsubject = NULL;
12347       }
12348       if (pagerbody) {
12349          ast_free(pagerbody);
12350          pagerbody = NULL;
12351       }
12352       if (pagersubject) {
12353          ast_free(pagersubject);
12354          pagersubject = NULL;
12355       }
12356       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12357          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12358       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12359          ast_copy_string(fromstring, val, sizeof(fromstring));
12360       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12361          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12362       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12363          ast_copy_string(charset, val, sizeof(charset));
12364       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12365          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12366          for (x = 0; x < 4; x++) {
12367             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12368          }
12369       }
12370       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12371          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12372          for (x = 0; x < 4; x++) {
12373             memcpy(&adsisec[x], &tmpadsi[x], 1);
12374          }
12375       }
12376       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12377          if (atoi(val)) {
12378             adsiver = atoi(val);
12379          }
12380       }
12381       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12382          ast_copy_string(zonetag, val, sizeof(zonetag));
12383       }
12384       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12385          ast_copy_string(locale, val, sizeof(locale));
12386       }
12387       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12388          emailsubject = ast_strdup(substitute_escapes(val));
12389       }
12390       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12391          emailbody = ast_strdup(substitute_escapes(val));
12392       }
12393       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12394          pagersubject = ast_strdup(substitute_escapes(val));
12395       }
12396       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12397          pagerbody = ast_strdup(substitute_escapes(val));
12398       }
12399 
12400       /* load mailboxes from users.conf */
12401       if (ucfg) { 
12402          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12403             if (!strcasecmp(cat, "general")) {
12404                continue;
12405             }
12406             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12407                continue;
12408             if ((current = find_or_create(userscontext, cat))) {
12409                populate_defaults(current);
12410                apply_options_full(current, ast_variable_browse(ucfg, cat));
12411                ast_copy_string(current->context, userscontext, sizeof(current->context));
12412                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12413                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12414                }
12415 
12416                switch (current->passwordlocation) {
12417                case OPT_PWLOC_SPOOLDIR:
12418                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12419                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12420                }
12421             }
12422          }
12423       }
12424 
12425       /* load mailboxes from voicemail.conf */
12426       cat = ast_category_browse(cfg, NULL);
12427       while (cat) {
12428          if (strcasecmp(cat, "general")) {
12429             var = ast_variable_browse(cfg, cat);
12430             if (strcasecmp(cat, "zonemessages")) {
12431                /* Process mailboxes in this context */
12432                while (var) {
12433                   append_mailbox(cat, var->name, var->value);
12434                   var = var->next;
12435                }
12436             } else {
12437                /* Timezones in this context */
12438                while (var) {
12439                   struct vm_zone *z;
12440                   if ((z = ast_malloc(sizeof(*z)))) {
12441                      char *msg_format, *tzone;
12442                      msg_format = ast_strdupa(var->value);
12443                      tzone = strsep(&msg_format, "|,");
12444                      if (msg_format) {
12445                         ast_copy_string(z->name, var->name, sizeof(z->name));
12446                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12447                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12448                         AST_LIST_LOCK(&zones);
12449                         AST_LIST_INSERT_HEAD(&zones, z, list);
12450                         AST_LIST_UNLOCK(&zones);
12451                      } else {
12452                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12453                         ast_free(z);
12454                      }
12455                   } else {
12456                      AST_LIST_UNLOCK(&users);
12457                      return -1;
12458                   }
12459                   var = var->next;
12460                }
12461             }
12462          }
12463          cat = ast_category_browse(cfg, cat);
12464       }
12465 
12466       AST_LIST_UNLOCK(&users);
12467 
12468       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12469          start_poll_thread();
12470       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12471          stop_poll_thread();;
12472 
12473       return 0;
12474    } else {
12475       AST_LIST_UNLOCK(&users);
12476       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12477       return 0;
12478    }
12479 }
12480 
12481 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12482 {
12483    int res = -1;
12484    char dir[PATH_MAX];
12485    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12486    ast_debug(2, "About to try retrieving name file %s\n", dir);
12487    RETRIEVE(dir, -1, mailbox, context);
12488    if (ast_fileexists(dir, NULL, NULL)) {
12489       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12490    }
12491    DISPOSE(dir, -1);
12492    return res;
12493 }
12494 
12495 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12496    struct ast_config *pwconf;
12497    struct ast_flags config_flags = { 0 };
12498 
12499    pwconf = ast_config_load(secretfn, config_flags);
12500    if (pwconf) {
12501       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12502       if (val) {
12503          ast_copy_string(password, val, passwordlen);
12504          return;
12505       }
12506    }
12507    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12508 }
12509 
12510 static int write_password_to_file(const char *secretfn, const char *password) {
12511    struct ast_config *conf;
12512    struct ast_category *cat;
12513    struct ast_variable *var;
12514 
12515    if (!(conf=ast_config_new())) {
12516       ast_log(LOG_ERROR, "Error creating new config structure\n");
12517       return -1;
12518    }
12519    if (!(cat=ast_category_new("general","",1))) {
12520       ast_log(LOG_ERROR, "Error creating new category structure\n");
12521       return -1;
12522    }
12523    if (!(var=ast_variable_new("password",password,""))) {
12524       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12525       return -1;
12526    }
12527    ast_category_append(conf,cat);
12528    ast_variable_append(cat,var);
12529    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12530       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12531       return -1;
12532    }
12533    return 0;
12534 }
12535 
12536 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12537 {
12538    char *context;
12539    char *args_copy;
12540    int res;
12541 
12542    if (ast_strlen_zero(data)) {
12543       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12544       return -1;
12545    }
12546 
12547    args_copy = ast_strdupa(data);
12548    if ((context = strchr(args_copy, '@'))) {
12549       *context++ = '\0';
12550    } else {
12551       context = "default";
12552    }
12553 
12554    if ((res = sayname(chan, args_copy, context) < 0)) {
12555       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12556       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12557       if (!res) {
12558          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12559       }
12560    }
12561 
12562    return res;
12563 }
12564 
12565 #ifdef TEST_FRAMEWORK
12566 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12567 {
12568    return 0;
12569 }
12570 
12571 static struct ast_frame *fake_read(struct ast_channel *ast)
12572 {
12573    return &ast_null_frame;
12574 }
12575 
12576 AST_TEST_DEFINE(test_voicemail_vmsayname)
12577 {
12578    char dir[PATH_MAX];
12579    char dir2[PATH_MAX];
12580    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12581    static const char TEST_EXTENSION[] = "1234";
12582 
12583    struct ast_channel *test_channel1 = NULL;
12584    int res = -1;
12585 
12586    static const struct ast_channel_tech fake_tech = {
12587       .write = fake_write,
12588       .read = fake_read,
12589    };
12590 
12591    switch (cmd) {
12592    case TEST_INIT:
12593       info->name = "vmsayname_exec";
12594       info->category = "/apps/app_voicemail/";
12595       info->summary = "Vmsayname unit test";
12596       info->description =
12597          "This tests passing various parameters to vmsayname";
12598       return AST_TEST_NOT_RUN;
12599    case TEST_EXECUTE:
12600       break;
12601    }
12602 
12603    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12604         NULL, NULL, 0, 0, "TestChannel1"))) {
12605       goto exit_vmsayname_test;
12606    }
12607 
12608    /* normally this is done in the channel driver */
12609    test_channel1->nativeformats = AST_FORMAT_GSM;
12610    test_channel1->writeformat = AST_FORMAT_GSM;
12611    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12612    test_channel1->readformat = AST_FORMAT_GSM;
12613    test_channel1->rawreadformat = AST_FORMAT_GSM;
12614    test_channel1->tech = &fake_tech;
12615 
12616    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12617    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12618    if (!(res = vmsayname_exec(test_channel1, dir))) {
12619       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12620       if (ast_fileexists(dir, NULL, NULL)) {
12621          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12622          res = -1;
12623          goto exit_vmsayname_test;
12624       } else {
12625          /* no greeting already exists as expected, let's create one to fully test sayname */
12626          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12627             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12628             goto exit_vmsayname_test;
12629          }
12630          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12631          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12632          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12633          if ((res = symlink(dir, dir2))) {
12634             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12635             goto exit_vmsayname_test;
12636          }
12637          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12638          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12639          res = vmsayname_exec(test_channel1, dir);
12640 
12641          /* TODO: there may be a better way to do this */
12642          unlink(dir2);
12643          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12644          rmdir(dir2);
12645          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12646          rmdir(dir2);
12647       }
12648    }
12649 
12650 exit_vmsayname_test:
12651 
12652    if (test_channel1) {
12653       ast_hangup(test_channel1);
12654    }
12655 
12656    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12657 }
12658 
12659 AST_TEST_DEFINE(test_voicemail_msgcount)
12660 {
12661    int i, j, res = AST_TEST_PASS, syserr;
12662    struct ast_vm_user *vmu;
12663    struct vm_state vms;
12664 #ifdef IMAP_STORAGE
12665    struct ast_channel *chan = NULL;
12666 #endif
12667    struct {
12668       char dir[256];
12669       char file[256];
12670       char txtfile[256];
12671    } tmp[3];
12672    char syscmd[256];
12673    const char origweasels[] = "tt-weasels";
12674    const char testcontext[] = "test";
12675    const char testmailbox[] = "00000000";
12676    const char testspec[] = "00000000@test";
12677    FILE *txt;
12678    int new, old, urgent;
12679    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12680    const int folder2mbox[3] = { 1, 11, 0 };
12681    const int expected_results[3][12] = {
12682       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12683       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12684       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12685       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12686    };
12687 
12688    switch (cmd) {
12689    case TEST_INIT:
12690       info->name = "test_voicemail_msgcount";
12691       info->category = "/apps/app_voicemail/";
12692       info->summary = "Test Voicemail status checks";
12693       info->description =
12694          "Verify that message counts are correct when retrieved through the public API";
12695       return AST_TEST_NOT_RUN;
12696    case TEST_EXECUTE:
12697       break;
12698    }
12699 
12700    /* Make sure the original path was completely empty */
12701    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12702    if ((syserr = ast_safe_system(syscmd))) {
12703       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12704          syserr > 0 ? strerror(syserr) : "unable to fork()");
12705       return AST_TEST_FAIL;
12706    }
12707 
12708 #ifdef IMAP_STORAGE
12709    if (!(chan = ast_dummy_channel_alloc())) {
12710       ast_test_status_update(test, "Unable to create dummy channel\n");
12711       return AST_TEST_FAIL;
12712    }
12713 #endif
12714 
12715    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12716       !(vmu = find_or_create(testcontext, testmailbox))) {
12717       ast_test_status_update(test, "Cannot create vmu structure\n");
12718       ast_unreplace_sigchld();
12719 #ifdef IMAP_STORAGE
12720       chan = ast_channel_unref(chan);
12721 #endif
12722       return AST_TEST_FAIL;
12723    }
12724 
12725    populate_defaults(vmu);
12726    memset(&vms, 0, sizeof(vms));
12727 
12728    /* Create temporary voicemail */
12729    for (i = 0; i < 3; i++) {
12730       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12731       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12732       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12733 
12734       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12735          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12736             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12737          if ((syserr = ast_safe_system(syscmd))) {
12738             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12739                syserr > 0 ? strerror(syserr) : "unable to fork()");
12740             ast_unreplace_sigchld();
12741 #ifdef IMAP_STORAGE
12742             chan = ast_channel_unref(chan);
12743 #endif
12744             return AST_TEST_FAIL;
12745          }
12746       }
12747 
12748       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12749          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12750          fclose(txt);
12751       } else {
12752          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12753          res = AST_TEST_FAIL;
12754          break;
12755       }
12756       open_mailbox(&vms, vmu, folder2mbox[i]);
12757       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12758 
12759       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12760       for (j = 0; j < 3; j++) {
12761          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12762          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12763             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12764                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12765             res = AST_TEST_FAIL;
12766          }
12767       }
12768 
12769       new = old = urgent = 0;
12770       if (ast_app_inboxcount(testspec, &new, &old)) {
12771          ast_test_status_update(test, "inboxcount returned failure\n");
12772          res = AST_TEST_FAIL;
12773       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12774          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12775             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12776          res = AST_TEST_FAIL;
12777       }
12778 
12779       new = old = urgent = 0;
12780       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12781          ast_test_status_update(test, "inboxcount2 returned failure\n");
12782          res = AST_TEST_FAIL;
12783       } else if (old != expected_results[i][6 + 0] ||
12784             urgent != expected_results[i][6 + 1] ||
12785                new != expected_results[i][6 + 2]    ) {
12786          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12787             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12788          res = AST_TEST_FAIL;
12789       }
12790 
12791       new = old = urgent = 0;
12792       for (j = 0; j < 3; j++) {
12793          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12794             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12795                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12796             res = AST_TEST_FAIL;
12797          }
12798       }
12799    }
12800 
12801    for (i = 0; i < 3; i++) {
12802       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12803        * server, in which case, the rm below will not affect the
12804        * voicemails. */
12805       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12806       DISPOSE(tmp[i].dir, 0);
12807    }
12808 
12809    if (vms.deleted) {
12810       ast_free(vms.deleted);
12811    }
12812    if (vms.heard) {
12813       ast_free(vms.heard);
12814    }
12815 
12816 #ifdef IMAP_STORAGE
12817    chan = ast_channel_unref(chan);
12818 #endif
12819 
12820    /* And remove test directory */
12821    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12822    if ((syserr = ast_safe_system(syscmd))) {
12823       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12824          syserr > 0 ? strerror(syserr) : "unable to fork()");
12825    }
12826 
12827    return res;
12828 }
12829 
12830 AST_TEST_DEFINE(test_voicemail_notify_endl)
12831 {
12832    int res = AST_TEST_PASS;
12833    char testcontext[] = "test";
12834    char testmailbox[] = "00000000";
12835    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12836    char attach[256], attach2[256];
12837    char buf[256] = ""; /* No line should actually be longer than 80 */
12838    struct ast_channel *chan = NULL;
12839    struct ast_vm_user *vmu, vmus = {
12840       .flags = 0,
12841    };
12842    FILE *file;
12843    struct {
12844       char *name;
12845       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12846       void *location;
12847       union {
12848          int intval;
12849          char *strval;
12850       } u;
12851    } test_items[] = {
12852       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12853       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12854       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12855       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12856       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12857       { "attach2", STRPTR, attach2, .u.strval = "" },
12858       { "attach", STRPTR, attach, .u.strval = "" },
12859    };
12860    int which;
12861 
12862    switch (cmd) {
12863    case TEST_INIT:
12864       info->name = "test_voicemail_notify_endl";
12865       info->category = "/apps/app_voicemail/";
12866       info->summary = "Test Voicemail notification end-of-line";
12867       info->description =
12868          "Verify that notification emails use a consistent end-of-line character";
12869       return AST_TEST_NOT_RUN;
12870    case TEST_EXECUTE:
12871       break;
12872    }
12873 
12874    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12875    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12876 
12877    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12878       !(vmu = find_or_create(testcontext, testmailbox))) {
12879       ast_test_status_update(test, "Cannot create vmu structure\n");
12880       return AST_TEST_NOT_RUN;
12881    }
12882 
12883    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12884       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12885       return AST_TEST_NOT_RUN;
12886    }
12887 
12888    populate_defaults(vmu);
12889    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12890 #ifdef IMAP_STORAGE
12891    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12892 #endif
12893 
12894    file = tmpfile();
12895    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12896       /* Kill previous test, if any */
12897       rewind(file);
12898       if (ftruncate(fileno(file), 0)) {
12899          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12900          res = AST_TEST_FAIL;
12901          break;
12902       }
12903 
12904       /* Make each change, in order, to the test mailbox */
12905       if (test_items[which].type == INT) {
12906          *((int *) test_items[which].location) = test_items[which].u.intval;
12907       } else if (test_items[which].type == FLAGVAL) {
12908          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12909             ast_clear_flag(vmu, test_items[which].u.intval);
12910          } else {
12911             ast_set_flag(vmu, test_items[which].u.intval);
12912          }
12913       } else if (test_items[which].type == STATIC) {
12914          strcpy(test_items[which].location, test_items[which].u.strval);
12915       } else if (test_items[which].type == STRPTR) {
12916          test_items[which].location = test_items[which].u.strval;
12917       }
12918 
12919       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12920       rewind(file);
12921       while (fgets(buf, sizeof(buf), file)) {
12922          if (
12923 #ifdef IMAP_STORAGE
12924          buf[strlen(buf) - 2] != '\r'
12925 #else
12926          buf[strlen(buf) - 2] == '\r'
12927 #endif
12928          || buf[strlen(buf) - 1] != '\n') {
12929             res = AST_TEST_FAIL;
12930          }
12931       }
12932    }
12933    fclose(file);
12934    return res;
12935 }
12936 
12937 AST_TEST_DEFINE(test_voicemail_load_config)
12938 {
12939    int res = AST_TEST_PASS;
12940    struct ast_vm_user *vmu;
12941    struct ast_config *cfg;
12942    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
12943    int fd;
12944    FILE *file;
12945    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
12946 
12947    switch (cmd) {
12948    case TEST_INIT:
12949       info->name = "test_voicemail_load_config";
12950       info->category = "/apps/app_voicemail/";
12951       info->summary = "Test loading Voicemail config";
12952       info->description =
12953          "Verify that configuration is loaded consistently. "
12954          "This is to test regressions of ASTERISK-18838 where it was noticed that "
12955          "some options were loaded after the mailboxes were instantiated, causing "
12956          "those options not to be set correctly.";
12957       return AST_TEST_NOT_RUN;
12958    case TEST_EXECUTE:
12959       break;
12960    }
12961 
12962    /* build a config file by hand... */
12963    if ((fd = mkstemp(config_filename)) < 0) {
12964       return AST_TEST_FAIL;
12965    }
12966    if (!(file = fdopen(fd, "w"))) {
12967       close(fd);
12968       unlink(config_filename);
12969       return AST_TEST_FAIL;
12970    }
12971    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
12972    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
12973    fputs("00000002 => 9999,Mrs. Test\n", file);
12974    fclose(file);
12975 
12976    if (!(cfg = ast_config_load(config_filename, config_flags))) {
12977       res = AST_TEST_FAIL;
12978       goto cleanup;
12979    }
12980 
12981    load_config_from_memory(1, cfg, NULL);
12982    ast_config_destroy(cfg);
12983 
12984 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
12985    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
12986    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
12987 
12988    AST_LIST_LOCK(&users);
12989    AST_LIST_TRAVERSE(&users, vmu, list) {
12990       if (!strcmp(vmu->mailbox, "00000001")) {
12991          if (0); /* trick to get CHECK to work */
12992          CHECK(vmu, callback, "othercontext")
12993          CHECK(vmu, locale, "nl_NL.UTF-8")
12994          CHECK(vmu, zonetag, "central")
12995       } else if (!strcmp(vmu->mailbox, "00000002")) {
12996          if (0); /* trick to get CHECK to work */
12997          CHECK(vmu, callback, "somecontext")
12998          CHECK(vmu, locale, "de_DE.UTF-8")
12999          CHECK(vmu, zonetag, "european")
13000       }
13001    }
13002    AST_LIST_UNLOCK(&users);
13003 
13004 #undef CHECK
13005 
13006    /* restore config */
13007    load_config(1); /* this might say "Failed to load configuration file." */
13008 
13009 cleanup:
13010    unlink(config_filename);
13011    return res;
13012 }
13013 
13014 #endif /* defined(TEST_FRAMEWORK) */
13015 
13016 static int reload(void)
13017 {
13018    return load_config(1);
13019 }
13020 
13021 static int unload_module(void)
13022 {
13023    int res;
13024 
13025    res = ast_unregister_application(app);
13026    res |= ast_unregister_application(app2);
13027    res |= ast_unregister_application(app3);
13028    res |= ast_unregister_application(app4);
13029    res |= ast_unregister_application(sayname_app);
13030    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13031    res |= ast_manager_unregister("VoicemailUsersList");
13032    res |= ast_data_unregister(NULL);
13033 #ifdef TEST_FRAMEWORK
13034    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13035    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13036    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13037    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13038    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13039 #endif
13040    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13041    ast_uninstall_vm_functions();
13042    ao2_ref(inprocess_container, -1);
13043 
13044    if (poll_thread != AST_PTHREADT_NULL)
13045       stop_poll_thread();
13046 
13047    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13048    ast_unload_realtime("voicemail");
13049    ast_unload_realtime("voicemail_data");
13050 
13051    free_vm_users();
13052    free_vm_zones();
13053    return res;
13054 }
13055 
13056 static int load_module(void)
13057 {
13058    int res;
13059    my_umask = umask(0);
13060    umask(my_umask);
13061 
13062    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13063       return AST_MODULE_LOAD_DECLINE;
13064    }
13065 
13066    /* compute the location of the voicemail spool directory */
13067    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13068    
13069    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13070       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13071    }
13072 
13073    if ((res = load_config(0)))
13074       return res;
13075 
13076    res = ast_register_application_xml(app, vm_exec);
13077    res |= ast_register_application_xml(app2, vm_execmain);
13078    res |= ast_register_application_xml(app3, vm_box_exists);
13079    res |= ast_register_application_xml(app4, vmauthenticate);
13080    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13081    res |= ast_custom_function_register(&mailbox_exists_acf);
13082    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13083 #ifdef TEST_FRAMEWORK
13084    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13085    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13086    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13087    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13088    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13089 #endif
13090 
13091    if (res)
13092       return res;
13093 
13094    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13095    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13096 
13097    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13098    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13099    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13100 
13101    return res;
13102 }
13103 
13104 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13105 {
13106    int cmd = 0;
13107    char destination[80] = "";
13108    int retries = 0;
13109 
13110    if (!num) {
13111       ast_verb(3, "Destination number will be entered manually\n");
13112       while (retries < 3 && cmd != 't') {
13113          destination[1] = '\0';
13114          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13115          if (!cmd)
13116             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13117          if (!cmd)
13118             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13119          if (!cmd) {
13120             cmd = ast_waitfordigit(chan, 6000);
13121             if (cmd)
13122                destination[0] = cmd;
13123          }
13124          if (!cmd) {
13125             retries++;
13126          } else {
13127 
13128             if (cmd < 0)
13129                return 0;
13130             if (cmd == '*') {
13131                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13132                return 0;
13133             }
13134             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13135                retries++;
13136             else
13137                cmd = 't';
13138          }
13139          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13140       }
13141       if (retries >= 3) {
13142          return 0;
13143       }
13144       
13145    } else {
13146       if (option_verbose > 2)
13147          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13148       ast_copy_string(destination, num, sizeof(destination));
13149    }
13150 
13151    if (!ast_strlen_zero(destination)) {
13152       if (destination[strlen(destination) -1 ] == '*')
13153          return 0; 
13154       if (option_verbose > 2)
13155          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13156       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13157       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13158       chan->priority = 0;
13159       return 9;
13160    }
13161    return 0;
13162 }
13163 
13164 /*!
13165  * \brief The advanced options within a message.
13166  * \param chan
13167  * \param vmu 
13168  * \param vms
13169  * \param msg
13170  * \param option
13171  * \param record_gain
13172  *
13173  * Provides handling for the play message envelope, call the person back, or reply to message. 
13174  *
13175  * \return zero on success, -1 on error.
13176  */
13177 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)
13178 {
13179    int res = 0;
13180    char filename[PATH_MAX];
13181    struct ast_config *msg_cfg = NULL;
13182    const char *origtime, *context;
13183    char *name, *num;
13184    int retries = 0;
13185    char *cid;
13186    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13187 
13188    vms->starting = 0; 
13189 
13190    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13191 
13192    /* Retrieve info from VM attribute file */
13193    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13194    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13195    msg_cfg = ast_config_load(filename, config_flags);
13196    DISPOSE(vms->curdir, vms->curmsg);
13197    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13198       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13199       return 0;
13200    }
13201 
13202    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13203       ast_config_destroy(msg_cfg);
13204       return 0;
13205    }
13206 
13207    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13208 
13209    context = ast_variable_retrieve(msg_cfg, "message", "context");
13210    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13211       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13212    switch (option) {
13213    case 3: /* Play message envelope */
13214       if (!res)
13215          res = play_message_datetime(chan, vmu, origtime, filename);
13216       if (!res)
13217          res = play_message_callerid(chan, vms, cid, context, 0);
13218 
13219       res = 't';
13220       break;
13221 
13222    case 2:  /* Call back */
13223 
13224       if (ast_strlen_zero(cid))
13225          break;
13226 
13227       ast_callerid_parse(cid, &name, &num);
13228       while ((res > -1) && (res != 't')) {
13229          switch (res) {
13230          case '1':
13231             if (num) {
13232                /* Dial the CID number */
13233                res = dialout(chan, vmu, num, vmu->callback);
13234                if (res) {
13235                   ast_config_destroy(msg_cfg);
13236                   return 9;
13237                }
13238             } else {
13239                res = '2';
13240             }
13241             break;
13242 
13243          case '2':
13244             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13245             if (!ast_strlen_zero(vmu->dialout)) {
13246                res = dialout(chan, vmu, NULL, vmu->dialout);
13247                if (res) {
13248                   ast_config_destroy(msg_cfg);
13249                   return 9;
13250                }
13251             } else {
13252                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13253                res = ast_play_and_wait(chan, "vm-sorry");
13254             }
13255             ast_config_destroy(msg_cfg);
13256             return res;
13257          case '*':
13258             res = 't';
13259             break;
13260          case '3':
13261          case '4':
13262          case '5':
13263          case '6':
13264          case '7':
13265          case '8':
13266          case '9':
13267          case '0':
13268 
13269             res = ast_play_and_wait(chan, "vm-sorry");
13270             retries++;
13271             break;
13272          default:
13273             if (num) {
13274                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13275                res = ast_play_and_wait(chan, "vm-num-i-have");
13276                if (!res)
13277                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13278                if (!res)
13279                   res = ast_play_and_wait(chan, "vm-tocallnum");
13280                /* Only prompt for a caller-specified number if there is a dialout context specified */
13281                if (!ast_strlen_zero(vmu->dialout)) {
13282                   if (!res)
13283                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13284                }
13285             } else {
13286                res = ast_play_and_wait(chan, "vm-nonumber");
13287                if (!ast_strlen_zero(vmu->dialout)) {
13288                   if (!res)
13289                      res = ast_play_and_wait(chan, "vm-toenternumber");
13290                }
13291             }
13292             if (!res) {
13293                res = ast_play_and_wait(chan, "vm-star-cancel");
13294             }
13295             if (!res) {
13296                res = ast_waitfordigit(chan, 6000);
13297             }
13298             if (!res) {
13299                retries++;
13300                if (retries > 3) {
13301                   res = 't';
13302                }
13303             }
13304             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13305             break; 
13306             
13307          }
13308          if (res == 't')
13309             res = 0;
13310          else if (res == '*')
13311             res = -1;
13312       }
13313       break;
13314       
13315    case 1:  /* Reply */
13316       /* Send reply directly to sender */
13317       if (ast_strlen_zero(cid))
13318          break;
13319 
13320       ast_callerid_parse(cid, &name, &num);
13321       if (!num) {
13322          ast_verb(3, "No CID number available, no reply sent\n");
13323          if (!res)
13324             res = ast_play_and_wait(chan, "vm-nonumber");
13325          ast_config_destroy(msg_cfg);
13326          return res;
13327       } else {
13328          struct ast_vm_user vmu2;
13329          if (find_user(&vmu2, vmu->context, num)) {
13330             struct leave_vm_options leave_options;
13331             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13332             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13333 
13334             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13335             
13336             memset(&leave_options, 0, sizeof(leave_options));
13337             leave_options.record_gain = record_gain;
13338             res = leave_voicemail(chan, mailbox, &leave_options);
13339             if (!res)
13340                res = 't';
13341             ast_config_destroy(msg_cfg);
13342             return res;
13343          } else {
13344             /* Sender has no mailbox, can't reply */
13345             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13346             ast_play_and_wait(chan, "vm-nobox");
13347             res = 't';
13348             ast_config_destroy(msg_cfg);
13349             return res;
13350          }
13351       } 
13352       res = 0;
13353 
13354       break;
13355    }
13356 
13357 #ifndef IMAP_STORAGE
13358    ast_config_destroy(msg_cfg);
13359 
13360    if (!res) {
13361       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13362       vms->heard[msg] = 1;
13363       res = wait_file(chan, vms, vms->fn);
13364    }
13365 #endif
13366    return res;
13367 }
13368 
13369 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13370          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13371          signed char record_gain, struct vm_state *vms, char *flag)
13372 {
13373    /* Record message & let caller review or re-record it, or set options if applicable */
13374    int res = 0;
13375    int cmd = 0;
13376    int max_attempts = 3;
13377    int attempts = 0;
13378    int recorded = 0;
13379    int msg_exists = 0;
13380    signed char zero_gain = 0;
13381    char tempfile[PATH_MAX];
13382    char *acceptdtmf = "#";
13383    char *canceldtmf = "";
13384    int canceleddtmf = 0;
13385 
13386    /* Note that urgent and private are for flagging messages as such in the future */
13387 
13388    /* barf if no pointer passed to store duration in */
13389    if (duration == NULL) {
13390       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13391       return -1;
13392    }
13393 
13394    if (!outsidecaller)
13395       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13396    else
13397       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13398 
13399    cmd = '3';  /* Want to start by recording */
13400 
13401    while ((cmd >= 0) && (cmd != 't')) {
13402       switch (cmd) {
13403       case '1':
13404          if (!msg_exists) {
13405             /* In this case, 1 is to record a message */
13406             cmd = '3';
13407             break;
13408          } else {
13409             /* Otherwise 1 is to save the existing message */
13410             ast_verb(3, "Saving message as is\n");
13411             if (!outsidecaller) 
13412                ast_filerename(tempfile, recordfile, NULL);
13413             ast_stream_and_wait(chan, "vm-msgsaved", "");
13414             if (!outsidecaller) {
13415                /* Saves to IMAP server only if imapgreeting=yes */
13416                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13417                DISPOSE(recordfile, -1);
13418             }
13419             cmd = 't';
13420             return res;
13421          }
13422       case '2':
13423          /* Review */
13424          ast_verb(3, "Reviewing the message\n");
13425          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13426          break;
13427       case '3':
13428          msg_exists = 0;
13429          /* Record */
13430          if (recorded == 1) 
13431             ast_verb(3, "Re-recording the message\n");
13432          else  
13433             ast_verb(3, "Recording the message\n");
13434          
13435          if (recorded && outsidecaller) {
13436             cmd = ast_play_and_wait(chan, INTRO);
13437             cmd = ast_play_and_wait(chan, "beep");
13438          }
13439          recorded = 1;
13440          /* 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 */
13441          if (record_gain)
13442             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13443          if (ast_test_flag(vmu, VM_OPERATOR))
13444             canceldtmf = "0";
13445          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13446          if (strchr(canceldtmf, cmd)) {
13447          /* need this flag here to distinguish between pressing '0' during message recording or after */
13448             canceleddtmf = 1;
13449          }
13450          if (record_gain)
13451             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13452          if (cmd == -1) {
13453             /* User has hung up, no options to give */
13454             if (!outsidecaller) {
13455                /* user was recording a greeting and they hung up, so let's delete the recording. */
13456                ast_filedelete(tempfile, NULL);
13457             }     
13458             return cmd;
13459          }
13460          if (cmd == '0') {
13461             break;
13462          } else if (cmd == '*') {
13463             break;
13464 #if 0
13465          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13466             /* Message is too short */
13467             ast_verb(3, "Message too short\n");
13468             cmd = ast_play_and_wait(chan, "vm-tooshort");
13469             cmd = ast_filedelete(tempfile, NULL);
13470             break;
13471          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13472             /* Message is all silence */
13473             ast_verb(3, "Nothing recorded\n");
13474             cmd = ast_filedelete(tempfile, NULL);
13475             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13476             if (!cmd)
13477                cmd = ast_play_and_wait(chan, "vm-speakup");
13478             break;
13479 #endif
13480          } else {
13481             /* If all is well, a message exists */
13482             msg_exists = 1;
13483             cmd = 0;
13484          }
13485          break;
13486       case '4':
13487          if (outsidecaller) {  /* only mark vm messages */
13488             /* Mark Urgent */
13489             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13490                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13491                res = ast_play_and_wait(chan, "vm-marked-urgent");
13492                strcpy(flag, "Urgent");
13493             } else if (flag) {
13494                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13495                res = ast_play_and_wait(chan, "vm-urgent-removed");
13496                strcpy(flag, "");
13497             } else {
13498                ast_play_and_wait(chan, "vm-sorry");
13499             }
13500             cmd = 0;
13501          } else {
13502             cmd = ast_play_and_wait(chan, "vm-sorry");
13503          }
13504          break;
13505       case '5':
13506       case '6':
13507       case '7':
13508       case '8':
13509       case '9':
13510       case '*':
13511       case '#':
13512          cmd = ast_play_and_wait(chan, "vm-sorry");
13513          break;
13514 #if 0 
13515 /*  XXX Commented out for the moment because of the dangers of deleting
13516     a message while recording (can put the message numbers out of sync) */
13517       case '*':
13518          /* Cancel recording, delete message, offer to take another message*/
13519          cmd = ast_play_and_wait(chan, "vm-deleted");
13520          cmd = ast_filedelete(tempfile, NULL);
13521          if (outsidecaller) {
13522             res = vm_exec(chan, NULL);
13523             return res;
13524          }
13525          else
13526             return 1;
13527 #endif
13528       case '0':
13529          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13530             cmd = ast_play_and_wait(chan, "vm-sorry");
13531             break;
13532          }
13533          if (msg_exists || recorded) {
13534             cmd = ast_play_and_wait(chan, "vm-saveoper");
13535             if (!cmd)
13536                cmd = ast_waitfordigit(chan, 3000);
13537             if (cmd == '1') {
13538                ast_filerename(tempfile, recordfile, NULL);
13539                ast_play_and_wait(chan, "vm-msgsaved");
13540                cmd = '0';
13541             } else if (cmd == '4') {
13542                if (flag) {
13543                   ast_play_and_wait(chan, "vm-marked-urgent");
13544                   strcpy(flag, "Urgent");
13545                }
13546                ast_play_and_wait(chan, "vm-msgsaved");
13547                cmd = '0';
13548             } else {
13549                ast_play_and_wait(chan, "vm-deleted");
13550                DELETE(tempfile, -1, tempfile, vmu);
13551                cmd = '0';
13552             }
13553          }
13554          return cmd;
13555       default:
13556          /* If the caller is an ouside caller, and the review option is enabled,
13557             allow them to review the message, but let the owner of the box review
13558             their OGM's */
13559          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13560             return cmd;
13561          if (msg_exists) {
13562             cmd = ast_play_and_wait(chan, "vm-review");
13563             if (!cmd && outsidecaller) {
13564                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13565                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13566                } else if (flag) {
13567                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13568                }
13569             }
13570          } else {
13571             cmd = ast_play_and_wait(chan, "vm-torerecord");
13572             if (!cmd)
13573                cmd = ast_waitfordigit(chan, 600);
13574          }
13575          
13576          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13577             cmd = ast_play_and_wait(chan, "vm-reachoper");
13578             if (!cmd)
13579                cmd = ast_waitfordigit(chan, 600);
13580          }
13581 #if 0
13582          if (!cmd)
13583             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13584 #endif
13585          if (!cmd)
13586             cmd = ast_waitfordigit(chan, 6000);
13587          if (!cmd) {
13588             attempts++;
13589          }
13590          if (attempts > max_attempts) {
13591             cmd = 't';
13592          }
13593       }
13594    }
13595    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13596       /* Hang up or timeout, so delete the recording. */
13597       ast_filedelete(tempfile, NULL);
13598    }
13599 
13600    if (cmd != 't' && outsidecaller)
13601       ast_play_and_wait(chan, "vm-goodbye");
13602 
13603    return cmd;
13604 }
13605 
13606 /* This is a workaround so that menuselect displays a proper description
13607  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13608  */
13609 
13610 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13611       .load = load_module,
13612       .unload = unload_module,
13613       .reload = reload,
13614       .nonoptreq = "res_adsi,res_smdi",
13615       );