Mon Mar 19 11:30:21 2012

Asterisk developer's documentation


app_voicemail_imapstorage.c

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

Generated on Mon Mar 19 11:30:22 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7