Mon Oct 8 12:38:56 2012

Asterisk developer's documentation


app_voicemail.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \author Mark Spencer <markster@digium.com>
00022  * \brief Comedian Mail - Voicemail System
00023  *
00024  * \extref unixODBC (http://www.unixodbc.org/)
00025  * \extref A source distribution of University of Washington's IMAP c-client
00026  *         (http://www.washington.edu/imap/)
00027  *
00028  * \par See also
00029  * \arg \ref Config_vm
00030  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
00031  * \ingroup applications
00032  * \note This module requires res_adsi to load. This needs to be optional
00033  * during compilation.
00034  *
00035  * \note This file is now almost impossible to work with, due to all \#ifdefs.
00036  *       Feels like the database code before realtime. Someone - please come up
00037  *       with a plan to clean this up.
00038  */
00039 
00040 /*** MODULEINFO
00041    <use>res_adsi</use>
00042    <use>res_smdi</use>
00043    <support_level>core</support_level>
00044  ***/
00045 
00046 /*** MAKEOPTS
00047 <category name="MENUSELECT_OPTS_app_voicemail_odbcstorage" displayname="Voicemail ODBC Storage Build Options" positive_output="yes" touch_on_change="apps/app_voicemail_odbcstorage.c apps/app_directory_odbcstorage.c">
00048    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00049       <depend>generic_odbc</depend>
00050       <depend>ltdl</depend>
00051       <defaultenabled>yes</defaultenabled>
00052       <support_level>core</support_level>
00053    </member>
00054 </category>
00055 <category name="MENUSELECT_OPTS_app_voicemail_imapstorage" displayname="Voicemail IMAP Storage Build Options" positive_output="yes" touch_on_change="apps/app_voicemail_imapstorage.c apps/app_directory_imapstorage.c">
00056    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00057       <depend>imap_tk</depend>
00058       <use>openssl</use>
00059       <defaultenabled>yes</defaultenabled>
00060       <support_level>core</support_level>
00061    </member>
00062 </category>
00063 ***/
00064 
00065 #include "asterisk.h"
00066 
00067 #ifdef IMAP_STORAGE
00068 #include <ctype.h>
00069 #include <signal.h>
00070 #include <pwd.h>
00071 #ifdef USE_SYSTEM_IMAP
00072 #include <imap/c-client.h>
00073 #include <imap/imap4r1.h>
00074 #include <imap/linkage.h>
00075 #elif defined (USE_SYSTEM_CCLIENT)
00076 #include <c-client/c-client.h>
00077 #include <c-client/imap4r1.h>
00078 #include <c-client/linkage.h>
00079 #else
00080 #include "c-client.h"
00081 #include "imap4r1.h"
00082 #include "linkage.h"
00083 #endif
00084 #endif
00085 
00086 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369652 $")
00087 
00088 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00089 #include <sys/time.h>
00090 #include <sys/stat.h>
00091 #include <sys/mman.h>
00092 #include <time.h>
00093 #include <dirent.h>
00094 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00095 #include <sys/wait.h>
00096 #endif
00097 
00098 #include "asterisk/logger.h"
00099 #include "asterisk/lock.h"
00100 #include "asterisk/file.h"
00101 #include "asterisk/channel.h"
00102 #include "asterisk/pbx.h"
00103 #include "asterisk/config.h"
00104 #include "asterisk/say.h"
00105 #include "asterisk/module.h"
00106 #include "asterisk/adsi.h"
00107 #include "asterisk/app.h"
00108 #include "asterisk/manager.h"
00109 #include "asterisk/dsp.h"
00110 #include "asterisk/localtime.h"
00111 #include "asterisk/cli.h"
00112 #include "asterisk/utils.h"
00113 #include "asterisk/stringfields.h"
00114 #include "asterisk/smdi.h"
00115 #include "asterisk/astobj2.h"
00116 #include "asterisk/event.h"
00117 #include "asterisk/taskprocessor.h"
00118 #include "asterisk/test.h"
00119 
00120 #ifdef ODBC_STORAGE
00121 #include "asterisk/res_odbc.h"
00122 #endif
00123 
00124 #ifdef IMAP_STORAGE
00125 #include "asterisk/threadstorage.h"
00126 #endif
00127 
00128 /*** DOCUMENTATION
00129    <application name="VoiceMail" language="en_US">
00130       <synopsis>
00131          Leave a Voicemail message.
00132       </synopsis>
00133       <syntax>
00134          <parameter name="mailboxs" argsep="&amp;" required="true">
00135             <argument name="mailbox1" argsep="@" required="true">
00136                <argument name="mailbox" required="true" />
00137                <argument name="context" />
00138             </argument>
00139             <argument name="mailbox2" argsep="@" multiple="true">
00140                <argument name="mailbox" required="true" />
00141                <argument name="context" />
00142             </argument>
00143          </parameter>
00144          <parameter name="options">
00145             <optionlist>
00146                <option name="b">
00147                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00148                </option>
00149                <option name="d">
00150                   <argument name="c" />
00151                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00152                   if played during the greeting. Context defaults to the current context.</para>
00153                </option>
00154                <option name="g">
00155                   <argument name="#" required="true" />
00156                   <para>Use the specified amount of gain when recording the voicemail
00157                   message. The units are whole-number decibels (dB). Only works on supported
00158                   technologies, which is DAHDI only.</para>
00159                </option>
00160                <option name="s">
00161                   <para>Skip the playback of instructions for leaving a message to the
00162                   calling party.</para>
00163                </option>
00164                <option name="u">
00165                   <para>Play the <literal>unavailable</literal> greeting.</para>
00166                </option>
00167                <option name="U">
00168                   <para>Mark message as <literal>URGENT</literal>.</para>
00169                </option>
00170                <option name="P">
00171                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00172                </option>
00173             </optionlist>
00174          </parameter>
00175       </syntax>
00176       <description>
00177          <para>This application allows the calling party to leave a message for the specified
00178          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00179          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00180          exist.</para>
00181          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00182          <enumlist>
00183             <enum name="0">
00184                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00185             </enum>
00186             <enum name="*">
00187                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00188             </enum>
00189          </enumlist>
00190          <para>This application will set the following channel variable upon completion:</para>
00191          <variablelist>
00192             <variable name="VMSTATUS">
00193                <para>This indicates the status of the execution of the VoiceMail application.</para>
00194                <value name="SUCCESS" />
00195                <value name="USEREXIT" />
00196                <value name="FAILED" />
00197             </variable>
00198          </variablelist>
00199       </description>
00200       <see-also>
00201          <ref type="application">VoiceMailMain</ref>
00202       </see-also>
00203    </application>
00204    <application name="VoiceMailMain" language="en_US">
00205       <synopsis>
00206          Check Voicemail messages.
00207       </synopsis>
00208       <syntax>
00209          <parameter name="mailbox" required="true" argsep="@">
00210             <argument name="mailbox" />
00211             <argument name="context" />
00212          </parameter>
00213          <parameter name="options">
00214             <optionlist>
00215                <option name="p">
00216                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00217                   the mailbox that is entered by the caller.</para>
00218                </option>
00219                <option name="g">
00220                   <argument name="#" required="true" />
00221                   <para>Use the specified amount of gain when recording a voicemail message.
00222                   The units are whole-number decibels (dB).</para>
00223                </option>
00224                <option name="s">
00225                   <para>Skip checking the passcode for the mailbox.</para>
00226                </option>
00227                <option name="a">
00228                   <argument name="folder" required="true" />
00229                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00230                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00231                   <enumlist>
00232                      <enum name="0"><para>INBOX</para></enum>
00233                      <enum name="1"><para>Old</para></enum>
00234                      <enum name="2"><para>Work</para></enum>
00235                      <enum name="3"><para>Family</para></enum>
00236                      <enum name="4"><para>Friends</para></enum>
00237                      <enum name="5"><para>Cust1</para></enum>
00238                      <enum name="6"><para>Cust2</para></enum>
00239                      <enum name="7"><para>Cust3</para></enum>
00240                      <enum name="8"><para>Cust4</para></enum>
00241                      <enum name="9"><para>Cust5</para></enum>
00242                   </enumlist>
00243                </option>
00244             </optionlist>
00245          </parameter>
00246       </syntax>
00247       <description>
00248          <para>This application allows the calling party to check voicemail messages. A specific
00249          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00250          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00251          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00252          <literal>default</literal> context will be used.</para>
00253          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00254          or Password, and the extension exists:</para>
00255          <enumlist>
00256             <enum name="*">
00257                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00258             </enum>
00259          </enumlist>
00260       </description>
00261       <see-also>
00262          <ref type="application">VoiceMail</ref>
00263       </see-also>
00264    </application>
00265    <application name="MailboxExists" language="en_US">
00266       <synopsis>
00267          Check to see if Voicemail mailbox exists.
00268       </synopsis>
00269       <syntax>
00270          <parameter name="mailbox" required="true" argsep="@">
00271             <argument name="mailbox" required="true" />
00272             <argument name="context" />
00273          </parameter>
00274          <parameter name="options">
00275             <para>None options.</para>
00276          </parameter>
00277       </syntax>
00278       <description>
00279          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00280          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00281          will be used.</para>
00282          <para>This application will set the following channel variable upon completion:</para>
00283          <variablelist>
00284             <variable name="VMBOXEXISTSSTATUS">
00285                <para>This will contain the status of the execution of the MailboxExists application.
00286                Possible values include:</para>
00287                <value name="SUCCESS" />
00288                <value name="FAILED" />
00289             </variable>
00290          </variablelist>
00291       </description>
00292    </application>
00293    <application name="VMAuthenticate" language="en_US">
00294       <synopsis>
00295          Authenticate with Voicemail passwords.
00296       </synopsis>
00297       <syntax>
00298          <parameter name="mailbox" required="true" argsep="@">
00299             <argument name="mailbox" />
00300             <argument name="context" />
00301          </parameter>
00302          <parameter name="options">
00303             <optionlist>
00304                <option name="s">
00305                   <para>Skip playing the initial prompts.</para>
00306                </option>
00307             </optionlist>
00308          </parameter>
00309       </syntax>
00310       <description>
00311          <para>This application behaves the same way as the Authenticate application, but the passwords
00312          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00313          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00314          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00315          mailbox.</para>
00316          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00317          or Password, and the extension exists:</para>
00318          <enumlist>
00319             <enum name="*">
00320                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00321             </enum>
00322          </enumlist>
00323       </description>
00324    </application>
00325    <application name="VMSayName" language="en_US">
00326       <synopsis>
00327          Play the name of a voicemail user
00328       </synopsis>
00329       <syntax>
00330          <parameter name="mailbox" required="true" argsep="@">
00331             <argument name="mailbox" />
00332             <argument name="context" />
00333          </parameter>
00334       </syntax>
00335       <description>
00336          <para>This application will say the recorded name of the voicemail user specified as the
00337          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00338       </description>
00339    </application>
00340    <function name="MAILBOX_EXISTS" language="en_US">
00341       <synopsis>
00342          Tell if a mailbox is configured.
00343       </synopsis>
00344       <syntax argsep="@">
00345          <parameter name="mailbox" required="true" />
00346          <parameter name="context" />
00347       </syntax>
00348       <description>
00349          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00350          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00351          context.</para>
00352       </description>
00353    </function>
00354    <manager name="VoicemailUsersList" language="en_US">
00355       <synopsis>
00356          List All Voicemail User Information.
00357       </synopsis>
00358       <syntax>
00359          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00360       </syntax>
00361       <description>
00362       </description>
00363    </manager>
00364  ***/
00365 
00366 #ifdef IMAP_STORAGE
00367 static char imapserver[48];
00368 static char imapport[8];
00369 static char imapflags[128];
00370 static char imapfolder[64];
00371 static char imapparentfolder[64] = "\0";
00372 static char greetingfolder[64];
00373 static char authuser[32];
00374 static char authpassword[42];
00375 static int imapversion = 1;
00376 
00377 static int expungeonhangup = 1;
00378 static int imapgreetings = 0;
00379 static char delimiter = '\0';
00380 
00381 struct vm_state;
00382 struct ast_vm_user;
00383 
00384 AST_THREADSTORAGE(ts_vmstate);
00385 
00386 /* Forward declarations for IMAP */
00387 static int init_mailstream(struct vm_state *vms, int box);
00388 static void write_file(char *filename, char *buffer, unsigned long len);
00389 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00390 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00391 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00392 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00393 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00394 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00395 static void vmstate_insert(struct vm_state *vms);
00396 static void vmstate_delete(struct vm_state *vms);
00397 static void set_update(MAILSTREAM * stream);
00398 static void init_vm_state(struct vm_state *vms);
00399 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00400 static void get_mailbox_delimiter(MAILSTREAM *stream);
00401 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00402 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00403 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);
00404 static void update_messages_by_imapuser(const char *user, unsigned long number);
00405 static int vm_delete(char *file);
00406 
00407 static int imap_remove_file (char *dir, int msgnum);
00408 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00409 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00410 static void check_quota(struct vm_state *vms, char *mailbox);
00411 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00412 struct vmstate {
00413    struct vm_state *vms;
00414    AST_LIST_ENTRY(vmstate) list;
00415 };
00416 
00417 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00418 
00419 #endif
00420 
00421 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00422 
00423 #define COMMAND_TIMEOUT 5000
00424 /* Don't modify these here; set your umask at runtime instead */
00425 #define  VOICEMAIL_DIR_MODE   0777
00426 #define  VOICEMAIL_FILE_MODE  0666
00427 #define  CHUNKSIZE   65536
00428 
00429 #define VOICEMAIL_CONFIG "voicemail.conf"
00430 #define ASTERISK_USERNAME "asterisk"
00431 
00432 /* Define fast-forward, pause, restart, and reverse keys
00433  * while listening to a voicemail message - these are
00434  * strings, not characters */
00435 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00436 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00437 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00438 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00439 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00440 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00441 
00442 /* Default mail command to mail voicemail. Change it with the
00443  * mailcmd= command in voicemail.conf */
00444 #define SENDMAIL "/usr/sbin/sendmail -t"
00445 
00446 #define INTRO "vm-intro"
00447 
00448 #define MAXMSG 100
00449 #define MAXMSGLIMIT 9999
00450 
00451 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00452 
00453 #define BASELINELEN 72
00454 #define BASEMAXINLINE 256
00455 #ifdef IMAP_STORAGE
00456 #define ENDL "\r\n"
00457 #else
00458 #define ENDL "\n"
00459 #endif
00460 
00461 #define MAX_DATETIME_FORMAT   512
00462 #define MAX_NUM_CID_CONTEXTS 10
00463 
00464 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00465 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00466 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00467 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00468 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00469 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00470 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00471 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00472 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00473 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00474 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00475 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00476 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00477 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00478 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00479 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00480 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00481 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00482 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00483 #define ERROR_LOCK_PATH  -100
00484 #define OPERATOR_EXIT     300
00485 
00486 
00487 enum vm_box {
00488    NEW_FOLDER,
00489    OLD_FOLDER,
00490    WORK_FOLDER,
00491    FAMILY_FOLDER,
00492    FRIENDS_FOLDER,
00493    GREETINGS_FOLDER
00494 };
00495 
00496 enum vm_option_flags {
00497    OPT_SILENT =           (1 << 0),
00498    OPT_BUSY_GREETING =    (1 << 1),
00499    OPT_UNAVAIL_GREETING = (1 << 2),
00500    OPT_RECORDGAIN =       (1 << 3),
00501    OPT_PREPEND_MAILBOX =  (1 << 4),
00502    OPT_AUTOPLAY =         (1 << 6),
00503    OPT_DTMFEXIT =         (1 << 7),
00504    OPT_MESSAGE_Urgent =   (1 << 8),
00505    OPT_MESSAGE_PRIORITY = (1 << 9)
00506 };
00507 
00508 enum vm_option_args {
00509    OPT_ARG_RECORDGAIN = 0,
00510    OPT_ARG_PLAYFOLDER = 1,
00511    OPT_ARG_DTMFEXIT   = 2,
00512    /* This *must* be the last value in this enum! */
00513    OPT_ARG_ARRAY_SIZE = 3,
00514 };
00515 
00516 enum vm_passwordlocation {
00517    OPT_PWLOC_VOICEMAILCONF = 0,
00518    OPT_PWLOC_SPOOLDIR      = 1,
00519    OPT_PWLOC_USERSCONF     = 2,
00520 };
00521 
00522 AST_APP_OPTIONS(vm_app_options, {
00523    AST_APP_OPTION('s', OPT_SILENT),
00524    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00525    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00526    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00527    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00528    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00529    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00530    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00531    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00532 });
00533 
00534 static int load_config(int reload);
00535 #ifdef TEST_FRAMEWORK
00536 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00537 #endif
00538 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00539 
00540 /*! \page vmlang Voicemail Language Syntaxes Supported
00541 
00542    \par Syntaxes supported, not really language codes.
00543    \arg \b en    - English
00544    \arg \b de    - German
00545    \arg \b es    - Spanish
00546    \arg \b fr    - French
00547    \arg \b it    - Italian
00548    \arg \b nl    - Dutch
00549    \arg \b pt    - Portuguese
00550    \arg \b pt_BR - Portuguese (Brazil)
00551    \arg \b gr    - Greek
00552    \arg \b no    - Norwegian
00553    \arg \b se    - Swedish
00554    \arg \b tw    - Chinese (Taiwan)
00555    \arg \b ua - Ukrainian
00556 
00557 German requires the following additional soundfile:
00558 \arg \b 1F  einE (feminine)
00559 
00560 Spanish requires the following additional soundfile:
00561 \arg \b 1M      un (masculine)
00562 
00563 Dutch, Portuguese & Spanish require the following additional soundfiles:
00564 \arg \b vm-INBOXs singular of 'new'
00565 \arg \b vm-Olds      singular of 'old/heard/read'
00566 
00567 NB these are plural:
00568 \arg \b vm-INBOX  nieuwe (nl)
00569 \arg \b vm-Old    oude (nl)
00570 
00571 Polish uses:
00572 \arg \b vm-new-a  'new', feminine singular accusative
00573 \arg \b vm-new-e  'new', feminine plural accusative
00574 \arg \b vm-new-ych   'new', feminine plural genitive
00575 \arg \b vm-old-a  'old', feminine singular accusative
00576 \arg \b vm-old-e  'old', feminine plural accusative
00577 \arg \b vm-old-ych   'old', feminine plural genitive
00578 \arg \b digits/1-a   'one', not always same as 'digits/1'
00579 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00580 
00581 Swedish uses:
00582 \arg \b vm-nytt      singular of 'new'
00583 \arg \b vm-nya    plural of 'new'
00584 \arg \b vm-gammalt   singular of 'old'
00585 \arg \b vm-gamla  plural of 'old'
00586 \arg \b digits/ett   'one', not always same as 'digits/1'
00587 
00588 Norwegian uses:
00589 \arg \b vm-ny     singular of 'new'
00590 \arg \b vm-nye    plural of 'new'
00591 \arg \b vm-gammel singular of 'old'
00592 \arg \b vm-gamle  plural of 'old'
00593 
00594 Dutch also uses:
00595 \arg \b nl-om     'at'?
00596 
00597 Spanish also uses:
00598 \arg \b vm-youhaveno
00599 
00600 Italian requires the following additional soundfile:
00601 
00602 For vm_intro_it:
00603 \arg \b vm-nuovo  new
00604 \arg \b vm-nuovi  new plural
00605 \arg \b vm-vecchio   old
00606 \arg \b vm-vecchi old plural
00607 
00608 Chinese (Taiwan) requires the following additional soundfile:
00609 \arg \b vm-tong      A class-word for call (tong1)
00610 \arg \b vm-ri     A class-word for day (ri4)
00611 \arg \b vm-you    You (ni3)
00612 \arg \b vm-haveno   Have no (mei2 you3)
00613 \arg \b vm-have     Have (you3)
00614 \arg \b vm-listen   To listen (yao4 ting1)
00615 
00616 
00617 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00618 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00619 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00620 
00621 */
00622 
00623 struct baseio {
00624    int iocp;
00625    int iolen;
00626    int linelength;
00627    int ateof;
00628    unsigned char iobuf[BASEMAXINLINE];
00629 };
00630 
00631 /*! Structure for linked list of users 
00632  * Use ast_vm_user_destroy() to free one of these structures. */
00633 struct ast_vm_user {
00634    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00635    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00636    char password[80];               /*!< Secret pin code, numbers only */
00637    char fullname[80];               /*!< Full name, for directory app */
00638    char email[80];                  /*!< E-mail address */
00639    char *emailsubject;              /*!< E-mail subject */
00640    char *emailbody;                 /*!< E-mail body */
00641    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00642    char serveremail[80];            /*!< From: Mail address */
00643    char mailcmd[160];               /*!< Configurable mail command */
00644    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00645    char zonetag[80];                /*!< Time zone */
00646    char locale[20];                 /*!< The locale (for presentation of date/time) */
00647    char callback[80];
00648    char dialout[80];
00649    char uniqueid[80];               /*!< Unique integer identifier */
00650    char exit[80];
00651    char attachfmt[20];              /*!< Attachment format */
00652    unsigned int flags;              /*!< VM_ flags */ 
00653    int saydurationm;
00654    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00655    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00656    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00657    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00658    int passwordlocation;            /*!< Storage location of the password */
00659 #ifdef IMAP_STORAGE
00660    char imapuser[80];               /*!< IMAP server login */
00661    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00662    char imapfolder[64];             /*!< IMAP voicemail folder */
00663    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00664    int imapversion;                 /*!< If configuration changes, use the new values */
00665 #endif
00666    double volgain;                  /*!< Volume gain for voicemails sent via email */
00667    AST_LIST_ENTRY(ast_vm_user) list;
00668 };
00669 
00670 /*! Voicemail time zones */
00671 struct vm_zone {
00672    AST_LIST_ENTRY(vm_zone) list;
00673    char name[80];
00674    char timezone[80];
00675    char msg_format[512];
00676 };
00677 
00678 #define VMSTATE_MAX_MSG_ARRAY 256
00679 
00680 /*! Voicemail mailbox state */
00681 struct vm_state {
00682    char curbox[80];
00683    char username[80];
00684    char context[80];
00685    char curdir[PATH_MAX];
00686    char vmbox[PATH_MAX];
00687    char fn[PATH_MAX];
00688    char intro[PATH_MAX];
00689    int *deleted;
00690    int *heard;
00691    int dh_arraysize; /* used for deleted / heard allocation */
00692    int curmsg;
00693    int lastmsg;
00694    int newmessages;
00695    int oldmessages;
00696    int urgentmessages;
00697    int starting;
00698    int repeats;
00699 #ifdef IMAP_STORAGE
00700    ast_mutex_t lock;
00701    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00702    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00703    MAILSTREAM *mailstream;
00704    int vmArrayIndex;
00705    char imapuser[80];                   /*!< IMAP server login */
00706    char imapfolder[64];                 /*!< IMAP voicemail folder */
00707    int imapversion;
00708    int interactive;
00709    char introfn[PATH_MAX];              /*!< Name of prepended file */
00710    unsigned int quota_limit;
00711    unsigned int quota_usage;
00712    struct vm_state *persist_vms;
00713 #endif
00714 };
00715 
00716 #ifdef ODBC_STORAGE
00717 static char odbc_database[80];
00718 static char odbc_table[80];
00719 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00720 #define DISPOSE(a,b) remove_file(a,b)
00721 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00722 #define EXISTS(a,b,c,d) (message_exists(a,b))
00723 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00724 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00725 #define DELETE(a,b,c,d) (delete_file(a,b))
00726 #else
00727 #ifdef IMAP_STORAGE
00728 #define DISPOSE(a,b) (imap_remove_file(a,b))
00729 #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))
00730 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00731 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00732 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00733 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00734 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00735 #else
00736 #define RETRIEVE(a,b,c,d)
00737 #define DISPOSE(a,b)
00738 #define STORE(a,b,c,d,e,f,g,h,i,j)
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_plain_file(g,h)); 
00742 #define DELETE(a,b,c,d) (vm_delete(c))
00743 #endif
00744 #endif
00745 
00746 static char VM_SPOOL_DIR[PATH_MAX];
00747 
00748 static char ext_pass_cmd[128];
00749 static char ext_pass_check_cmd[128];
00750 
00751 static int my_umask;
00752 
00753 #define PWDCHANGE_INTERNAL (1 << 1)
00754 #define PWDCHANGE_EXTERNAL (1 << 2)
00755 static int pwdchange = PWDCHANGE_INTERNAL;
00756 
00757 #ifdef ODBC_STORAGE
00758 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00759 #else
00760 # ifdef IMAP_STORAGE
00761 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00762 # else
00763 # define tdesc "Comedian Mail (Voicemail System)"
00764 # endif
00765 #endif
00766 
00767 static char userscontext[AST_MAX_EXTENSION] = "default";
00768 
00769 static char *addesc = "Comedian Mail";
00770 
00771 /* Leave a message */
00772 static char *app = "VoiceMail";
00773 
00774 /* Check mail, control, etc */
00775 static char *app2 = "VoiceMailMain";
00776 
00777 static char *app3 = "MailboxExists";
00778 static char *app4 = "VMAuthenticate";
00779 
00780 static char *sayname_app = "VMSayName";
00781 
00782 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00783 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00784 static char zonetag[80];
00785 static char locale[20];
00786 static int maxsilence;
00787 static int maxmsg;
00788 static int maxdeletedmsg;
00789 static int silencethreshold = 128;
00790 static char serveremail[80];
00791 static char mailcmd[160];  /* Configurable mail cmd */
00792 static char externnotify[160]; 
00793 static struct ast_smdi_interface *smdi_iface = NULL;
00794 static char vmfmts[80];
00795 static double volgain;
00796 static int vmminsecs;
00797 static int vmmaxsecs;
00798 static int maxgreet;
00799 static int skipms;
00800 static int maxlogins;
00801 static int minpassword;
00802 static int passwordlocation;
00803 
00804 /*! Poll mailboxes for changes since there is something external to
00805  *  app_voicemail that may change them. */
00806 static unsigned int poll_mailboxes;
00807 
00808 /*! Polling frequency */
00809 static unsigned int poll_freq;
00810 /*! By default, poll every 30 seconds */
00811 #define DEFAULT_POLL_FREQ 30
00812 
00813 AST_MUTEX_DEFINE_STATIC(poll_lock);
00814 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00815 static pthread_t poll_thread = AST_PTHREADT_NULL;
00816 static unsigned char poll_thread_run;
00817 
00818 /*! Subscription to ... MWI event subscriptions */
00819 static struct ast_event_sub *mwi_sub_sub;
00820 /*! Subscription to ... MWI event un-subscriptions */
00821 static struct ast_event_sub *mwi_unsub_sub;
00822 
00823 /*!
00824  * \brief An MWI subscription
00825  *
00826  * This is so we can keep track of which mailboxes are subscribed to.
00827  * This way, we know which mailboxes to poll when the pollmailboxes
00828  * option is being used.
00829  */
00830 struct mwi_sub {
00831    AST_RWLIST_ENTRY(mwi_sub) entry;
00832    int old_urgent;
00833    int old_new;
00834    int old_old;
00835    uint32_t uniqueid;
00836    char mailbox[1];
00837 };
00838 
00839 struct mwi_sub_task {
00840    const char *mailbox;
00841    const char *context;
00842    uint32_t uniqueid;
00843 };
00844 
00845 static struct ast_taskprocessor *mwi_subscription_tps;
00846 
00847 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00848 
00849 /* custom audio control prompts for voicemail playback */
00850 static char listen_control_forward_key[12];
00851 static char listen_control_reverse_key[12];
00852 static char listen_control_pause_key[12];
00853 static char listen_control_restart_key[12];
00854 static char listen_control_stop_key[12];
00855 
00856 /* custom password sounds */
00857 static char vm_password[80] = "vm-password";
00858 static char vm_newpassword[80] = "vm-newpassword";
00859 static char vm_passchanged[80] = "vm-passchanged";
00860 static char vm_reenterpassword[80] = "vm-reenterpassword";
00861 static char vm_mismatch[80] = "vm-mismatch";
00862 static char vm_invalid_password[80] = "vm-invalid-password";
00863 static char vm_pls_try_again[80] = "vm-pls-try-again";
00864 
00865 /*
00866  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00867  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00868  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00869  * app.c's __ast_play_and_record function
00870  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00871  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00872  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00873  * more effort than either of the other two.
00874  */
00875 static char vm_prepend_timeout[80] = "vm-then-pound";
00876 
00877 static struct ast_flags globalflags = {0};
00878 
00879 static int saydurationminfo;
00880 
00881 static char dialcontext[AST_MAX_CONTEXT] = "";
00882 static char callcontext[AST_MAX_CONTEXT] = "";
00883 static char exitcontext[AST_MAX_CONTEXT] = "";
00884 
00885 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00886 
00887 
00888 static char *emailbody = NULL;
00889 static char *emailsubject = NULL;
00890 static char *pagerbody = NULL;
00891 static char *pagersubject = NULL;
00892 static char fromstring[100];
00893 static char pagerfromstring[100];
00894 static char charset[32] = "ISO-8859-1";
00895 
00896 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00897 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00898 static int adsiver = 1;
00899 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00900 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00901 
00902 /* Forward declarations - generic */
00903 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00904 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);
00905 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00906 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00907          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00908          signed char record_gain, struct vm_state *vms, char *flag);
00909 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00910 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00911 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);
00912 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);
00913 static void apply_options(struct ast_vm_user *vmu, const char *options);
00914 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);
00915 static int is_valid_dtmf(const char *key);
00916 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00917 static int write_password_to_file(const char *secretfn, const char *password);
00918 static const char *substitute_escapes(const char *value);
00919 static void free_user(struct ast_vm_user *vmu);
00920 
00921 struct ao2_container *inprocess_container;
00922 
00923 struct inprocess {
00924    int count;
00925    char *context;
00926    char mailbox[0];
00927 };
00928 
00929 static int inprocess_hash_fn(const void *obj, const int flags)
00930 {
00931    const struct inprocess *i = obj;
00932    return atoi(i->mailbox);
00933 }
00934 
00935 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00936 {
00937    struct inprocess *i = obj, *j = arg;
00938    if (strcmp(i->mailbox, j->mailbox)) {
00939       return 0;
00940    }
00941    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00942 }
00943 
00944 static int inprocess_count(const char *context, const char *mailbox, int delta)
00945 {
00946    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00947    arg->context = arg->mailbox + strlen(mailbox) + 1;
00948    strcpy(arg->mailbox, mailbox); /* SAFE */
00949    strcpy(arg->context, context); /* SAFE */
00950    ao2_lock(inprocess_container);
00951    if ((i = ao2_find(inprocess_container, arg, 0))) {
00952       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00953       ao2_unlock(inprocess_container);
00954       ao2_ref(i, -1);
00955       return ret;
00956    }
00957    if (delta < 0) {
00958       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00959    }
00960    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00961       ao2_unlock(inprocess_container);
00962       return 0;
00963    }
00964    i->context = i->mailbox + strlen(mailbox) + 1;
00965    strcpy(i->mailbox, mailbox); /* SAFE */
00966    strcpy(i->context, context); /* SAFE */
00967    i->count = delta;
00968    ao2_link(inprocess_container, i);
00969    ao2_unlock(inprocess_container);
00970    ao2_ref(i, -1);
00971    return 0;
00972 }
00973 
00974 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00975 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00976 #endif
00977 
00978 /*!
00979  * \brief Strips control and non 7-bit clean characters from input string.
00980  *
00981  * \note To map control and none 7-bit characters to a 7-bit clean characters
00982  *  please use ast_str_encode_mine().
00983  */
00984 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00985 {
00986    char *bufptr = buf;
00987    for (; *input; input++) {
00988       if (*input < 32) {
00989          continue;
00990       }
00991       *bufptr++ = *input;
00992       if (bufptr == buf + buflen - 1) {
00993          break;
00994       }
00995    }
00996    *bufptr = '\0';
00997    return buf;
00998 }
00999 
01000 
01001 /*!
01002  * \brief Sets default voicemail system options to a voicemail user.
01003  *
01004  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01005  * - all the globalflags
01006  * - the saydurationminfo
01007  * - the callcontext
01008  * - the dialcontext
01009  * - the exitcontext
01010  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01011  * - volume gain
01012  * - emailsubject, emailbody set to NULL
01013  */
01014 static void populate_defaults(struct ast_vm_user *vmu)
01015 {
01016    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01017    vmu->passwordlocation = passwordlocation;
01018    if (saydurationminfo) {
01019       vmu->saydurationm = saydurationminfo;
01020    }
01021    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01022    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01023    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01024    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01025    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01026    if (vmminsecs) {
01027       vmu->minsecs = vmminsecs;
01028    }
01029    if (vmmaxsecs) {
01030       vmu->maxsecs = vmmaxsecs;
01031    }
01032    if (maxmsg) {
01033       vmu->maxmsg = maxmsg;
01034    }
01035    if (maxdeletedmsg) {
01036       vmu->maxdeletedmsg = maxdeletedmsg;
01037    }
01038    vmu->volgain = volgain;
01039    ast_free(vmu->emailsubject);
01040    vmu->emailsubject = NULL;
01041    ast_free(vmu->emailbody);
01042    vmu->emailbody = NULL;
01043 #ifdef IMAP_STORAGE
01044    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01045 #endif
01046 }
01047 
01048 /*!
01049  * \brief Sets a a specific property value.
01050  * \param vmu The voicemail user object to work with.
01051  * \param var The name of the property to be set.
01052  * \param value The value to be set to the property.
01053  * 
01054  * The property name must be one of the understood properties. See the source for details.
01055  */
01056 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01057 {
01058    int x;
01059    if (!strcasecmp(var, "attach")) {
01060       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01061    } else if (!strcasecmp(var, "attachfmt")) {
01062       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01063    } else if (!strcasecmp(var, "serveremail")) {
01064       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01065    } else if (!strcasecmp(var, "emailbody")) {
01066       vmu->emailbody = ast_strdup(substitute_escapes(value));
01067    } else if (!strcasecmp(var, "emailsubject")) {
01068       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01069    } else if (!strcasecmp(var, "language")) {
01070       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01071    } else if (!strcasecmp(var, "tz")) {
01072       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01073    } else if (!strcasecmp(var, "locale")) {
01074       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01075 #ifdef IMAP_STORAGE
01076    } else if (!strcasecmp(var, "imapuser")) {
01077       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01078       vmu->imapversion = imapversion;
01079    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01080       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01081       vmu->imapversion = imapversion;
01082    } else if (!strcasecmp(var, "imapfolder")) {
01083       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01084    } else if (!strcasecmp(var, "imapvmshareid")) {
01085       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01086       vmu->imapversion = imapversion;
01087 #endif
01088    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01089       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01090    } else if (!strcasecmp(var, "saycid")){
01091       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01092    } else if (!strcasecmp(var, "sendvoicemail")){
01093       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01094    } else if (!strcasecmp(var, "review")){
01095       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01096    } else if (!strcasecmp(var, "tempgreetwarn")){
01097       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01098    } else if (!strcasecmp(var, "messagewrap")){
01099       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01100    } else if (!strcasecmp(var, "operator")) {
01101       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01102    } else if (!strcasecmp(var, "envelope")){
01103       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01104    } else if (!strcasecmp(var, "moveheard")){
01105       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01106    } else if (!strcasecmp(var, "sayduration")){
01107       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01108    } else if (!strcasecmp(var, "saydurationm")){
01109       if (sscanf(value, "%30d", &x) == 1) {
01110          vmu->saydurationm = x;
01111       } else {
01112          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01113       }
01114    } else if (!strcasecmp(var, "forcename")){
01115       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01116    } else if (!strcasecmp(var, "forcegreetings")){
01117       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01118    } else if (!strcasecmp(var, "callback")) {
01119       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01120    } else if (!strcasecmp(var, "dialout")) {
01121       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01122    } else if (!strcasecmp(var, "exitcontext")) {
01123       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01124    } else if (!strcasecmp(var, "minsecs")) {
01125       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01126          vmu->minsecs = x;
01127       } else {
01128          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01129          vmu->minsecs = vmminsecs;
01130       }
01131    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01132       vmu->maxsecs = atoi(value);
01133       if (vmu->maxsecs <= 0) {
01134          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01135          vmu->maxsecs = vmmaxsecs;
01136       } else {
01137          vmu->maxsecs = atoi(value);
01138       }
01139       if (!strcasecmp(var, "maxmessage"))
01140          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01141    } else if (!strcasecmp(var, "maxmsg")) {
01142       vmu->maxmsg = atoi(value);
01143       /* Accept maxmsg=0 (Greetings only voicemail) */
01144       if (vmu->maxmsg < 0) {
01145          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01146          vmu->maxmsg = MAXMSG;
01147       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01148          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01149          vmu->maxmsg = MAXMSGLIMIT;
01150       }
01151    } else if (!strcasecmp(var, "nextaftercmd")) {
01152       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01153    } else if (!strcasecmp(var, "backupdeleted")) {
01154       if (sscanf(value, "%30d", &x) == 1)
01155          vmu->maxdeletedmsg = x;
01156       else if (ast_true(value))
01157          vmu->maxdeletedmsg = MAXMSG;
01158       else
01159          vmu->maxdeletedmsg = 0;
01160 
01161       if (vmu->maxdeletedmsg < 0) {
01162          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01163          vmu->maxdeletedmsg = MAXMSG;
01164       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01165          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01166          vmu->maxdeletedmsg = MAXMSGLIMIT;
01167       }
01168    } else if (!strcasecmp(var, "volgain")) {
01169       sscanf(value, "%30lf", &vmu->volgain);
01170    } else if (!strcasecmp(var, "passwordlocation")) {
01171       if (!strcasecmp(value, "spooldir")) {
01172          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01173       } else {
01174          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01175       }
01176    } else if (!strcasecmp(var, "options")) {
01177       apply_options(vmu, value);
01178    }
01179 }
01180 
01181 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01182 {
01183    int fds[2], pid = 0;
01184 
01185    memset(buf, 0, len);
01186 
01187    if (pipe(fds)) {
01188       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01189    } else {
01190       /* good to go*/
01191       pid = ast_safe_fork(0);
01192 
01193       if (pid < 0) {
01194          /* ok maybe not */
01195          close(fds[0]);
01196          close(fds[1]);
01197          snprintf(buf, len, "FAILURE: Fork failed");
01198       } else if (pid) {
01199          /* parent */
01200          close(fds[1]);
01201          if (read(fds[0], buf, len) < 0) {
01202             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01203          }
01204          close(fds[0]);
01205       } else {
01206          /*  child */
01207          AST_DECLARE_APP_ARGS(arg,
01208             AST_APP_ARG(v)[20];
01209          );
01210          char *mycmd = ast_strdupa(command);
01211 
01212          close(fds[0]);
01213          dup2(fds[1], STDOUT_FILENO);
01214          close(fds[1]);
01215          ast_close_fds_above_n(STDOUT_FILENO);
01216 
01217          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01218 
01219          execv(arg.v[0], arg.v); 
01220          printf("FAILURE: %s", strerror(errno));
01221          _exit(0);
01222       }
01223    }
01224    return buf;
01225 }
01226 
01227 /*!
01228  * \brief Check that password meets minimum required length
01229  * \param vmu The voicemail user to change the password for.
01230  * \param password The password string to check
01231  *
01232  * \return zero on ok, 1 on not ok.
01233  */
01234 static int check_password(struct ast_vm_user *vmu, char *password)
01235 {
01236    /* check minimum length */
01237    if (strlen(password) < minpassword)
01238       return 1;
01239    /* check that password does not contain '*' character */
01240    if (!ast_strlen_zero(password) && password[0] == '*')
01241       return 1;
01242    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01243       char cmd[255], buf[255];
01244 
01245       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01246 
01247       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01248       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01249          ast_debug(5, "Result: %s\n", buf);
01250          if (!strncasecmp(buf, "VALID", 5)) {
01251             ast_debug(3, "Passed password check: '%s'\n", buf);
01252             return 0;
01253          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01254             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01255             return 0;
01256          } else {
01257             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01258             return 1;
01259          }
01260       }
01261    }
01262    return 0;
01263 }
01264 
01265 /*! 
01266  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01267  * \param vmu The voicemail user to change the password for.
01268  * \param password The new value to be set to the password for this user.
01269  * 
01270  * This only works if there is a realtime engine configured.
01271  * This is called from the (top level) vm_change_password.
01272  *
01273  * \return zero on success, -1 on error.
01274  */
01275 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01276 {
01277    int res = -1;
01278    if (!strcmp(vmu->password, password)) {
01279       /* No change (but an update would return 0 rows updated, so we opt out here) */
01280       return 0;
01281    }
01282 
01283    if (strlen(password) > 10) {
01284       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01285    }
01286    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01287       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01288       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01289       res = 0;
01290    }
01291    return res;
01292 }
01293 
01294 /*!
01295  * \brief Destructively Parse options and apply.
01296  */
01297 static void apply_options(struct ast_vm_user *vmu, const char *options)
01298 {  
01299    char *stringp;
01300    char *s;
01301    char *var, *value;
01302    stringp = ast_strdupa(options);
01303    while ((s = strsep(&stringp, "|"))) {
01304       value = s;
01305       if ((var = strsep(&value, "=")) && value) {
01306          apply_option(vmu, var, value);
01307       }
01308    }  
01309 }
01310 
01311 /*!
01312  * \brief Loads the options specific to a voicemail user.
01313  * 
01314  * This is called when a vm_user structure is being set up, such as from load_options.
01315  */
01316 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01317 {
01318    for (; var; var = var->next) {
01319       if (!strcasecmp(var->name, "vmsecret")) {
01320          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01321       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01322          if (ast_strlen_zero(retval->password)) {
01323             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01324                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01325                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01326             } else {
01327                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01328             }
01329          }
01330       } else if (!strcasecmp(var->name, "uniqueid")) {
01331          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01332       } else if (!strcasecmp(var->name, "pager")) {
01333          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01334       } else if (!strcasecmp(var->name, "email")) {
01335          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01336       } else if (!strcasecmp(var->name, "fullname")) {
01337          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01338       } else if (!strcasecmp(var->name, "context")) {
01339          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01340       } else if (!strcasecmp(var->name, "emailsubject")) {
01341          ast_free(retval->emailsubject);
01342          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01343       } else if (!strcasecmp(var->name, "emailbody")) {
01344          ast_free(retval->emailbody);
01345          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01346 #ifdef IMAP_STORAGE
01347       } else if (!strcasecmp(var->name, "imapuser")) {
01348          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01349          retval->imapversion = imapversion;
01350       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01351          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01352          retval->imapversion = imapversion;
01353       } else if (!strcasecmp(var->name, "imapfolder")) {
01354          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01355       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01356          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01357          retval->imapversion = imapversion;
01358 #endif
01359       } else
01360          apply_option(retval, var->name, var->value);
01361    }
01362 }
01363 
01364 /*!
01365  * \brief Determines if a DTMF key entered is valid.
01366  * \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.
01367  *
01368  * Tests the character entered against the set of valid DTMF characters. 
01369  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01370  */
01371 static int is_valid_dtmf(const char *key)
01372 {
01373    int i;
01374    char *local_key = ast_strdupa(key);
01375 
01376    for (i = 0; i < strlen(key); ++i) {
01377       if (!strchr(VALID_DTMF, *local_key)) {
01378          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01379          return 0;
01380       }
01381       local_key++;
01382    }
01383    return 1;
01384 }
01385 
01386 /*!
01387  * \brief Finds a voicemail user from the realtime engine.
01388  * \param ivm
01389  * \param context
01390  * \param mailbox
01391  *
01392  * 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.
01393  *
01394  * \return The ast_vm_user structure for the user that was found.
01395  */
01396 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01397 {
01398    struct ast_variable *var;
01399    struct ast_vm_user *retval;
01400 
01401    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01402       if (!ivm)
01403          ast_set_flag(retval, VM_ALLOCED);   
01404       else
01405          memset(retval, 0, sizeof(*retval));
01406       if (mailbox) 
01407          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01408       populate_defaults(retval);
01409       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01410          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01411       else
01412          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01413       if (var) {
01414          apply_options_full(retval, var);
01415          ast_variables_destroy(var);
01416       } else { 
01417          if (!ivm) 
01418             free_user(retval);
01419          retval = NULL;
01420       }  
01421    } 
01422    return retval;
01423 }
01424 
01425 /*!
01426  * \brief Finds a voicemail user from the users file or the realtime engine.
01427  * \param ivm
01428  * \param context
01429  * \param mailbox
01430  * 
01431  * \return The ast_vm_user structure for the user that was found.
01432  */
01433 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01434 {
01435    /* This function could be made to generate one from a database, too */
01436    struct ast_vm_user *vmu = NULL, *cur;
01437    AST_LIST_LOCK(&users);
01438 
01439    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01440       context = "default";
01441 
01442    AST_LIST_TRAVERSE(&users, cur, list) {
01443 #ifdef IMAP_STORAGE
01444       if (cur->imapversion != imapversion) {
01445          continue;
01446       }
01447 #endif
01448       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01449          break;
01450       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01451          break;
01452    }
01453    if (cur) {
01454       /* Make a copy, so that on a reload, we have no race */
01455       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01456          *vmu = *cur;
01457          if (!ivm) {
01458             vmu->emailbody = ast_strdup(cur->emailbody);
01459             vmu->emailsubject = ast_strdup(cur->emailsubject);
01460          }
01461          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01462          AST_LIST_NEXT(vmu, list) = NULL;
01463       }
01464    } else
01465       vmu = find_user_realtime(ivm, context, mailbox);
01466    AST_LIST_UNLOCK(&users);
01467    return vmu;
01468 }
01469 
01470 /*!
01471  * \brief Resets a user password to a specified password.
01472  * \param context
01473  * \param mailbox
01474  * \param newpass
01475  *
01476  * This does the actual change password work, called by the vm_change_password() function.
01477  *
01478  * \return zero on success, -1 on error.
01479  */
01480 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01481 {
01482    /* This function could be made to generate one from a database, too */
01483    struct ast_vm_user *cur;
01484    int res = -1;
01485    AST_LIST_LOCK(&users);
01486    AST_LIST_TRAVERSE(&users, cur, list) {
01487       if ((!context || !strcasecmp(context, cur->context)) &&
01488          (!strcasecmp(mailbox, cur->mailbox)))
01489             break;
01490    }
01491    if (cur) {
01492       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01493       res = 0;
01494    }
01495    AST_LIST_UNLOCK(&users);
01496    return res;
01497 }
01498 
01499 /*! 
01500  * \brief The handler for the change password option.
01501  * \param vmu The voicemail user to work with.
01502  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01503  * 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.
01504  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01505  */
01506 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01507 {
01508    struct ast_config   *cfg = NULL;
01509    struct ast_variable *var = NULL;
01510    struct ast_category *cat = NULL;
01511    char *category = NULL, *value = NULL, *new = NULL;
01512    const char *tmp = NULL;
01513    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01514    char secretfn[PATH_MAX] = "";
01515    int found = 0;
01516 
01517    if (!change_password_realtime(vmu, newpassword))
01518       return;
01519 
01520    /* check if we should store the secret in the spool directory next to the messages */
01521    switch (vmu->passwordlocation) {
01522    case OPT_PWLOC_SPOOLDIR:
01523       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01524       if (write_password_to_file(secretfn, newpassword) == 0) {
01525          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01526          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01527          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01528          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01529          break;
01530       } else {
01531          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01532       }
01533       /* Fall-through */
01534    case OPT_PWLOC_VOICEMAILCONF:
01535       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01536          while ((category = ast_category_browse(cfg, category))) {
01537             if (!strcasecmp(category, vmu->context)) {
01538                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01539                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01540                   break;
01541                }
01542                value = strstr(tmp, ",");
01543                if (!value) {
01544                   new = alloca(strlen(newpassword)+1);
01545                   sprintf(new, "%s", newpassword);
01546                } else {
01547                   new = alloca((strlen(value) + strlen(newpassword) + 1));
01548                   sprintf(new, "%s%s", newpassword, value);
01549                }
01550                if (!(cat = ast_category_get(cfg, category))) {
01551                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01552                   break;
01553                }
01554                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01555                found = 1;
01556             }
01557          }
01558          /* save the results */
01559          if (found) {
01560             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01561             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01562             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01563             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01564             break;
01565          }
01566       }
01567       /* Fall-through */
01568    case OPT_PWLOC_USERSCONF:
01569       /* check users.conf and update the password stored for the mailbox */
01570       /* if no vmsecret entry exists create one. */
01571       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01572          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01573          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01574             ast_debug(4, "users.conf: %s\n", category);
01575             if (!strcasecmp(category, vmu->mailbox)) {
01576                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01577                   ast_debug(3, "looks like we need to make vmsecret!\n");
01578                   var = ast_variable_new("vmsecret", newpassword, "");
01579                } else {
01580                   var = NULL;
01581                }
01582                new = alloca(strlen(newpassword) + 1);
01583                sprintf(new, "%s", newpassword);
01584                if (!(cat = ast_category_get(cfg, category))) {
01585                   ast_debug(4, "failed to get category!\n");
01586                   ast_free(var);
01587                   break;
01588                }
01589                if (!var) {
01590                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01591                } else {
01592                   ast_variable_append(cat, var);
01593                }
01594                found = 1;
01595                break;
01596             }
01597          }
01598          /* save the results and clean things up */
01599          if (found) {
01600             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01601             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01602             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01603             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01604          }
01605       }
01606    }
01607 }
01608 
01609 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01610 {
01611    char buf[255];
01612    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01613    ast_debug(1, "External password: %s\n",buf);
01614    if (!ast_safe_system(buf)) {
01615       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01616       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01617       /* Reset the password in memory, too */
01618       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01619    }
01620 }
01621 
01622 /*! 
01623  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01624  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01625  * \param len The length of the path string that was written out.
01626  * \param context
01627  * \param ext 
01628  * \param folder 
01629  * 
01630  * The path is constructed as 
01631  *    VM_SPOOL_DIRcontext/ext/folder
01632  *
01633  * \return zero on success, -1 on error.
01634  */
01635 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01636 {
01637    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01638 }
01639 
01640 /*! 
01641  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01642  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01643  * \param len The length of the path string that was written out.
01644  * \param dir 
01645  * \param num 
01646  * 
01647  * The path is constructed as 
01648  *    VM_SPOOL_DIRcontext/ext/folder
01649  *
01650  * \return zero on success, -1 on error.
01651  */
01652 static int make_file(char *dest, const int len, const char *dir, const int num)
01653 {
01654    return snprintf(dest, len, "%s/msg%04d", dir, num);
01655 }
01656 
01657 /* same as mkstemp, but return a FILE * */
01658 static FILE *vm_mkftemp(char *template)
01659 {
01660    FILE *p = NULL;
01661    int pfd = mkstemp(template);
01662    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01663    if (pfd > -1) {
01664       p = fdopen(pfd, "w+");
01665       if (!p) {
01666          close(pfd);
01667          pfd = -1;
01668       }
01669    }
01670    return p;
01671 }
01672 
01673 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01674  * \param dest    String. base directory.
01675  * \param len     Length of dest.
01676  * \param context String. Ignored if is null or empty string.
01677  * \param ext     String. Ignored if is null or empty string.
01678  * \param folder  String. Ignored if is null or empty string. 
01679  * \return -1 on failure, 0 on success.
01680  */
01681 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01682 {
01683    mode_t   mode = VOICEMAIL_DIR_MODE;
01684    int res;
01685 
01686    make_dir(dest, len, context, ext, folder);
01687    if ((res = ast_mkdir(dest, mode))) {
01688       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01689       return -1;
01690    }
01691    return 0;
01692 }
01693 
01694 static const char * const mailbox_folders[] = {
01695 #ifdef IMAP_STORAGE
01696    imapfolder,
01697 #else
01698    "INBOX",
01699 #endif
01700    "Old",
01701    "Work",
01702    "Family",
01703    "Friends",
01704    "Cust1",
01705    "Cust2",
01706    "Cust3",
01707    "Cust4",
01708    "Cust5",
01709    "Deleted",
01710    "Urgent",
01711 };
01712 
01713 static const char *mbox(struct ast_vm_user *vmu, int id)
01714 {
01715 #ifdef IMAP_STORAGE
01716    if (vmu && id == 0) {
01717       return vmu->imapfolder;
01718    }
01719 #endif
01720    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01721 }
01722 
01723 static int get_folder_by_name(const char *name)
01724 {
01725    size_t i;
01726 
01727    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01728       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01729          return i;
01730       }
01731    }
01732 
01733    return -1;
01734 }
01735 
01736 static void free_user(struct ast_vm_user *vmu)
01737 {
01738    if (ast_test_flag(vmu, VM_ALLOCED)) {
01739 
01740       ast_free(vmu->emailbody);
01741       vmu->emailbody = NULL;
01742 
01743       ast_free(vmu->emailsubject);
01744       vmu->emailsubject = NULL;
01745 
01746       ast_free(vmu);
01747    }
01748 }
01749 
01750 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01751 
01752    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01753 
01754    /* remove old allocation */
01755    if (vms->deleted) {
01756       ast_free(vms->deleted);
01757       vms->deleted = NULL;
01758    }
01759    if (vms->heard) {
01760       ast_free(vms->heard);
01761       vms->heard = NULL;
01762    }
01763    vms->dh_arraysize = 0;
01764 
01765    if (arraysize > 0) {
01766       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01767          return -1;
01768       }
01769       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01770          ast_free(vms->deleted);
01771          vms->deleted = NULL;
01772          return -1;
01773       }
01774       vms->dh_arraysize = arraysize;
01775    }
01776 
01777    return 0;
01778 }
01779 
01780 /* All IMAP-specific functions should go in this block. This
01781  * keeps them from being spread out all over the code */
01782 #ifdef IMAP_STORAGE
01783 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01784 {
01785    char arg[10];
01786    struct vm_state *vms;
01787    unsigned long messageNum;
01788 
01789    /* If greetings aren't stored in IMAP, just delete the file */
01790    if (msgnum < 0 && !imapgreetings) {
01791       ast_filedelete(file, NULL);
01792       return;
01793    }
01794 
01795    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01796       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);
01797       return;
01798    }
01799 
01800    /* find real message number based on msgnum */
01801    /* this may be an index into vms->msgArray based on the msgnum. */
01802    messageNum = vms->msgArray[msgnum];
01803    if (messageNum == 0) {
01804       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01805       return;
01806    }
01807    if (option_debug > 2)
01808       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01809    /* delete message */
01810    snprintf (arg, sizeof(arg), "%lu", messageNum);
01811    ast_mutex_lock(&vms->lock);
01812    mail_setflag (vms->mailstream, arg, "\\DELETED");
01813    mail_expunge(vms->mailstream);
01814    ast_mutex_unlock(&vms->lock);
01815 }
01816 
01817 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01818 {
01819    struct vm_state *vms_p;
01820    char *file, *filename;
01821    char *attachment;
01822    int i;
01823    BODY *body;
01824 
01825    /* This function is only used for retrieval of IMAP greetings
01826     * regular messages are not retrieved this way, nor are greetings
01827     * if they are stored locally*/
01828    if (msgnum > -1 || !imapgreetings) {
01829       return 0;
01830    } else {
01831       file = strrchr(ast_strdupa(dir), '/');
01832       if (file)
01833          *file++ = '\0';
01834       else {
01835          ast_debug (1, "Failed to procure file name from directory passed.\n");
01836          return -1;
01837       }
01838    }
01839 
01840    /* check if someone is accessing this box right now... */
01841    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01842       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01843       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01844       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01845       * that's all we need to do.
01846       */
01847       if (!(vms_p = create_vm_state_from_user(vmu))) {
01848          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01849          return -1;
01850       }
01851    }
01852 
01853    /* Greetings will never have a prepended message */
01854    *vms_p->introfn = '\0';
01855 
01856    ast_mutex_lock(&vms_p->lock);
01857    init_mailstream(vms_p, GREETINGS_FOLDER);
01858    if (!vms_p->mailstream) {
01859       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01860       ast_mutex_unlock(&vms_p->lock);
01861       return -1;
01862    }
01863 
01864    /*XXX Yuck, this could probably be done a lot better */
01865    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01866       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01867       /* We have the body, now we extract the file name of the first attachment. */
01868       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01869          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01870       } else {
01871          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01872          ast_mutex_unlock(&vms_p->lock);
01873          return -1;
01874       }
01875       filename = strsep(&attachment, ".");
01876       if (!strcmp(filename, file)) {
01877          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01878          vms_p->msgArray[vms_p->curmsg] = i + 1;
01879          save_body(body, vms_p, "2", attachment, 0);
01880          ast_mutex_unlock(&vms_p->lock);
01881          return 0;
01882       }
01883    }
01884    ast_mutex_unlock(&vms_p->lock);
01885 
01886    return -1;
01887 }
01888 
01889 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01890 {
01891    BODY *body;
01892    char *header_content;
01893    char *attachedfilefmt;
01894    char buf[80];
01895    struct vm_state *vms;
01896    char text_file[PATH_MAX];
01897    FILE *text_file_ptr;
01898    int res = 0;
01899    struct ast_vm_user *vmu;
01900 
01901    if (!(vmu = find_user(NULL, context, mailbox))) {
01902       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01903       return -1;
01904    }
01905    
01906    if (msgnum < 0) {
01907       if (imapgreetings) {
01908          res = imap_retrieve_greeting(dir, msgnum, vmu);
01909          goto exit;
01910       } else {
01911          res = 0;
01912          goto exit;
01913       }
01914    }
01915 
01916    /* Before anything can happen, we need a vm_state so that we can
01917     * actually access the imap server through the vms->mailstream
01918     */
01919    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01920       /* This should not happen. If it does, then I guess we'd
01921        * need to create the vm_state, extract which mailbox to
01922        * open, and then set up the msgArray so that the correct
01923        * IMAP message could be accessed. If I have seen correctly
01924        * though, the vms should be obtainable from the vmstates list
01925        * and should have its msgArray properly set up.
01926        */
01927       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01928       res = -1;
01929       goto exit;
01930    }
01931    
01932    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01933    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01934 
01935    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01936    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01937       res = 0;
01938       goto exit;
01939    }
01940 
01941    if (option_debug > 2)
01942       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01943    if (vms->msgArray[msgnum] == 0) {
01944       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01945       res = -1;
01946       goto exit;
01947    }
01948 
01949    /* This will only work for new messages... */
01950    ast_mutex_lock(&vms->lock);
01951    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01952    ast_mutex_unlock(&vms->lock);
01953    /* empty string means no valid header */
01954    if (ast_strlen_zero(header_content)) {
01955       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01956       res = -1;
01957       goto exit;
01958    }
01959 
01960    ast_mutex_lock(&vms->lock);
01961    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01962    ast_mutex_unlock(&vms->lock);
01963 
01964    /* We have the body, now we extract the file name of the first attachment. */
01965    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01966       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01967    } else {
01968       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01969       res = -1;
01970       goto exit;
01971    }
01972    
01973    /* Find the format of the attached file */
01974 
01975    strsep(&attachedfilefmt, ".");
01976    if (!attachedfilefmt) {
01977       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01978       res = -1;
01979       goto exit;
01980    }
01981    
01982    save_body(body, vms, "2", attachedfilefmt, 0);
01983    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01984       *vms->introfn = '\0';
01985    }
01986 
01987    /* Get info from headers!! */
01988    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01989 
01990    if (!(text_file_ptr = fopen(text_file, "w"))) {
01991       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01992    }
01993 
01994    fprintf(text_file_ptr, "%s\n", "[message]");
01995 
01996    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01997    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01998    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01999    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02000    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
02001    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02002    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02003    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02004    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02005    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02006    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02007    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02008    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02009    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02010    fclose(text_file_ptr);
02011 
02012 exit:
02013    free_user(vmu);
02014    return res;
02015 }
02016 
02017 static int folder_int(const char *folder)
02018 {
02019    /*assume a NULL folder means INBOX*/
02020    if (!folder) {
02021       return 0;
02022    }
02023    if (!strcasecmp(folder, imapfolder)) {
02024       return 0;
02025    } else if (!strcasecmp(folder, "Old")) {
02026       return 1;
02027    } else if (!strcasecmp(folder, "Work")) {
02028       return 2;
02029    } else if (!strcasecmp(folder, "Family")) {
02030       return 3;
02031    } else if (!strcasecmp(folder, "Friends")) {
02032       return 4;
02033    } else if (!strcasecmp(folder, "Cust1")) {
02034       return 5;
02035    } else if (!strcasecmp(folder, "Cust2")) {
02036       return 6;
02037    } else if (!strcasecmp(folder, "Cust3")) {
02038       return 7;
02039    } else if (!strcasecmp(folder, "Cust4")) {
02040       return 8;
02041    } else if (!strcasecmp(folder, "Cust5")) {
02042       return 9;
02043    } else if (!strcasecmp(folder, "Urgent")) {
02044       return 11;
02045    } else { /*assume they meant INBOX if folder is not found otherwise*/
02046       return 0;
02047    }
02048 }
02049 
02050 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02051 {
02052    SEARCHPGM *pgm;
02053    SEARCHHEADER *hdr;
02054 
02055    struct ast_vm_user *vmu, vmus;
02056    struct vm_state *vms_p;
02057    int ret = 0;
02058    int fold = folder_int(folder);
02059    int urgent = 0;
02060    
02061    /* If URGENT, then look at INBOX */
02062    if (fold == 11) {
02063       fold = NEW_FOLDER;
02064       urgent = 1;
02065    }
02066 
02067    if (ast_strlen_zero(mailbox))
02068       return 0;
02069 
02070    /* We have to get the user before we can open the stream! */
02071    vmu = find_user(&vmus, context, mailbox);
02072    if (!vmu) {
02073       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02074       return -1;
02075    } else {
02076       /* No IMAP account available */
02077       if (vmu->imapuser[0] == '\0') {
02078          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02079          return -1;
02080       }
02081    }
02082    
02083    /* No IMAP account available */
02084    if (vmu->imapuser[0] == '\0') {
02085       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02086       free_user(vmu);
02087       return -1;
02088    }
02089 
02090    /* check if someone is accessing this box right now... */
02091    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02092    if (!vms_p) {
02093       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02094    }
02095    if (vms_p) {
02096       ast_debug(3, "Returning before search - user is logged in\n");
02097       if (fold == 0) { /* INBOX */
02098          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02099       }
02100       if (fold == 1) { /* Old messages */
02101          return vms_p->oldmessages;
02102       }
02103    }
02104 
02105    /* add one if not there... */
02106    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02107    if (!vms_p) {
02108       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02109    }
02110 
02111    if (!vms_p) {
02112       vms_p = create_vm_state_from_user(vmu);
02113    }
02114    ret = init_mailstream(vms_p, fold);
02115    if (!vms_p->mailstream) {
02116       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02117       return -1;
02118    }
02119    if (ret == 0) {
02120       ast_mutex_lock(&vms_p->lock);
02121       pgm = mail_newsearchpgm ();
02122       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02123       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02124       pgm->header = hdr;
02125       if (fold != OLD_FOLDER) {
02126          pgm->unseen = 1;
02127          pgm->seen = 0;
02128       }
02129       /* In the special case where fold is 1 (old messages) we have to do things a bit
02130        * differently. Old messages are stored in the INBOX but are marked as "seen"
02131        */
02132       else {
02133          pgm->unseen = 0;
02134          pgm->seen = 1;
02135       }
02136       /* look for urgent messages */
02137       if (fold == NEW_FOLDER) {
02138          if (urgent) {
02139             pgm->flagged = 1;
02140             pgm->unflagged = 0;
02141          } else {
02142             pgm->flagged = 0;
02143             pgm->unflagged = 1;
02144          }
02145       }
02146       pgm->undeleted = 1;
02147       pgm->deleted = 0;
02148 
02149       vms_p->vmArrayIndex = 0;
02150       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02151       if (fold == 0 && urgent == 0)
02152          vms_p->newmessages = vms_p->vmArrayIndex;
02153       if (fold == 1)
02154          vms_p->oldmessages = vms_p->vmArrayIndex;
02155       if (fold == 0 && urgent == 1)
02156          vms_p->urgentmessages = vms_p->vmArrayIndex;
02157       /*Freeing the searchpgm also frees the searchhdr*/
02158       mail_free_searchpgm(&pgm);
02159       ast_mutex_unlock(&vms_p->lock);
02160       vms_p->updated = 0;
02161       return vms_p->vmArrayIndex;
02162    } else {
02163       ast_mutex_lock(&vms_p->lock);
02164       mail_ping(vms_p->mailstream);
02165       ast_mutex_unlock(&vms_p->lock);
02166    }
02167    return 0;
02168 }
02169 
02170 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02171 {
02172    /* Check if mailbox is full */
02173    check_quota(vms, vmu->imapfolder);
02174    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02175       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02176       ast_play_and_wait(chan, "vm-mailboxfull");
02177       return -1;
02178    }
02179    
02180    /* Check if we have exceeded maxmsg */
02181    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));
02182    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02183       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02184       ast_play_and_wait(chan, "vm-mailboxfull");
02185       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02186       return -1;
02187    }
02188 
02189    return 0;
02190 }
02191 
02192 /*!
02193  * \brief Gets the number of messages that exist in a mailbox folder.
02194  * \param context
02195  * \param mailbox
02196  * \param folder
02197  * 
02198  * This method is used when IMAP backend is used.
02199  * \return The number of messages in this mailbox folder (zero or more).
02200  */
02201 static int messagecount(const char *context, const char *mailbox, const char *folder)
02202 {
02203    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02204       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02205    } else {
02206       return __messagecount(context, mailbox, folder);
02207    }
02208 }
02209 
02210 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)
02211 {
02212    char *myserveremail = serveremail;
02213    char fn[PATH_MAX];
02214    char introfn[PATH_MAX];
02215    char mailbox[256];
02216    char *stringp;
02217    FILE *p = NULL;
02218    char tmp[80] = "/tmp/astmail-XXXXXX";
02219    long len;
02220    void *buf;
02221    int tempcopy = 0;
02222    STRING str;
02223    int ret; /* for better error checking */
02224    char *imap_flags = NIL;
02225    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02226    int box = NEW_FOLDER;
02227 
02228    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02229    if (msgnum < 0) {
02230       if(!imapgreetings) {
02231          return 0;
02232       } else {
02233          box = GREETINGS_FOLDER;
02234       }
02235    }
02236    
02237    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02238       return -1;
02239    }
02240 
02241    /* Set urgent flag for IMAP message */
02242    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02243       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02244       imap_flags = "\\FLAGGED";
02245    }
02246    
02247    /* Attach only the first format */
02248    fmt = ast_strdupa(fmt);
02249    stringp = fmt;
02250    strsep(&stringp, "|");
02251 
02252    if (!ast_strlen_zero(vmu->serveremail))
02253       myserveremail = vmu->serveremail;
02254 
02255    if (msgnum > -1)
02256       make_file(fn, sizeof(fn), dir, msgnum);
02257    else
02258       ast_copy_string (fn, dir, sizeof(fn));
02259 
02260    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02261    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02262       *introfn = '\0';
02263    }
02264    
02265    if (ast_strlen_zero(vmu->email)) {
02266       /* We need the vmu->email to be set when we call make_email_file, but
02267        * if we keep it set, a duplicate e-mail will be created. So at the end
02268        * of this function, we will revert back to an empty string if tempcopy
02269        * is 1.
02270        */
02271       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02272       tempcopy = 1;
02273    }
02274 
02275    if (!strcmp(fmt, "wav49"))
02276       fmt = "WAV";
02277    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02278 
02279    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02280       command hangs. */
02281    if (!(p = vm_mkftemp(tmp))) {
02282       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02283       if (tempcopy)
02284          *(vmu->email) = '\0';
02285       return -1;
02286    }
02287 
02288    if (msgnum < 0 && imapgreetings) {
02289       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02290          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02291          return -1;
02292       }
02293       imap_delete_old_greeting(fn, vms);
02294    }
02295 
02296    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02297       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02298       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02299       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02300    /* read mail file to memory */
02301    len = ftell(p);
02302    rewind(p);
02303    if (!(buf = ast_malloc(len + 1))) {
02304       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02305       fclose(p);
02306       if (tempcopy)
02307          *(vmu->email) = '\0';
02308       return -1;
02309    }
02310    if (fread(buf, len, 1, p) < len) {
02311       if (ferror(p)) {
02312          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02313          return -1;
02314       }
02315    }
02316    ((char *) buf)[len] = '\0';
02317    INIT(&str, mail_string, buf, len);
02318    ret = init_mailstream(vms, box);
02319    if (ret == 0) {
02320       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02321       ast_mutex_lock(&vms->lock);
02322       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02323          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02324       ast_mutex_unlock(&vms->lock);
02325       fclose(p);
02326       unlink(tmp);
02327       ast_free(buf);
02328    } else {
02329       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02330       fclose(p);
02331       unlink(tmp);
02332       ast_free(buf);
02333       return -1;
02334    }
02335    ast_debug(3, "%s stored\n", fn);
02336    
02337    if (tempcopy)
02338       *(vmu->email) = '\0';
02339    inprocess_count(vmu->mailbox, vmu->context, -1);
02340    return 0;
02341 
02342 }
02343 
02344 /*!
02345  * \brief Gets the number of messages that exist in the inbox folder.
02346  * \param mailbox_context
02347  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02348  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02349  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02350  * 
02351  * This method is used when IMAP backend is used.
02352  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02353  *
02354  * \return zero on success, -1 on error.
02355  */
02356 
02357 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02358 {
02359    char tmp[PATH_MAX] = "";
02360    char *mailboxnc;
02361    char *context;
02362    char *mb;
02363    char *cur;
02364    if (newmsgs)
02365       *newmsgs = 0;
02366    if (oldmsgs)
02367       *oldmsgs = 0;
02368    if (urgentmsgs)
02369       *urgentmsgs = 0;
02370 
02371    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02372    /* If no mailbox, return immediately */
02373    if (ast_strlen_zero(mailbox_context))
02374       return 0;
02375    
02376    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02377    context = strchr(tmp, '@');
02378    if (strchr(mailbox_context, ',')) {
02379       int tmpnew, tmpold, tmpurgent;
02380       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02381       mb = tmp;
02382       while ((cur = strsep(&mb, ", "))) {
02383          if (!ast_strlen_zero(cur)) {
02384             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02385                return -1;
02386             else {
02387                if (newmsgs)
02388                   *newmsgs += tmpnew; 
02389                if (oldmsgs)
02390                   *oldmsgs += tmpold;
02391                if (urgentmsgs)
02392                   *urgentmsgs += tmpurgent;
02393             }
02394          }
02395       }
02396       return 0;
02397    }
02398    if (context) {
02399       *context = '\0';
02400       mailboxnc = tmp;
02401       context++;
02402    } else {
02403       context = "default";
02404       mailboxnc = (char *) mailbox_context;
02405    }
02406 
02407    if (newmsgs) {
02408       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02409       if (!vmu) {
02410          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02411          return -1;
02412       }
02413       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02414          return -1;
02415       }
02416    }
02417    if (oldmsgs) {
02418       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02419          return -1;
02420       }
02421    }
02422    if (urgentmsgs) {
02423       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02424          return -1;
02425       }
02426    }
02427    return 0;
02428 }
02429 
02430 /** 
02431  * \brief Determines if the given folder has messages.
02432  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02433  * \param folder the folder to look in
02434  *
02435  * This function is used when the mailbox is stored in an IMAP back end.
02436  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02437  * \return 1 if the folder has one or more messages. zero otherwise.
02438  */
02439 
02440 static int has_voicemail(const char *mailbox, const char *folder)
02441 {
02442    char tmp[256], *tmp2, *box, *context;
02443    ast_copy_string(tmp, mailbox, sizeof(tmp));
02444    tmp2 = tmp;
02445    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02446       while ((box = strsep(&tmp2, ",&"))) {
02447          if (!ast_strlen_zero(box)) {
02448             if (has_voicemail(box, folder)) {
02449                return 1;
02450             }
02451          }
02452       }
02453    }
02454    if ((context = strchr(tmp, '@'))) {
02455       *context++ = '\0';
02456    } else {
02457       context = "default";
02458    }
02459    return __messagecount(context, tmp, folder) ? 1 : 0;
02460 }
02461 
02462 /*!
02463  * \brief Copies a message from one mailbox to another.
02464  * \param chan
02465  * \param vmu
02466  * \param imbox
02467  * \param msgnum
02468  * \param duration
02469  * \param recip
02470  * \param fmt
02471  * \param dir
02472  *
02473  * This works with IMAP storage based mailboxes.
02474  *
02475  * \return zero on success, -1 on error.
02476  */
02477 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)
02478 {
02479    struct vm_state *sendvms = NULL, *destvms = NULL;
02480    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02481    if (msgnum >= recip->maxmsg) {
02482       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02483       return -1;
02484    }
02485    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02486       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02487       return -1;
02488    }
02489    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02490       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02491       return -1;
02492    }
02493    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02494    ast_mutex_lock(&sendvms->lock);
02495    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02496       ast_mutex_unlock(&sendvms->lock);
02497       return 0;
02498    }
02499    ast_mutex_unlock(&sendvms->lock);
02500    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02501    return -1;
02502 }
02503 
02504 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02505 {
02506    char tmp[256], *t = tmp;
02507    size_t left = sizeof(tmp);
02508    
02509    if (box == OLD_FOLDER) {
02510       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02511    } else {
02512       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02513    }
02514 
02515    if (box == NEW_FOLDER) {
02516       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02517    } else {
02518       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02519    }
02520 
02521    /* Build up server information */
02522    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02523 
02524    /* Add authentication user if present */
02525    if (!ast_strlen_zero(authuser))
02526       ast_build_string(&t, &left, "/authuser=%s", authuser);
02527 
02528    /* Add flags if present */
02529    if (!ast_strlen_zero(imapflags))
02530       ast_build_string(&t, &left, "/%s", imapflags);
02531 
02532    /* End with username */
02533 #if 1
02534    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02535 #else
02536    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02537 #endif
02538    if (box == NEW_FOLDER || box == OLD_FOLDER)
02539       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02540    else if (box == GREETINGS_FOLDER)
02541       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02542    else {   /* Other folders such as Friends, Family, etc... */
02543       if (!ast_strlen_zero(imapparentfolder)) {
02544          /* imapparentfolder would typically be set to INBOX */
02545          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02546       } else {
02547          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02548       }
02549    }
02550 }
02551 
02552 static int init_mailstream(struct vm_state *vms, int box)
02553 {
02554    MAILSTREAM *stream = NIL;
02555    long debug;
02556    char tmp[256];
02557    
02558    if (!vms) {
02559       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02560       return -1;
02561    }
02562    if (option_debug > 2)
02563       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02564    if (vms->mailstream == NIL || !vms->mailstream) {
02565       if (option_debug)
02566          ast_log(LOG_DEBUG, "mailstream not set.\n");
02567    } else {
02568       stream = vms->mailstream;
02569    }
02570    /* debug = T;  user wants protocol telemetry? */
02571    debug = NIL;  /* NO protocol telemetry? */
02572 
02573    if (delimiter == '\0') {      /* did not probe the server yet */
02574       char *cp;
02575 #ifdef USE_SYSTEM_IMAP
02576 #include <imap/linkage.c>
02577 #elif defined(USE_SYSTEM_CCLIENT)
02578 #include <c-client/linkage.c>
02579 #else
02580 #include "linkage.c"
02581 #endif
02582       /* Connect to INBOX first to get folders delimiter */
02583       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02584       ast_mutex_lock(&vms->lock);
02585       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02586       ast_mutex_unlock(&vms->lock);
02587       if (stream == NIL) {
02588          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02589          return -1;
02590       }
02591       get_mailbox_delimiter(stream);
02592       /* update delimiter in imapfolder */
02593       for (cp = vms->imapfolder; *cp; cp++)
02594          if (*cp == '/')
02595             *cp = delimiter;
02596    }
02597    /* Now connect to the target folder */
02598    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02599    if (option_debug > 2)
02600       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02601    ast_mutex_lock(&vms->lock);
02602    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02603    ast_mutex_unlock(&vms->lock);
02604    if (vms->mailstream == NIL) {
02605       return -1;
02606    } else {
02607       return 0;
02608    }
02609 }
02610 
02611 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02612 {
02613    SEARCHPGM *pgm;
02614    SEARCHHEADER *hdr;
02615    int ret, urgent = 0;
02616 
02617    /* If Urgent, then look at INBOX */
02618    if (box == 11) {
02619       box = NEW_FOLDER;
02620       urgent = 1;
02621    }
02622 
02623    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02624    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02625    vms->imapversion = vmu->imapversion;
02626    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02627 
02628    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02629       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02630       return -1;
02631    }
02632    
02633    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02634    
02635    /* Check Quota */
02636    if  (box == 0)  {
02637       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02638       check_quota(vms, (char *) mbox(vmu, box));
02639    }
02640 
02641    ast_mutex_lock(&vms->lock);
02642    pgm = mail_newsearchpgm();
02643 
02644    /* Check IMAP folder for Asterisk messages only... */
02645    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02646    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02647    pgm->header = hdr;
02648    pgm->deleted = 0;
02649    pgm->undeleted = 1;
02650 
02651    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02652    if (box == NEW_FOLDER && urgent == 1) {
02653       pgm->unseen = 1;
02654       pgm->seen = 0;
02655       pgm->flagged = 1;
02656       pgm->unflagged = 0;
02657    } else if (box == NEW_FOLDER && urgent == 0) {
02658       pgm->unseen = 1;
02659       pgm->seen = 0;
02660       pgm->flagged = 0;
02661       pgm->unflagged = 1;
02662    } else if (box == OLD_FOLDER) {
02663       pgm->seen = 1;
02664       pgm->unseen = 0;
02665    }
02666 
02667    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02668 
02669    vms->vmArrayIndex = 0;
02670    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02671    vms->lastmsg = vms->vmArrayIndex - 1;
02672    mail_free_searchpgm(&pgm);
02673    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02674     * ensure to allocate enough space to account for all of them. Warn if old messages
02675     * have not been checked first as that is required.
02676     */
02677    if (box == 0 && !vms->dh_arraysize) {
02678       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02679    }
02680    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02681       ast_mutex_unlock(&vms->lock);
02682       return -1;
02683    }
02684 
02685    ast_mutex_unlock(&vms->lock);
02686    return 0;
02687 }
02688 
02689 static void write_file(char *filename, char *buffer, unsigned long len)
02690 {
02691    FILE *output;
02692 
02693    output = fopen (filename, "w");
02694    if (fwrite(buffer, len, 1, output) != 1) {
02695       if (ferror(output)) {
02696          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02697       }
02698    }
02699    fclose (output);
02700 }
02701 
02702 static void update_messages_by_imapuser(const char *user, unsigned long number)
02703 {
02704    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02705 
02706    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02707       return;
02708    }
02709 
02710    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02711    vms->msgArray[vms->vmArrayIndex++] = number;
02712 }
02713 
02714 void mm_searched(MAILSTREAM *stream, unsigned long number)
02715 {
02716    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02717 
02718    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02719       return;
02720 
02721    update_messages_by_imapuser(user, number);
02722 }
02723 
02724 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02725 {
02726    struct ast_variable *var;
02727    struct ast_vm_user *vmu;
02728 
02729    vmu = ast_calloc(1, sizeof *vmu);
02730    if (!vmu)
02731       return NULL;
02732    ast_set_flag(vmu, VM_ALLOCED);
02733    populate_defaults(vmu);
02734 
02735    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02736    if (var) {
02737       apply_options_full(vmu, var);
02738       ast_variables_destroy(var);
02739       return vmu;
02740    } else {
02741       ast_free(vmu);
02742       return NULL;
02743    }
02744 }
02745 
02746 /* Interfaces to C-client */
02747 
02748 void mm_exists(MAILSTREAM * stream, unsigned long number)
02749 {
02750    /* mail_ping will callback here if new mail! */
02751    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02752    if (number == 0) return;
02753    set_update(stream);
02754 }
02755 
02756 
02757 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02758 {
02759    /* mail_ping will callback here if expunged mail! */
02760    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02761    if (number == 0) return;
02762    set_update(stream);
02763 }
02764 
02765 
02766 void mm_flags(MAILSTREAM * stream, unsigned long number)
02767 {
02768    /* mail_ping will callback here if read mail! */
02769    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02770    if (number == 0) return;
02771    set_update(stream);
02772 }
02773 
02774 
02775 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02776 {
02777    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02778    mm_log (string, errflg);
02779 }
02780 
02781 
02782 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02783 {
02784    if (delimiter == '\0') {
02785       delimiter = delim;
02786    }
02787 
02788    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02789    if (attributes & LATT_NOINFERIORS)
02790       ast_debug(5, "no inferiors\n");
02791    if (attributes & LATT_NOSELECT)
02792       ast_debug(5, "no select\n");
02793    if (attributes & LATT_MARKED)
02794       ast_debug(5, "marked\n");
02795    if (attributes & LATT_UNMARKED)
02796       ast_debug(5, "unmarked\n");
02797 }
02798 
02799 
02800 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02801 {
02802    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02803    if (attributes & LATT_NOINFERIORS)
02804       ast_debug(5, "no inferiors\n");
02805    if (attributes & LATT_NOSELECT)
02806       ast_debug(5, "no select\n");
02807    if (attributes & LATT_MARKED)
02808       ast_debug(5, "marked\n");
02809    if (attributes & LATT_UNMARKED)
02810       ast_debug(5, "unmarked\n");
02811 }
02812 
02813 
02814 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02815 {
02816    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02817    if (status->flags & SA_MESSAGES)
02818       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02819    if (status->flags & SA_RECENT)
02820       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02821    if (status->flags & SA_UNSEEN)
02822       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02823    if (status->flags & SA_UIDVALIDITY)
02824       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02825    if (status->flags & SA_UIDNEXT)
02826       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02827    ast_log(AST_LOG_NOTICE, "\n");
02828 }
02829 
02830 
02831 void mm_log(char *string, long errflg)
02832 {
02833    switch ((short) errflg) {
02834       case NIL:
02835          ast_debug(1, "IMAP Info: %s\n", string);
02836          break;
02837       case PARSE:
02838       case WARN:
02839          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02840          break;
02841       case ERROR:
02842          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02843          break;
02844    }
02845 }
02846 
02847 
02848 void mm_dlog(char *string)
02849 {
02850    ast_log(AST_LOG_NOTICE, "%s\n", string);
02851 }
02852 
02853 
02854 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02855 {
02856    struct ast_vm_user *vmu;
02857 
02858    ast_debug(4, "Entering callback mm_login\n");
02859 
02860    ast_copy_string(user, mb->user, MAILTMPLEN);
02861 
02862    /* We should only do this when necessary */
02863    if (!ast_strlen_zero(authpassword)) {
02864       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02865    } else {
02866       AST_LIST_TRAVERSE(&users, vmu, list) {
02867          if (!strcasecmp(mb->user, vmu->imapuser)) {
02868             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02869             break;
02870          }
02871       }
02872       if (!vmu) {
02873          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02874             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02875             free_user(vmu);
02876          }
02877       }
02878    }
02879 }
02880 
02881 
02882 void mm_critical(MAILSTREAM * stream)
02883 {
02884 }
02885 
02886 
02887 void mm_nocritical(MAILSTREAM * stream)
02888 {
02889 }
02890 
02891 
02892 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02893 {
02894    kill (getpid (), SIGSTOP);
02895    return NIL;
02896 }
02897 
02898 
02899 void mm_fatal(char *string)
02900 {
02901    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02902 }
02903 
02904 /* C-client callback to handle quota */
02905 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02906 {
02907    struct vm_state *vms;
02908    char *mailbox = stream->mailbox, *user;
02909    char buf[1024] = "";
02910    unsigned long usage = 0, limit = 0;
02911    
02912    while (pquota) {
02913       usage = pquota->usage;
02914       limit = pquota->limit;
02915       pquota = pquota->next;
02916    }
02917    
02918    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)))) {
02919       ast_log(AST_LOG_ERROR, "No state found.\n");
02920       return;
02921    }
02922 
02923    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02924 
02925    vms->quota_usage = usage;
02926    vms->quota_limit = limit;
02927 }
02928 
02929 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02930 {
02931    char *start, *eol_pnt;
02932    int taglen;
02933 
02934    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02935       return NULL;
02936 
02937    taglen = strlen(tag) + 1;
02938    if (taglen < 1)
02939       return NULL;
02940 
02941    if (!(start = strstr(header, tag)))
02942       return NULL;
02943 
02944    /* Since we can be called multiple times we should clear our buffer */
02945    memset(buf, 0, len);
02946 
02947    ast_copy_string(buf, start+taglen, len);
02948    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02949       *eol_pnt = '\0';
02950    return buf;
02951 }
02952 
02953 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02954 {
02955    char *start, *quote, *eol_pnt;
02956 
02957    if (ast_strlen_zero(mailbox))
02958       return NULL;
02959 
02960    if (!(start = strstr(mailbox, "/user=")))
02961       return NULL;
02962 
02963    ast_copy_string(buf, start+6, len);
02964 
02965    if (!(quote = strchr(buf, '\"'))) {
02966       if (!(eol_pnt = strchr(buf, '/')))
02967          eol_pnt = strchr(buf,'}');
02968       *eol_pnt = '\0';
02969       return buf;
02970    } else {
02971       eol_pnt = strchr(buf+1,'\"');
02972       *eol_pnt = '\0';
02973       return buf+1;
02974    }
02975 }
02976 
02977 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02978 {
02979    struct vm_state *vms_p;
02980 
02981    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02982    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02983       return vms_p;
02984    }
02985    if (option_debug > 4)
02986       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02987    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02988       return NULL;
02989    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02990    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02991    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02992    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02993    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02994    vms_p->imapversion = vmu->imapversion;
02995    if (option_debug > 4)
02996       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
02997    vms_p->updated = 1;
02998    /* set mailbox to INBOX! */
02999    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03000    init_vm_state(vms_p);
03001    vmstate_insert(vms_p);
03002    return vms_p;
03003 }
03004 
03005 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03006 {
03007    struct vmstate *vlist = NULL;
03008 
03009    if (interactive) {
03010       struct vm_state *vms;
03011       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03012       vms = pthread_getspecific(ts_vmstate.key);
03013       return vms;
03014    }
03015 
03016    AST_LIST_LOCK(&vmstates);
03017    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03018       if (!vlist->vms) {
03019          ast_debug(3, "error: vms is NULL for %s\n", user);
03020          continue;
03021       }
03022       if (vlist->vms->imapversion != imapversion) {
03023          continue;
03024       }
03025       if (!vlist->vms->imapuser) {
03026          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03027          continue;
03028       }
03029 
03030       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03031          AST_LIST_UNLOCK(&vmstates);
03032          return vlist->vms;
03033       }
03034    }
03035    AST_LIST_UNLOCK(&vmstates);
03036 
03037    ast_debug(3, "%s not found in vmstates\n", user);
03038 
03039    return NULL;
03040 }
03041 
03042 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03043 {
03044 
03045    struct vmstate *vlist = NULL;
03046    const char *local_context = S_OR(context, "default");
03047 
03048    if (interactive) {
03049       struct vm_state *vms;
03050       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03051       vms = pthread_getspecific(ts_vmstate.key);
03052       return vms;
03053    }
03054 
03055    AST_LIST_LOCK(&vmstates);
03056    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03057       if (!vlist->vms) {
03058          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03059          continue;
03060       }
03061       if (vlist->vms->imapversion != imapversion) {
03062          continue;
03063       }
03064       if (!vlist->vms->username || !vlist->vms->context) {
03065          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03066          continue;
03067       }
03068 
03069       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);
03070       
03071       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03072          ast_debug(3, "Found it!\n");
03073          AST_LIST_UNLOCK(&vmstates);
03074          return vlist->vms;
03075       }
03076    }
03077    AST_LIST_UNLOCK(&vmstates);
03078 
03079    ast_debug(3, "%s not found in vmstates\n", mailbox);
03080 
03081    return NULL;
03082 }
03083 
03084 static void vmstate_insert(struct vm_state *vms) 
03085 {
03086    struct vmstate *v;
03087    struct vm_state *altvms;
03088 
03089    /* If interactive, it probably already exists, and we should
03090       use the one we already have since it is more up to date.
03091       We can compare the username to find the duplicate */
03092    if (vms->interactive == 1) {
03093       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03094       if (altvms) {  
03095          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03096          vms->newmessages = altvms->newmessages;
03097          vms->oldmessages = altvms->oldmessages;
03098          vms->vmArrayIndex = altvms->vmArrayIndex;
03099          vms->lastmsg = altvms->lastmsg;
03100          vms->curmsg = altvms->curmsg;
03101          /* get a pointer to the persistent store */
03102          vms->persist_vms = altvms;
03103          /* Reuse the mailstream? */
03104 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03105          vms->mailstream = altvms->mailstream;
03106 #else
03107          vms->mailstream = NIL;
03108 #endif
03109       }
03110       return;
03111    }
03112 
03113    if (!(v = ast_calloc(1, sizeof(*v))))
03114       return;
03115    
03116    v->vms = vms;
03117 
03118    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03119 
03120    AST_LIST_LOCK(&vmstates);
03121    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03122    AST_LIST_UNLOCK(&vmstates);
03123 }
03124 
03125 static void vmstate_delete(struct vm_state *vms) 
03126 {
03127    struct vmstate *vc = NULL;
03128    struct vm_state *altvms = NULL;
03129 
03130    /* If interactive, we should copy pertinent info
03131       back to the persistent state (to make update immediate) */
03132    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03133       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03134       altvms->newmessages = vms->newmessages;
03135       altvms->oldmessages = vms->oldmessages;
03136       altvms->updated = 1;
03137       vms->mailstream = mail_close(vms->mailstream);
03138 
03139       /* Interactive states are not stored within the persistent list */
03140       return;
03141    }
03142    
03143    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03144    
03145    AST_LIST_LOCK(&vmstates);
03146    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03147       if (vc->vms == vms) {
03148          AST_LIST_REMOVE_CURRENT(list);
03149          break;
03150       }
03151    }
03152    AST_LIST_TRAVERSE_SAFE_END
03153    AST_LIST_UNLOCK(&vmstates);
03154    
03155    if (vc) {
03156       ast_mutex_destroy(&vc->vms->lock);
03157       ast_free(vc);
03158    }
03159    else
03160       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03161 }
03162 
03163 static void set_update(MAILSTREAM * stream) 
03164 {
03165    struct vm_state *vms;
03166    char *mailbox = stream->mailbox, *user;
03167    char buf[1024] = "";
03168 
03169    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03170       if (user && option_debug > 2)
03171          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03172       return;
03173    }
03174 
03175    ast_debug(3, "User %s mailbox set for update.\n", user);
03176 
03177    vms->updated = 1; /* Set updated flag since mailbox changed */
03178 }
03179 
03180 static void init_vm_state(struct vm_state *vms) 
03181 {
03182    int x;
03183    vms->vmArrayIndex = 0;
03184    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03185       vms->msgArray[x] = 0;
03186    }
03187    ast_mutex_init(&vms->lock);
03188 }
03189 
03190 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03191 {
03192    char *body_content;
03193    char *body_decoded;
03194    char *fn = is_intro ? vms->introfn : vms->fn;
03195    unsigned long len;
03196    unsigned long newlen;
03197    char filename[256];
03198    
03199    if (!body || body == NIL)
03200       return -1;
03201 
03202    ast_mutex_lock(&vms->lock);
03203    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03204    ast_mutex_unlock(&vms->lock);
03205    if (body_content != NIL) {
03206       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03207       /* ast_debug(1,body_content); */
03208       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03209       /* If the body of the file is empty, return an error */
03210       if (!newlen) {
03211          return -1;
03212       }
03213       write_file(filename, (char *) body_decoded, newlen);
03214    } else {
03215       ast_debug(5, "Body of message is NULL.\n");
03216       return -1;
03217    }
03218    return 0;
03219 }
03220 
03221 /*! 
03222  * \brief Get delimiter via mm_list callback 
03223  * \param stream
03224  *
03225  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03226  */
03227 /* MUTEX should already be held */
03228 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03229    char tmp[50];
03230    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03231    mail_list(stream, tmp, "*");
03232 }
03233 
03234 /*! 
03235  * \brief Check Quota for user 
03236  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03237  * \param mailbox the mailbox to check the quota for.
03238  *
03239  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03240  */
03241 static void check_quota(struct vm_state *vms, char *mailbox) {
03242    ast_mutex_lock(&vms->lock);
03243    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03244    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03245    if (vms && vms->mailstream != NULL) {
03246       imap_getquotaroot(vms->mailstream, mailbox);
03247    } else {
03248       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03249    }
03250    ast_mutex_unlock(&vms->lock);
03251 }
03252 
03253 #endif /* IMAP_STORAGE */
03254 
03255 /*! \brief Lock file path
03256  * only return failure if ast_lock_path returns 'timeout',
03257  * not if the path does not exist or any other reason
03258  */
03259 static int vm_lock_path(const char *path)
03260 {
03261    switch (ast_lock_path(path)) {
03262    case AST_LOCK_TIMEOUT:
03263       return -1;
03264    default:
03265       return 0;
03266    }
03267 }
03268 
03269 
03270 #ifdef ODBC_STORAGE
03271 struct generic_prepare_struct {
03272    char *sql;
03273    int argc;
03274    char **argv;
03275 };
03276 
03277 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03278 {
03279    struct generic_prepare_struct *gps = data;
03280    int res, i;
03281    SQLHSTMT stmt;
03282 
03283    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03284    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03285       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03286       return NULL;
03287    }
03288    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03289    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03290       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03291       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03292       return NULL;
03293    }
03294    for (i = 0; i < gps->argc; i++)
03295       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03296 
03297    return stmt;
03298 }
03299 
03300 /*!
03301  * \brief Retrieves a file from an ODBC data store.
03302  * \param dir the path to the file to be retreived.
03303  * \param msgnum the message number, such as within a mailbox folder.
03304  * 
03305  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03306  * 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.
03307  *
03308  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03309  * The output is the message information file with the name msgnum and the extension .txt
03310  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03311  * 
03312  * \return 0 on success, -1 on error.
03313  */
03314 static int retrieve_file(char *dir, int msgnum)
03315 {
03316    int x = 0;
03317    int res;
03318    int fd = -1;
03319    size_t fdlen = 0;
03320    void *fdm = MAP_FAILED;
03321    SQLSMALLINT colcount = 0;
03322    SQLHSTMT stmt;
03323    char sql[PATH_MAX];
03324    char fmt[80]="";
03325    char *c;
03326    char coltitle[256];
03327    SQLSMALLINT collen;
03328    SQLSMALLINT datatype;
03329    SQLSMALLINT decimaldigits;
03330    SQLSMALLINT nullable;
03331    SQLULEN colsize;
03332    SQLLEN colsize2;
03333    FILE *f = NULL;
03334    char rowdata[80];
03335    char fn[PATH_MAX];
03336    char full_fn[PATH_MAX];
03337    char msgnums[80];
03338    char *argv[] = { dir, msgnums };
03339    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03340 
03341    struct odbc_obj *obj;
03342    obj = ast_odbc_request_obj(odbc_database, 0);
03343    if (obj) {
03344       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03345       c = strchr(fmt, '|');
03346       if (c)
03347          *c = '\0';
03348       if (!strcasecmp(fmt, "wav49"))
03349          strcpy(fmt, "WAV");
03350       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03351       if (msgnum > -1)
03352          make_file(fn, sizeof(fn), dir, msgnum);
03353       else
03354          ast_copy_string(fn, dir, sizeof(fn));
03355 
03356       /* Create the information file */
03357       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03358       
03359       if (!(f = fopen(full_fn, "w+"))) {
03360          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03361          goto yuck;
03362       }
03363       
03364       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03365       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03366       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03367       if (!stmt) {
03368          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03369          ast_odbc_release_obj(obj);
03370          goto yuck;
03371       }
03372       res = SQLFetch(stmt);
03373       if (res == SQL_NO_DATA) {
03374          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03375          ast_odbc_release_obj(obj);
03376          goto yuck;
03377       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03378          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03379          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03380          ast_odbc_release_obj(obj);
03381          goto yuck;
03382       }
03383       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03384       if (fd < 0) {
03385          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03386          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03387          ast_odbc_release_obj(obj);
03388          goto yuck;
03389       }
03390       res = SQLNumResultCols(stmt, &colcount);
03391       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03392          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03393          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03394          ast_odbc_release_obj(obj);
03395          goto yuck;
03396       }
03397       if (f) 
03398          fprintf(f, "[message]\n");
03399       for (x = 0; x < colcount; x++) {
03400          rowdata[0] = '\0';
03401          colsize = 0;
03402          collen = sizeof(coltitle);
03403          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03404                   &datatype, &colsize, &decimaldigits, &nullable);
03405          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03406             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03407             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03408             ast_odbc_release_obj(obj);
03409             goto yuck;
03410          }
03411          if (!strcasecmp(coltitle, "recording")) {
03412             off_t offset;
03413             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03414             fdlen = colsize2;
03415             if (fd > -1) {
03416                char tmp[1]="";
03417                lseek(fd, fdlen - 1, SEEK_SET);
03418                if (write(fd, tmp, 1) != 1) {
03419                   close(fd);
03420                   fd = -1;
03421                   continue;
03422                }
03423                /* Read out in small chunks */
03424                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03425                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03426                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03427                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03428                      ast_odbc_release_obj(obj);
03429                      goto yuck;
03430                   } else {
03431                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03432                      munmap(fdm, CHUNKSIZE);
03433                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03434                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03435                         unlink(full_fn);
03436                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03437                         ast_odbc_release_obj(obj);
03438                         goto yuck;
03439                      }
03440                   }
03441                }
03442                if (truncate(full_fn, fdlen) < 0) {
03443                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03444                }
03445             }
03446          } else {
03447             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03448             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03449                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03450                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03451                ast_odbc_release_obj(obj);
03452                goto yuck;
03453             }
03454             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03455                fprintf(f, "%s=%s\n", coltitle, rowdata);
03456          }
03457       }
03458       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03459       ast_odbc_release_obj(obj);
03460    } else
03461       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03462 yuck:
03463    if (f)
03464       fclose(f);
03465    if (fd > -1)
03466       close(fd);
03467    return x - 1;
03468 }
03469 
03470 /*!
03471  * \brief Determines the highest message number in use for a given user and mailbox folder.
03472  * \param vmu 
03473  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03474  *
03475  * This method is used when mailboxes are stored in an ODBC back end.
03476  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03477  *
03478  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03479 
03480  */
03481 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03482 {
03483    int x = 0;
03484    int res;
03485    SQLHSTMT stmt;
03486    char sql[PATH_MAX];
03487    char rowdata[20];
03488    char *argv[] = { dir };
03489    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03490 
03491    struct odbc_obj *obj;
03492    obj = ast_odbc_request_obj(odbc_database, 0);
03493    if (obj) {
03494       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03495 
03496       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03497       if (!stmt) {
03498          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03499          ast_odbc_release_obj(obj);
03500          goto yuck;
03501       }
03502       res = SQLFetch(stmt);
03503       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03504          if (res == SQL_NO_DATA) {
03505             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03506          } else {
03507             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03508          }
03509 
03510          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03511          ast_odbc_release_obj(obj);
03512          goto yuck;
03513       }
03514       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03515       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03516          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03517          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03518          ast_odbc_release_obj(obj);
03519          goto yuck;
03520       }
03521       if (sscanf(rowdata, "%30d", &x) != 1)
03522          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03523       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03524       ast_odbc_release_obj(obj);
03525       return x;
03526    } else
03527       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03528 yuck:
03529    return x - 1;
03530 }
03531 
03532 /*!
03533  * \brief Determines if the specified message exists.
03534  * \param dir the folder the mailbox folder to look for messages. 
03535  * \param msgnum the message index to query for.
03536  *
03537  * This method is used when mailboxes are stored in an ODBC back end.
03538  *
03539  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03540  */
03541 static int message_exists(char *dir, int msgnum)
03542 {
03543    int x = 0;
03544    int res;
03545    SQLHSTMT stmt;
03546    char sql[PATH_MAX];
03547    char rowdata[20];
03548    char msgnums[20];
03549    char *argv[] = { dir, msgnums };
03550    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03551 
03552    struct odbc_obj *obj;
03553    obj = ast_odbc_request_obj(odbc_database, 0);
03554    if (obj) {
03555       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03556       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03557       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03558       if (!stmt) {
03559          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03560          ast_odbc_release_obj(obj);
03561          goto yuck;
03562       }
03563       res = SQLFetch(stmt);
03564       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03565          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03566          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03567          ast_odbc_release_obj(obj);
03568          goto yuck;
03569       }
03570       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03571       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03572          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03573          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03574          ast_odbc_release_obj(obj);
03575          goto yuck;
03576       }
03577       if (sscanf(rowdata, "%30d", &x) != 1)
03578          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03579       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03580       ast_odbc_release_obj(obj);
03581    } else
03582       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03583 yuck:
03584    return x;
03585 }
03586 
03587 /*!
03588  * \brief returns the number of messages found.
03589  * \param vmu
03590  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03591  *
03592  * This method is used when mailboxes are stored in an ODBC back end.
03593  *
03594  * \return The count of messages being zero or more, less than zero on error.
03595  */
03596 static int count_messages(struct ast_vm_user *vmu, char *dir)
03597 {
03598    int x = 0;
03599    int res;
03600    SQLHSTMT stmt;
03601    char sql[PATH_MAX];
03602    char rowdata[20];
03603    char *argv[] = { dir };
03604    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03605 
03606    struct odbc_obj *obj;
03607    obj = ast_odbc_request_obj(odbc_database, 0);
03608    if (obj) {
03609       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03610       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03611       if (!stmt) {
03612          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03613          ast_odbc_release_obj(obj);
03614          goto yuck;
03615       }
03616       res = SQLFetch(stmt);
03617       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03618          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03619          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03620          ast_odbc_release_obj(obj);
03621          goto yuck;
03622       }
03623       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03624       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03625          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03626          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03627          ast_odbc_release_obj(obj);
03628          goto yuck;
03629       }
03630       if (sscanf(rowdata, "%30d", &x) != 1)
03631          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03632       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03633       ast_odbc_release_obj(obj);
03634       return x;
03635    } else
03636       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03637 yuck:
03638    return x - 1;
03639 
03640 }
03641 
03642 /*!
03643  * \brief Deletes a message from the mailbox folder.
03644  * \param sdir The mailbox folder to work in.
03645  * \param smsg The message index to be deleted.
03646  *
03647  * This method is used when mailboxes are stored in an ODBC back end.
03648  * The specified message is directly deleted from the database 'voicemessages' table.
03649  * 
03650  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03651  */
03652 static void delete_file(const char *sdir, int smsg)
03653 {
03654    SQLHSTMT stmt;
03655    char sql[PATH_MAX];
03656    char msgnums[20];
03657    char *argv[] = { NULL, msgnums };
03658    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03659    struct odbc_obj *obj;
03660 
03661    argv[0] = ast_strdupa(sdir);
03662 
03663    obj = ast_odbc_request_obj(odbc_database, 0);
03664    if (obj) {
03665       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03666       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03667       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03668       if (!stmt)
03669          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03670       else
03671          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03672       ast_odbc_release_obj(obj);
03673    } else
03674       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03675    return;  
03676 }
03677 
03678 /*!
03679  * \brief Copies a voicemail from one mailbox to another.
03680  * \param sdir the folder for which to look for the message to be copied.
03681  * \param smsg the index of the message to be copied.
03682  * \param ddir the destination folder to copy the message into.
03683  * \param dmsg the index to be used for the copied message.
03684  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03685  * \param dmailboxcontext The context for the destination user.
03686  *
03687  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03688  */
03689 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03690 {
03691    SQLHSTMT stmt;
03692    char sql[512];
03693    char msgnums[20];
03694    char msgnumd[20];
03695    struct odbc_obj *obj;
03696    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03697    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03698 
03699    delete_file(ddir, dmsg);
03700    obj = ast_odbc_request_obj(odbc_database, 0);
03701    if (obj) {
03702       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03703       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03704       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);
03705       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03706       if (!stmt)
03707          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03708       else
03709          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03710       ast_odbc_release_obj(obj);
03711    } else
03712       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03713    return;  
03714 }
03715 
03716 struct insert_data {
03717    char *sql;
03718    const char *dir;
03719    const char *msgnums;
03720    void *data;
03721    SQLLEN datalen;
03722    SQLLEN indlen;
03723    const char *context;
03724    const char *macrocontext;
03725    const char *callerid;
03726    const char *origtime;
03727    const char *duration;
03728    const char *mailboxuser;
03729    const char *mailboxcontext;
03730    const char *category;
03731    const char *flag;
03732 };
03733 
03734 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03735 {
03736    struct insert_data *data = vdata;
03737    int res;
03738    SQLHSTMT stmt;
03739 
03740    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03741    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03742       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03743       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03744       return NULL;
03745    }
03746 
03747    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03748    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03749    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03750    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03751    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03752    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03753    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03754    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03755    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03756    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03757    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03758    if (!ast_strlen_zero(data->category)) {
03759       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03760    }
03761    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03762    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03763       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03764       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03765       return NULL;
03766    }
03767 
03768    return stmt;
03769 }
03770 
03771 /*!
03772  * \brief Stores a voicemail into the database.
03773  * \param dir the folder the mailbox folder to store the message.
03774  * \param mailboxuser the user owning the mailbox folder.
03775  * \param mailboxcontext
03776  * \param msgnum the message index for the message to be stored.
03777  *
03778  * This method is used when mailboxes are stored in an ODBC back end.
03779  * The message sound file and information file is looked up on the file system. 
03780  * A SQL query is invoked to store the message into the (MySQL) database.
03781  *
03782  * \return the zero on success -1 on error.
03783  */
03784 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03785 {
03786    int res = 0;
03787    int fd = -1;
03788    void *fdm = MAP_FAILED;
03789    off_t fdlen = -1;
03790    SQLHSTMT stmt;
03791    char sql[PATH_MAX];
03792    char msgnums[20];
03793    char fn[PATH_MAX];
03794    char full_fn[PATH_MAX];
03795    char fmt[80]="";
03796    char *c;
03797    struct ast_config *cfg = NULL;
03798    struct odbc_obj *obj;
03799    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03800       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03801    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03802 
03803    delete_file(dir, msgnum);
03804    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03805       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03806       return -1;
03807    }
03808 
03809    do {
03810       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03811       c = strchr(fmt, '|');
03812       if (c)
03813          *c = '\0';
03814       if (!strcasecmp(fmt, "wav49"))
03815          strcpy(fmt, "WAV");
03816       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03817       if (msgnum > -1)
03818          make_file(fn, sizeof(fn), dir, msgnum);
03819       else
03820          ast_copy_string(fn, dir, sizeof(fn));
03821       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03822       cfg = ast_config_load(full_fn, config_flags);
03823       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03824       fd = open(full_fn, O_RDWR);
03825       if (fd < 0) {
03826          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03827          res = -1;
03828          break;
03829       }
03830       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03831          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03832             idata.context = "";
03833          }
03834          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03835             idata.macrocontext = "";
03836          }
03837          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03838             idata.callerid = "";
03839          }
03840          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03841             idata.origtime = "";
03842          }
03843          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03844             idata.duration = "";
03845          }
03846          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03847             idata.category = "";
03848          }
03849          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03850             idata.flag = "";
03851          }
03852       }
03853       fdlen = lseek(fd, 0, SEEK_END);
03854       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
03855          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
03856          res = -1;
03857          break;
03858       }
03859       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03860       if (fdm == MAP_FAILED) {
03861          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
03862          res = -1;
03863          break;
03864       } 
03865       idata.data = fdm;
03866       idata.datalen = idata.indlen = fdlen;
03867 
03868       if (!ast_strlen_zero(idata.category)) 
03869          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03870       else
03871          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03872 
03873       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03874          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03875       } else {
03876          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03877          res = -1;
03878       }
03879    } while (0);
03880    if (obj) {
03881       ast_odbc_release_obj(obj);
03882    }
03883    if (cfg)
03884       ast_config_destroy(cfg);
03885    if (fdm != MAP_FAILED)
03886       munmap(fdm, fdlen);
03887    if (fd > -1)
03888       close(fd);
03889    return res;
03890 }
03891 
03892 /*!
03893  * \brief Renames a message in a mailbox folder.
03894  * \param sdir The folder of the message to be renamed.
03895  * \param smsg The index of the message to be renamed.
03896  * \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.
03897  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03898  * \param ddir The destination folder for the message to be renamed into
03899  * \param dmsg The destination message for the message to be renamed.
03900  *
03901  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03902  * 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.
03903  * 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.
03904  */
03905 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03906 {
03907    SQLHSTMT stmt;
03908    char sql[PATH_MAX];
03909    char msgnums[20];
03910    char msgnumd[20];
03911    struct odbc_obj *obj;
03912    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03913    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03914 
03915    delete_file(ddir, dmsg);
03916    obj = ast_odbc_request_obj(odbc_database, 0);
03917    if (obj) {
03918       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03919       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03920       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03921       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03922       if (!stmt)
03923          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03924       else
03925          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03926       ast_odbc_release_obj(obj);
03927    } else
03928       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03929    return;  
03930 }
03931 
03932 /*!
03933  * \brief Removes a voicemail message file.
03934  * \param dir the path to the message file.
03935  * \param msgnum the unique number for the message within the mailbox.
03936  *
03937  * Removes the message content file and the information file.
03938  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03939  * Typical use is to clean up after a RETRIEVE operation. 
03940  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03941  * \return zero on success, -1 on error.
03942  */
03943 static int remove_file(char *dir, int msgnum)
03944 {
03945    char fn[PATH_MAX];
03946    char full_fn[PATH_MAX];
03947    char msgnums[80];
03948    
03949    if (msgnum > -1) {
03950       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03951       make_file(fn, sizeof(fn), dir, msgnum);
03952    } else
03953       ast_copy_string(fn, dir, sizeof(fn));
03954    ast_filedelete(fn, NULL);  
03955    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03956    unlink(full_fn);
03957    return 0;
03958 }
03959 #else
03960 #ifndef IMAP_STORAGE
03961 /*!
03962  * \brief Find all .txt files - even if they are not in sequence from 0000.
03963  * \param vmu
03964  * \param dir
03965  *
03966  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03967  *
03968  * \return the count of messages, zero or more.
03969  */
03970 static int count_messages(struct ast_vm_user *vmu, char *dir)
03971 {
03972 
03973    int vmcount = 0;
03974    DIR *vmdir = NULL;
03975    struct dirent *vment = NULL;
03976 
03977    if (vm_lock_path(dir))
03978       return ERROR_LOCK_PATH;
03979 
03980    if ((vmdir = opendir(dir))) {
03981       while ((vment = readdir(vmdir))) {
03982          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03983             vmcount++;
03984          }
03985       }
03986       closedir(vmdir);
03987    }
03988    ast_unlock_path(dir);
03989    
03990    return vmcount;
03991 }
03992 
03993 /*!
03994  * \brief Renames a message in a mailbox folder.
03995  * \param sfn The path to the mailbox information and data file to be renamed.
03996  * \param dfn The path for where the message data and information files will be renamed to.
03997  *
03998  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03999  */
04000 static void rename_file(char *sfn, char *dfn)
04001 {
04002    char stxt[PATH_MAX];
04003    char dtxt[PATH_MAX];
04004    ast_filerename(sfn, dfn, NULL);
04005    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04006    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04007    if (ast_check_realtime("voicemail_data")) {
04008       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04009    }
04010    rename(stxt, dtxt);
04011 }
04012 
04013 /*! 
04014  * \brief Determines the highest message number in use for a given user and mailbox folder.
04015  * \param vmu 
04016  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04017  *
04018  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04019  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04020  *
04021  * \note Should always be called with a lock already set on dir.
04022  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04023  */
04024 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04025 {
04026    int x;
04027    unsigned char map[MAXMSGLIMIT] = "";
04028    DIR *msgdir;
04029    struct dirent *msgdirent;
04030    int msgdirint;
04031    char extension[4];
04032    int stopcount = 0;
04033 
04034    /* Reading the entire directory into a file map scales better than
04035     * doing a stat repeatedly on a predicted sequence.  I suspect this
04036     * is partially due to stat(2) internally doing a readdir(2) itself to
04037     * find each file. */
04038    if (!(msgdir = opendir(dir))) {
04039       return -1;
04040    }
04041 
04042    while ((msgdirent = readdir(msgdir))) {
04043       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04044          map[msgdirint] = 1;
04045          stopcount++;
04046          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04047       }
04048    }
04049    closedir(msgdir);
04050 
04051    for (x = 0; x < vmu->maxmsg; x++) {
04052       if (map[x] == 1) {
04053          stopcount--;
04054       } else if (map[x] == 0 && !stopcount) {
04055          break;
04056       }
04057    }
04058 
04059    return x - 1;
04060 }
04061 
04062 #endif /* #ifndef IMAP_STORAGE */
04063 #endif /* #else of #ifdef ODBC_STORAGE */
04064 #ifndef IMAP_STORAGE
04065 /*!
04066  * \brief Utility function to copy a file.
04067  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04068  * \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.
04069  *
04070  * 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.
04071  * The copy operation copies up to 4096 bytes at once.
04072  *
04073  * \return zero on success, -1 on error.
04074  */
04075 static int copy(char *infile, char *outfile)
04076 {
04077    int ifd;
04078    int ofd;
04079    int res;
04080    int len;
04081    char buf[4096];
04082 
04083 #ifdef HARDLINK_WHEN_POSSIBLE
04084    /* Hard link if possible; saves disk space & is faster */
04085    if (link(infile, outfile)) {
04086 #endif
04087       if ((ifd = open(infile, O_RDONLY)) < 0) {
04088          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04089          return -1;
04090       }
04091       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04092          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04093          close(ifd);
04094          return -1;
04095       }
04096       do {
04097          len = read(ifd, buf, sizeof(buf));
04098          if (len < 0) {
04099             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04100             close(ifd);
04101             close(ofd);
04102             unlink(outfile);
04103          } else if (len) {
04104             res = write(ofd, buf, len);
04105             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04106                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04107                close(ifd);
04108                close(ofd);
04109                unlink(outfile);
04110             }
04111          }
04112       } while (len);
04113       close(ifd);
04114       close(ofd);
04115       return 0;
04116 #ifdef HARDLINK_WHEN_POSSIBLE
04117    } else {
04118       /* Hard link succeeded */
04119       return 0;
04120    }
04121 #endif
04122 }
04123 
04124 /*!
04125  * \brief Copies a voicemail information (envelope) file.
04126  * \param frompath
04127  * \param topath 
04128  *
04129  * Every voicemail has the data (.wav) file, and the information file.
04130  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04131  * This is used by the COPY macro when not using IMAP storage.
04132  */
04133 static void copy_plain_file(char *frompath, char *topath)
04134 {
04135    char frompath2[PATH_MAX], topath2[PATH_MAX];
04136    struct ast_variable *tmp,*var = NULL;
04137    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04138    ast_filecopy(frompath, topath, NULL);
04139    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04140    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04141    if (ast_check_realtime("voicemail_data")) {
04142       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04143       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04144       for (tmp = var; tmp; tmp = tmp->next) {
04145          if (!strcasecmp(tmp->name, "origmailbox")) {
04146             origmailbox = tmp->value;
04147          } else if (!strcasecmp(tmp->name, "context")) {
04148             context = tmp->value;
04149          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04150             macrocontext = tmp->value;
04151          } else if (!strcasecmp(tmp->name, "exten")) {
04152             exten = tmp->value;
04153          } else if (!strcasecmp(tmp->name, "priority")) {
04154             priority = tmp->value;
04155          } else if (!strcasecmp(tmp->name, "callerchan")) {
04156             callerchan = tmp->value;
04157          } else if (!strcasecmp(tmp->name, "callerid")) {
04158             callerid = tmp->value;
04159          } else if (!strcasecmp(tmp->name, "origdate")) {
04160             origdate = tmp->value;
04161          } else if (!strcasecmp(tmp->name, "origtime")) {
04162             origtime = tmp->value;
04163          } else if (!strcasecmp(tmp->name, "category")) {
04164             category = tmp->value;
04165          } else if (!strcasecmp(tmp->name, "duration")) {
04166             duration = tmp->value;
04167          }
04168       }
04169       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);
04170    }
04171    copy(frompath2, topath2);
04172    ast_variables_destroy(var);
04173 }
04174 #endif
04175 
04176 /*! 
04177  * \brief Removes the voicemail sound and information file.
04178  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04179  *
04180  * This is used by the DELETE macro when voicemails are stored on the file system.
04181  *
04182  * \return zero on success, -1 on error.
04183  */
04184 static int vm_delete(char *file)
04185 {
04186    char *txt;
04187    int txtsize = 0;
04188 
04189    txtsize = (strlen(file) + 5)*sizeof(char);
04190    txt = alloca(txtsize);
04191    /* Sprintf here would safe because we alloca'd exactly the right length,
04192     * but trying to eliminate all sprintf's anyhow
04193     */
04194    if (ast_check_realtime("voicemail_data")) {
04195       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04196    }
04197    snprintf(txt, txtsize, "%s.txt", file);
04198    unlink(txt);
04199    return ast_filedelete(file, NULL);
04200 }
04201 
04202 /*!
04203  * \brief utility used by inchar(), for base_encode()
04204  */
04205 static int inbuf(struct baseio *bio, FILE *fi)
04206 {
04207    int l;
04208 
04209    if (bio->ateof)
04210       return 0;
04211 
04212    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04213       if (ferror(fi))
04214          return -1;
04215 
04216       bio->ateof = 1;
04217       return 0;
04218    }
04219 
04220    bio->iolen = l;
04221    bio->iocp = 0;
04222 
04223    return 1;
04224 }
04225 
04226 /*!
04227  * \brief utility used by base_encode()
04228  */
04229 static int inchar(struct baseio *bio, FILE *fi)
04230 {
04231    if (bio->iocp>=bio->iolen) {
04232       if (!inbuf(bio, fi))
04233          return EOF;
04234    }
04235 
04236    return bio->iobuf[bio->iocp++];
04237 }
04238 
04239 /*!
04240  * \brief utility used by base_encode()
04241  */
04242 static int ochar(struct baseio *bio, int c, FILE *so)
04243 {
04244    if (bio->linelength >= BASELINELEN) {
04245       if (fputs(ENDL, so) == EOF) {
04246          return -1;
04247       }
04248 
04249       bio->linelength = 0;
04250    }
04251 
04252    if (putc(((unsigned char) c), so) == EOF) {
04253       return -1;
04254    }
04255 
04256    bio->linelength++;
04257 
04258    return 1;
04259 }
04260 
04261 /*!
04262  * \brief Performs a base 64 encode algorithm on the contents of a File
04263  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04264  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04265  *
04266  * 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 ?
04267  *
04268  * \return zero on success, -1 on error.
04269  */
04270 static int base_encode(char *filename, FILE *so)
04271 {
04272    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04273       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04274       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04275       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04276    int i, hiteof = 0;
04277    FILE *fi;
04278    struct baseio bio;
04279 
04280    memset(&bio, 0, sizeof(bio));
04281    bio.iocp = BASEMAXINLINE;
04282 
04283    if (!(fi = fopen(filename, "rb"))) {
04284       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04285       return -1;
04286    }
04287 
04288    while (!hiteof){
04289       unsigned char igroup[3], ogroup[4];
04290       int c, n;
04291 
04292       memset(igroup, 0, sizeof(igroup));
04293 
04294       for (n = 0; n < 3; n++) {
04295          if ((c = inchar(&bio, fi)) == EOF) {
04296             hiteof = 1;
04297             break;
04298          }
04299 
04300          igroup[n] = (unsigned char) c;
04301       }
04302 
04303       if (n > 0) {
04304          ogroup[0]= dtable[igroup[0] >> 2];
04305          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04306          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04307          ogroup[3]= dtable[igroup[2] & 0x3F];
04308 
04309          if (n < 3) {
04310             ogroup[3] = '=';
04311 
04312             if (n < 2)
04313                ogroup[2] = '=';
04314          }
04315 
04316          for (i = 0; i < 4; i++)
04317             ochar(&bio, ogroup[i], so);
04318       }
04319    }
04320 
04321    fclose(fi);
04322    
04323    if (fputs(ENDL, so) == EOF) {
04324       return 0;
04325    }
04326 
04327    return 1;
04328 }
04329 
04330 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)
04331 {
04332    char callerid[256];
04333    char num[12];
04334    char fromdir[256], fromfile[256];
04335    struct ast_config *msg_cfg;
04336    const char *origcallerid, *origtime;
04337    char origcidname[80], origcidnum[80], origdate[80];
04338    int inttime;
04339    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04340 
04341    /* Prepare variables for substitution in email body and subject */
04342    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04343    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04344    snprintf(num, sizeof(num), "%d", msgnum);
04345    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04346    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04347    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04348    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04349       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04350    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04351    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04352    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04353    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04354    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04355 
04356    /* Retrieve info from VM attribute file */
04357    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04358    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04359    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04360       strcat(fromfile, ".txt");
04361    }
04362    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04363       if (option_debug > 0) {
04364          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04365       }
04366       return;
04367    }
04368 
04369    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04370       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04371       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04372       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04373       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04374    }
04375 
04376    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04377       struct timeval tv = { inttime, };
04378       struct ast_tm tm;
04379       ast_localtime(&tv, &tm, NULL);
04380       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04381       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04382    }
04383    ast_config_destroy(msg_cfg);
04384 }
04385 
04386 /*!
04387  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04388  * \param from The string to work with.
04389  * \param buf The buffer into which to write the modified quoted string.
04390  * \param maxlen Always zero, but see \see ast_str
04391  * 
04392  * \return The destination string with quotes wrapped on it (the to field).
04393  */
04394 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04395 {
04396    const char *ptr;
04397 
04398    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04399    ast_str_set(buf, maxlen, "\"");
04400    for (ptr = from; *ptr; ptr++) {
04401       if (*ptr == '"' || *ptr == '\\') {
04402          ast_str_append(buf, maxlen, "\\%c", *ptr);
04403       } else {
04404          ast_str_append(buf, maxlen, "%c", *ptr);
04405       }
04406    }
04407    ast_str_append(buf, maxlen, "\"");
04408 
04409    return ast_str_buffer(*buf);
04410 }
04411 
04412 /*! \brief
04413  * fill in *tm for current time according to the proper timezone, if any.
04414  * \return tm so it can be used as a function argument.
04415  */
04416 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04417 {
04418    const struct vm_zone *z = NULL;
04419    struct timeval t = ast_tvnow();
04420 
04421    /* Does this user have a timezone specified? */
04422    if (!ast_strlen_zero(vmu->zonetag)) {
04423       /* Find the zone in the list */
04424       AST_LIST_LOCK(&zones);
04425       AST_LIST_TRAVERSE(&zones, z, list) {
04426          if (!strcmp(z->name, vmu->zonetag))
04427             break;
04428       }
04429       AST_LIST_UNLOCK(&zones);
04430    }
04431    ast_localtime(&t, tm, z ? z->timezone : NULL);
04432    return tm;
04433 }
04434 
04435 /*!\brief Check if the string would need encoding within the MIME standard, to
04436  * avoid confusing certain mail software that expects messages to be 7-bit
04437  * clean.
04438  */
04439 static int check_mime(const char *str)
04440 {
04441    for (; *str; str++) {
04442       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04443          return 1;
04444       }
04445    }
04446    return 0;
04447 }
04448 
04449 /*!\brief Encode a string according to the MIME rules for encoding strings
04450  * that are not 7-bit clean or contain control characters.
04451  *
04452  * Additionally, if the encoded string would exceed the MIME limit of 76
04453  * characters per line, then the encoding will be broken up into multiple
04454  * sections, separated by a space character, in order to facilitate
04455  * breaking up the associated header across multiple lines.
04456  *
04457  * \param end An expandable buffer for holding the result
04458  * \param maxlen Always zero, but see \see ast_str
04459  * \param start A string to be encoded
04460  * \param preamble The length of the first line already used for this string,
04461  * to ensure that each line maintains a maximum length of 76 chars.
04462  * \param postamble the length of any additional characters appended to the
04463  * line, used to ensure proper field wrapping.
04464  * \retval The encoded string.
04465  */
04466 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04467 {
04468    struct ast_str *tmp = ast_str_alloca(80);
04469    int first_section = 1;
04470 
04471    ast_str_reset(*end);
04472    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04473    for (; *start; start++) {
04474       int need_encoding = 0;
04475       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04476          need_encoding = 1;
04477       }
04478       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04479          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04480          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04481          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04482          /* Start new line */
04483          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04484          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04485          first_section = 0;
04486       }
04487       if (need_encoding && *start == ' ') {
04488          ast_str_append(&tmp, -1, "_");
04489       } else if (need_encoding) {
04490          ast_str_append(&tmp, -1, "=%hhX", *start);
04491       } else {
04492          ast_str_append(&tmp, -1, "%c", *start);
04493       }
04494    }
04495    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04496    return ast_str_buffer(*end);
04497 }
04498 
04499 /*!
04500  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04501  * \param p The output file to generate the email contents into.
04502  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04503  * \param vmu The voicemail user who is sending the voicemail.
04504  * \param msgnum The message index in the mailbox folder.
04505  * \param context 
04506  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04507  * \param fromfolder
04508  * \param cidnum The caller ID number.
04509  * \param cidname The caller ID name.
04510  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04511  * \param attach2 
04512  * \param format The message sound file format. i.e. .wav
04513  * \param duration The time of the message content, in seconds.
04514  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04515  * \param chan
04516  * \param category
04517  * \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.
04518  * \param flag
04519  *
04520  * 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.
04521  */
04522 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)
04523 {
04524    char date[256];
04525    char host[MAXHOSTNAMELEN] = "";
04526    char who[256];
04527    char bound[256];
04528    char dur[256];
04529    struct ast_tm tm;
04530    char enc_cidnum[256] = "", enc_cidname[256] = "";
04531    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04532    char *greeting_attachment; 
04533    char filename[256];
04534 
04535    if (!str1 || !str2) {
04536       ast_free(str1);
04537       ast_free(str2);
04538       return;
04539    }
04540 
04541    if (cidnum) {
04542       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04543    }
04544    if (cidname) {
04545       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04546    }
04547    gethostname(host, sizeof(host) - 1);
04548 
04549    if (strchr(srcemail, '@')) {
04550       ast_copy_string(who, srcemail, sizeof(who));
04551    } else {
04552       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04553    }
04554 
04555    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04556    if (greeting_attachment) {
04557       *greeting_attachment++ = '\0';
04558    }
04559 
04560    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04561    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04562    fprintf(p, "Date: %s" ENDL, date);
04563 
04564    /* Set date format for voicemail mail */
04565    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04566 
04567    if (!ast_strlen_zero(fromstring)) {
04568       struct ast_channel *ast;
04569       if ((ast = ast_dummy_channel_alloc())) {
04570          char *ptr;
04571          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04572          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04573 
04574          if (check_mime(ast_str_buffer(str1))) {
04575             int first_line = 1;
04576             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04577             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04578                *ptr = '\0';
04579                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04580                first_line = 0;
04581                /* Substring is smaller, so this will never grow */
04582                ast_str_set(&str2, 0, "%s", ptr + 1);
04583             }
04584             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04585          } else {
04586             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04587          }
04588          ast = ast_channel_unref(ast);
04589       } else {
04590          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04591       }
04592    } else {
04593       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04594    }
04595 
04596    if (check_mime(vmu->fullname)) {
04597       int first_line = 1;
04598       char *ptr;
04599       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04600       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04601          *ptr = '\0';
04602          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04603          first_line = 0;
04604          /* Substring is smaller, so this will never grow */
04605          ast_str_set(&str2, 0, "%s", ptr + 1);
04606       }
04607       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04608    } else {
04609       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04610    }
04611 
04612    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04613       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04614       struct ast_channel *ast;
04615       if ((ast = ast_dummy_channel_alloc())) {
04616          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04617          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04618          if (check_mime(ast_str_buffer(str1))) {
04619             int first_line = 1;
04620             char *ptr;
04621             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04622             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04623                *ptr = '\0';
04624                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04625                first_line = 0;
04626                /* Substring is smaller, so this will never grow */
04627                ast_str_set(&str2, 0, "%s", ptr + 1);
04628             }
04629             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04630          } else {
04631             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04632          }
04633          ast = ast_channel_unref(ast);
04634       } else {
04635          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04636       }
04637    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04638       if (ast_strlen_zero(flag)) {
04639          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04640       } else {
04641          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04642       }
04643    } else {
04644       if (ast_strlen_zero(flag)) {
04645          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04646       } else {
04647          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04648       }
04649    }
04650 
04651    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04652       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04653    if (imap) {
04654       /* additional information needed for IMAP searching */
04655       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04656       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04657       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04658       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04659 #ifdef IMAP_STORAGE
04660       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04661 #else
04662       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04663 #endif
04664       /* flag added for Urgent */
04665       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04666       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04667       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04668       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04669       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04670       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04671       if (!ast_strlen_zero(category)) {
04672          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04673       } else {
04674          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04675       }
04676       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04677       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04678       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04679    }
04680    if (!ast_strlen_zero(cidnum)) {
04681       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04682    }
04683    if (!ast_strlen_zero(cidname)) {
04684       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04685    }
04686    fprintf(p, "MIME-Version: 1.0" ENDL);
04687    if (attach_user_voicemail) {
04688       /* Something unique. */
04689       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04690          (int) getpid(), (unsigned int) ast_random());
04691 
04692       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04693       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04694       fprintf(p, "--%s" ENDL, bound);
04695    }
04696    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04697    if (emailbody || vmu->emailbody) {
04698       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04699       struct ast_channel *ast;
04700       if ((ast = ast_dummy_channel_alloc())) {
04701          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04702          ast_str_substitute_variables(&str1, 0, ast, e_body);
04703 #ifdef IMAP_STORAGE
04704             {
04705                /* Convert body to native line terminators for IMAP backend */
04706                char *line = ast_str_buffer(str1), *next;
04707                do {
04708                   /* Terminate line before outputting it to the file */
04709                   if ((next = strchr(line, '\n'))) {
04710                      *next++ = '\0';
04711                   }
04712                   fprintf(p, "%s" ENDL, line);
04713                   line = next;
04714                } while (!ast_strlen_zero(line));
04715             }
04716 #else
04717          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04718 #endif
04719          ast = ast_channel_unref(ast);
04720       } else {
04721          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04722       }
04723    } else if (msgnum > -1) {
04724       if (strcmp(vmu->mailbox, mailbox)) {
04725          /* Forwarded type */
04726          struct ast_config *msg_cfg;
04727          const char *v;
04728          int inttime;
04729          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04730          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04731          /* Retrieve info from VM attribute file */
04732          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04733          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04734          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04735             strcat(fromfile, ".txt");
04736          }
04737          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04738             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04739                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04740             }
04741 
04742             /* You might be tempted to do origdate, except that a) it's in the wrong
04743              * format, and b) it's missing for IMAP recordings. */
04744             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04745                struct timeval tv = { inttime, };
04746                struct ast_tm tm;
04747                ast_localtime(&tv, &tm, NULL);
04748                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04749             }
04750             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04751                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04752                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04753                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04754                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04755                date, origcallerid, origdate);
04756             ast_config_destroy(msg_cfg);
04757          } else {
04758             goto plain_message;
04759          }
04760       } else {
04761 plain_message:
04762          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04763             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04764             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04765             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04766             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04767       }
04768    } else {
04769       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04770             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04771    }
04772 
04773    if (imap || attach_user_voicemail) {
04774       if (!ast_strlen_zero(attach2)) {
04775          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04776          ast_debug(5, "creating second attachment filename %s\n", filename);
04777          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04778          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04779          ast_debug(5, "creating attachment filename %s\n", filename);
04780          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04781       } else {
04782          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04783          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04784          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04785       }
04786    }
04787    ast_free(str1);
04788    ast_free(str2);
04789 }
04790 
04791 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)
04792 {
04793    char tmpdir[256], newtmp[256];
04794    char fname[256];
04795    char tmpcmd[256];
04796    int tmpfd = -1;
04797    int soxstatus = 0;
04798 
04799    /* Eww. We want formats to tell us their own MIME type */
04800    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04801 
04802    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04803       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04804       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04805       tmpfd = mkstemp(newtmp);
04806       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04807       ast_debug(3, "newtmp: %s\n", newtmp);
04808       if (tmpfd > -1) {
04809          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04810          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04811             attach = newtmp;
04812             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04813          } else {
04814             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04815                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04816             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04817          }
04818       }
04819    }
04820    fprintf(p, "--%s" ENDL, bound);
04821    if (msgnum > -1)
04822       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04823    else
04824       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04825    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04826    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04827    if (msgnum > -1)
04828       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04829    else
04830       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04831    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04832    base_encode(fname, p);
04833    if (last)
04834       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04835    if (tmpfd > -1) {
04836       if (soxstatus == 0) {
04837          unlink(fname);
04838       }
04839       close(tmpfd);
04840       unlink(newtmp);
04841    }
04842    return 0;
04843 }
04844 
04845 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)
04846 {
04847    FILE *p = NULL;
04848    char tmp[80] = "/tmp/astmail-XXXXXX";
04849    char tmp2[256];
04850    char *stringp;
04851 
04852    if (vmu && ast_strlen_zero(vmu->email)) {
04853       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04854       return(0);
04855    }
04856 
04857    /* Mail only the first format */
04858    format = ast_strdupa(format);
04859    stringp = format;
04860    strsep(&stringp, "|");
04861 
04862    if (!strcmp(format, "wav49"))
04863       format = "WAV";
04864    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));
04865    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04866       command hangs */
04867    if ((p = vm_mkftemp(tmp)) == NULL) {
04868       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04869       return -1;
04870    } else {
04871       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04872       fclose(p);
04873       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04874       ast_safe_system(tmp2);
04875       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04876    }
04877    return 0;
04878 }
04879 
04880 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)
04881 {
04882    char enc_cidnum[256], enc_cidname[256];
04883    char date[256];
04884    char host[MAXHOSTNAMELEN] = "";
04885    char who[256];
04886    char dur[PATH_MAX];
04887    char tmp[80] = "/tmp/astmail-XXXXXX";
04888    char tmp2[PATH_MAX];
04889    struct ast_tm tm;
04890    FILE *p;
04891    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04892 
04893    if (!str1 || !str2) {
04894       ast_free(str1);
04895       ast_free(str2);
04896       return -1;
04897    }
04898 
04899    if (cidnum) {
04900       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04901    }
04902    if (cidname) {
04903       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04904    }
04905 
04906    if ((p = vm_mkftemp(tmp)) == NULL) {
04907       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04908       ast_free(str1);
04909       ast_free(str2);
04910       return -1;
04911    }
04912    gethostname(host, sizeof(host)-1);
04913    if (strchr(srcemail, '@')) {
04914       ast_copy_string(who, srcemail, sizeof(who));
04915    } else {
04916       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04917    }
04918    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04919    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04920    fprintf(p, "Date: %s\n", date);
04921 
04922    /* Reformat for custom pager format */
04923    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04924 
04925    if (!ast_strlen_zero(pagerfromstring)) {
04926       struct ast_channel *ast;
04927       if ((ast = ast_dummy_channel_alloc())) {
04928          char *ptr;
04929          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04930          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04931 
04932          if (check_mime(ast_str_buffer(str1))) {
04933             int first_line = 1;
04934             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04935             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04936                *ptr = '\0';
04937                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04938                first_line = 0;
04939                /* Substring is smaller, so this will never grow */
04940                ast_str_set(&str2, 0, "%s", ptr + 1);
04941             }
04942             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04943          } else {
04944             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04945          }
04946          ast = ast_channel_unref(ast);
04947       } else {
04948          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04949       }
04950    } else {
04951       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04952    }
04953 
04954    if (check_mime(vmu->fullname)) {
04955       int first_line = 1;
04956       char *ptr;
04957       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04958       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04959          *ptr = '\0';
04960          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04961          first_line = 0;
04962          /* Substring is smaller, so this will never grow */
04963          ast_str_set(&str2, 0, "%s", ptr + 1);
04964       }
04965       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04966    } else {
04967       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04968    }
04969 
04970    if (!ast_strlen_zero(pagersubject)) {
04971       struct ast_channel *ast;
04972       if ((ast = ast_dummy_channel_alloc())) {
04973          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04974          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04975          if (check_mime(ast_str_buffer(str1))) {
04976             int first_line = 1;
04977             char *ptr;
04978             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04979             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04980                *ptr = '\0';
04981                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04982                first_line = 0;
04983                /* Substring is smaller, so this will never grow */
04984                ast_str_set(&str2, 0, "%s", ptr + 1);
04985             }
04986             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04987          } else {
04988             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04989          }
04990          ast = ast_channel_unref(ast);
04991       } else {
04992          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04993       }
04994    } else {
04995       if (ast_strlen_zero(flag)) {
04996          fprintf(p, "Subject: New VM\n\n");
04997       } else {
04998          fprintf(p, "Subject: New %s VM\n\n", flag);
04999       }
05000    }
05001 
05002    if (pagerbody) {
05003       struct ast_channel *ast;
05004       if ((ast = ast_dummy_channel_alloc())) {
05005          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05006          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05007          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05008          ast = ast_channel_unref(ast);
05009       } else {
05010          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05011       }
05012    } else {
05013       fprintf(p, "New %s long %s msg in box %s\n"
05014             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05015    }
05016 
05017    fclose(p);
05018    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05019    ast_safe_system(tmp2);
05020    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05021    ast_free(str1);
05022    ast_free(str2);
05023    return 0;
05024 }
05025 
05026 /*!
05027  * \brief Gets the current date and time, as formatted string.
05028  * \param s The buffer to hold the output formatted date.
05029  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05030  * 
05031  * The date format string used is "%a %b %e %r UTC %Y".
05032  * 
05033  * \return zero on success, -1 on error.
05034  */
05035 static int get_date(char *s, int len)
05036 {
05037    struct ast_tm tm;
05038    struct timeval t = ast_tvnow();
05039    
05040    ast_localtime(&t, &tm, "UTC");
05041 
05042    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05043 }
05044 
05045 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05046 {
05047    int res;
05048    char fn[PATH_MAX];
05049    char dest[PATH_MAX];
05050 
05051    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05052 
05053    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05054       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05055       return -1;
05056    }
05057 
05058    RETRIEVE(fn, -1, ext, context);
05059    if (ast_fileexists(fn, NULL, NULL) > 0) {
05060       res = ast_stream_and_wait(chan, fn, ecodes);
05061       if (res) {
05062          DISPOSE(fn, -1);
05063          return res;
05064       }
05065    } else {
05066       /* Dispose just in case */
05067       DISPOSE(fn, -1);
05068       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05069       if (res)
05070          return res;
05071       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05072       if (res)
05073          return res;
05074    }
05075    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05076    return res;
05077 }
05078 
05079 static void free_zone(struct vm_zone *z)
05080 {
05081    ast_free(z);
05082 }
05083 
05084 #ifdef ODBC_STORAGE
05085 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05086 {
05087    int x = -1;
05088    int res;
05089    SQLHSTMT stmt = NULL;
05090    char sql[PATH_MAX];
05091    char rowdata[20];
05092    char tmp[PATH_MAX] = "";
05093    struct odbc_obj *obj = NULL;
05094    char *context;
05095    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05096 
05097    if (newmsgs)
05098       *newmsgs = 0;
05099    if (oldmsgs)
05100       *oldmsgs = 0;
05101    if (urgentmsgs)
05102       *urgentmsgs = 0;
05103 
05104    /* If no mailbox, return immediately */
05105    if (ast_strlen_zero(mailbox))
05106       return 0;
05107 
05108    ast_copy_string(tmp, mailbox, sizeof(tmp));
05109 
05110    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05111       int u, n, o;
05112       char *next, *remaining = tmp;
05113       while ((next = strsep(&remaining, " ,"))) {
05114          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05115             return -1;
05116          }
05117          if (urgentmsgs) {
05118             *urgentmsgs += u;
05119          }
05120          if (newmsgs) {
05121             *newmsgs += n;
05122          }
05123          if (oldmsgs) {
05124             *oldmsgs += o;
05125          }
05126       }
05127       return 0;
05128    }
05129 
05130    context = strchr(tmp, '@');
05131    if (context) {
05132       *context = '\0';
05133       context++;
05134    } else
05135       context = "default";
05136 
05137    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05138       do {
05139          if (newmsgs) {
05140             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05141             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05142                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05143                break;
05144             }
05145             res = SQLFetch(stmt);
05146             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05147                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05148                break;
05149             }
05150             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05151             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05152                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05153                break;
05154             }
05155             *newmsgs = atoi(rowdata);
05156             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05157          }
05158 
05159          if (oldmsgs) {
05160             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05161             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05162                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05163                break;
05164             }
05165             res = SQLFetch(stmt);
05166             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05167                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05168                break;
05169             }
05170             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05171             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05172                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05173                break;
05174             }
05175             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05176             *oldmsgs = atoi(rowdata);
05177          }
05178 
05179          if (urgentmsgs) {
05180             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05181             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05182                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05183                break;
05184             }
05185             res = SQLFetch(stmt);
05186             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05187                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05188                break;
05189             }
05190             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05191             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05192                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05193                break;
05194             }
05195             *urgentmsgs = atoi(rowdata);
05196          }
05197 
05198          x = 0;
05199       } while (0);
05200    } else {
05201       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05202    }
05203 
05204    if (stmt) {
05205       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05206    }
05207    if (obj) {
05208       ast_odbc_release_obj(obj);
05209    }
05210    return x;
05211 }
05212 
05213 /*!
05214  * \brief Gets the number of messages that exist in a mailbox folder.
05215  * \param context
05216  * \param mailbox
05217  * \param folder
05218  * 
05219  * This method is used when ODBC backend is used.
05220  * \return The number of messages in this mailbox folder (zero or more).
05221  */
05222 static int messagecount(const char *context, const char *mailbox, const char *folder)
05223 {
05224    struct odbc_obj *obj = NULL;
05225    int nummsgs = 0;
05226    int res;
05227    SQLHSTMT stmt = NULL;
05228    char sql[PATH_MAX];
05229    char rowdata[20];
05230    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05231    if (!folder)
05232       folder = "INBOX";
05233    /* If no mailbox, return immediately */
05234    if (ast_strlen_zero(mailbox))
05235       return 0;
05236 
05237    obj = ast_odbc_request_obj(odbc_database, 0);
05238    if (obj) {
05239       if (!strcmp(folder, "INBOX")) {
05240          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);
05241       } else {
05242          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05243       }
05244       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05245       if (!stmt) {
05246          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05247          goto yuck;
05248       }
05249       res = SQLFetch(stmt);
05250       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05251          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05252          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05253          goto yuck;
05254       }
05255       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05256       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05257          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05258          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05259          goto yuck;
05260       }
05261       nummsgs = atoi(rowdata);
05262       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05263    } else
05264       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05265 
05266 yuck:
05267    if (obj)
05268       ast_odbc_release_obj(obj);
05269    return nummsgs;
05270 }
05271 
05272 /** 
05273  * \brief Determines if the given folder has messages.
05274  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05275  * 
05276  * This function is used when the mailbox is stored in an ODBC back end.
05277  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05278  * \return 1 if the folder has one or more messages. zero otherwise.
05279  */
05280 static int has_voicemail(const char *mailbox, const char *folder)
05281 {
05282    char tmp[256], *tmp2 = tmp, *box, *context;
05283    ast_copy_string(tmp, mailbox, sizeof(tmp));
05284    while ((context = box = strsep(&tmp2, ",&"))) {
05285       strsep(&context, "@");
05286       if (ast_strlen_zero(context))
05287          context = "default";
05288       if (messagecount(context, box, folder))
05289          return 1;
05290    }
05291    return 0;
05292 }
05293 #endif
05294 #ifndef IMAP_STORAGE
05295 /*! 
05296  * \brief Copies a message from one mailbox to another.
05297  * \param chan
05298  * \param vmu
05299  * \param imbox
05300  * \param msgnum
05301  * \param duration
05302  * \param recip
05303  * \param fmt
05304  * \param dir
05305  * \param flag
05306  *
05307  * This is only used by file storage based mailboxes.
05308  *
05309  * \return zero on success, -1 on error.
05310  */
05311 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)
05312 {
05313    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05314    const char *frombox = mbox(vmu, imbox);
05315    const char *userfolder;
05316    int recipmsgnum;
05317    int res = 0;
05318 
05319    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05320 
05321    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05322       userfolder = "Urgent";
05323    } else {
05324       userfolder = "INBOX";
05325    }
05326 
05327    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05328 
05329    if (!dir)
05330       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05331    else
05332       ast_copy_string(fromdir, dir, sizeof(fromdir));
05333 
05334    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05335    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05336 
05337    if (vm_lock_path(todir))
05338       return ERROR_LOCK_PATH;
05339 
05340    recipmsgnum = last_message_index(recip, todir) + 1;
05341    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05342       make_file(topath, sizeof(topath), todir, recipmsgnum);
05343 #ifndef ODBC_STORAGE
05344       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05345          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05346       } else {
05347 #endif
05348          /* If we are prepending a message for ODBC, then the message already
05349           * exists in the database, but we want to force copying from the
05350           * filesystem (since only the FS contains the prepend). */
05351          copy_plain_file(frompath, topath);
05352          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05353          vm_delete(topath);
05354 #ifndef ODBC_STORAGE
05355       }
05356 #endif
05357    } else {
05358       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05359       res = -1;
05360    }
05361    ast_unlock_path(todir);
05362    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05363       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05364       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05365       flag);
05366    
05367    return res;
05368 }
05369 #endif
05370 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05371 
05372 static int messagecount(const char *context, const char *mailbox, const char *folder)
05373 {
05374    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05375 }
05376 
05377 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05378 {
05379    DIR *dir;
05380    struct dirent *de;
05381    char fn[256];
05382    int ret = 0;
05383 
05384    /* If no mailbox, return immediately */
05385    if (ast_strlen_zero(mailbox))
05386       return 0;
05387 
05388    if (ast_strlen_zero(folder))
05389       folder = "INBOX";
05390    if (ast_strlen_zero(context))
05391       context = "default";
05392 
05393    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05394 
05395    if (!(dir = opendir(fn)))
05396       return 0;
05397 
05398    while ((de = readdir(dir))) {
05399       if (!strncasecmp(de->d_name, "msg", 3)) {
05400          if (shortcircuit) {
05401             ret = 1;
05402             break;
05403          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05404             ret++;
05405          }
05406       }
05407    }
05408 
05409    closedir(dir);
05410 
05411    return ret;
05412 }
05413 
05414 /** 
05415  * \brief Determines if the given folder has messages.
05416  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05417  * \param folder the folder to look in
05418  *
05419  * This function is used when the mailbox is stored in a filesystem back end.
05420  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05421  * \return 1 if the folder has one or more messages. zero otherwise.
05422  */
05423 static int has_voicemail(const char *mailbox, const char *folder)
05424 {
05425    char tmp[256], *tmp2 = tmp, *box, *context;
05426    ast_copy_string(tmp, mailbox, sizeof(tmp));
05427    if (ast_strlen_zero(folder)) {
05428       folder = "INBOX";
05429    }
05430    while ((box = strsep(&tmp2, ",&"))) {
05431       if ((context = strchr(box, '@')))
05432          *context++ = '\0';
05433       else
05434          context = "default";
05435       if (__has_voicemail(context, box, folder, 1))
05436          return 1;
05437       /* If we are checking INBOX, we should check Urgent as well */
05438       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05439          return 1;
05440       }
05441    }
05442    return 0;
05443 }
05444 
05445 
05446 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05447 {
05448    char tmp[256];
05449    char *context;
05450 
05451    /* If no mailbox, return immediately */
05452    if (ast_strlen_zero(mailbox))
05453       return 0;
05454 
05455    if (newmsgs)
05456       *newmsgs = 0;
05457    if (oldmsgs)
05458       *oldmsgs = 0;
05459    if (urgentmsgs)
05460       *urgentmsgs = 0;
05461 
05462    if (strchr(mailbox, ',')) {
05463       int tmpnew, tmpold, tmpurgent;
05464       char *mb, *cur;
05465 
05466       ast_copy_string(tmp, mailbox, sizeof(tmp));
05467       mb = tmp;
05468       while ((cur = strsep(&mb, ", "))) {
05469          if (!ast_strlen_zero(cur)) {
05470             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05471                return -1;
05472             else {
05473                if (newmsgs)
05474                   *newmsgs += tmpnew; 
05475                if (oldmsgs)
05476                   *oldmsgs += tmpold;
05477                if (urgentmsgs)
05478                   *urgentmsgs += tmpurgent;
05479             }
05480          }
05481       }
05482       return 0;
05483    }
05484 
05485    ast_copy_string(tmp, mailbox, sizeof(tmp));
05486    
05487    if ((context = strchr(tmp, '@')))
05488       *context++ = '\0';
05489    else
05490       context = "default";
05491 
05492    if (newmsgs)
05493       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05494    if (oldmsgs)
05495       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05496    if (urgentmsgs)
05497       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05498 
05499    return 0;
05500 }
05501 
05502 #endif
05503 
05504 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05505 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05506 {
05507    int urgentmsgs = 0;
05508    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05509    if (newmsgs) {
05510       *newmsgs += urgentmsgs;
05511    }
05512    return res;
05513 }
05514 
05515 static void run_externnotify(char *context, char *extension, const char *flag)
05516 {
05517    char arguments[255];
05518    char ext_context[256] = "";
05519    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05520    struct ast_smdi_mwi_message *mwi_msg;
05521 
05522    if (!ast_strlen_zero(context))
05523       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05524    else
05525       ast_copy_string(ext_context, extension, sizeof(ext_context));
05526 
05527    if (smdi_iface) {
05528       if (ast_app_has_voicemail(ext_context, NULL)) 
05529          ast_smdi_mwi_set(smdi_iface, extension);
05530       else
05531          ast_smdi_mwi_unset(smdi_iface, extension);
05532 
05533       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05534          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05535          if (!strncmp(mwi_msg->cause, "INV", 3))
05536             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05537          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05538             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05539          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05540          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05541       } else {
05542          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05543       }
05544    }
05545 
05546    if (!ast_strlen_zero(externnotify)) {
05547       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05548          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05549       } else {
05550          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05551          ast_debug(1, "Executing %s\n", arguments);
05552          ast_safe_system(arguments);
05553       }
05554    }
05555 }
05556 
05557 /*!
05558  * \brief Variables used for saving a voicemail.
05559  *
05560  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05561  */
05562 struct leave_vm_options {
05563    unsigned int flags;
05564    signed char record_gain;
05565    char *exitcontext;
05566 };
05567 
05568 /*!
05569  * \brief Prompts the user and records a voicemail to a mailbox.
05570  * \param chan
05571  * \param ext
05572  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05573  * 
05574  * 
05575  * 
05576  * \return zero on success, -1 on error.
05577  */
05578 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05579 {
05580 #ifdef IMAP_STORAGE
05581    int newmsgs, oldmsgs;
05582 #else
05583    char urgdir[PATH_MAX];
05584 #endif
05585    char txtfile[PATH_MAX];
05586    char tmptxtfile[PATH_MAX];
05587    struct vm_state *vms = NULL;
05588    char callerid[256];
05589    FILE *txt;
05590    char date[256];
05591    int txtdes;
05592    int res = 0;
05593    int msgnum;
05594    int duration = 0;
05595    int sound_duration = 0;
05596    int ausemacro = 0;
05597    int ousemacro = 0;
05598    int ouseexten = 0;
05599    char tmpdur[16];
05600    char priority[16];
05601    char origtime[16];
05602    char dir[PATH_MAX];
05603    char tmpdir[PATH_MAX];
05604    char fn[PATH_MAX];
05605    char prefile[PATH_MAX] = "";
05606    char tempfile[PATH_MAX] = "";
05607    char ext_context[256] = "";
05608    char fmt[80];
05609    char *context;
05610    char ecodes[17] = "#";
05611    struct ast_str *tmp = ast_str_create(16);
05612    char *tmpptr;
05613    struct ast_vm_user *vmu;
05614    struct ast_vm_user svm;
05615    const char *category = NULL;
05616    const char *code;
05617    const char *alldtmf = "0123456789ABCD*#";
05618    char flag[80];
05619 
05620    if (!tmp) {
05621       return -1;
05622    }
05623 
05624    ast_str_set(&tmp, 0, "%s", ext);
05625    ext = ast_str_buffer(tmp);
05626    if ((context = strchr(ext, '@'))) {
05627       *context++ = '\0';
05628       tmpptr = strchr(context, '&');
05629    } else {
05630       tmpptr = strchr(ext, '&');
05631    }
05632 
05633    if (tmpptr)
05634       *tmpptr++ = '\0';
05635 
05636    ast_channel_lock(chan);
05637    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05638       category = ast_strdupa(category);
05639    }
05640    ast_channel_unlock(chan);
05641 
05642    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05643       ast_copy_string(flag, "Urgent", sizeof(flag));
05644    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05645       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05646    } else {
05647       flag[0] = '\0';
05648    }
05649 
05650    ast_debug(3, "Before find_user\n");
05651    if (!(vmu = find_user(&svm, context, ext))) {
05652       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05653       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05654       ast_free(tmp);
05655       return res;
05656    }
05657    /* Setup pre-file if appropriate */
05658    if (strcmp(vmu->context, "default"))
05659       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05660    else
05661       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05662 
05663    /* Set the path to the prefile. Will be one of 
05664       VM_SPOOL_DIRcontext/ext/busy
05665       VM_SPOOL_DIRcontext/ext/unavail
05666       Depending on the flag set in options.
05667    */
05668    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05669       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05670    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05671       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05672    }
05673    /* Set the path to the tmpfile as
05674       VM_SPOOL_DIR/context/ext/temp
05675       and attempt to create the folder structure.
05676    */
05677    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05678    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05679       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05680       ast_free(tmp);
05681       return -1;
05682    }
05683    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05684    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05685       ast_copy_string(prefile, tempfile, sizeof(prefile));
05686 
05687    DISPOSE(tempfile, -1);
05688    /* It's easier just to try to make it than to check for its existence */
05689 #ifndef IMAP_STORAGE
05690    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05691 #else
05692    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05693    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05694       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05695    }
05696 #endif
05697 
05698    /* Check current or macro-calling context for special extensions */
05699    if (ast_test_flag(vmu, VM_OPERATOR)) {
05700       if (!ast_strlen_zero(vmu->exit)) {
05701          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05702             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05703             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05704             ouseexten = 1;
05705          }
05706       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05707          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05708          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05709          ouseexten = 1;
05710       } else if (!ast_strlen_zero(chan->macrocontext)
05711          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05712             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05713          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05714          ousemacro = 1;
05715       }
05716    }
05717 
05718    if (!ast_strlen_zero(vmu->exit)) {
05719       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05720          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05721          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05722       }
05723    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05724       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05725       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05726    } else if (!ast_strlen_zero(chan->macrocontext)
05727       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05728          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05729       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05730       ausemacro = 1;
05731    }
05732 
05733    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05734       for (code = alldtmf; *code; code++) {
05735          char e[2] = "";
05736          e[0] = *code;
05737          if (strchr(ecodes, e[0]) == NULL
05738             && ast_canmatch_extension(chan,
05739                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
05740                e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05741             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05742          }
05743       }
05744    }
05745 
05746    /* Play the beginning intro if desired */
05747    if (!ast_strlen_zero(prefile)) {
05748 #ifdef ODBC_STORAGE
05749       int success = 
05750 #endif
05751          RETRIEVE(prefile, -1, ext, context);
05752       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05753          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05754             res = ast_waitstream(chan, ecodes);
05755 #ifdef ODBC_STORAGE
05756          if (success == -1) {
05757             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05758             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05759             store_file(prefile, vmu->mailbox, vmu->context, -1);
05760          }
05761 #endif
05762       } else {
05763          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05764          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05765       }
05766       DISPOSE(prefile, -1);
05767       if (res < 0) {
05768          ast_debug(1, "Hang up during prefile playback\n");
05769          free_user(vmu);
05770          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05771          ast_free(tmp);
05772          return -1;
05773       }
05774    }
05775    if (res == '#') {
05776       /* On a '#' we skip the instructions */
05777       ast_set_flag(options, OPT_SILENT);
05778       res = 0;
05779    }
05780    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05781    if (vmu->maxmsg == 0) {
05782       if (option_debug > 2)
05783          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05784       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05785       goto leave_vm_out;
05786    }
05787    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05788       res = ast_stream_and_wait(chan, INTRO, ecodes);
05789       if (res == '#') {
05790          ast_set_flag(options, OPT_SILENT);
05791          res = 0;
05792       }
05793    }
05794    if (res > 0)
05795       ast_stopstream(chan);
05796    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05797     other than the operator -- an automated attendant or mailbox login for example */
05798    if (res == '*') {
05799       chan->exten[0] = 'a';
05800       chan->exten[1] = '\0';
05801       if (!ast_strlen_zero(vmu->exit)) {
05802          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05803       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05804          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05805       }
05806       chan->priority = 0;
05807       free_user(vmu);
05808       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05809       ast_free(tmp);
05810       return 0;
05811    }
05812 
05813    /* Check for a '0' here */
05814    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05815    transfer:
05816       if (ouseexten || ousemacro) {
05817          chan->exten[0] = 'o';
05818          chan->exten[1] = '\0';
05819          if (!ast_strlen_zero(vmu->exit)) {
05820             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05821          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05822             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05823          }
05824          ast_play_and_wait(chan, "transfer");
05825          chan->priority = 0;
05826          free_user(vmu);
05827          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05828       }
05829       ast_free(tmp);
05830       return OPERATOR_EXIT;
05831    }
05832 
05833    /* Allow all other digits to exit Voicemail and return to the dialplan */
05834    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05835       if (!ast_strlen_zero(options->exitcontext)) {
05836          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05837       }
05838       free_user(vmu);
05839       ast_free(tmp);
05840       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05841       return res;
05842    }
05843 
05844    if (res < 0) {
05845       free_user(vmu);
05846       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05847       ast_free(tmp);
05848       return -1;
05849    }
05850    /* The meat of recording the message...  All the announcements and beeps have been played*/
05851    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05852    if (!ast_strlen_zero(fmt)) {
05853       msgnum = 0;
05854 
05855 #ifdef IMAP_STORAGE
05856       /* Is ext a mailbox? */
05857       /* must open stream for this user to get info! */
05858       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05859       if (res < 0) {
05860          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05861          ast_free(tmp);
05862          return -1;
05863       }
05864       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05865       /* It is possible under certain circumstances that inboxcount did not
05866        * create a vm_state when it was needed. This is a catchall which will
05867        * rarely be used.
05868        */
05869          if (!(vms = create_vm_state_from_user(vmu))) {
05870             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05871             ast_free(tmp);
05872             return -1;
05873          }
05874       }
05875       vms->newmessages++;
05876       
05877       /* here is a big difference! We add one to it later */
05878       msgnum = newmsgs + oldmsgs;
05879       ast_debug(3, "Messagecount set to %d\n", msgnum);
05880       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05881       /* set variable for compatibility */
05882       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05883 
05884       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05885          goto leave_vm_out;
05886       }
05887 #else
05888       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05889          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05890          if (!res)
05891             res = ast_waitstream(chan, "");
05892          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05893          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05894          inprocess_count(vmu->mailbox, vmu->context, -1);
05895          goto leave_vm_out;
05896       }
05897 
05898 #endif
05899       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05900       txtdes = mkstemp(tmptxtfile);
05901       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05902       if (txtdes < 0) {
05903          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05904          if (!res)
05905             res = ast_waitstream(chan, "");
05906          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05907          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05908          inprocess_count(vmu->mailbox, vmu->context, -1);
05909          goto leave_vm_out;
05910       }
05911 
05912       /* Now play the beep once we have the message number for our next message. */
05913       if (res >= 0) {
05914          /* Unless we're *really* silent, try to send the beep */
05915          res = ast_stream_and_wait(chan, "beep", "");
05916       }
05917             
05918       /* Store information in real-time storage */
05919       if (ast_check_realtime("voicemail_data")) {
05920          snprintf(priority, sizeof(priority), "%d", chan->priority);
05921          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05922          get_date(date, sizeof(date));
05923          ast_callerid_merge(callerid, sizeof(callerid),
05924             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05925             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05926             "Unknown");
05927          ast_store_realtime("voicemail_data",
05928             "origmailbox", ext,
05929             "context", chan->context,
05930             "macrocontext", chan->macrocontext,
05931             "exten", chan->exten,
05932             "priority", priority,
05933             "callerchan", chan->name,
05934             "callerid", callerid,
05935             "origdate", date,
05936             "origtime", origtime,
05937             "category", S_OR(category, ""),
05938             "filename", tmptxtfile,
05939             SENTINEL);
05940       }
05941 
05942       /* Store information */
05943       txt = fdopen(txtdes, "w+");
05944       if (txt) {
05945          get_date(date, sizeof(date));
05946          ast_callerid_merge(callerid, sizeof(callerid),
05947             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05948             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05949             "Unknown");
05950          fprintf(txt, 
05951             ";\n"
05952             "; Message Information file\n"
05953             ";\n"
05954             "[message]\n"
05955             "origmailbox=%s\n"
05956             "context=%s\n"
05957             "macrocontext=%s\n"
05958             "exten=%s\n"
05959             "rdnis=%s\n"
05960             "priority=%d\n"
05961             "callerchan=%s\n"
05962             "callerid=%s\n"
05963             "origdate=%s\n"
05964             "origtime=%ld\n"
05965             "category=%s\n",
05966             ext,
05967             chan->context,
05968             chan->macrocontext, 
05969             chan->exten,
05970             S_COR(chan->redirecting.from.number.valid,
05971                chan->redirecting.from.number.str, "unknown"),
05972             chan->priority,
05973             chan->name,
05974             callerid,
05975             date, (long) time(NULL),
05976             category ? category : "");
05977       } else {
05978          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05979          inprocess_count(vmu->mailbox, vmu->context, -1);
05980          if (ast_check_realtime("voicemail_data")) {
05981             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05982          }
05983          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05984          goto leave_vm_out;
05985       }
05986       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
05987 
05988       if (txt) {
05989          fprintf(txt, "flag=%s\n", flag);
05990          if (sound_duration < vmu->minsecs) {
05991             fclose(txt);
05992             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
05993             ast_filedelete(tmptxtfile, NULL);
05994             unlink(tmptxtfile);
05995             if (ast_check_realtime("voicemail_data")) {
05996                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05997             }
05998             inprocess_count(vmu->mailbox, vmu->context, -1);
05999          } else {
06000             fprintf(txt, "duration=%d\n", duration);
06001             fclose(txt);
06002             if (vm_lock_path(dir)) {
06003                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06004                /* Delete files */
06005                ast_filedelete(tmptxtfile, NULL);
06006                unlink(tmptxtfile);
06007                inprocess_count(vmu->mailbox, vmu->context, -1);
06008             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06009                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06010                unlink(tmptxtfile);
06011                ast_unlock_path(dir);
06012                inprocess_count(vmu->mailbox, vmu->context, -1);
06013                if (ast_check_realtime("voicemail_data")) {
06014                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06015                }
06016             } else {
06017 #ifndef IMAP_STORAGE
06018                msgnum = last_message_index(vmu, dir) + 1;
06019 #endif
06020                make_file(fn, sizeof(fn), dir, msgnum);
06021 
06022                /* assign a variable with the name of the voicemail file */ 
06023 #ifndef IMAP_STORAGE
06024                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06025 #else
06026                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06027 #endif
06028 
06029                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06030                ast_filerename(tmptxtfile, fn, NULL);
06031                rename(tmptxtfile, txtfile);
06032                inprocess_count(vmu->mailbox, vmu->context, -1);
06033 
06034                /* Properly set permissions on voicemail text descriptor file.
06035                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06036                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06037                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06038 
06039                ast_unlock_path(dir);
06040                if (ast_check_realtime("voicemail_data")) {
06041                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06042                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06043                }
06044                /* We must store the file first, before copying the message, because
06045                 * ODBC storage does the entire copy with SQL.
06046                 */
06047                if (ast_fileexists(fn, NULL, NULL) > 0) {
06048                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06049                }
06050 
06051                /* Are there to be more recipients of this message? */
06052                while (tmpptr) {
06053                   struct ast_vm_user recipu, *recip;
06054                   char *exten, *cntx;
06055 
06056                   exten = strsep(&tmpptr, "&");
06057                   cntx = strchr(exten, '@');
06058                   if (cntx) {
06059                      *cntx = '\0';
06060                      cntx++;
06061                   }
06062                   if ((recip = find_user(&recipu, cntx, exten))) {
06063                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06064                      free_user(recip);
06065                   }
06066                }
06067 #ifndef IMAP_STORAGE
06068                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06069                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06070                   char sfn[PATH_MAX];
06071                   char dfn[PATH_MAX];
06072                   int x;
06073                   /* It's easier just to try to make it than to check for its existence */
06074                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06075                   x = last_message_index(vmu, urgdir) + 1;
06076                   make_file(sfn, sizeof(sfn), dir, msgnum);
06077                   make_file(dfn, sizeof(dfn), urgdir, x);
06078                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06079                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06080                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06081                   ast_copy_string(fn, dfn, sizeof(fn));
06082                   msgnum = x;
06083                }
06084 #endif
06085                /* Notification needs to happen after the copy, though. */
06086                if (ast_fileexists(fn, NULL, NULL)) {
06087 #ifdef IMAP_STORAGE
06088                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06089                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06090                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06091                      flag);
06092 #else
06093                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06094                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06095                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06096                      flag);
06097 #endif
06098                }
06099 
06100                /* Disposal needs to happen after the optional move and copy */
06101                if (ast_fileexists(fn, NULL, NULL)) {
06102                   DISPOSE(dir, msgnum);
06103                }
06104             }
06105          }
06106       } else {
06107          inprocess_count(vmu->mailbox, vmu->context, -1);
06108       }
06109       if (res == '0') {
06110          goto transfer;
06111       } else if (res > 0 && res != 't')
06112          res = 0;
06113 
06114       if (sound_duration < vmu->minsecs)
06115          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06116          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06117       else
06118          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06119    } else
06120       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06121 leave_vm_out:
06122    free_user(vmu);
06123 
06124 #ifdef IMAP_STORAGE
06125    /* expunge message - use UID Expunge if supported on IMAP server*/
06126    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06127    if (expungeonhangup == 1) {
06128       ast_mutex_lock(&vms->lock);
06129 #ifdef HAVE_IMAP_TK2006
06130       if (LEVELUIDPLUS (vms->mailstream)) {
06131          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06132       } else 
06133 #endif
06134          mail_expunge(vms->mailstream);
06135       ast_mutex_unlock(&vms->lock);
06136    }
06137 #endif
06138 
06139    ast_free(tmp);
06140    return res;
06141 }
06142 
06143 #if !defined(IMAP_STORAGE)
06144 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06145 {
06146    /* we know the actual number of messages, so stop process when number is hit */
06147 
06148    int x, dest;
06149    char sfn[PATH_MAX];
06150    char dfn[PATH_MAX];
06151 
06152    if (vm_lock_path(dir)) {
06153       return ERROR_LOCK_PATH;
06154    }
06155 
06156    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06157       make_file(sfn, sizeof(sfn), dir, x);
06158       if (EXISTS(dir, x, sfn, NULL)) {
06159 
06160          if (x != dest) {
06161             make_file(dfn, sizeof(dfn), dir, dest);
06162             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06163          }
06164 
06165          dest++;
06166       }
06167    }
06168    ast_unlock_path(dir);
06169 
06170    return dest;
06171 }
06172 #endif
06173 
06174 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06175 {
06176    int d;
06177    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06178    return d;
06179 }
06180 
06181 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06182 {
06183 #ifdef IMAP_STORAGE
06184    /* we must use mbox(x) folder names, and copy the message there */
06185    /* simple. huh? */
06186    char sequence[10];
06187    char mailbox[256];
06188    int res;
06189 
06190    /* get the real IMAP message number for this message */
06191    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06192    
06193    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06194    ast_mutex_lock(&vms->lock);
06195    /* if save to Old folder, put in INBOX as read */
06196    if (box == OLD_FOLDER) {
06197       mail_setflag(vms->mailstream, sequence, "\\Seen");
06198       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06199    } else if (box == NEW_FOLDER) {
06200       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06201       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06202    }
06203    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06204       ast_mutex_unlock(&vms->lock);
06205       return 0;
06206    }
06207    /* Create the folder if it don't exist */
06208    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06209    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06210    if (mail_create(vms->mailstream, mailbox) == NIL) 
06211       ast_debug(5, "Folder exists.\n");
06212    else
06213       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06214    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06215    ast_mutex_unlock(&vms->lock);
06216    return res;
06217 #else
06218    char *dir = vms->curdir;
06219    char *username = vms->username;
06220    char *context = vmu->context;
06221    char sfn[PATH_MAX];
06222    char dfn[PATH_MAX];
06223    char ddir[PATH_MAX];
06224    const char *dbox = mbox(vmu, box);
06225    int x, i;
06226    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06227 
06228    if (vm_lock_path(ddir))
06229       return ERROR_LOCK_PATH;
06230 
06231    x = last_message_index(vmu, ddir) + 1;
06232 
06233    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06234       x--;
06235       for (i = 1; i <= x; i++) {
06236          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06237          make_file(sfn, sizeof(sfn), ddir, i);
06238          make_file(dfn, sizeof(dfn), ddir, i - 1);
06239          if (EXISTS(ddir, i, sfn, NULL)) {
06240             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06241          } else
06242             break;
06243       }
06244    } else {
06245       if (x >= vmu->maxmsg) {
06246          ast_unlock_path(ddir);
06247          return -1;
06248       }
06249    }
06250    make_file(sfn, sizeof(sfn), dir, msg);
06251    make_file(dfn, sizeof(dfn), ddir, x);
06252    if (strcmp(sfn, dfn)) {
06253       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06254    }
06255    ast_unlock_path(ddir);
06256 #endif
06257    return 0;
06258 }
06259 
06260 static int adsi_logo(unsigned char *buf)
06261 {
06262    int bytes = 0;
06263    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06264    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06265    return bytes;
06266 }
06267 
06268 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06269 {
06270    unsigned char buf[256];
06271    int bytes = 0;
06272    int x;
06273    char num[5];
06274 
06275    *useadsi = 0;
06276    bytes += ast_adsi_data_mode(buf + bytes);
06277    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06278 
06279    bytes = 0;
06280    bytes += adsi_logo(buf);
06281    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06282 #ifdef DISPLAY
06283    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06284 #endif
06285    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06286    bytes += ast_adsi_data_mode(buf + bytes);
06287    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06288 
06289    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06290       bytes = 0;
06291       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06292       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06293       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06294       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06295       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06296       return 0;
06297    }
06298 
06299 #ifdef DISPLAY
06300    /* Add a dot */
06301    bytes = 0;
06302    bytes += ast_adsi_logo(buf);
06303    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06304    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06305    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06306    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06307 #endif
06308    bytes = 0;
06309    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06310    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06311    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06312    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06313    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06314    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06315    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06316 
06317 #ifdef DISPLAY
06318    /* Add another dot */
06319    bytes = 0;
06320    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06321    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06322 
06323    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06324    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06325 #endif
06326 
06327    bytes = 0;
06328    /* These buttons we load but don't use yet */
06329    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06330    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06331    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06332    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06333    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06334    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06335    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06336 
06337 #ifdef DISPLAY
06338    /* Add another dot */
06339    bytes = 0;
06340    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06341    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06342    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06343 #endif
06344 
06345    bytes = 0;
06346    for (x = 0; x < 5; x++) {
06347       snprintf(num, sizeof(num), "%d", x);
06348       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06349    }
06350    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06351    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06352 
06353 #ifdef DISPLAY
06354    /* Add another dot */
06355    bytes = 0;
06356    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06357    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06358    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06359 #endif
06360 
06361    if (ast_adsi_end_download(chan)) {
06362       bytes = 0;
06363       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06364       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06365       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06366       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06367       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06368       return 0;
06369    }
06370    bytes = 0;
06371    bytes += ast_adsi_download_disconnect(buf + bytes);
06372    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06373    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06374 
06375    ast_debug(1, "Done downloading scripts...\n");
06376 
06377 #ifdef DISPLAY
06378    /* Add last dot */
06379    bytes = 0;
06380    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06381    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06382 #endif
06383    ast_debug(1, "Restarting session...\n");
06384 
06385    bytes = 0;
06386    /* Load the session now */
06387    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06388       *useadsi = 1;
06389       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06390    } else
06391       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06392 
06393    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06394    return 0;
06395 }
06396 
06397 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06398 {
06399    int x;
06400    if (!ast_adsi_available(chan))
06401       return;
06402    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06403    if (x < 0)
06404       return;
06405    if (!x) {
06406       if (adsi_load_vmail(chan, useadsi)) {
06407          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06408          return;
06409       }
06410    } else
06411       *useadsi = 1;
06412 }
06413 
06414 static void adsi_login(struct ast_channel *chan)
06415 {
06416    unsigned char buf[256];
06417    int bytes = 0;
06418    unsigned char keys[8];
06419    int x;
06420    if (!ast_adsi_available(chan))
06421       return;
06422 
06423    for (x = 0; x < 8; x++)
06424       keys[x] = 0;
06425    /* Set one key for next */
06426    keys[3] = ADSI_KEY_APPS + 3;
06427 
06428    bytes += adsi_logo(buf + bytes);
06429    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06430    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06431    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06432    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06433    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06434    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06435    bytes += ast_adsi_set_keys(buf + bytes, keys);
06436    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06437    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06438 }
06439 
06440 static void adsi_password(struct ast_channel *chan)
06441 {
06442    unsigned char buf[256];
06443    int bytes = 0;
06444    unsigned char keys[8];
06445    int x;
06446    if (!ast_adsi_available(chan))
06447       return;
06448 
06449    for (x = 0; x < 8; x++)
06450       keys[x] = 0;
06451    /* Set one key for next */
06452    keys[3] = ADSI_KEY_APPS + 3;
06453 
06454    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06455    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06456    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06457    bytes += ast_adsi_set_keys(buf + bytes, keys);
06458    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06459    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06460 }
06461 
06462 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06463 {
06464    unsigned char buf[256];
06465    int bytes = 0;
06466    unsigned char keys[8];
06467    int x, y;
06468 
06469    if (!ast_adsi_available(chan))
06470       return;
06471 
06472    for (x = 0; x < 5; x++) {
06473       y = ADSI_KEY_APPS + 12 + start + x;
06474       if (y > ADSI_KEY_APPS + 12 + 4)
06475          y = 0;
06476       keys[x] = ADSI_KEY_SKT | y;
06477    }
06478    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06479    keys[6] = 0;
06480    keys[7] = 0;
06481 
06482    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06483    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06484    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06485    bytes += ast_adsi_set_keys(buf + bytes, keys);
06486    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06487 
06488    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06489 }
06490 
06491 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06492 {
06493    int bytes = 0;
06494    unsigned char buf[256]; 
06495    char buf1[256], buf2[256];
06496    char fn2[PATH_MAX];
06497 
06498    char cid[256] = "";
06499    char *val;
06500    char *name, *num;
06501    char datetime[21] = "";
06502    FILE *f;
06503 
06504    unsigned char keys[8];
06505 
06506    int x;
06507 
06508    if (!ast_adsi_available(chan))
06509       return;
06510 
06511    /* Retrieve important info */
06512    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06513    f = fopen(fn2, "r");
06514    if (f) {
06515       while (!feof(f)) {   
06516          if (!fgets((char *) buf, sizeof(buf), f)) {
06517             continue;
06518          }
06519          if (!feof(f)) {
06520             char *stringp = NULL;
06521             stringp = (char *) buf;
06522             strsep(&stringp, "=");
06523             val = strsep(&stringp, "=");
06524             if (!ast_strlen_zero(val)) {
06525                if (!strcmp((char *) buf, "callerid"))
06526                   ast_copy_string(cid, val, sizeof(cid));
06527                if (!strcmp((char *) buf, "origdate"))
06528                   ast_copy_string(datetime, val, sizeof(datetime));
06529             }
06530          }
06531       }
06532       fclose(f);
06533    }
06534    /* New meaning for keys */
06535    for (x = 0; x < 5; x++)
06536       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06537    keys[6] = 0x0;
06538    keys[7] = 0x0;
06539 
06540    if (!vms->curmsg) {
06541       /* No prev key, provide "Folder" instead */
06542       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06543    }
06544    if (vms->curmsg >= vms->lastmsg) {
06545       /* If last message ... */
06546       if (vms->curmsg) {
06547          /* but not only message, provide "Folder" instead */
06548          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06549          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06550 
06551       } else {
06552          /* Otherwise if only message, leave blank */
06553          keys[3] = 1;
06554       }
06555    }
06556 
06557    if (!ast_strlen_zero(cid)) {
06558       ast_callerid_parse(cid, &name, &num);
06559       if (!name)
06560          name = num;
06561    } else
06562       name = "Unknown Caller";
06563 
06564    /* If deleted, show "undeleted" */
06565 #ifdef IMAP_STORAGE
06566    ast_mutex_lock(&vms->lock);
06567 #endif
06568    if (vms->deleted[vms->curmsg]) {
06569       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06570    }
06571 #ifdef IMAP_STORAGE
06572    ast_mutex_unlock(&vms->lock);
06573 #endif
06574 
06575    /* Except "Exit" */
06576    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06577    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06578       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06579    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06580 
06581    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06582    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06583    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06584    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06585    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06586    bytes += ast_adsi_set_keys(buf + bytes, keys);
06587    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06588 
06589    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06590 }
06591 
06592 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06593 {
06594    int bytes = 0;
06595    unsigned char buf[256];
06596    unsigned char keys[8];
06597 
06598    int x;
06599 
06600    if (!ast_adsi_available(chan))
06601       return;
06602 
06603    /* New meaning for keys */
06604    for (x = 0; x < 5; x++)
06605       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06606 
06607    keys[6] = 0x0;
06608    keys[7] = 0x0;
06609 
06610    if (!vms->curmsg) {
06611       /* No prev key, provide "Folder" instead */
06612       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06613    }
06614    if (vms->curmsg >= vms->lastmsg) {
06615       /* If last message ... */
06616       if (vms->curmsg) {
06617          /* but not only message, provide "Folder" instead */
06618          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06619       } else {
06620          /* Otherwise if only message, leave blank */
06621          keys[3] = 1;
06622       }
06623    }
06624 
06625    /* If deleted, show "undeleted" */
06626 #ifdef IMAP_STORAGE
06627    ast_mutex_lock(&vms->lock);
06628 #endif
06629    if (vms->deleted[vms->curmsg]) {
06630       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06631    }
06632 #ifdef IMAP_STORAGE
06633    ast_mutex_unlock(&vms->lock);
06634 #endif
06635 
06636    /* Except "Exit" */
06637    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06638    bytes += ast_adsi_set_keys(buf + bytes, keys);
06639    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06640 
06641    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06642 }
06643 
06644 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06645 {
06646    unsigned char buf[256] = "";
06647    char buf1[256] = "", buf2[256] = "";
06648    int bytes = 0;
06649    unsigned char keys[8];
06650    int x;
06651 
06652    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06653    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06654    if (!ast_adsi_available(chan))
06655       return;
06656    if (vms->newmessages) {
06657       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06658       if (vms->oldmessages) {
06659          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06660          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06661       } else {
06662          snprintf(buf2, sizeof(buf2), "%s.", newm);
06663       }
06664    } else if (vms->oldmessages) {
06665       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06666       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06667    } else {
06668       strcpy(buf1, "You have no messages.");
06669       buf2[0] = ' ';
06670       buf2[1] = '\0';
06671    }
06672    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06673    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06674    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06675 
06676    for (x = 0; x < 6; x++)
06677       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06678    keys[6] = 0;
06679    keys[7] = 0;
06680 
06681    /* Don't let them listen if there are none */
06682    if (vms->lastmsg < 0)
06683       keys[0] = 1;
06684    bytes += ast_adsi_set_keys(buf + bytes, keys);
06685 
06686    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06687 
06688    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06689 }
06690 
06691 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06692 {
06693    unsigned char buf[256] = "";
06694    char buf1[256] = "", buf2[256] = "";
06695    int bytes = 0;
06696    unsigned char keys[8];
06697    int x;
06698 
06699    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06700 
06701    if (!ast_adsi_available(chan))
06702       return;
06703 
06704    /* Original command keys */
06705    for (x = 0; x < 6; x++)
06706       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06707 
06708    keys[6] = 0;
06709    keys[7] = 0;
06710 
06711    if ((vms->lastmsg + 1) < 1)
06712       keys[0] = 0;
06713 
06714    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06715       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06716 
06717    if (vms->lastmsg + 1)
06718       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06719    else
06720       strcpy(buf2, "no messages.");
06721    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06722    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06723    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06724    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06725    bytes += ast_adsi_set_keys(buf + bytes, keys);
06726 
06727    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06728 
06729    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06730    
06731 }
06732 
06733 /*
06734 static void adsi_clear(struct ast_channel *chan)
06735 {
06736    char buf[256];
06737    int bytes=0;
06738    if (!ast_adsi_available(chan))
06739       return;
06740    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06741    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06742 
06743    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06744 }
06745 */
06746 
06747 static void adsi_goodbye(struct ast_channel *chan)
06748 {
06749    unsigned char buf[256];
06750    int bytes = 0;
06751 
06752    if (!ast_adsi_available(chan))
06753       return;
06754    bytes += adsi_logo(buf + bytes);
06755    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06756    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06757    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06758    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06759 
06760    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06761 }
06762 
06763 /*!\brief get_folder: Folder menu
06764  * Plays "press 1 for INBOX messages" etc.
06765  * Should possibly be internationalized
06766  */
06767 static int get_folder(struct ast_channel *chan, int start)
06768 {
06769    int x;
06770    int d;
06771    char fn[PATH_MAX];
06772    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06773    if (d)
06774       return d;
06775    for (x = start; x < 5; x++) { /* For all folders */
06776       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06777          return d;
06778       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06779       if (d)
06780          return d;
06781       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06782 
06783       /* The inbox folder can have its name changed under certain conditions
06784        * so this checks if the sound file exists for the inbox folder name and
06785        * if it doesn't, plays the default name instead. */
06786       if (x == 0) {
06787          if (ast_fileexists(fn, NULL, NULL)) {
06788             d = vm_play_folder_name(chan, fn);
06789          } else {
06790             ast_verb(1, "failed to find %s\n", fn);
06791             d = vm_play_folder_name(chan, "vm-INBOX");
06792          }
06793       } else {
06794          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06795          d = vm_play_folder_name(chan, fn);
06796       }
06797 
06798       if (d)
06799          return d;
06800       d = ast_waitfordigit(chan, 500);
06801       if (d)
06802          return d;
06803    }
06804 
06805    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06806    if (d)
06807       return d;
06808    d = ast_waitfordigit(chan, 4000);
06809    return d;
06810 }
06811 
06812 /*!
06813  * \brief plays a prompt and waits for a keypress.
06814  * \param chan
06815  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06816  * \param start Does not appear to be used at this time.
06817  *
06818  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06819  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06820  * prompting for the number inputs that correspond to the available folders.
06821  * 
06822  * \return zero on success, or -1 on error.
06823  */
06824 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06825 {
06826    int res = 0;
06827    int loops = 0;
06828 
06829    res = ast_play_and_wait(chan, fn);  /* Folder name */
06830    while (((res < '0') || (res > '9')) &&
06831          (res != '#') && (res >= 0) &&
06832          loops < 4) {
06833       res = get_folder(chan, 0);
06834       loops++;
06835    }
06836    if (loops == 4) { /* give up */
06837       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06838       return '#';
06839    }
06840    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06841    return res;
06842 }
06843 
06844 /*!
06845  * \brief presents the option to prepend to an existing message when forwarding it.
06846  * \param chan
06847  * \param vmu
06848  * \param curdir
06849  * \param curmsg
06850  * \param vm_fmts
06851  * \param context
06852  * \param record_gain
06853  * \param duration
06854  * \param vms
06855  * \param flag 
06856  *
06857  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06858  *
06859  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06860  * \return zero on success, -1 on error.
06861  */
06862 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06863          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06864 {
06865    int cmd = 0;
06866    int retries = 0, prepend_duration = 0, already_recorded = 0;
06867    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06868    char textfile[PATH_MAX];
06869    struct ast_config *msg_cfg;
06870    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06871 #ifndef IMAP_STORAGE
06872    signed char zero_gain = 0;
06873 #endif
06874    const char *duration_str;
06875 
06876    /* Must always populate duration correctly */
06877    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06878    strcpy(textfile, msgfile);
06879    strcpy(backup, msgfile);
06880    strcpy(backup_textfile, msgfile);
06881    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06882    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06883    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06884 
06885    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06886       *duration = atoi(duration_str);
06887    } else {
06888       *duration = 0;
06889    }
06890 
06891    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06892       if (cmd)
06893          retries = 0;
06894       switch (cmd) {
06895       case '1': 
06896 
06897 #ifdef IMAP_STORAGE
06898          /* Record new intro file */
06899          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06900          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06901          ast_play_and_wait(chan, INTRO);
06902          ast_play_and_wait(chan, "beep");
06903          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06904          if (cmd == -1) {
06905             break;
06906          }
06907          cmd = 't';
06908 #else
06909 
06910          /* prepend a message to the current message, update the metadata and return */
06911 
06912          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06913          strcpy(textfile, msgfile);
06914          strncat(textfile, ".txt", sizeof(textfile) - 1);
06915          *duration = 0;
06916 
06917          /* if we can't read the message metadata, stop now */
06918          if (!msg_cfg) {
06919             cmd = 0;
06920             break;
06921          }
06922 
06923          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06924 #ifndef IMAP_STORAGE
06925          if (already_recorded) {
06926             ast_filecopy(backup, msgfile, NULL);
06927             copy(backup_textfile, textfile);
06928          }
06929          else {
06930             ast_filecopy(msgfile, backup, NULL);
06931             copy(textfile, backup_textfile);
06932          }
06933 #endif
06934          already_recorded = 1;
06935 
06936          if (record_gain)
06937             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06938 
06939          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06940 
06941          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06942             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06943             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06944             ast_filerename(backup, msgfile, NULL);
06945          }
06946 
06947          if (record_gain)
06948             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06949 
06950          
06951          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06952             *duration = atoi(duration_str);
06953 
06954          if (prepend_duration) {
06955             struct ast_category *msg_cat;
06956             /* need enough space for a maximum-length message duration */
06957             char duration_buf[12];
06958 
06959             *duration += prepend_duration;
06960             msg_cat = ast_category_get(msg_cfg, "message");
06961             snprintf(duration_buf, 11, "%ld", *duration);
06962             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06963                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06964             }
06965          }
06966 
06967 #endif
06968          break;
06969       case '2': 
06970          /* NULL out introfile so we know there is no intro! */
06971 #ifdef IMAP_STORAGE
06972          *vms->introfn = '\0';
06973 #endif
06974          cmd = 't';
06975          break;
06976       case '*':
06977          cmd = '*';
06978          break;
06979       default: 
06980          /* If time_out and return to menu, reset already_recorded */
06981          already_recorded = 0;
06982 
06983          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06984             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06985          if (!cmd) {
06986             cmd = ast_play_and_wait(chan, "vm-starmain");
06987             /* "press star to return to the main menu" */
06988          }
06989          if (!cmd) {
06990             cmd = ast_waitfordigit(chan, 6000);
06991          }
06992          if (!cmd) {
06993             retries++;
06994          }
06995          if (retries > 3) {
06996             cmd = '*'; /* Let's cancel this beast */
06997          }
06998          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
06999       }
07000    }
07001 
07002    if (msg_cfg)
07003       ast_config_destroy(msg_cfg);
07004    if (prepend_duration)
07005       *duration = prepend_duration;
07006 
07007    if (already_recorded && cmd == -1) {
07008       /* restore original message if prepention cancelled */
07009       ast_filerename(backup, msgfile, NULL);
07010       rename(backup_textfile, textfile);
07011    }
07012 
07013    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07014       cmd = 0;
07015    return cmd;
07016 }
07017 
07018 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07019 {
07020    struct ast_event *event;
07021    char *mailbox, *context;
07022 
07023    /* Strip off @default */
07024    context = mailbox = ast_strdupa(box);
07025    strsep(&context, "@");
07026    if (ast_strlen_zero(context))
07027       context = "default";
07028 
07029    if (!(event = ast_event_new(AST_EVENT_MWI,
07030          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07031          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07032          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07033          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07034          AST_EVENT_IE_END))) {
07035       return;
07036    }
07037 
07038    ast_event_queue_and_cache(event);
07039 }
07040 
07041 /*!
07042  * \brief Sends email notification that a user has a new voicemail waiting for them.
07043  * \param chan
07044  * \param vmu
07045  * \param vms
07046  * \param msgnum
07047  * \param duration
07048  * \param fmt
07049  * \param cidnum The Caller ID phone number value.
07050  * \param cidname The Caller ID name value.
07051  * \param flag
07052  *
07053  * \return zero on success, -1 on error.
07054  */
07055 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)
07056 {
07057    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07058    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07059    const char *category;
07060    char *myserveremail = serveremail;
07061 
07062    ast_channel_lock(chan);
07063    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07064       category = ast_strdupa(category);
07065    }
07066    ast_channel_unlock(chan);
07067 
07068 #ifndef IMAP_STORAGE
07069    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07070 #else
07071    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07072 #endif
07073    make_file(fn, sizeof(fn), todir, msgnum);
07074    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07075 
07076    if (!ast_strlen_zero(vmu->attachfmt)) {
07077       if (strstr(fmt, vmu->attachfmt))
07078          fmt = vmu->attachfmt;
07079       else
07080          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);
07081    }
07082 
07083    /* Attach only the first format */
07084    fmt = ast_strdupa(fmt);
07085    stringp = fmt;
07086    strsep(&stringp, "|");
07087 
07088    if (!ast_strlen_zero(vmu->serveremail))
07089       myserveremail = vmu->serveremail;
07090 
07091    if (!ast_strlen_zero(vmu->email)) {
07092       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07093 
07094       if (attach_user_voicemail)
07095          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07096 
07097       /* XXX possible imap issue, should category be NULL XXX */
07098       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07099 
07100       if (attach_user_voicemail)
07101          DISPOSE(todir, msgnum);
07102    }
07103 
07104    if (!ast_strlen_zero(vmu->pager)) {
07105       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07106    }
07107 
07108    if (ast_test_flag(vmu, VM_DELETE))
07109       DELETE(todir, msgnum, fn, vmu);
07110 
07111    /* Leave voicemail for someone */
07112    if (ast_app_has_voicemail(ext_context, NULL)) 
07113       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07114 
07115    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07116 
07117    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);
07118    run_externnotify(vmu->context, vmu->mailbox, flag);
07119 
07120 #ifdef IMAP_STORAGE
07121    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07122    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07123       vm_imap_delete(NULL, vms->curmsg, vmu);
07124       vms->newmessages--;  /* Fix new message count */
07125    }
07126 #endif
07127 
07128    return 0;
07129 }
07130 
07131 /*!
07132  * \brief Sends a voicemail message to a mailbox recipient.
07133  * \param chan
07134  * \param context
07135  * \param vms
07136  * \param sender
07137  * \param fmt
07138  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07139  *             Will be 0 when called to forward an existing message (option 8)
07140  *             Will be 1 when called to leave a message (option 3->5)
07141  * \param record_gain 
07142  * \param urgent
07143  *
07144  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07145  * 
07146  * When in the leave message mode (is_new_message == 1):
07147  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07148  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07149  *
07150  * When in the forward message mode (is_new_message == 0):
07151  *   - retreives the current message to be forwarded
07152  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07153  *   - determines the target mailbox and folders
07154  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07155  *
07156  * \return zero on success, -1 on error.
07157  */
07158 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)
07159 {
07160 #ifdef IMAP_STORAGE
07161    int todircount = 0;
07162    struct vm_state *dstvms;
07163 #endif
07164    char username[70]="";
07165    char fn[PATH_MAX]; /* for playback of name greeting */
07166    char ecodes[16] = "#";
07167    int res = 0, cmd = 0;
07168    struct ast_vm_user *receiver = NULL, *vmtmp;
07169    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07170    char *stringp;
07171    const char *s;
07172    int saved_messages = 0;
07173    int valid_extensions = 0;
07174    char *dir;
07175    int curmsg;
07176    char urgent_str[7] = "";
07177    int prompt_played = 0;
07178 #ifndef IMAP_STORAGE
07179    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07180 #endif
07181    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07182       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07183    }
07184 
07185    if (vms == NULL) return -1;
07186    dir = vms->curdir;
07187    curmsg = vms->curmsg;
07188 
07189    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07190    while (!res && !valid_extensions) {
07191       int use_directory = 0;
07192       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07193          int done = 0;
07194          int retries = 0;
07195          cmd = 0;
07196          while ((cmd >= 0) && !done ){
07197             if (cmd)
07198                retries = 0;
07199             switch (cmd) {
07200             case '1': 
07201                use_directory = 0;
07202                done = 1;
07203                break;
07204             case '2': 
07205                use_directory = 1;
07206                done = 1;
07207                break;
07208             case '*': 
07209                cmd = 't';
07210                done = 1;
07211                break;
07212             default: 
07213                /* Press 1 to enter an extension press 2 to use the directory */
07214                cmd = ast_play_and_wait(chan, "vm-forward");
07215                if (!cmd) {
07216                   cmd = ast_waitfordigit(chan, 3000);
07217                }
07218                if (!cmd) {
07219                   retries++;
07220                }
07221                if (retries > 3) {
07222                   cmd = 't';
07223                   done = 1;
07224                }
07225                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07226             }
07227          }
07228          if (cmd < 0 || cmd == 't')
07229             break;
07230       }
07231       
07232       if (use_directory) {
07233          /* use app_directory */
07234          
07235          char old_context[sizeof(chan->context)];
07236          char old_exten[sizeof(chan->exten)];
07237          int old_priority;
07238          struct ast_app* directory_app;
07239 
07240          directory_app = pbx_findapp("Directory");
07241          if (directory_app) {
07242             char vmcontext[256];
07243             /* make backup copies */
07244             memcpy(old_context, chan->context, sizeof(chan->context));
07245             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07246             old_priority = chan->priority;
07247             
07248             /* call the the Directory, changes the channel */
07249             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07250             res = pbx_exec(chan, directory_app, vmcontext);
07251             
07252             ast_copy_string(username, chan->exten, sizeof(username));
07253             
07254             /* restore the old context, exten, and priority */
07255             memcpy(chan->context, old_context, sizeof(chan->context));
07256             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07257             chan->priority = old_priority;
07258          } else {
07259             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07260             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07261          }
07262       } else {
07263          /* Ask for an extension */
07264          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07265          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07266          prompt_played++;
07267          if (res || prompt_played > 4)
07268             break;
07269          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07270             break;
07271       }
07272       
07273       /* start all over if no username */
07274       if (ast_strlen_zero(username))
07275          continue;
07276       stringp = username;
07277       s = strsep(&stringp, "*");
07278       /* start optimistic */
07279       valid_extensions = 1;
07280       while (s) {
07281          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07282             int oldmsgs;
07283             int newmsgs;
07284             int capacity;
07285             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07286                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07287                /* Shouldn't happen, but allow trying another extension if it does */
07288                res = ast_play_and_wait(chan, "pbx-invalid");
07289                valid_extensions = 0;
07290                break;
07291             }
07292             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07293             if ((newmsgs + oldmsgs) >= capacity) {
07294                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07295                res = ast_play_and_wait(chan, "vm-mailboxfull");
07296                valid_extensions = 0;
07297                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07298                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07299                   free_user(vmtmp);
07300                }
07301                inprocess_count(receiver->mailbox, receiver->context, -1);
07302                break;
07303             }
07304             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07305          } else {
07306             /* XXX Optimization for the future.  When we encounter a single bad extension,
07307              * bailing out on all of the extensions may not be the way to go.  We should
07308              * probably just bail on that single extension, then allow the user to enter
07309              * several more. XXX
07310              */
07311             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07312                free_user(receiver);
07313             }
07314             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07315             /* "I am sorry, that's not a valid extension.  Please try again." */
07316             res = ast_play_and_wait(chan, "pbx-invalid");
07317             valid_extensions = 0;
07318             break;
07319          }
07320 
07321          /* play name if available, else play extension number */
07322          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07323          RETRIEVE(fn, -1, s, receiver->context);
07324          if (ast_fileexists(fn, NULL, NULL) > 0) {
07325             res = ast_stream_and_wait(chan, fn, ecodes);
07326             if (res) {
07327                DISPOSE(fn, -1);
07328                return res;
07329             }
07330          } else {
07331             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07332          }
07333          DISPOSE(fn, -1);
07334 
07335          s = strsep(&stringp, "*");
07336       }
07337       /* break from the loop of reading the extensions */
07338       if (valid_extensions)
07339          break;
07340    }
07341    /* check if we're clear to proceed */
07342    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07343       return res;
07344    if (is_new_message == 1) {
07345       struct leave_vm_options leave_options;
07346       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07347       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07348 
07349       /* Send VoiceMail */
07350       memset(&leave_options, 0, sizeof(leave_options));
07351       leave_options.record_gain = record_gain;
07352       cmd = leave_voicemail(chan, mailbox, &leave_options);
07353    } else {
07354       /* Forward VoiceMail */
07355       long duration = 0;
07356       struct vm_state vmstmp;
07357       int copy_msg_result = 0;
07358       memcpy(&vmstmp, vms, sizeof(vmstmp));
07359 
07360       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07361 
07362       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07363       if (!cmd) {
07364          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07365 #ifdef IMAP_STORAGE
07366             int attach_user_voicemail;
07367             char *myserveremail = serveremail;
07368             
07369             /* get destination mailbox */
07370             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07371             if (!dstvms) {
07372                dstvms = create_vm_state_from_user(vmtmp);
07373             }
07374             if (dstvms) {
07375                init_mailstream(dstvms, 0);
07376                if (!dstvms->mailstream) {
07377                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07378                } else {
07379                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07380                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07381                }
07382             } else {
07383                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07384             }
07385             if (!ast_strlen_zero(vmtmp->serveremail))
07386                myserveremail = vmtmp->serveremail;
07387             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07388             /* NULL category for IMAP storage */
07389             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07390                dstvms->curbox,
07391                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07392                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07393                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07394                NULL, urgent_str);
07395 #else
07396             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07397 #endif
07398             saved_messages++;
07399             AST_LIST_REMOVE_CURRENT(list);
07400             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07401             free_user(vmtmp);
07402             if (res)
07403                break;
07404          }
07405          AST_LIST_TRAVERSE_SAFE_END;
07406          if (saved_messages > 0 && !copy_msg_result) {
07407             /* give confirmation that the message was saved */
07408             /* commented out since we can't forward batches yet
07409             if (saved_messages == 1)
07410                res = ast_play_and_wait(chan, "vm-message");
07411             else
07412                res = ast_play_and_wait(chan, "vm-messages");
07413             if (!res)
07414                res = ast_play_and_wait(chan, "vm-saved"); */
07415 #ifdef IMAP_STORAGE
07416             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07417             if (ast_strlen_zero(vmstmp.introfn))
07418 #endif
07419             res = ast_play_and_wait(chan, "vm-msgsaved");
07420          }
07421 #ifndef IMAP_STORAGE
07422          else {
07423             /* with IMAP, mailbox full warning played by imap_check_limits */
07424             res = ast_play_and_wait(chan, "vm-mailboxfull");
07425          }
07426          /* Restore original message without prepended message if backup exists */
07427          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07428          strcpy(textfile, msgfile);
07429          strcpy(backup, msgfile);
07430          strcpy(backup_textfile, msgfile);
07431          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07432          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07433          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07434          if (ast_fileexists(backup, NULL, NULL) > 0) {
07435             ast_filerename(backup, msgfile, NULL);
07436             rename(backup_textfile, textfile);
07437          }
07438 #endif
07439       }
07440       DISPOSE(dir, curmsg);
07441 #ifndef IMAP_STORAGE
07442       if (cmd) { /* assuming hangup, cleanup backup file */
07443          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07444          strcpy(textfile, msgfile);
07445          strcpy(backup_textfile, msgfile);
07446          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07447          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07448          rename(backup_textfile, textfile);
07449       }
07450 #endif
07451    }
07452 
07453    /* If anything failed above, we still have this list to free */
07454    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07455       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07456       free_user(vmtmp);
07457    }
07458    return res ? res : cmd;
07459 }
07460 
07461 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07462 {
07463    int res;
07464    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07465       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07466    return res;
07467 }
07468 
07469 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07470 {
07471    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07472    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);
07473 }
07474 
07475 static int play_message_category(struct ast_channel *chan, const char *category)
07476 {
07477    int res = 0;
07478 
07479    if (!ast_strlen_zero(category))
07480       res = ast_play_and_wait(chan, category);
07481 
07482    if (res) {
07483       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07484       res = 0;
07485    }
07486 
07487    return res;
07488 }
07489 
07490 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07491 {
07492    int res = 0;
07493    struct vm_zone *the_zone = NULL;
07494    time_t t;
07495 
07496    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07497       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07498       return 0;
07499    }
07500 
07501    /* Does this user have a timezone specified? */
07502    if (!ast_strlen_zero(vmu->zonetag)) {
07503       /* Find the zone in the list */
07504       struct vm_zone *z;
07505       AST_LIST_LOCK(&zones);
07506       AST_LIST_TRAVERSE(&zones, z, list) {
07507          if (!strcmp(z->name, vmu->zonetag)) {
07508             the_zone = z;
07509             break;
07510          }
07511       }
07512       AST_LIST_UNLOCK(&zones);
07513    }
07514 
07515 /* No internal variable parsing for now, so we'll comment it out for the time being */
07516 #if 0
07517    /* Set the DIFF_* variables */
07518    ast_localtime(&t, &time_now, NULL);
07519    tv_now = ast_tvnow();
07520    ast_localtime(&tv_now, &time_then, NULL);
07521 
07522    /* Day difference */
07523    if (time_now.tm_year == time_then.tm_year)
07524       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07525    else
07526       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07527    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07528 
07529    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07530 #endif
07531    if (the_zone) {
07532       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07533    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07534       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07535    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07536       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07537    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07538       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);
07539    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07540       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07541    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07542       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07543    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07544       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07545    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07546       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);
07547    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07548       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07549    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07550       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07551    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07552       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);
07553    } else {
07554       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07555    }
07556 #if 0
07557    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07558 #endif
07559    return res;
07560 }
07561 
07562 
07563 
07564 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07565 {
07566    int res = 0;
07567    int i;
07568    char *callerid, *name;
07569    char prefile[PATH_MAX] = "";
07570    
07571 
07572    /* If voicemail cid is not enabled, or we didn't get cid or context from
07573     * the attribute file, leave now.
07574     *
07575     * TODO Still need to change this so that if this function is called by the
07576     * message envelope (and someone is explicitly requesting to hear the CID),
07577     * it does not check to see if CID is enabled in the config file.
07578     */
07579    if ((cid == NULL)||(context == NULL))
07580       return res;
07581 
07582    /* Strip off caller ID number from name */
07583    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07584    ast_callerid_parse(cid, &name, &callerid);
07585    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07586       /* Check for internal contexts and only */
07587       /* say extension when the call didn't come from an internal context in the list */
07588       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07589          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07590          if ((strcmp(cidinternalcontexts[i], context) == 0))
07591             break;
07592       }
07593       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07594          if (!res) {
07595             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07596             if (!ast_strlen_zero(prefile)) {
07597             /* See if we can find a recorded name for this person instead of their extension number */
07598                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07599                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07600                   if (!callback)
07601                      res = wait_file2(chan, vms, "vm-from");
07602                   res = ast_stream_and_wait(chan, prefile, "");
07603                } else {
07604                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07605                   /* Say "from extension" as one saying to sound smoother */
07606                   if (!callback)
07607                      res = wait_file2(chan, vms, "vm-from-extension");
07608                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07609                }
07610             }
07611          }
07612       } else if (!res) {
07613          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07614          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07615          if (!callback)
07616             res = wait_file2(chan, vms, "vm-from-phonenumber");
07617          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07618       }
07619    } else {
07620       /* Number unknown */
07621       ast_debug(1, "VM-CID: From an unknown number\n");
07622       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07623       res = wait_file2(chan, vms, "vm-unknown-caller");
07624    }
07625    return res;
07626 }
07627 
07628 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07629 {
07630    int res = 0;
07631    int durationm;
07632    int durations;
07633    /* Verify that we have a duration for the message */
07634    if (duration == NULL)
07635       return res;
07636 
07637    /* Convert from seconds to minutes */
07638    durations = atoi(duration);
07639    durationm = (durations / 60);
07640 
07641    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07642 
07643    if ((!res) && (durationm >= minduration)) {
07644       res = wait_file2(chan, vms, "vm-duration");
07645 
07646       /* POLISH syntax */
07647       if (!strncasecmp(chan->language, "pl", 2)) {
07648          div_t num = div(durationm, 10);
07649 
07650          if (durationm == 1) {
07651             res = ast_play_and_wait(chan, "digits/1z");
07652             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07653          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07654             if (num.rem == 2) {
07655                if (!num.quot) {
07656                   res = ast_play_and_wait(chan, "digits/2-ie");
07657                } else {
07658                   res = say_and_wait(chan, durationm - 2 , chan->language);
07659                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07660                }
07661             } else {
07662                res = say_and_wait(chan, durationm, chan->language);
07663             }
07664             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07665          } else {
07666             res = say_and_wait(chan, durationm, chan->language);
07667             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07668          }
07669       /* DEFAULT syntax */
07670       } else {
07671          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07672          res = wait_file2(chan, vms, "vm-minutes");
07673       }
07674    }
07675    return res;
07676 }
07677 
07678 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07679 {
07680    int res = 0;
07681    char filename[256], *cid;
07682    const char *origtime, *context, *category, *duration, *flag;
07683    struct ast_config *msg_cfg;
07684    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07685 
07686    vms->starting = 0;
07687    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07688    adsi_message(chan, vms);
07689    if (!vms->curmsg) {
07690       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07691    } else if (vms->curmsg == vms->lastmsg) {
07692       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07693    }
07694 
07695    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07696    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07697    msg_cfg = ast_config_load(filename, config_flags);
07698    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07699       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07700       return 0;
07701    }
07702    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07703 
07704    /* Play the word urgent if we are listening to urgent messages */
07705    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07706       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07707    }
07708 
07709    if (!res) {
07710       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07711       /* POLISH syntax */
07712       if (!strncasecmp(chan->language, "pl", 2)) {
07713          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07714             int ten, one;
07715             char nextmsg[256];
07716             ten = (vms->curmsg + 1) / 10;
07717             one = (vms->curmsg + 1) % 10;
07718 
07719             if (vms->curmsg < 20) {
07720                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07721                res = wait_file2(chan, vms, nextmsg);
07722             } else {
07723                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07724                res = wait_file2(chan, vms, nextmsg);
07725                if (one > 0) {
07726                   if (!res) {
07727                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07728                      res = wait_file2(chan, vms, nextmsg);
07729                   }
07730                }
07731             }
07732          }
07733          if (!res)
07734             res = wait_file2(chan, vms, "vm-message");
07735       /* HEBREW syntax */
07736       } else if (!strncasecmp(chan->language, "he", 2)) {
07737          if (!vms->curmsg) {
07738             res = wait_file2(chan, vms, "vm-message");
07739             res = wait_file2(chan, vms, "vm-first");
07740          } else if (vms->curmsg == vms->lastmsg) {
07741             res = wait_file2(chan, vms, "vm-message");
07742             res = wait_file2(chan, vms, "vm-last");
07743          } else {
07744             res = wait_file2(chan, vms, "vm-message");
07745             res = wait_file2(chan, vms, "vm-number");
07746             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07747          }
07748       /* VIETNAMESE syntax */
07749       } else if (!strncasecmp(chan->language, "vi", 2)) {
07750          if (!vms->curmsg) {
07751             res = wait_file2(chan, vms, "vm-message");
07752             res = wait_file2(chan, vms, "vm-first");
07753          } else if (vms->curmsg == vms->lastmsg) {
07754             res = wait_file2(chan, vms, "vm-message");
07755             res = wait_file2(chan, vms, "vm-last");
07756          } else {
07757             res = wait_file2(chan, vms, "vm-message");
07758             res = wait_file2(chan, vms, "vm-number");
07759             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07760          }
07761       } else {
07762          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07763             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07764          } else { /* DEFAULT syntax */
07765             res = wait_file2(chan, vms, "vm-message");
07766          }
07767          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07768             if (!res) {
07769                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07770                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07771             }
07772          }
07773       }
07774    }
07775 
07776    if (!msg_cfg) {
07777       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07778       return 0;
07779    }
07780 
07781    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07782       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07783       DISPOSE(vms->curdir, vms->curmsg);
07784       ast_config_destroy(msg_cfg);
07785       return 0;
07786    }
07787 
07788    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07789    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07790    category = ast_variable_retrieve(msg_cfg, "message", "category");
07791 
07792    context = ast_variable_retrieve(msg_cfg, "message", "context");
07793    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07794       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07795    if (!res) {
07796       res = play_message_category(chan, category);
07797    }
07798    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07799       res = play_message_datetime(chan, vmu, origtime, filename);
07800    }
07801    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07802       res = play_message_callerid(chan, vms, cid, context, 0);
07803    }
07804    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07805       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07806    }
07807    /* Allow pressing '1' to skip envelope / callerid */
07808    if (res == '1') {
07809       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07810       res = 0;
07811    }
07812    ast_config_destroy(msg_cfg);
07813 
07814    if (!res) {
07815       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07816 #ifdef IMAP_STORAGE
07817       ast_mutex_lock(&vms->lock);
07818 #endif
07819       vms->heard[vms->curmsg] = 1;
07820 #ifdef IMAP_STORAGE
07821       ast_mutex_unlock(&vms->lock);
07822       /*IMAP storage stores any prepended message from a forward
07823        * as a separate file from the rest of the message
07824        */
07825       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07826          wait_file(chan, vms, vms->introfn);
07827       }
07828 #endif
07829       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07830          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07831          res = 0;
07832       }
07833       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07834    }
07835    DISPOSE(vms->curdir, vms->curmsg);
07836    return res;
07837 }
07838 
07839 #ifdef IMAP_STORAGE
07840 static int imap_remove_file(char *dir, int msgnum)
07841 {
07842    char fn[PATH_MAX];
07843    char full_fn[PATH_MAX];
07844    char intro[PATH_MAX] = {0,};
07845    
07846    if (msgnum > -1) {
07847       make_file(fn, sizeof(fn), dir, msgnum);
07848       snprintf(intro, sizeof(intro), "%sintro", fn);
07849    } else
07850       ast_copy_string(fn, dir, sizeof(fn));
07851    
07852    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07853       ast_filedelete(fn, NULL);
07854       if (!ast_strlen_zero(intro)) {
07855          ast_filedelete(intro, NULL);
07856       }
07857       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07858       unlink(full_fn);
07859    }
07860    return 0;
07861 }
07862 
07863 
07864 
07865 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07866 {
07867    char *file, *filename;
07868    char *attachment;
07869    char arg[10];
07870    int i;
07871    BODY* body;
07872 
07873    file = strrchr(ast_strdupa(dir), '/');
07874    if (file) {
07875       *file++ = '\0';
07876    } else {
07877       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07878       return -1;
07879    }
07880 
07881    ast_mutex_lock(&vms->lock);
07882    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07883       mail_fetchstructure(vms->mailstream, i + 1, &body);
07884       /* We have the body, now we extract the file name of the first attachment. */
07885       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07886          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07887       } else {
07888          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07889          ast_mutex_unlock(&vms->lock);
07890          return -1;
07891       }
07892       filename = strsep(&attachment, ".");
07893       if (!strcmp(filename, file)) {
07894          sprintf(arg, "%d", i + 1);
07895          mail_setflag(vms->mailstream, arg, "\\DELETED");
07896       }
07897    }
07898    mail_expunge(vms->mailstream);
07899    ast_mutex_unlock(&vms->lock);
07900    return 0;
07901 }
07902 
07903 #elif !defined(IMAP_STORAGE)
07904 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07905 {
07906    int count_msg, last_msg;
07907 
07908    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07909 
07910    /* Rename the member vmbox HERE so that we don't try to return before
07911     * we know what's going on.
07912     */
07913    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07914 
07915    /* Faster to make the directory than to check if it exists. */
07916    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07917 
07918    /* traverses directory using readdir (or select query for ODBC) */
07919    count_msg = count_messages(vmu, vms->curdir);
07920    if (count_msg < 0) {
07921       return count_msg;
07922    } else {
07923       vms->lastmsg = count_msg - 1;
07924    }
07925 
07926    if (vm_allocate_dh(vms, vmu, count_msg)) {
07927       return -1;
07928    }
07929 
07930    /*
07931    The following test is needed in case sequencing gets messed up.
07932    There appears to be more than one way to mess up sequence, so
07933    we will not try to find all of the root causes--just fix it when
07934    detected.
07935    */
07936 
07937    if (vm_lock_path(vms->curdir)) {
07938       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07939       return ERROR_LOCK_PATH;
07940    }
07941 
07942    /* for local storage, checks directory for messages up to maxmsg limit */
07943    last_msg = last_message_index(vmu, vms->curdir);
07944    ast_unlock_path(vms->curdir);
07945 
07946    if (last_msg < -1) {
07947       return last_msg;
07948    } else if (vms->lastmsg != last_msg) {
07949       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);
07950       resequence_mailbox(vmu, vms->curdir, count_msg);
07951    }
07952 
07953    return 0;
07954 }
07955 #endif
07956 
07957 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07958 {
07959    int x = 0;
07960    int last_msg_idx = 0;
07961 
07962 #ifndef IMAP_STORAGE
07963    int res = 0, nummsg;
07964    char fn2[PATH_MAX];
07965 #endif
07966 
07967    if (vms->lastmsg <= -1) {
07968       goto done;
07969    }
07970 
07971    vms->curmsg = -1;
07972 #ifndef IMAP_STORAGE
07973    /* Get the deleted messages fixed */
07974    if (vm_lock_path(vms->curdir)) {
07975       return ERROR_LOCK_PATH;
07976    }
07977 
07978    /* update count as message may have arrived while we've got mailbox open */
07979    last_msg_idx = last_message_index(vmu, vms->curdir);
07980    if (last_msg_idx != vms->lastmsg) {
07981       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07982    }
07983 
07984    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07985    for (x = 0; x < last_msg_idx + 1; x++) {
07986       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07987          /* Save this message.  It's not in INBOX or hasn't been heard */
07988          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07989          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07990             break;
07991          }
07992          vms->curmsg++;
07993          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07994          if (strcmp(vms->fn, fn2)) {
07995             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07996          }
07997       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07998          /* Move to old folder before deleting */
07999          res = save_to_folder(vmu, vms, x, 1);
08000          if (res == ERROR_LOCK_PATH) {
08001             /* If save failed do not delete the message */
08002             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
08003             vms->deleted[x] = 0;
08004             vms->heard[x] = 0;
08005             --x;
08006          }
08007       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08008          /* Move to deleted folder */
08009          res = save_to_folder(vmu, vms, x, 10);
08010          if (res == ERROR_LOCK_PATH) {
08011             /* If save failed do not delete the message */
08012             vms->deleted[x] = 0;
08013             vms->heard[x] = 0;
08014             --x;
08015          }
08016       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08017          /* If realtime storage enabled - we should explicitly delete this message,
08018          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08019          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08020          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08021             DELETE(vms->curdir, x, vms->fn, vmu);
08022          }
08023       }
08024    }
08025 
08026    /* Delete ALL remaining messages */
08027    nummsg = x - 1;
08028    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08029       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08030       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08031          DELETE(vms->curdir, x, vms->fn, vmu);
08032       }
08033    }
08034    ast_unlock_path(vms->curdir);
08035 #else /* defined(IMAP_STORAGE) */
08036    ast_mutex_lock(&vms->lock);
08037    if (vms->deleted) {
08038       /* Since we now expunge after each delete, deleting in reverse order
08039        * ensures that no reordering occurs between each step. */
08040       last_msg_idx = vms->dh_arraysize;
08041       for (x = last_msg_idx - 1; x >= 0; x--) {
08042          if (vms->deleted[x]) {
08043             ast_debug(3, "IMAP delete of %d\n", x);
08044             DELETE(vms->curdir, x, vms->fn, vmu);
08045          }
08046       }
08047    }
08048 #endif
08049 
08050 done:
08051    if (vms->deleted) {
08052       ast_free(vms->deleted);
08053       vms->deleted = NULL;
08054    }
08055    if (vms->heard) {
08056       ast_free(vms->heard);
08057       vms->heard = NULL;
08058    }
08059    vms->dh_arraysize = 0;
08060 #ifdef IMAP_STORAGE
08061    ast_mutex_unlock(&vms->lock);
08062 #endif
08063 
08064    return 0;
08065 }
08066 
08067 /* In Greek even though we CAN use a syntax like "friends messages"
08068  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08069  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08070  * syntax for the above three categories which is more elegant.
08071  */
08072 
08073 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08074 {
08075    int cmd;
08076    char *buf;
08077 
08078    buf = alloca(strlen(box) + 2);
08079    strcpy(buf, box);
08080    strcat(buf, "s");
08081 
08082    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08083       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08084       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08085    } else {
08086       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08087       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08088    }
08089 }
08090 
08091 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08092 {
08093    int cmd;
08094 
08095    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08096       if (!strcasecmp(box, "vm-INBOX"))
08097          cmd = ast_play_and_wait(chan, "vm-new-e");
08098       else
08099          cmd = ast_play_and_wait(chan, "vm-old-e");
08100       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08101    } else {
08102       cmd = ast_play_and_wait(chan, "vm-messages");
08103       return cmd ? cmd : ast_play_and_wait(chan, box);
08104    }
08105 }
08106 
08107 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08108 {
08109    int cmd;
08110 
08111    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08112       cmd = ast_play_and_wait(chan, "vm-messages");
08113       return cmd ? cmd : ast_play_and_wait(chan, box);
08114    } else {
08115       cmd = ast_play_and_wait(chan, box);
08116       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08117    }
08118 }
08119 
08120 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08121 {
08122    int cmd;
08123 
08124    if (  !strncasecmp(chan->language, "it", 2) ||
08125         !strncasecmp(chan->language, "es", 2) ||
08126         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08127       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08128       return cmd ? cmd : ast_play_and_wait(chan, box);
08129    } else if (!strncasecmp(chan->language, "gr", 2)) {
08130       return vm_play_folder_name_gr(chan, box);
08131    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08132       return ast_play_and_wait(chan, box);
08133    } else if (!strncasecmp(chan->language, "pl", 2)) {
08134       return vm_play_folder_name_pl(chan, box);
08135    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08136       return vm_play_folder_name_ua(chan, box);
08137    } else if (!strncasecmp(chan->language, "vi", 2)) {
08138       return ast_play_and_wait(chan, box);
08139    } else {  /* Default English */
08140       cmd = ast_play_and_wait(chan, box);
08141       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08142    }
08143 }
08144 
08145 /* GREEK SYNTAX
08146    In greek the plural for old/new is
08147    different so we need the following files
08148    We also need vm-denExeteMynhmata because
08149    this syntax is different.
08150 
08151    -> vm-Olds.wav : "Palia"
08152    -> vm-INBOXs.wav : "Nea"
08153    -> vm-denExeteMynhmata : "den exete mynhmata"
08154 */
08155 
08156 
08157 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08158 {
08159    int res = 0;
08160 
08161    if (vms->newmessages) {
08162       res = ast_play_and_wait(chan, "vm-youhave");
08163       if (!res) 
08164          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08165       if (!res) {
08166          if ((vms->newmessages == 1)) {
08167             res = ast_play_and_wait(chan, "vm-INBOX");
08168             if (!res)
08169                res = ast_play_and_wait(chan, "vm-message");
08170          } else {
08171             res = ast_play_and_wait(chan, "vm-INBOXs");
08172             if (!res)
08173                res = ast_play_and_wait(chan, "vm-messages");
08174          }
08175       }
08176    } else if (vms->oldmessages){
08177       res = ast_play_and_wait(chan, "vm-youhave");
08178       if (!res)
08179          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08180       if ((vms->oldmessages == 1)){
08181          res = ast_play_and_wait(chan, "vm-Old");
08182          if (!res)
08183             res = ast_play_and_wait(chan, "vm-message");
08184       } else {
08185          res = ast_play_and_wait(chan, "vm-Olds");
08186          if (!res)
08187             res = ast_play_and_wait(chan, "vm-messages");
08188       }
08189    } else if (!vms->oldmessages && !vms->newmessages) 
08190       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08191    return res;
08192 }
08193 
08194 /* Version of vm_intro() designed to work for many languages.
08195  *
08196  * It is hoped that this function can prevent the proliferation of 
08197  * language-specific vm_intro() functions and in time replace the language-
08198  * specific functions which already exist.  An examination of the language-
08199  * specific functions revealed that they all corrected the same deficiencies
08200  * in vm_intro_en() (which was the default function). Namely:
08201  *
08202  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08203  *     wording of the voicemail greeting hides this problem.  For example,
08204  *     vm-INBOX contains only the word "new".  This means that both of these
08205  *     sequences produce valid utterances:
08206  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08207  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08208  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08209  *     in many languages) the first utterance becomes "you have 1 the new message".
08210  *  2) The function contains hardcoded rules for pluralizing the word "message".
08211  *     These rules are correct for English, but not for many other languages.
08212  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08213  *     required in many languages.
08214  *  4) The gender of the word for "message" is not specified. This is a problem
08215  *     because in many languages the gender of the number in phrases such
08216  *     as "you have one new message" must match the gender of the word
08217  *     meaning "message".
08218  *
08219  * Fixing these problems for each new language has meant duplication of effort.
08220  * This new function solves the problems in the following general ways:
08221  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08222  *     and vm-Old respectively for those languages where it makes sense.
08223  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08224  *     on vm-message.
08225  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08226  *     prefix on vm-new and vm-old (none for English).
08227  *  4) Pass the gender of the language's word for "message" as an agument to
08228  *     this function which is can in turn pass on to the functions which 
08229  *     say numbers and put endings on nounds and adjectives.
08230  *
08231  * All languages require these messages:
08232  *  vm-youhave    "You have..."
08233  *  vm-and     "and"
08234  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08235  *
08236  * To use it for English, you will need these additional sound files:
08237  *  vm-new     "new"
08238  *  vm-message    "message", singular
08239  *  vm-messages      "messages", plural
08240  *
08241  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08242  *
08243  *  vm-newn    "novoye" (singular, neuter)
08244  *  vm-newx    "novikh" (counting plural form, genative plural)
08245  *  vm-message    "sobsheniye" (singular form)
08246  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08247  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08248  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08249  *  digits/2n     "dva" (neuter singular)
08250  */
08251 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08252 {
08253    int res;
08254    int lastnum = 0;
08255 
08256    res = ast_play_and_wait(chan, "vm-youhave");
08257 
08258    if (!res && vms->newmessages) {
08259       lastnum = vms->newmessages;
08260 
08261       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08262          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08263       }
08264 
08265       if (!res && vms->oldmessages) {
08266          res = ast_play_and_wait(chan, "vm-and");
08267       }
08268    }
08269 
08270    if (!res && vms->oldmessages) {
08271       lastnum = vms->oldmessages;
08272 
08273       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08274          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08275       }
08276    }
08277 
08278    if (!res) {
08279       if (lastnum == 0) {
08280          res = ast_play_and_wait(chan, "vm-no");
08281       }
08282       if (!res) {
08283          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08284       }
08285    }
08286 
08287    return res;
08288 }
08289 
08290 /* Default Hebrew syntax */
08291 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08292 {
08293    int res = 0;
08294 
08295    /* Introduce messages they have */
08296    if (!res) {
08297       if ((vms->newmessages) || (vms->oldmessages)) {
08298          res = ast_play_and_wait(chan, "vm-youhave");
08299       }
08300       /*
08301        * The word "shtei" refers to the number 2 in hebrew when performing a count
08302        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08303        * an element, this is one of them.
08304        */
08305       if (vms->newmessages) {
08306          if (!res) {
08307             if (vms->newmessages == 1) {
08308                res = ast_play_and_wait(chan, "vm-INBOX1");
08309             } else {
08310                if (vms->newmessages == 2) {
08311                   res = ast_play_and_wait(chan, "vm-shtei");
08312                } else {
08313                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08314                }
08315                res = ast_play_and_wait(chan, "vm-INBOX");
08316             }
08317          }
08318          if (vms->oldmessages && !res) {
08319             res = ast_play_and_wait(chan, "vm-and");
08320             if (vms->oldmessages == 1) {
08321                res = ast_play_and_wait(chan, "vm-Old1");
08322             } else {
08323                if (vms->oldmessages == 2) {
08324                   res = ast_play_and_wait(chan, "vm-shtei");
08325                } else {
08326                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08327                }
08328                res = ast_play_and_wait(chan, "vm-Old");
08329             }
08330          }
08331       }
08332       if (!res && vms->oldmessages && !vms->newmessages) {
08333          if (!res) {
08334             if (vms->oldmessages == 1) {
08335                res = ast_play_and_wait(chan, "vm-Old1");
08336             } else {
08337                if (vms->oldmessages == 2) {
08338                   res = ast_play_and_wait(chan, "vm-shtei");
08339                } else {
08340                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08341                }
08342                res = ast_play_and_wait(chan, "vm-Old");
08343             }
08344          }
08345       }
08346       if (!res) {
08347          if (!vms->oldmessages && !vms->newmessages) {
08348             if (!res) {
08349                res = ast_play_and_wait(chan, "vm-nomessages");
08350             }
08351          }
08352       }
08353    }
08354    return res;
08355 }
08356    
08357 /* Default English syntax */
08358 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08359 {
08360    int res;
08361 
08362    /* Introduce messages they have */
08363    res = ast_play_and_wait(chan, "vm-youhave");
08364    if (!res) {
08365       if (vms->urgentmessages) {
08366          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08367          if (!res)
08368             res = ast_play_and_wait(chan, "vm-Urgent");
08369          if ((vms->oldmessages || vms->newmessages) && !res) {
08370             res = ast_play_and_wait(chan, "vm-and");
08371          } else if (!res) {
08372             if ((vms->urgentmessages == 1))
08373                res = ast_play_and_wait(chan, "vm-message");
08374             else
08375                res = ast_play_and_wait(chan, "vm-messages");
08376          }
08377       }
08378       if (vms->newmessages) {
08379          res = say_and_wait(chan, vms->newmessages, chan->language);
08380          if (!res)
08381             res = ast_play_and_wait(chan, "vm-INBOX");
08382          if (vms->oldmessages && !res)
08383             res = ast_play_and_wait(chan, "vm-and");
08384          else if (!res) {
08385             if ((vms->newmessages == 1))
08386                res = ast_play_and_wait(chan, "vm-message");
08387             else
08388                res = ast_play_and_wait(chan, "vm-messages");
08389          }
08390             
08391       }
08392       if (!res && vms->oldmessages) {
08393          res = say_and_wait(chan, vms->oldmessages, chan->language);
08394          if (!res)
08395             res = ast_play_and_wait(chan, "vm-Old");
08396          if (!res) {
08397             if (vms->oldmessages == 1)
08398                res = ast_play_and_wait(chan, "vm-message");
08399             else
08400                res = ast_play_and_wait(chan, "vm-messages");
08401          }
08402       }
08403       if (!res) {
08404          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08405             res = ast_play_and_wait(chan, "vm-no");
08406             if (!res)
08407                res = ast_play_and_wait(chan, "vm-messages");
08408          }
08409       }
08410    }
08411    return res;
08412 }
08413 
08414 /* ITALIAN syntax */
08415 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08416 {
08417    /* Introduce messages they have */
08418    int res;
08419    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08420       res = ast_play_and_wait(chan, "vm-no") ||
08421          ast_play_and_wait(chan, "vm-message");
08422    else
08423       res = ast_play_and_wait(chan, "vm-youhave");
08424    if (!res && vms->newmessages) {
08425       res = (vms->newmessages == 1) ?
08426          ast_play_and_wait(chan, "digits/un") ||
08427          ast_play_and_wait(chan, "vm-nuovo") ||
08428          ast_play_and_wait(chan, "vm-message") :
08429          /* 2 or more new messages */
08430          say_and_wait(chan, vms->newmessages, chan->language) ||
08431          ast_play_and_wait(chan, "vm-nuovi") ||
08432          ast_play_and_wait(chan, "vm-messages");
08433       if (!res && vms->oldmessages)
08434          res = ast_play_and_wait(chan, "vm-and");
08435    }
08436    if (!res && vms->oldmessages) {
08437       res = (vms->oldmessages == 1) ?
08438          ast_play_and_wait(chan, "digits/un") ||
08439          ast_play_and_wait(chan, "vm-vecchio") ||
08440          ast_play_and_wait(chan, "vm-message") :
08441          /* 2 or more old messages */
08442          say_and_wait(chan, vms->oldmessages, chan->language) ||
08443          ast_play_and_wait(chan, "vm-vecchi") ||
08444          ast_play_and_wait(chan, "vm-messages");
08445    }
08446    return res;
08447 }
08448 
08449 /* POLISH syntax */
08450 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08451 {
08452    /* Introduce messages they have */
08453    int res;
08454    div_t num;
08455 
08456    if (!vms->oldmessages && !vms->newmessages) {
08457       res = ast_play_and_wait(chan, "vm-no");
08458       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08459       return res;
08460    } else {
08461       res = ast_play_and_wait(chan, "vm-youhave");
08462    }
08463 
08464    if (vms->newmessages) {
08465       num = div(vms->newmessages, 10);
08466       if (vms->newmessages == 1) {
08467          res = ast_play_and_wait(chan, "digits/1-a");
08468          res = res ? res : ast_play_and_wait(chan, "vm-new-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->newmessages - 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->newmessages, chan->language);
08480          }
08481          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08482          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08483       } else {
08484          res = say_and_wait(chan, vms->newmessages, chan->language);
08485          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08486          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08487       }
08488       if (!res && vms->oldmessages)
08489          res = ast_play_and_wait(chan, "vm-and");
08490    }
08491    if (!res && vms->oldmessages) {
08492       num = div(vms->oldmessages, 10);
08493       if (vms->oldmessages == 1) {
08494          res = ast_play_and_wait(chan, "digits/1-a");
08495          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08496          res = res ? res : ast_play_and_wait(chan, "vm-message");
08497       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08498          if (num.rem == 2) {
08499             if (!num.quot) {
08500                res = ast_play_and_wait(chan, "digits/2-ie");
08501             } else {
08502                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08503                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08504             }
08505          } else {
08506             res = say_and_wait(chan, vms->oldmessages, chan->language);
08507          }
08508          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08509          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08510       } else {
08511          res = say_and_wait(chan, vms->oldmessages, chan->language);
08512          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08513          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08514       }
08515    }
08516 
08517    return res;
08518 }
08519 
08520 /* SWEDISH syntax */
08521 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08522 {
08523    /* Introduce messages they have */
08524    int res;
08525 
08526    res = ast_play_and_wait(chan, "vm-youhave");
08527    if (res)
08528       return res;
08529 
08530    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08531       res = ast_play_and_wait(chan, "vm-no");
08532       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08533       return res;
08534    }
08535 
08536    if (vms->newmessages) {
08537       if ((vms->newmessages == 1)) {
08538          res = ast_play_and_wait(chan, "digits/ett");
08539          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08540          res = res ? res : ast_play_and_wait(chan, "vm-message");
08541       } else {
08542          res = say_and_wait(chan, vms->newmessages, chan->language);
08543          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08544          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08545       }
08546       if (!res && vms->oldmessages)
08547          res = ast_play_and_wait(chan, "vm-and");
08548    }
08549    if (!res && vms->oldmessages) {
08550       if (vms->oldmessages == 1) {
08551          res = ast_play_and_wait(chan, "digits/ett");
08552          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08553          res = res ? res : ast_play_and_wait(chan, "vm-message");
08554       } else {
08555          res = say_and_wait(chan, vms->oldmessages, chan->language);
08556          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08557          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08558       }
08559    }
08560 
08561    return res;
08562 }
08563 
08564 /* NORWEGIAN syntax */
08565 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08566 {
08567    /* Introduce messages they have */
08568    int res;
08569 
08570    res = ast_play_and_wait(chan, "vm-youhave");
08571    if (res)
08572       return res;
08573 
08574    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08575       res = ast_play_and_wait(chan, "vm-no");
08576       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08577       return res;
08578    }
08579 
08580    if (vms->newmessages) {
08581       if ((vms->newmessages == 1)) {
08582          res = ast_play_and_wait(chan, "digits/1");
08583          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08584          res = res ? res : ast_play_and_wait(chan, "vm-message");
08585       } else {
08586          res = say_and_wait(chan, vms->newmessages, chan->language);
08587          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08588          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08589       }
08590       if (!res && vms->oldmessages)
08591          res = ast_play_and_wait(chan, "vm-and");
08592    }
08593    if (!res && vms->oldmessages) {
08594       if (vms->oldmessages == 1) {
08595          res = ast_play_and_wait(chan, "digits/1");
08596          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08597          res = res ? res : ast_play_and_wait(chan, "vm-message");
08598       } else {
08599          res = say_and_wait(chan, vms->oldmessages, chan->language);
08600          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08601          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08602       }
08603    }
08604 
08605    return res;
08606 }
08607 
08608 /* GERMAN syntax */
08609 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08610 {
08611    /* Introduce messages they have */
08612    int res;
08613    res = ast_play_and_wait(chan, "vm-youhave");
08614    if (!res) {
08615       if (vms->newmessages) {
08616          if ((vms->newmessages == 1))
08617             res = ast_play_and_wait(chan, "digits/1F");
08618          else
08619             res = say_and_wait(chan, vms->newmessages, chan->language);
08620          if (!res)
08621             res = ast_play_and_wait(chan, "vm-INBOX");
08622          if (vms->oldmessages && !res)
08623             res = ast_play_and_wait(chan, "vm-and");
08624          else if (!res) {
08625             if ((vms->newmessages == 1))
08626                res = ast_play_and_wait(chan, "vm-message");
08627             else
08628                res = ast_play_and_wait(chan, "vm-messages");
08629          }
08630             
08631       }
08632       if (!res && vms->oldmessages) {
08633          if (vms->oldmessages == 1)
08634             res = ast_play_and_wait(chan, "digits/1F");
08635          else
08636             res = say_and_wait(chan, vms->oldmessages, chan->language);
08637          if (!res)
08638             res = ast_play_and_wait(chan, "vm-Old");
08639          if (!res) {
08640             if (vms->oldmessages == 1)
08641                res = ast_play_and_wait(chan, "vm-message");
08642             else
08643                res = ast_play_and_wait(chan, "vm-messages");
08644          }
08645       }
08646       if (!res) {
08647          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08648             res = ast_play_and_wait(chan, "vm-no");
08649             if (!res)
08650                res = ast_play_and_wait(chan, "vm-messages");
08651          }
08652       }
08653    }
08654    return res;
08655 }
08656 
08657 /* SPANISH syntax */
08658 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08659 {
08660    /* Introduce messages they have */
08661    int res;
08662    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08663       res = ast_play_and_wait(chan, "vm-youhaveno");
08664       if (!res)
08665          res = ast_play_and_wait(chan, "vm-messages");
08666    } else {
08667       res = ast_play_and_wait(chan, "vm-youhave");
08668    }
08669    if (!res) {
08670       if (vms->newmessages) {
08671          if (!res) {
08672             if ((vms->newmessages == 1)) {
08673                res = ast_play_and_wait(chan, "digits/1M");
08674                if (!res)
08675                   res = ast_play_and_wait(chan, "vm-message");
08676                if (!res)
08677                   res = ast_play_and_wait(chan, "vm-INBOXs");
08678             } else {
08679                res = say_and_wait(chan, vms->newmessages, chan->language);
08680                if (!res)
08681                   res = ast_play_and_wait(chan, "vm-messages");
08682                if (!res)
08683                   res = ast_play_and_wait(chan, "vm-INBOX");
08684             }
08685          }
08686          if (vms->oldmessages && !res)
08687             res = ast_play_and_wait(chan, "vm-and");
08688       }
08689       if (vms->oldmessages) {
08690          if (!res) {
08691             if (vms->oldmessages == 1) {
08692                res = ast_play_and_wait(chan, "digits/1M");
08693                if (!res)
08694                   res = ast_play_and_wait(chan, "vm-message");
08695                if (!res)
08696                   res = ast_play_and_wait(chan, "vm-Olds");
08697             } else {
08698                res = say_and_wait(chan, vms->oldmessages, chan->language);
08699                if (!res)
08700                   res = ast_play_and_wait(chan, "vm-messages");
08701                if (!res)
08702                   res = ast_play_and_wait(chan, "vm-Old");
08703             }
08704          }
08705       }
08706    }
08707 return res;
08708 }
08709 
08710 /* BRAZILIAN PORTUGUESE syntax */
08711 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08712    /* Introduce messages they have */
08713    int res;
08714    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08715       res = ast_play_and_wait(chan, "vm-nomessages");
08716       return res;
08717    } else {
08718       res = ast_play_and_wait(chan, "vm-youhave");
08719    }
08720    if (vms->newmessages) {
08721       if (!res)
08722          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08723       if ((vms->newmessages == 1)) {
08724          if (!res)
08725             res = ast_play_and_wait(chan, "vm-message");
08726          if (!res)
08727             res = ast_play_and_wait(chan, "vm-INBOXs");
08728       } else {
08729          if (!res)
08730             res = ast_play_and_wait(chan, "vm-messages");
08731          if (!res)
08732             res = ast_play_and_wait(chan, "vm-INBOX");
08733       }
08734       if (vms->oldmessages && !res)
08735          res = ast_play_and_wait(chan, "vm-and");
08736    }
08737    if (vms->oldmessages) {
08738       if (!res)
08739          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08740       if (vms->oldmessages == 1) {
08741          if (!res)
08742             res = ast_play_and_wait(chan, "vm-message");
08743          if (!res)
08744             res = ast_play_and_wait(chan, "vm-Olds");
08745       } else {
08746          if (!res)
08747             res = ast_play_and_wait(chan, "vm-messages");
08748          if (!res)
08749             res = ast_play_and_wait(chan, "vm-Old");
08750       }
08751    }
08752    return res;
08753 }
08754 
08755 /* FRENCH syntax */
08756 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08757 {
08758    /* Introduce messages they have */
08759    int res;
08760    res = ast_play_and_wait(chan, "vm-youhave");
08761    if (!res) {
08762       if (vms->newmessages) {
08763          res = say_and_wait(chan, vms->newmessages, chan->language);
08764          if (!res)
08765             res = ast_play_and_wait(chan, "vm-INBOX");
08766          if (vms->oldmessages && !res)
08767             res = ast_play_and_wait(chan, "vm-and");
08768          else if (!res) {
08769             if ((vms->newmessages == 1))
08770                res = ast_play_and_wait(chan, "vm-message");
08771             else
08772                res = ast_play_and_wait(chan, "vm-messages");
08773          }
08774             
08775       }
08776       if (!res && vms->oldmessages) {
08777          res = say_and_wait(chan, vms->oldmessages, chan->language);
08778          if (!res)
08779             res = ast_play_and_wait(chan, "vm-Old");
08780          if (!res) {
08781             if (vms->oldmessages == 1)
08782                res = ast_play_and_wait(chan, "vm-message");
08783             else
08784                res = ast_play_and_wait(chan, "vm-messages");
08785          }
08786       }
08787       if (!res) {
08788          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08789             res = ast_play_and_wait(chan, "vm-no");
08790             if (!res)
08791                res = ast_play_and_wait(chan, "vm-messages");
08792          }
08793       }
08794    }
08795    return res;
08796 }
08797 
08798 /* DUTCH syntax */
08799 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08800 {
08801    /* Introduce messages they have */
08802    int res;
08803    res = ast_play_and_wait(chan, "vm-youhave");
08804    if (!res) {
08805       if (vms->newmessages) {
08806          res = say_and_wait(chan, vms->newmessages, chan->language);
08807          if (!res) {
08808             if (vms->newmessages == 1)
08809                res = ast_play_and_wait(chan, "vm-INBOXs");
08810             else
08811                res = ast_play_and_wait(chan, "vm-INBOX");
08812          }
08813          if (vms->oldmessages && !res)
08814             res = ast_play_and_wait(chan, "vm-and");
08815          else if (!res) {
08816             if ((vms->newmessages == 1))
08817                res = ast_play_and_wait(chan, "vm-message");
08818             else
08819                res = ast_play_and_wait(chan, "vm-messages");
08820          }
08821             
08822       }
08823       if (!res && vms->oldmessages) {
08824          res = say_and_wait(chan, vms->oldmessages, chan->language);
08825          if (!res) {
08826             if (vms->oldmessages == 1)
08827                res = ast_play_and_wait(chan, "vm-Olds");
08828             else
08829                res = ast_play_and_wait(chan, "vm-Old");
08830          }
08831          if (!res) {
08832             if (vms->oldmessages == 1)
08833                res = ast_play_and_wait(chan, "vm-message");
08834             else
08835                res = ast_play_and_wait(chan, "vm-messages");
08836          }
08837       }
08838       if (!res) {
08839          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08840             res = ast_play_and_wait(chan, "vm-no");
08841             if (!res)
08842                res = ast_play_and_wait(chan, "vm-messages");
08843          }
08844       }
08845    }
08846    return res;
08847 }
08848 
08849 /* PORTUGUESE syntax */
08850 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08851 {
08852    /* Introduce messages they have */
08853    int res;
08854    res = ast_play_and_wait(chan, "vm-youhave");
08855    if (!res) {
08856       if (vms->newmessages) {
08857          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08858          if (!res) {
08859             if ((vms->newmessages == 1)) {
08860                res = ast_play_and_wait(chan, "vm-message");
08861                if (!res)
08862                   res = ast_play_and_wait(chan, "vm-INBOXs");
08863             } else {
08864                res = ast_play_and_wait(chan, "vm-messages");
08865                if (!res)
08866                   res = ast_play_and_wait(chan, "vm-INBOX");
08867             }
08868          }
08869          if (vms->oldmessages && !res)
08870             res = ast_play_and_wait(chan, "vm-and");
08871       }
08872       if (!res && vms->oldmessages) {
08873          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08874          if (!res) {
08875             if (vms->oldmessages == 1) {
08876                res = ast_play_and_wait(chan, "vm-message");
08877                if (!res)
08878                   res = ast_play_and_wait(chan, "vm-Olds");
08879             } else {
08880                res = ast_play_and_wait(chan, "vm-messages");
08881                if (!res)
08882                   res = ast_play_and_wait(chan, "vm-Old");
08883             }
08884          }
08885       }
08886       if (!res) {
08887          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08888             res = ast_play_and_wait(chan, "vm-no");
08889             if (!res)
08890                res = ast_play_and_wait(chan, "vm-messages");
08891          }
08892       }
08893    }
08894    return res;
08895 }
08896 
08897 
08898 /* CZECH syntax */
08899 /* in czech there must be declension of word new and message
08900  * czech        : english        : czech      : english
08901  * --------------------------------------------------------
08902  * vm-youhave   : you have 
08903  * vm-novou     : one new        : vm-zpravu  : message
08904  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08905  * vm-novych    : 5-infinite new : vm-zprav   : messages
08906  * vm-starou   : one old
08907  * vm-stare     : 2-4 old 
08908  * vm-starych   : 5-infinite old
08909  * jednu        : one   - falling 4. 
08910  * vm-no        : no  ( no messages )
08911  */
08912 
08913 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08914 {
08915    int res;
08916    res = ast_play_and_wait(chan, "vm-youhave");
08917    if (!res) {
08918       if (vms->newmessages) {
08919          if (vms->newmessages == 1) {
08920             res = ast_play_and_wait(chan, "digits/jednu");
08921          } else {
08922             res = say_and_wait(chan, vms->newmessages, chan->language);
08923          }
08924          if (!res) {
08925             if ((vms->newmessages == 1))
08926                res = ast_play_and_wait(chan, "vm-novou");
08927             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08928                res = ast_play_and_wait(chan, "vm-nove");
08929             if (vms->newmessages > 4)
08930                res = ast_play_and_wait(chan, "vm-novych");
08931          }
08932          if (vms->oldmessages && !res)
08933             res = ast_play_and_wait(chan, "vm-and");
08934          else if (!res) {
08935             if ((vms->newmessages == 1))
08936                res = ast_play_and_wait(chan, "vm-zpravu");
08937             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08938                res = ast_play_and_wait(chan, "vm-zpravy");
08939             if (vms->newmessages > 4)
08940                res = ast_play_and_wait(chan, "vm-zprav");
08941          }
08942       }
08943       if (!res && vms->oldmessages) {
08944          res = say_and_wait(chan, vms->oldmessages, chan->language);
08945          if (!res) {
08946             if ((vms->oldmessages == 1))
08947                res = ast_play_and_wait(chan, "vm-starou");
08948             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08949                res = ast_play_and_wait(chan, "vm-stare");
08950             if (vms->oldmessages > 4)
08951                res = ast_play_and_wait(chan, "vm-starych");
08952          }
08953          if (!res) {
08954             if ((vms->oldmessages == 1))
08955                res = ast_play_and_wait(chan, "vm-zpravu");
08956             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08957                res = ast_play_and_wait(chan, "vm-zpravy");
08958             if (vms->oldmessages > 4)
08959                res = ast_play_and_wait(chan, "vm-zprav");
08960          }
08961       }
08962       if (!res) {
08963          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08964             res = ast_play_and_wait(chan, "vm-no");
08965             if (!res)
08966                res = ast_play_and_wait(chan, "vm-zpravy");
08967          }
08968       }
08969    }
08970    return res;
08971 }
08972 
08973 /* CHINESE (Taiwan) syntax */
08974 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08975 {
08976    int res;
08977    /* Introduce messages they have */
08978    res = ast_play_and_wait(chan, "vm-you");
08979 
08980    if (!res && vms->newmessages) {
08981       res = ast_play_and_wait(chan, "vm-have");
08982       if (!res)
08983          res = say_and_wait(chan, vms->newmessages, chan->language);
08984       if (!res)
08985          res = ast_play_and_wait(chan, "vm-tong");
08986       if (!res)
08987          res = ast_play_and_wait(chan, "vm-INBOX");
08988       if (vms->oldmessages && !res)
08989          res = ast_play_and_wait(chan, "vm-and");
08990       else if (!res) 
08991          res = ast_play_and_wait(chan, "vm-messages");
08992    }
08993    if (!res && vms->oldmessages) {
08994       res = ast_play_and_wait(chan, "vm-have");
08995       if (!res)
08996          res = say_and_wait(chan, vms->oldmessages, chan->language);
08997       if (!res)
08998          res = ast_play_and_wait(chan, "vm-tong");
08999       if (!res)
09000          res = ast_play_and_wait(chan, "vm-Old");
09001       if (!res)
09002          res = ast_play_and_wait(chan, "vm-messages");
09003    }
09004    if (!res && !vms->oldmessages && !vms->newmessages) {
09005       res = ast_play_and_wait(chan, "vm-haveno");
09006       if (!res)
09007          res = ast_play_and_wait(chan, "vm-messages");
09008    }
09009    return res;
09010 }
09011 
09012 /* Vietnamese syntax */
09013 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09014 {
09015    int res;
09016 
09017    /* Introduce messages they have */
09018    res = ast_play_and_wait(chan, "vm-youhave");
09019    if (!res) {
09020       if (vms->newmessages) {
09021          res = say_and_wait(chan, vms->newmessages, chan->language);
09022          if (!res)
09023             res = ast_play_and_wait(chan, "vm-INBOX");
09024          if (vms->oldmessages && !res)
09025             res = ast_play_and_wait(chan, "vm-and");
09026       }
09027       if (!res && vms->oldmessages) {
09028          res = say_and_wait(chan, vms->oldmessages, chan->language);
09029          if (!res)
09030             res = ast_play_and_wait(chan, "vm-Old");        
09031       }
09032       if (!res) {
09033          if (!vms->oldmessages && !vms->newmessages) {
09034             res = ast_play_and_wait(chan, "vm-no");
09035             if (!res)
09036                res = ast_play_and_wait(chan, "vm-message");
09037          }
09038       }
09039    }
09040    return res;
09041 }
09042 
09043 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09044 {
09045    char prefile[256];
09046    
09047    /* Notify the user that the temp greeting is set and give them the option to remove it */
09048    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09049    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09050       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09051       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09052          ast_play_and_wait(chan, "vm-tempgreetactive");
09053       }
09054       DISPOSE(prefile, -1);
09055    }
09056 
09057    /* Play voicemail intro - syntax is different for different languages */
09058    if (0) {
09059       return 0;
09060    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09061       return vm_intro_cs(chan, vms);
09062    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09063       static int deprecation_warning = 0;
09064       if (deprecation_warning++ % 10 == 0) {
09065          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09066       }
09067       return vm_intro_cs(chan, vms);
09068    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09069       return vm_intro_de(chan, vms);
09070    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09071       return vm_intro_es(chan, vms);
09072    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09073       return vm_intro_fr(chan, vms);
09074    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09075       return vm_intro_gr(chan, vms);
09076    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09077       return vm_intro_he(chan, vms);
09078    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09079       return vm_intro_it(chan, vms);
09080    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09081       return vm_intro_nl(chan, vms);
09082    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09083       return vm_intro_no(chan, vms);
09084    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09085       return vm_intro_pl(chan, vms);
09086    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09087       return vm_intro_pt_BR(chan, vms);
09088    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09089       return vm_intro_pt(chan, vms);
09090    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09091       return vm_intro_multilang(chan, vms, "n");
09092    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09093       return vm_intro_se(chan, vms);
09094    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09095       return vm_intro_multilang(chan, vms, "n");
09096    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09097       return vm_intro_vi(chan, vms);
09098    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09099       return vm_intro_zh(chan, vms);
09100    } else {                                             /* Default to ENGLISH */
09101       return vm_intro_en(chan, vms);
09102    }
09103 }
09104 
09105 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09106 {
09107    int res = 0;
09108    /* Play instructions and wait for new command */
09109    while (!res) {
09110       if (vms->starting) {
09111          if (vms->lastmsg > -1) {
09112             if (skipadvanced)
09113                res = ast_play_and_wait(chan, "vm-onefor-full");
09114             else
09115                res = ast_play_and_wait(chan, "vm-onefor");
09116             if (!res)
09117                res = vm_play_folder_name(chan, vms->vmbox);
09118          }
09119          if (!res) {
09120             if (skipadvanced)
09121                res = ast_play_and_wait(chan, "vm-opts-full");
09122             else
09123                res = ast_play_and_wait(chan, "vm-opts");
09124          }
09125       } else {
09126          /* Added for additional help */
09127          if (skipadvanced) {
09128             res = ast_play_and_wait(chan, "vm-onefor-full");
09129             if (!res)
09130                res = vm_play_folder_name(chan, vms->vmbox);
09131             res = ast_play_and_wait(chan, "vm-opts-full");
09132          }
09133          /* Logic:
09134           * If the current message is not the first OR
09135           * if we're listening to the first new message and there are
09136           * also urgent messages, then prompt for navigation to the
09137           * previous message
09138           */
09139          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09140             res = ast_play_and_wait(chan, "vm-prev");
09141          }
09142          if (!res && !skipadvanced)
09143             res = ast_play_and_wait(chan, "vm-advopts");
09144          if (!res)
09145             res = ast_play_and_wait(chan, "vm-repeat");
09146          /* Logic:
09147           * If we're not listening to the last message OR
09148           * we're listening to the last urgent message and there are
09149           * also new non-urgent messages, then prompt for navigation
09150           * to the next message
09151           */
09152          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09153             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09154             res = ast_play_and_wait(chan, "vm-next");
09155          }
09156          if (!res) {
09157             int curmsg_deleted;
09158 #ifdef IMAP_STORAGE
09159             ast_mutex_lock(&vms->lock);
09160 #endif
09161             curmsg_deleted = vms->deleted[vms->curmsg];
09162 #ifdef IMAP_STORAGE
09163             ast_mutex_unlock(&vms->lock);
09164 #endif
09165             if (!curmsg_deleted) {
09166                res = ast_play_and_wait(chan, "vm-delete");
09167             } else {
09168                res = ast_play_and_wait(chan, "vm-undelete");
09169             }
09170             if (!res) {
09171                res = ast_play_and_wait(chan, "vm-toforward");
09172             }
09173             if (!res) {
09174                res = ast_play_and_wait(chan, "vm-savemessage");
09175             }
09176          }
09177       }
09178       if (!res) {
09179          res = ast_play_and_wait(chan, "vm-helpexit");
09180       }
09181       if (!res)
09182          res = ast_waitfordigit(chan, 6000);
09183       if (!res) {
09184          vms->repeats++;
09185          if (vms->repeats > 2) {
09186             res = 't';
09187          }
09188       }
09189    }
09190    return res;
09191 }
09192 
09193 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09194 {
09195    int res = 0;
09196    /* Play instructions and wait for new command */
09197    while (!res) {
09198       if (vms->lastmsg > -1) {
09199          res = ast_play_and_wait(chan, "vm-listen");
09200          if (!res)
09201             res = vm_play_folder_name(chan, vms->vmbox);
09202          if (!res)
09203             res = ast_play_and_wait(chan, "press");
09204          if (!res)
09205             res = ast_play_and_wait(chan, "digits/1");
09206       }
09207       if (!res)
09208          res = ast_play_and_wait(chan, "vm-opts");
09209       if (!res) {
09210          vms->starting = 0;
09211          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09212       }
09213    }
09214    return res;
09215 }
09216 
09217 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09218 {
09219    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09220       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09221    } else {             /* Default to ENGLISH */
09222       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09223    }
09224 }
09225 
09226 
09227 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09228 {
09229    int cmd = 0;
09230    int duration = 0;
09231    int tries = 0;
09232    char newpassword[80] = "";
09233    char newpassword2[80] = "";
09234    char prefile[PATH_MAX] = "";
09235    unsigned char buf[256];
09236    int bytes = 0;
09237 
09238    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09239    if (ast_adsi_available(chan)) {
09240       bytes += adsi_logo(buf + bytes);
09241       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09242       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09243       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09244       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09245       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09246    }
09247 
09248    /* If forcename is set, have the user record their name */
09249    if (ast_test_flag(vmu, VM_FORCENAME)) {
09250       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09251       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09252          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09253          if (cmd < 0 || cmd == 't' || cmd == '#')
09254             return cmd;
09255       }
09256    }
09257 
09258    /* If forcegreetings is set, have the user record their greetings */
09259    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09260       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09261       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09262          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09263          if (cmd < 0 || cmd == 't' || cmd == '#')
09264             return cmd;
09265       }
09266 
09267       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09268       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09269          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09270          if (cmd < 0 || cmd == 't' || cmd == '#')
09271             return cmd;
09272       }
09273    }
09274 
09275    /*
09276     * Change the password last since new users will be able to skip over any steps this one comes before
09277     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09278     */
09279    for (;;) {
09280       newpassword[1] = '\0';
09281       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09282       if (cmd == '#')
09283          newpassword[0] = '\0';
09284       if (cmd < 0 || cmd == 't' || cmd == '#')
09285          return cmd;
09286       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09287       if (cmd < 0 || cmd == 't' || cmd == '#')
09288          return cmd;
09289       cmd = check_password(vmu, newpassword); /* perform password validation */
09290       if (cmd != 0) {
09291          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09292          cmd = ast_play_and_wait(chan, vm_invalid_password);
09293       } else {
09294          newpassword2[1] = '\0';
09295          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09296          if (cmd == '#')
09297             newpassword2[0] = '\0';
09298          if (cmd < 0 || cmd == 't' || cmd == '#')
09299             return cmd;
09300          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09301          if (cmd < 0 || cmd == 't' || cmd == '#')
09302             return cmd;
09303          if (!strcmp(newpassword, newpassword2))
09304             break;
09305          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09306          cmd = ast_play_and_wait(chan, vm_mismatch);
09307       }
09308       if (++tries == 3)
09309          return -1;
09310       if (cmd != 0) {
09311          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09312       }
09313    }
09314    if (pwdchange & PWDCHANGE_INTERNAL)
09315       vm_change_password(vmu, newpassword);
09316    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09317       vm_change_password_shell(vmu, newpassword);
09318 
09319    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09320    cmd = ast_play_and_wait(chan, vm_passchanged);
09321 
09322    return cmd;
09323 }
09324 
09325 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09326 {
09327    int cmd = 0;
09328    int retries = 0;
09329    int duration = 0;
09330    char newpassword[80] = "";
09331    char newpassword2[80] = "";
09332    char prefile[PATH_MAX] = "";
09333    unsigned char buf[256];
09334    int bytes = 0;
09335 
09336    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09337    if (ast_adsi_available(chan)) {
09338       bytes += adsi_logo(buf + bytes);
09339       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09340       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09341       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09342       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09343       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09344    }
09345    while ((cmd >= 0) && (cmd != 't')) {
09346       if (cmd)
09347          retries = 0;
09348       switch (cmd) {
09349       case '1': /* Record your unavailable message */
09350          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09351          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09352          break;
09353       case '2':  /* Record your busy message */
09354          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09355          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09356          break;
09357       case '3': /* Record greeting */
09358          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09359          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09360          break;
09361       case '4':  /* manage the temporary greeting */
09362          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09363          break;
09364       case '5': /* change password */
09365          if (vmu->password[0] == '-') {
09366             cmd = ast_play_and_wait(chan, "vm-no");
09367             break;
09368          }
09369          newpassword[1] = '\0';
09370          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09371          if (cmd == '#')
09372             newpassword[0] = '\0';
09373          else {
09374             if (cmd < 0)
09375                break;
09376             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09377                break;
09378             }
09379          }
09380          cmd = check_password(vmu, newpassword); /* perform password validation */
09381          if (cmd != 0) {
09382             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09383             cmd = ast_play_and_wait(chan, vm_invalid_password);
09384             if (!cmd) {
09385                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09386             }
09387             break;
09388          }
09389          newpassword2[1] = '\0';
09390          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09391          if (cmd == '#')
09392             newpassword2[0] = '\0';
09393          else {
09394             if (cmd < 0)
09395                break;
09396 
09397             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09398                break;
09399             }
09400          }
09401          if (strcmp(newpassword, newpassword2)) {
09402             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09403             cmd = ast_play_and_wait(chan, vm_mismatch);
09404             if (!cmd) {
09405                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09406             }
09407             break;
09408          }
09409 
09410          if (pwdchange & PWDCHANGE_INTERNAL) {
09411             vm_change_password(vmu, newpassword);
09412          }
09413          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09414             vm_change_password_shell(vmu, newpassword);
09415          }
09416 
09417          ast_debug(1, "User %s set password to %s of length %d\n",
09418             vms->username, newpassword, (int) strlen(newpassword));
09419          cmd = ast_play_and_wait(chan, vm_passchanged);
09420          break;
09421       case '*': 
09422          cmd = 't';
09423          break;
09424       default: 
09425          cmd = 0;
09426          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09427          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09428          if (ast_fileexists(prefile, NULL, NULL)) {
09429             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09430          }
09431          DISPOSE(prefile, -1);
09432          if (!cmd) {
09433             cmd = ast_play_and_wait(chan, "vm-options");
09434          }
09435          if (!cmd) {
09436             cmd = ast_waitfordigit(chan, 6000);
09437          }
09438          if (!cmd) {
09439             retries++;
09440          }
09441          if (retries > 3) {
09442             cmd = 't';
09443          }
09444          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09445       }
09446    }
09447    if (cmd == 't')
09448       cmd = 0;
09449    return cmd;
09450 }
09451 
09452 /*!
09453  * \brief The handler for 'record a temporary greeting'. 
09454  * \param chan
09455  * \param vmu
09456  * \param vms
09457  * \param fmtc
09458  * \param record_gain
09459  *
09460  * This is option 4 from the mailbox options menu.
09461  * This function manages the following promptings:
09462  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09463  * 2: remove (delete) the temporary greeting.
09464  * *: return to the main menu.
09465  *
09466  * \return zero on success, -1 on error.
09467  */
09468 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09469 {
09470    int cmd = 0;
09471    int retries = 0;
09472    int duration = 0;
09473    char prefile[PATH_MAX] = "";
09474    unsigned char buf[256];
09475    int bytes = 0;
09476 
09477    if (ast_adsi_available(chan)) {
09478       bytes += adsi_logo(buf + bytes);
09479       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09480       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09481       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09482       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09483       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09484    }
09485 
09486    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09487    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09488    while ((cmd >= 0) && (cmd != 't')) {
09489       if (cmd)
09490          retries = 0;
09491       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09492       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09493          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09494          if (cmd == -1) {
09495             break;
09496          }
09497          cmd = 't';  
09498       } else {
09499          switch (cmd) {
09500          case '1':
09501             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09502             break;
09503          case '2':
09504             DELETE(prefile, -1, prefile, vmu);
09505             ast_play_and_wait(chan, "vm-tempremoved");
09506             cmd = 't';  
09507             break;
09508          case '*': 
09509             cmd = 't';
09510             break;
09511          default:
09512             cmd = ast_play_and_wait(chan,
09513                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09514                   "vm-tempgreeting2" : "vm-tempgreeting");
09515             if (!cmd) {
09516                cmd = ast_waitfordigit(chan, 6000);
09517             }
09518             if (!cmd) {
09519                retries++;
09520             }
09521             if (retries > 3) {
09522                cmd = 't';
09523             }
09524             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09525          }
09526       }
09527       DISPOSE(prefile, -1);
09528    }
09529    if (cmd == 't')
09530       cmd = 0;
09531    return cmd;
09532 }
09533 
09534 /*!
09535  * \brief Greek syntax for 'You have N messages' greeting.
09536  * \param chan
09537  * \param vms
09538  * \param vmu
09539  *
09540  * \return zero on success, -1 on error.
09541  */   
09542 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09543 {
09544    int cmd = 0;
09545 
09546    if (vms->lastmsg > -1) {
09547       cmd = play_message(chan, vmu, vms);
09548    } else {
09549       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09550       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09551          if (!cmd) {
09552             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09553             cmd = ast_play_and_wait(chan, vms->fn);
09554          }
09555          if (!cmd)
09556             cmd = ast_play_and_wait(chan, "vm-messages");
09557       } else {
09558          if (!cmd)
09559             cmd = ast_play_and_wait(chan, "vm-messages");
09560          if (!cmd) {
09561             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09562             cmd = ast_play_and_wait(chan, vms->fn);
09563          }
09564       }
09565    } 
09566    return cmd;
09567 }
09568 
09569 /* Hebrew Syntax */
09570 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09571 {
09572    int cmd = 0;
09573 
09574    if (vms->lastmsg > -1) {
09575       cmd = play_message(chan, vmu, vms);
09576    } else {
09577       if (!strcasecmp(vms->fn, "INBOX")) {
09578          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09579       } else {
09580          cmd = ast_play_and_wait(chan, "vm-nomessages");
09581       }
09582    }
09583    return cmd;
09584 }
09585 
09586 /*! 
09587  * \brief Default English syntax for 'You have N messages' greeting.
09588  * \param chan
09589  * \param vms
09590  * \param vmu
09591  *
09592  * \return zero on success, -1 on error.
09593  */
09594 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09595 {
09596    int cmd = 0;
09597 
09598    if (vms->lastmsg > -1) {
09599       cmd = play_message(chan, vmu, vms);
09600    } else {
09601       cmd = ast_play_and_wait(chan, "vm-youhave");
09602       if (!cmd) 
09603          cmd = ast_play_and_wait(chan, "vm-no");
09604       if (!cmd) {
09605          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09606          cmd = ast_play_and_wait(chan, vms->fn);
09607       }
09608       if (!cmd)
09609          cmd = ast_play_and_wait(chan, "vm-messages");
09610    }
09611    return cmd;
09612 }
09613 
09614 /*! 
09615  *\brief Italian syntax for 'You have N messages' greeting.
09616  * \param chan
09617  * \param vms
09618  * \param vmu
09619  *
09620  * \return zero on success, -1 on error.
09621  */
09622 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09623 {
09624    int cmd;
09625 
09626    if (vms->lastmsg > -1) {
09627       cmd = play_message(chan, vmu, vms);
09628    } else {
09629       cmd = ast_play_and_wait(chan, "vm-no");
09630       if (!cmd)
09631          cmd = ast_play_and_wait(chan, "vm-message");
09632       if (!cmd) {
09633          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09634          cmd = ast_play_and_wait(chan, vms->fn);
09635       }
09636    }
09637    return cmd;
09638 }
09639 
09640 /*! 
09641  * \brief Spanish syntax for 'You have N messages' greeting.
09642  * \param chan
09643  * \param vms
09644  * \param vmu
09645  *
09646  * \return zero on success, -1 on error.
09647  */
09648 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09649 {
09650    int cmd;
09651 
09652    if (vms->lastmsg > -1) {
09653       cmd = play_message(chan, vmu, vms);
09654    } else {
09655       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09656       if (!cmd)
09657          cmd = ast_play_and_wait(chan, "vm-messages");
09658       if (!cmd) {
09659          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09660          cmd = ast_play_and_wait(chan, vms->fn);
09661       }
09662    }
09663    return cmd;
09664 }
09665 
09666 /*! 
09667  * \brief Portuguese syntax for 'You have N messages' greeting.
09668  * \param chan
09669  * \param vms
09670  * \param vmu
09671  *
09672  * \return zero on success, -1 on error.
09673  */
09674 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09675 {
09676    int cmd;
09677 
09678    if (vms->lastmsg > -1) {
09679       cmd = play_message(chan, vmu, vms);
09680    } else {
09681       cmd = ast_play_and_wait(chan, "vm-no");
09682       if (!cmd) {
09683          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09684          cmd = ast_play_and_wait(chan, vms->fn);
09685       }
09686       if (!cmd)
09687          cmd = ast_play_and_wait(chan, "vm-messages");
09688    }
09689    return cmd;
09690 }
09691 
09692 /*! 
09693  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09694  * \param chan
09695  * \param vms
09696  * \param vmu
09697  *
09698  * \return zero on success, -1 on error.
09699  */
09700 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09701 {
09702    int cmd;
09703 
09704    if (vms->lastmsg > -1) {
09705       cmd = play_message(chan, vmu, vms);
09706    } else {
09707       cmd = ast_play_and_wait(chan, "vm-you");
09708       if (!cmd) 
09709          cmd = ast_play_and_wait(chan, "vm-haveno");
09710       if (!cmd)
09711          cmd = ast_play_and_wait(chan, "vm-messages");
09712       if (!cmd) {
09713          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09714          cmd = ast_play_and_wait(chan, vms->fn);
09715       }
09716    }
09717    return cmd;
09718 }
09719 
09720 /*! 
09721  * \brief Vietnamese syntax for 'You have N messages' greeting.
09722  * \param chan
09723  * \param vms
09724  * \param vmu
09725  *
09726  * \return zero on success, -1 on error.
09727  */
09728 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09729 {
09730    int cmd = 0;
09731 
09732    if (vms->lastmsg > -1) {
09733       cmd = play_message(chan, vmu, vms);
09734    } else {
09735       cmd = ast_play_and_wait(chan, "vm-no");
09736       if (!cmd) {
09737          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09738          cmd = ast_play_and_wait(chan, vms->fn);
09739       }
09740    }
09741    return cmd;
09742 }
09743 
09744 /*!
09745  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09746  * \param chan The channel for the current user. We read the language property from this.
09747  * \param vms passed into the language-specific vm_browse_messages function.
09748  * \param vmu passed into the language-specific vm_browse_messages function.
09749  * 
09750  * The method to be invoked is determined by the value of language code property in the user's channel.
09751  * The default (when unable to match) is to use english.
09752  *
09753  * \return zero on success, -1 on error.
09754  */
09755 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09756 {
09757    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09758       return vm_browse_messages_es(chan, vms, vmu);
09759    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09760       return vm_browse_messages_gr(chan, vms, vmu);
09761    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09762       return vm_browse_messages_he(chan, vms, vmu);
09763    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09764       return vm_browse_messages_it(chan, vms, vmu);
09765    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09766       return vm_browse_messages_pt(chan, vms, vmu);
09767    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09768       return vm_browse_messages_vi(chan, vms, vmu);
09769    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09770       return vm_browse_messages_zh(chan, vms, vmu);
09771    } else {                                             /* Default to English syntax */
09772       return vm_browse_messages_en(chan, vms, vmu);
09773    }
09774 }
09775 
09776 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09777          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09778          int skipuser, int max_logins, int silent)
09779 {
09780    int useadsi = 0, valid = 0, logretries = 0;
09781    char password[AST_MAX_EXTENSION]="", *passptr;
09782    struct ast_vm_user vmus, *vmu = NULL;
09783 
09784    /* If ADSI is supported, setup login screen */
09785    adsi_begin(chan, &useadsi);
09786    if (!skipuser && useadsi)
09787       adsi_login(chan);
09788    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09789    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09790       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09791       return -1;
09792    }
09793 
09794    /* Authenticate them and get their mailbox/password */
09795 
09796    while (!valid && (logretries < max_logins)) {
09797       /* Prompt for, and read in the username */
09798       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09799          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09800          return -1;
09801       }
09802       if (ast_strlen_zero(mailbox)) {
09803          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09804             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09805          } else {
09806             ast_verb(3, "Username not entered\n"); 
09807             return -1;
09808          }
09809       } else if (mailbox[0] == '*') {
09810          /* user entered '*' */
09811          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09812          if (ast_exists_extension(chan, chan->context, "a", 1,
09813             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09814             return -1;
09815          }
09816          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09817          mailbox[0] = '\0';
09818       }
09819 
09820       if (useadsi)
09821          adsi_password(chan);
09822 
09823       if (!ast_strlen_zero(prefix)) {
09824          char fullusername[80] = "";
09825          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09826          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09827          ast_copy_string(mailbox, fullusername, mailbox_size);
09828       }
09829 
09830       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09831       vmu = find_user(&vmus, context, mailbox);
09832       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09833          /* saved password is blank, so don't bother asking */
09834          password[0] = '\0';
09835       } else {
09836          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09837          if (ast_streamfile(chan, vm_password, chan->language)) {
09838             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09839             return -1;
09840          }
09841          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09842             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09843             return -1;
09844          } else if (password[0] == '*') {
09845             /* user entered '*' */
09846             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09847             if (ast_exists_extension(chan, chan->context, "a", 1,
09848                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09849                mailbox[0] = '*';
09850                return -1;
09851             }
09852             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09853             mailbox[0] = '\0';
09854             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09855             vmu = NULL;
09856          }
09857       }
09858 
09859       if (vmu) {
09860          passptr = vmu->password;
09861          if (passptr[0] == '-') passptr++;
09862       }
09863       if (vmu && !strcmp(passptr, password))
09864          valid++;
09865       else {
09866          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09867          if (!ast_strlen_zero(prefix))
09868             mailbox[0] = '\0';
09869       }
09870       logretries++;
09871       if (!valid) {
09872          if (skipuser || logretries >= max_logins) {
09873             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09874             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09875                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09876                return -1;
09877             }
09878          } else {
09879             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09880             if (useadsi)
09881                adsi_login(chan);
09882             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09883                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09884                return -1;
09885             }
09886          }
09887          if (ast_waitstream(chan, "")) /* Channel is hung up */
09888             return -1;
09889       }
09890    }
09891    if (!valid && (logretries >= max_logins)) {
09892       ast_stopstream(chan);
09893       ast_play_and_wait(chan, "vm-goodbye");
09894       return -1;
09895    }
09896    if (vmu && !skipuser) {
09897       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09898    }
09899    return 0;
09900 }
09901 
09902 static int vm_execmain(struct ast_channel *chan, const char *data)
09903 {
09904    /* XXX This is, admittedly, some pretty horrendous code.  For some
09905       reason it just seemed a lot easier to do with GOTO's.  I feel
09906       like I'm back in my GWBASIC days. XXX */
09907    int res = -1;
09908    int cmd = 0;
09909    int valid = 0;
09910    char prefixstr[80] ="";
09911    char ext_context[256]="";
09912    int box;
09913    int useadsi = 0;
09914    int skipuser = 0;
09915    struct vm_state vms;
09916    struct ast_vm_user *vmu = NULL, vmus;
09917    char *context = NULL;
09918    int silentexit = 0;
09919    struct ast_flags flags = { 0 };
09920    signed char record_gain = 0;
09921    int play_auto = 0;
09922    int play_folder = 0;
09923    int in_urgent = 0;
09924 #ifdef IMAP_STORAGE
09925    int deleted = 0;
09926 #endif
09927 
09928    /* Add the vm_state to the active list and keep it active */
09929    memset(&vms, 0, sizeof(vms));
09930 
09931    vms.lastmsg = -1;
09932 
09933    memset(&vmus, 0, sizeof(vmus));
09934 
09935    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09936    if (chan->_state != AST_STATE_UP) {
09937       ast_debug(1, "Before ast_answer\n");
09938       ast_answer(chan);
09939    }
09940 
09941    if (!ast_strlen_zero(data)) {
09942       char *opts[OPT_ARG_ARRAY_SIZE];
09943       char *parse;
09944       AST_DECLARE_APP_ARGS(args,
09945          AST_APP_ARG(argv0);
09946          AST_APP_ARG(argv1);
09947       );
09948 
09949       parse = ast_strdupa(data);
09950 
09951       AST_STANDARD_APP_ARGS(args, parse);
09952 
09953       if (args.argc == 2) {
09954          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09955             return -1;
09956          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09957             int gain;
09958             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09959                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09960                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09961                   return -1;
09962                } else {
09963                   record_gain = (signed char) gain;
09964                }
09965             } else {
09966                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09967             }
09968          }
09969          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09970             play_auto = 1;
09971             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09972                /* See if it is a folder name first */
09973                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09974                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09975                      play_folder = -1;
09976                   }
09977                } else {
09978                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09979                }
09980             } else {
09981                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09982             }
09983             if (play_folder > 9 || play_folder < 0) {
09984                ast_log(AST_LOG_WARNING,
09985                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09986                   opts[OPT_ARG_PLAYFOLDER]);
09987                play_folder = 0;
09988             }
09989          }
09990       } else {
09991          /* old style options parsing */
09992          while (*(args.argv0)) {
09993             if (*(args.argv0) == 's')
09994                ast_set_flag(&flags, OPT_SILENT);
09995             else if (*(args.argv0) == 'p')
09996                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09997             else 
09998                break;
09999             (args.argv0)++;
10000          }
10001 
10002       }
10003 
10004       valid = ast_test_flag(&flags, OPT_SILENT);
10005 
10006       if ((context = strchr(args.argv0, '@')))
10007          *context++ = '\0';
10008 
10009       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10010          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10011       else
10012          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10013 
10014       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10015          skipuser++;
10016       else
10017          valid = 0;
10018    }
10019 
10020    if (!valid)
10021       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10022 
10023    ast_debug(1, "After vm_authenticate\n");
10024 
10025    if (vms.username[0] == '*') {
10026       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10027 
10028       /* user entered '*' */
10029       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10030          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10031          res = 0; /* prevent hangup */
10032          goto out;
10033       }
10034    }
10035 
10036    if (!res) {
10037       valid = 1;
10038       if (!skipuser)
10039          vmu = &vmus;
10040    } else {
10041       res = 0;
10042    }
10043 
10044    /* If ADSI is supported, setup login screen */
10045    adsi_begin(chan, &useadsi);
10046 
10047    ast_test_suite_assert(valid);
10048    if (!valid) {
10049       goto out;
10050    }
10051    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10052 
10053 #ifdef IMAP_STORAGE
10054    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10055    pthread_setspecific(ts_vmstate.key, &vms);
10056 
10057    vms.interactive = 1;
10058    vms.updated = 1;
10059    if (vmu)
10060       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10061    vmstate_insert(&vms);
10062    init_vm_state(&vms);
10063 #endif
10064    
10065    /* Set language from config to override channel language */
10066    if (!ast_strlen_zero(vmu->language))
10067       ast_string_field_set(chan, language, vmu->language);
10068 
10069    /* Retrieve urgent, old and new message counts */
10070    ast_debug(1, "Before open_mailbox\n");
10071    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10072    if (res < 0)
10073       goto out;
10074    vms.oldmessages = vms.lastmsg + 1;
10075    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10076    /* check INBOX */
10077    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10078    if (res < 0)
10079       goto out;
10080    vms.newmessages = vms.lastmsg + 1;
10081    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10082    /* Start in Urgent */
10083    in_urgent = 1;
10084    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10085    if (res < 0)
10086       goto out;
10087    vms.urgentmessages = vms.lastmsg + 1;
10088    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10089 
10090    /* Select proper mailbox FIRST!! */
10091    if (play_auto) {
10092       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10093       if (vms.urgentmessages) {
10094          in_urgent = 1;
10095          res = open_mailbox(&vms, vmu, 11);
10096       } else {
10097          in_urgent = 0;
10098          res = open_mailbox(&vms, vmu, play_folder);
10099       }
10100       if (res < 0)
10101          goto out;
10102 
10103       /* If there are no new messages, inform the user and hangup */
10104       if (vms.lastmsg == -1) {
10105          in_urgent = 0;
10106          cmd = vm_browse_messages(chan, &vms, vmu);
10107          res = 0;
10108          goto out;
10109       }
10110    } else {
10111       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10112          /* If we only have old messages start here */
10113          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10114          in_urgent = 0;
10115          play_folder = 1;
10116          if (res < 0)
10117             goto out;
10118       } else if (!vms.urgentmessages && vms.newmessages) {
10119          /* If we have new messages but none are urgent */
10120          in_urgent = 0;
10121          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10122          if (res < 0)
10123             goto out;
10124       }
10125    }
10126 
10127    if (useadsi)
10128       adsi_status(chan, &vms);
10129    res = 0;
10130 
10131    /* Check to see if this is a new user */
10132    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10133       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10134       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10135          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10136       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10137       if ((cmd == 't') || (cmd == '#')) {
10138          /* Timeout */
10139          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10140          res = 0;
10141          goto out;
10142       } else if (cmd < 0) {
10143          /* Hangup */
10144          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10145          res = -1;
10146          goto out;
10147       }
10148    }
10149 #ifdef IMAP_STORAGE
10150       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10151       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10152          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10153          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10154       }
10155       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10156       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10157          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10158          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10159       }
10160 #endif
10161 
10162    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10163    if (play_auto) {
10164       cmd = '1';
10165    } else {
10166       cmd = vm_intro(chan, vmu, &vms);
10167    }
10168    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10169 
10170    vms.repeats = 0;
10171    vms.starting = 1;
10172    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10173       /* Run main menu */
10174       switch (cmd) {
10175       case '1': /* First message */
10176          vms.curmsg = 0;
10177          /* Fall through */
10178       case '5': /* Play current message */
10179          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10180          cmd = vm_browse_messages(chan, &vms, vmu);
10181          break;
10182       case '2': /* Change folders */
10183          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10184          if (useadsi)
10185             adsi_folders(chan, 0, "Change to folder...");
10186 
10187          cmd = get_folder2(chan, "vm-changeto", 0);
10188          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10189          if (cmd == '#') {
10190             cmd = 0;
10191          } else if (cmd > 0) {
10192             cmd = cmd - '0';
10193             res = close_mailbox(&vms, vmu);
10194             if (res == ERROR_LOCK_PATH)
10195                goto out;
10196             /* If folder is not urgent, set in_urgent to zero! */
10197             if (cmd != 11) in_urgent = 0;
10198             res = open_mailbox(&vms, vmu, cmd);
10199             if (res < 0)
10200                goto out;
10201             play_folder = cmd;
10202             cmd = 0;
10203          }
10204          if (useadsi)
10205             adsi_status2(chan, &vms);
10206 
10207          if (!cmd) {
10208             cmd = vm_play_folder_name(chan, vms.vmbox);
10209          }
10210 
10211          vms.starting = 1;
10212          vms.curmsg = 0;
10213          break;
10214       case '3': /* Advanced options */
10215          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10216          cmd = 0;
10217          vms.repeats = 0;
10218          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10219             switch (cmd) {
10220             case '1': /* Reply */
10221                if (vms.lastmsg > -1 && !vms.starting) {
10222                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10223                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10224                      res = cmd;
10225                      goto out;
10226                   }
10227                } else {
10228                   cmd = ast_play_and_wait(chan, "vm-sorry");
10229                }
10230                cmd = 't';
10231                break;
10232             case '2': /* Callback */
10233                if (!vms.starting)
10234                   ast_verb(3, "Callback Requested\n");
10235                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10236                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10237                   if (cmd == 9) {
10238                      silentexit = 1;
10239                      goto out;
10240                   } else if (cmd == ERROR_LOCK_PATH) {
10241                      res = cmd;
10242                      goto out;
10243                   }
10244                } else {
10245                   cmd = ast_play_and_wait(chan, "vm-sorry");
10246                }
10247                cmd = 't';
10248                break;
10249             case '3': /* Envelope */
10250                if (vms.lastmsg > -1 && !vms.starting) {
10251                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10252                   if (cmd == ERROR_LOCK_PATH) {
10253                      res = cmd;
10254                      goto out;
10255                   }
10256                } else {
10257                   cmd = ast_play_and_wait(chan, "vm-sorry");
10258                }
10259                cmd = 't';
10260                break;
10261             case '4': /* Dialout */
10262                if (!ast_strlen_zero(vmu->dialout)) {
10263                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10264                   if (cmd == 9) {
10265                      silentexit = 1;
10266                      goto out;
10267                   }
10268                } else {
10269                   cmd = ast_play_and_wait(chan, "vm-sorry");
10270                }
10271                cmd = 't';
10272                break;
10273 
10274             case '5': /* Leave VoiceMail */
10275                if (ast_test_flag(vmu, VM_SVMAIL)) {
10276                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10277                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10278                      res = cmd;
10279                      goto out;
10280                   }
10281                } else {
10282                   cmd = ast_play_and_wait(chan, "vm-sorry");
10283                }
10284                cmd = 't';
10285                break;
10286 
10287             case '*': /* Return to main menu */
10288                cmd = 't';
10289                break;
10290 
10291             default:
10292                cmd = 0;
10293                if (!vms.starting) {
10294                   cmd = ast_play_and_wait(chan, "vm-toreply");
10295                }
10296                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10297                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10298                }
10299                if (!cmd && !vms.starting) {
10300                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10301                }
10302                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10303                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10304                }
10305                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10306                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10307                }
10308                if (!cmd) {
10309                   cmd = ast_play_and_wait(chan, "vm-starmain");
10310                }
10311                if (!cmd) {
10312                   cmd = ast_waitfordigit(chan, 6000);
10313                }
10314                if (!cmd) {
10315                   vms.repeats++;
10316                }
10317                if (vms.repeats > 3) {
10318                   cmd = 't';
10319                }
10320                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10321             }
10322          }
10323          if (cmd == 't') {
10324             cmd = 0;
10325             vms.repeats = 0;
10326          }
10327          break;
10328       case '4': /* Go to the previous message */
10329          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10330          if (vms.curmsg > 0) {
10331             vms.curmsg--;
10332             cmd = play_message(chan, vmu, &vms);
10333          } else {
10334             /* Check if we were listening to new
10335                messages.  If so, go to Urgent messages
10336                instead of saying "no more messages"
10337             */
10338             if (in_urgent == 0 && vms.urgentmessages > 0) {
10339                /* Check for Urgent messages */
10340                in_urgent = 1;
10341                res = close_mailbox(&vms, vmu);
10342                if (res == ERROR_LOCK_PATH)
10343                   goto out;
10344                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10345                if (res < 0)
10346                   goto out;
10347                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10348                vms.curmsg = vms.lastmsg;
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 = vms.lastmsg;
10354                cmd = play_message(chan, vmu, &vms);
10355             } else {
10356                cmd = ast_play_and_wait(chan, "vm-nomore");
10357             }
10358          }
10359          break;
10360       case '6': /* Go to the next message */
10361          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10362          if (vms.curmsg < vms.lastmsg) {
10363             vms.curmsg++;
10364             cmd = play_message(chan, vmu, &vms);
10365          } else {
10366             if (in_urgent && vms.newmessages > 0) {
10367                /* Check if we were listening to urgent
10368                 * messages.  If so, go to regular new messages
10369                 * instead of saying "no more messages"
10370                 */
10371                in_urgent = 0;
10372                res = close_mailbox(&vms, vmu);
10373                if (res == ERROR_LOCK_PATH)
10374                   goto out;
10375                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10376                if (res < 0)
10377                   goto out;
10378                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10379                vms.curmsg = -1;
10380                if (vms.lastmsg < 0) {
10381                   cmd = ast_play_and_wait(chan, "vm-nomore");
10382                }
10383             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10384                vms.curmsg = 0;
10385                cmd = play_message(chan, vmu, &vms);
10386             } else {
10387                cmd = ast_play_and_wait(chan, "vm-nomore");
10388             }
10389          }
10390          break;
10391       case '7': /* Delete the current message */
10392          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10393             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10394             if (useadsi)
10395                adsi_delete(chan, &vms);
10396             if (vms.deleted[vms.curmsg]) {
10397                if (play_folder == 0) {
10398                   if (in_urgent) {
10399                      vms.urgentmessages--;
10400                   } else {
10401                      vms.newmessages--;
10402                   }
10403                }
10404                else if (play_folder == 1)
10405                   vms.oldmessages--;
10406                cmd = ast_play_and_wait(chan, "vm-deleted");
10407             } else {
10408                if (play_folder == 0) {
10409                   if (in_urgent) {
10410                      vms.urgentmessages++;
10411                   } else {
10412                      vms.newmessages++;
10413                   }
10414                }
10415                else if (play_folder == 1)
10416                   vms.oldmessages++;
10417                cmd = ast_play_and_wait(chan, "vm-undeleted");
10418             }
10419             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10420                if (vms.curmsg < vms.lastmsg) {
10421                   vms.curmsg++;
10422                   cmd = play_message(chan, vmu, &vms);
10423                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10424                   vms.curmsg = 0;
10425                   cmd = play_message(chan, vmu, &vms);
10426                } else {
10427                   /* Check if we were listening to urgent
10428                      messages.  If so, go to regular new messages
10429                      instead of saying "no more messages"
10430                   */
10431                   if (in_urgent == 1) {
10432                      /* Check for new messages */
10433                      in_urgent = 0;
10434                      res = close_mailbox(&vms, vmu);
10435                      if (res == ERROR_LOCK_PATH)
10436                         goto out;
10437                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10438                      if (res < 0)
10439                         goto out;
10440                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10441                      vms.curmsg = -1;
10442                      if (vms.lastmsg < 0) {
10443                         cmd = ast_play_and_wait(chan, "vm-nomore");
10444                      }
10445                   } else {
10446                      cmd = ast_play_and_wait(chan, "vm-nomore");
10447                   }
10448                }
10449             }
10450          } else /* Delete not valid if we haven't selected a message */
10451             cmd = 0;
10452 #ifdef IMAP_STORAGE
10453          deleted = 1;
10454 #endif
10455          break;
10456    
10457       case '8': /* Forward the current message */
10458          if (vms.lastmsg > -1) {
10459             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10460             if (cmd == ERROR_LOCK_PATH) {
10461                res = cmd;
10462                goto out;
10463             }
10464          } else {
10465             /* Check if we were listening to urgent
10466                messages.  If so, go to regular new messages
10467                instead of saying "no more messages"
10468             */
10469             if (in_urgent == 1 && vms.newmessages > 0) {
10470                /* Check for new messages */
10471                in_urgent = 0;
10472                res = close_mailbox(&vms, vmu);
10473                if (res == ERROR_LOCK_PATH)
10474                   goto out;
10475                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10476                if (res < 0)
10477                   goto out;
10478                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10479                vms.curmsg = -1;
10480                if (vms.lastmsg < 0) {
10481                   cmd = ast_play_and_wait(chan, "vm-nomore");
10482                }
10483             } else {
10484                cmd = ast_play_and_wait(chan, "vm-nomore");
10485             }
10486          }
10487          break;
10488       case '9': /* Save message to folder */
10489          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10490          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10491             /* No message selected */
10492             cmd = 0;
10493             break;
10494          }
10495          if (useadsi)
10496             adsi_folders(chan, 1, "Save to folder...");
10497          cmd = get_folder2(chan, "vm-savefolder", 1);
10498          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10499          box = 0; /* Shut up compiler */
10500          if (cmd == '#') {
10501             cmd = 0;
10502             break;
10503          } else if (cmd > 0) {
10504             box = cmd = cmd - '0';
10505             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10506             if (cmd == ERROR_LOCK_PATH) {
10507                res = cmd;
10508                goto out;
10509 #ifndef IMAP_STORAGE
10510             } else if (!cmd) {
10511                vms.deleted[vms.curmsg] = 1;
10512 #endif
10513             } else {
10514                vms.deleted[vms.curmsg] = 0;
10515                vms.heard[vms.curmsg] = 0;
10516             }
10517          }
10518          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10519          if (useadsi)
10520             adsi_message(chan, &vms);
10521          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10522          if (!cmd) {
10523             cmd = ast_play_and_wait(chan, "vm-message");
10524             if (!cmd) 
10525                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10526             if (!cmd)
10527                cmd = ast_play_and_wait(chan, "vm-savedto");
10528             if (!cmd)
10529                cmd = vm_play_folder_name(chan, vms.fn);
10530          } else {
10531             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10532          }
10533          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10534             if (vms.curmsg < vms.lastmsg) {
10535                vms.curmsg++;
10536                cmd = play_message(chan, vmu, &vms);
10537             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10538                vms.curmsg = 0;
10539                cmd = play_message(chan, vmu, &vms);
10540             } else {
10541                /* Check if we were listening to urgent
10542                   messages.  If so, go to regular new messages
10543                   instead of saying "no more messages"
10544                */
10545                if (in_urgent == 1 && vms.newmessages > 0) {
10546                   /* Check for new messages */
10547                   in_urgent = 0;
10548                   res = close_mailbox(&vms, vmu);
10549                   if (res == ERROR_LOCK_PATH)
10550                      goto out;
10551                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10552                   if (res < 0)
10553                      goto out;
10554                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10555                   vms.curmsg = -1;
10556                   if (vms.lastmsg < 0) {
10557                      cmd = ast_play_and_wait(chan, "vm-nomore");
10558                   }
10559                } else {
10560                   cmd = ast_play_and_wait(chan, "vm-nomore");
10561                }
10562             }
10563          }
10564          break;
10565       case '*': /* Help */
10566          if (!vms.starting) {
10567             cmd = ast_play_and_wait(chan, "vm-onefor");
10568             if (!strncasecmp(chan->language, "he", 2)) {
10569                cmd = ast_play_and_wait(chan, "vm-for");
10570             }
10571             if (!cmd)
10572                cmd = vm_play_folder_name(chan, vms.vmbox);
10573             if (!cmd)
10574                cmd = ast_play_and_wait(chan, "vm-opts");
10575             if (!cmd)
10576                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10577          } else
10578             cmd = 0;
10579          break;
10580       case '0': /* Mailbox options */
10581          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10582          if (useadsi)
10583             adsi_status(chan, &vms);
10584          break;
10585       default: /* Nothing */
10586          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10587          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10588          break;
10589       }
10590    }
10591    if ((cmd == 't') || (cmd == '#')) {
10592       /* Timeout */
10593       res = 0;
10594    } else {
10595       /* Hangup */
10596       res = -1;
10597    }
10598 
10599 out:
10600    if (res > -1) {
10601       ast_stopstream(chan);
10602       adsi_goodbye(chan);
10603       if (valid && res != OPERATOR_EXIT) {
10604          if (silentexit)
10605             res = ast_play_and_wait(chan, "vm-dialout");
10606          else 
10607             res = ast_play_and_wait(chan, "vm-goodbye");
10608       }
10609       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10610          res = 0;
10611       }
10612       if (useadsi)
10613          ast_adsi_unload_session(chan);
10614    }
10615    if (vmu)
10616       close_mailbox(&vms, vmu);
10617    if (valid) {
10618       int new = 0, old = 0, urgent = 0;
10619       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10620       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10621       /* Urgent flag not passwd to externnotify here */
10622       run_externnotify(vmu->context, vmu->mailbox, NULL);
10623       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10624       queue_mwi_event(ext_context, urgent, new, old);
10625    }
10626 #ifdef IMAP_STORAGE
10627    /* expunge message - use UID Expunge if supported on IMAP server*/
10628    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10629    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10630       ast_mutex_lock(&vms.lock);
10631 #ifdef HAVE_IMAP_TK2006
10632       if (LEVELUIDPLUS (vms.mailstream)) {
10633          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10634       } else 
10635 #endif
10636          mail_expunge(vms.mailstream);
10637       ast_mutex_unlock(&vms.lock);
10638    }
10639    /*  before we delete the state, we should copy pertinent info
10640     *  back to the persistent model */
10641    if (vmu) {
10642       vmstate_delete(&vms);
10643    }
10644 #endif
10645    if (vmu)
10646       free_user(vmu);
10647 
10648 #ifdef IMAP_STORAGE
10649    pthread_setspecific(ts_vmstate.key, NULL);
10650 #endif
10651    return res;
10652 }
10653 
10654 static int vm_exec(struct ast_channel *chan, const char *data)
10655 {
10656    int res = 0;
10657    char *tmp;
10658    struct leave_vm_options leave_options;
10659    struct ast_flags flags = { 0 };
10660    char *opts[OPT_ARG_ARRAY_SIZE];
10661    AST_DECLARE_APP_ARGS(args,
10662       AST_APP_ARG(argv0);
10663       AST_APP_ARG(argv1);
10664    );
10665    
10666    memset(&leave_options, 0, sizeof(leave_options));
10667 
10668    if (chan->_state != AST_STATE_UP)
10669       ast_answer(chan);
10670 
10671    if (!ast_strlen_zero(data)) {
10672       tmp = ast_strdupa(data);
10673       AST_STANDARD_APP_ARGS(args, tmp);
10674       if (args.argc == 2) {
10675          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10676             return -1;
10677          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10678          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10679             int gain;
10680 
10681             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10682                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10683                return -1;
10684             } else {
10685                leave_options.record_gain = (signed char) gain;
10686             }
10687          }
10688          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10689             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10690                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10691          }
10692       }
10693    } else {
10694       char temp[256];
10695       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10696       if (res < 0)
10697          return res;
10698       if (ast_strlen_zero(temp))
10699          return 0;
10700       args.argv0 = ast_strdupa(temp);
10701    }
10702 
10703    res = leave_voicemail(chan, args.argv0, &leave_options);
10704    if (res == 't') {
10705       ast_play_and_wait(chan, "vm-goodbye");
10706       res = 0;
10707    }
10708 
10709    if (res == OPERATOR_EXIT) {
10710       res = 0;
10711    }
10712 
10713    if (res == ERROR_LOCK_PATH) {
10714       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10715       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10716       res = 0;
10717    }
10718 
10719    return res;
10720 }
10721 
10722 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10723 {
10724    struct ast_vm_user *vmu;
10725 
10726    if (!ast_strlen_zero(box) && box[0] == '*') {
10727       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10728             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10729             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10730             "\n\tand will be ignored.\n", box, context);
10731       return NULL;
10732    }
10733 
10734    AST_LIST_TRAVERSE(&users, vmu, list) {
10735       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10736          if (strcasecmp(vmu->context, context)) {
10737             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10738                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10739                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10740                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10741          }
10742          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10743          return NULL;
10744       }
10745       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10746          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10747          return NULL;
10748       }
10749    }
10750    
10751    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10752       return NULL;
10753    
10754    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10755    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10756 
10757    AST_LIST_INSERT_TAIL(&users, vmu, list);
10758    
10759    return vmu;
10760 }
10761 
10762 static int append_mailbox(const char *context, const char *box, const char *data)
10763 {
10764    /* Assumes lock is already held */
10765    char *tmp;
10766    char *stringp;
10767    char *s;
10768    struct ast_vm_user *vmu;
10769    char *mailbox_full;
10770    int new = 0, old = 0, urgent = 0;
10771    char secretfn[PATH_MAX] = "";
10772 
10773    tmp = ast_strdupa(data);
10774 
10775    if (!(vmu = find_or_create(context, box)))
10776       return -1;
10777 
10778    populate_defaults(vmu);
10779 
10780    stringp = tmp;
10781    if ((s = strsep(&stringp, ","))) {
10782       if (!ast_strlen_zero(s) && s[0] == '*') {
10783          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10784             "\n\tmust be reset in voicemail.conf.\n", box);
10785       }
10786       /* assign password regardless of validity to prevent NULL password from being assigned */
10787       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10788    }
10789    if (stringp && (s = strsep(&stringp, ","))) {
10790       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10791    }
10792    if (stringp && (s = strsep(&stringp, ","))) {
10793       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10794    }
10795    if (stringp && (s = strsep(&stringp, ","))) {
10796       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10797    }
10798    if (stringp && (s = strsep(&stringp, ","))) {
10799       apply_options(vmu, s);
10800    }
10801 
10802    switch (vmu->passwordlocation) {
10803    case OPT_PWLOC_SPOOLDIR:
10804       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10805       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10806    }
10807 
10808    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10809    strcpy(mailbox_full, box);
10810    strcat(mailbox_full, "@");
10811    strcat(mailbox_full, context);
10812 
10813    inboxcount2(mailbox_full, &urgent, &new, &old);
10814    queue_mwi_event(mailbox_full, urgent, new, old);
10815 
10816    return 0;
10817 }
10818 
10819 AST_TEST_DEFINE(test_voicemail_vmuser)
10820 {
10821    int res = 0;
10822    struct ast_vm_user *vmu;
10823    /* language parameter seems to only be used for display in manager action */
10824    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10825       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10826       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10827       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10828       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10829       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10830       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10831       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10832       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10833 #ifdef IMAP_STORAGE
10834    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10835       "imapfolder=INBOX|imapvmshareid=6000";
10836 #endif
10837 
10838    switch (cmd) {
10839    case TEST_INIT:
10840       info->name = "vmuser";
10841       info->category = "/apps/app_voicemail/";
10842       info->summary = "Vmuser unit test";
10843       info->description =
10844          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10845       return AST_TEST_NOT_RUN;
10846    case TEST_EXECUTE:
10847       break;
10848    }
10849 
10850    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10851       return AST_TEST_NOT_RUN;
10852    }
10853    ast_set_flag(vmu, VM_ALLOCED);
10854    populate_defaults(vmu);
10855 
10856    apply_options(vmu, options_string);
10857 
10858    if (!ast_test_flag(vmu, VM_ATTACH)) {
10859       ast_test_status_update(test, "Parse failure for attach option\n");
10860       res = 1;
10861    }
10862    if (strcasecmp(vmu->attachfmt, "wav49")) {
10863       ast_test_status_update(test, "Parse failure for attachftm option\n");
10864       res = 1;
10865    }
10866    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10867       ast_test_status_update(test, "Parse failure for serveremail option\n");
10868       res = 1;
10869    }
10870    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10871       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10872       res = 1;
10873    }
10874    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10875       ast_test_status_update(test, "Parse failure for emailbody option\n");
10876       res = 1;
10877    }
10878    if (strcasecmp(vmu->zonetag, "central")) {
10879       ast_test_status_update(test, "Parse failure for tz option\n");
10880       res = 1;
10881    }
10882    if (!ast_test_flag(vmu, VM_DELETE)) {
10883       ast_test_status_update(test, "Parse failure for delete option\n");
10884       res = 1;
10885    }
10886    if (!ast_test_flag(vmu, VM_SAYCID)) {
10887       ast_test_status_update(test, "Parse failure for saycid option\n");
10888       res = 1;
10889    }
10890    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10891       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10892       res = 1;
10893    }
10894    if (!ast_test_flag(vmu, VM_REVIEW)) {
10895       ast_test_status_update(test, "Parse failure for review option\n");
10896       res = 1;
10897    }
10898    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10899       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10900       res = 1;
10901    }
10902    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10903       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10904       res = 1;
10905    }
10906    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10907       ast_test_status_update(test, "Parse failure for operator option\n");
10908       res = 1;
10909    }
10910    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10911       ast_test_status_update(test, "Parse failure for envelope option\n");
10912       res = 1;
10913    }
10914    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10915       ast_test_status_update(test, "Parse failure for moveheard option\n");
10916       res = 1;
10917    }
10918    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10919       ast_test_status_update(test, "Parse failure for sayduration option\n");
10920       res = 1;
10921    }
10922    if (vmu->saydurationm != 5) {
10923       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10924       res = 1;
10925    }
10926    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10927       ast_test_status_update(test, "Parse failure for forcename option\n");
10928       res = 1;
10929    }
10930    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10931       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10932       res = 1;
10933    }
10934    if (strcasecmp(vmu->callback, "somecontext")) {
10935       ast_test_status_update(test, "Parse failure for callbacks option\n");
10936       res = 1;
10937    }
10938    if (strcasecmp(vmu->dialout, "somecontext2")) {
10939       ast_test_status_update(test, "Parse failure for dialout option\n");
10940       res = 1;
10941    }
10942    if (strcasecmp(vmu->exit, "somecontext3")) {
10943       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10944       res = 1;
10945    }
10946    if (vmu->minsecs != 10) {
10947       ast_test_status_update(test, "Parse failure for minsecs option\n");
10948       res = 1;
10949    }
10950    if (vmu->maxsecs != 100) {
10951       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10952       res = 1;
10953    }
10954    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10955       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10956       res = 1;
10957    }
10958    if (vmu->maxdeletedmsg != 50) {
10959       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10960       res = 1;
10961    }
10962    if (vmu->volgain != 1.3) {
10963       ast_test_status_update(test, "Parse failure for volgain option\n");
10964       res = 1;
10965    }
10966    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10967       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10968       res = 1;
10969    }
10970 #ifdef IMAP_STORAGE
10971    apply_options(vmu, option_string2);
10972 
10973    if (strcasecmp(vmu->imapuser, "imapuser")) {
10974       ast_test_status_update(test, "Parse failure for imapuser option\n");
10975       res = 1;
10976    }
10977    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10978       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10979       res = 1;
10980    }
10981    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10982       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10983       res = 1;
10984    }
10985    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10986       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10987       res = 1;
10988    }
10989 #endif
10990 
10991    free_user(vmu);
10992    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10993 }
10994 
10995 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10996 {
10997    struct ast_vm_user svm;
10998    char *context, *box;
10999    AST_DECLARE_APP_ARGS(args,
11000       AST_APP_ARG(mbox);
11001       AST_APP_ARG(options);
11002    );
11003    static int dep_warning = 0;
11004 
11005    if (ast_strlen_zero(data)) {
11006       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
11007       return -1;
11008    }
11009 
11010    if (!dep_warning) {
11011       dep_warning = 1;
11012       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
11013    }
11014 
11015    box = ast_strdupa(data);
11016 
11017    AST_STANDARD_APP_ARGS(args, box);
11018 
11019    if (args.options) {
11020    }
11021 
11022    if ((context = strchr(args.mbox, '@'))) {
11023       *context = '\0';
11024       context++;
11025    }
11026 
11027    if (find_user(&svm, context, args.mbox)) {
11028       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11029    } else
11030       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11031 
11032    return 0;
11033 }
11034 
11035 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11036 {
11037    struct ast_vm_user svm;
11038    AST_DECLARE_APP_ARGS(arg,
11039       AST_APP_ARG(mbox);
11040       AST_APP_ARG(context);
11041    );
11042 
11043    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11044 
11045    if (ast_strlen_zero(arg.mbox)) {
11046       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11047       return -1;
11048    }
11049 
11050    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11051    return 0;
11052 }
11053 
11054 static struct ast_custom_function mailbox_exists_acf = {
11055    .name = "MAILBOX_EXISTS",
11056    .read = acf_mailbox_exists,
11057 };
11058 
11059 static int vmauthenticate(struct ast_channel *chan, const char *data)
11060 {
11061    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11062    struct ast_vm_user vmus;
11063    char *options = NULL;
11064    int silent = 0, skipuser = 0;
11065    int res = -1;
11066    
11067    if (data) {
11068       s = ast_strdupa(data);
11069       user = strsep(&s, ",");
11070       options = strsep(&s, ",");
11071       if (user) {
11072          s = user;
11073          user = strsep(&s, "@");
11074          context = strsep(&s, "");
11075          if (!ast_strlen_zero(user))
11076             skipuser++;
11077          ast_copy_string(mailbox, user, sizeof(mailbox));
11078       }
11079    }
11080 
11081    if (options) {
11082       silent = (strchr(options, 's')) != NULL;
11083    }
11084 
11085    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11086       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11087       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11088       ast_play_and_wait(chan, "auth-thankyou");
11089       res = 0;
11090    } else if (mailbox[0] == '*') {
11091       /* user entered '*' */
11092       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11093          res = 0; /* prevent hangup */
11094       }
11095    }
11096 
11097    return res;
11098 }
11099 
11100 static char *show_users_realtime(int fd, const char *context)
11101 {
11102    struct ast_config *cfg;
11103    const char *cat = NULL;
11104 
11105    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11106       "context", context, SENTINEL))) {
11107       return CLI_FAILURE;
11108    }
11109 
11110    ast_cli(fd,
11111       "\n"
11112       "=============================================================\n"
11113       "=== Configured Voicemail Users ==============================\n"
11114       "=============================================================\n"
11115       "===\n");
11116 
11117    while ((cat = ast_category_browse(cfg, cat))) {
11118       struct ast_variable *var = NULL;
11119       ast_cli(fd,
11120          "=== Mailbox ...\n"
11121          "===\n");
11122       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11123          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11124       ast_cli(fd,
11125          "===\n"
11126          "=== ---------------------------------------------------------\n"
11127          "===\n");
11128    }
11129 
11130    ast_cli(fd,
11131       "=============================================================\n"
11132       "\n");
11133 
11134    ast_config_destroy(cfg);
11135 
11136    return CLI_SUCCESS;
11137 }
11138 
11139 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11140 {
11141    int which = 0;
11142    int wordlen;
11143    struct ast_vm_user *vmu;
11144    const char *context = "";
11145 
11146    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11147    if (pos > 4)
11148       return NULL;
11149    if (pos == 3)
11150       return (state == 0) ? ast_strdup("for") : NULL;
11151    wordlen = strlen(word);
11152    AST_LIST_TRAVERSE(&users, vmu, list) {
11153       if (!strncasecmp(word, vmu->context, wordlen)) {
11154          if (context && strcmp(context, vmu->context) && ++which > state)
11155             return ast_strdup(vmu->context);
11156          /* ignore repeated contexts ? */
11157          context = vmu->context;
11158       }
11159    }
11160    return NULL;
11161 }
11162 
11163 /*! \brief Show a list of voicemail users in the CLI */
11164 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11165 {
11166    struct ast_vm_user *vmu;
11167 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11168    const char *context = NULL;
11169    int users_counter = 0;
11170 
11171    switch (cmd) {
11172    case CLI_INIT:
11173       e->command = "voicemail show users";
11174       e->usage =
11175          "Usage: voicemail show users [for <context>]\n"
11176          "       Lists all mailboxes currently set up\n";
11177       return NULL;
11178    case CLI_GENERATE:
11179       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11180    }  
11181 
11182    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11183       return CLI_SHOWUSAGE;
11184    if (a->argc == 5) {
11185       if (strcmp(a->argv[3],"for"))
11186          return CLI_SHOWUSAGE;
11187       context = a->argv[4];
11188    }
11189 
11190    if (ast_check_realtime("voicemail")) {
11191       if (!context) {
11192          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11193          return CLI_SHOWUSAGE;
11194       }
11195       return show_users_realtime(a->fd, context);
11196    }
11197 
11198    AST_LIST_LOCK(&users);
11199    if (AST_LIST_EMPTY(&users)) {
11200       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11201       AST_LIST_UNLOCK(&users);
11202       return CLI_FAILURE;
11203    }
11204    if (!context) {
11205       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11206    } else {
11207       int count = 0;
11208       AST_LIST_TRAVERSE(&users, vmu, list) {
11209          if (!strcmp(context, vmu->context)) {
11210             count++;
11211             break;
11212          }
11213       }
11214       if (count) {
11215          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11216       } else {
11217          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11218          AST_LIST_UNLOCK(&users);
11219          return CLI_FAILURE;
11220       }
11221    }
11222    AST_LIST_TRAVERSE(&users, vmu, list) {
11223       int newmsgs = 0, oldmsgs = 0;
11224       char count[12], tmp[256] = "";
11225 
11226       if (!context || !strcmp(context, vmu->context)) {
11227          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11228          inboxcount(tmp, &newmsgs, &oldmsgs);
11229          snprintf(count, sizeof(count), "%d", newmsgs);
11230          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11231          users_counter++;
11232       }
11233    }
11234    AST_LIST_UNLOCK(&users);
11235    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11236    return CLI_SUCCESS;
11237 }
11238 
11239 /*! \brief Show a list of voicemail zones in the CLI */
11240 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11241 {
11242    struct vm_zone *zone;
11243 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11244    char *res = CLI_SUCCESS;
11245 
11246    switch (cmd) {
11247    case CLI_INIT:
11248       e->command = "voicemail show zones";
11249       e->usage =
11250          "Usage: voicemail show zones\n"
11251          "       Lists zone message formats\n";
11252       return NULL;
11253    case CLI_GENERATE:
11254       return NULL;
11255    }
11256 
11257    if (a->argc != 3)
11258       return CLI_SHOWUSAGE;
11259 
11260    AST_LIST_LOCK(&zones);
11261    if (!AST_LIST_EMPTY(&zones)) {
11262       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11263       AST_LIST_TRAVERSE(&zones, zone, list) {
11264          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11265       }
11266    } else {
11267       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11268       res = CLI_FAILURE;
11269    }
11270    AST_LIST_UNLOCK(&zones);
11271 
11272    return res;
11273 }
11274 
11275 /*! \brief Reload voicemail configuration from the CLI */
11276 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11277 {
11278    switch (cmd) {
11279    case CLI_INIT:
11280       e->command = "voicemail reload";
11281       e->usage =
11282          "Usage: voicemail reload\n"
11283          "       Reload voicemail configuration\n";
11284       return NULL;
11285    case CLI_GENERATE:
11286       return NULL;
11287    }
11288 
11289    if (a->argc != 2)
11290       return CLI_SHOWUSAGE;
11291 
11292    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11293    load_config(1);
11294    
11295    return CLI_SUCCESS;
11296 }
11297 
11298 static struct ast_cli_entry cli_voicemail[] = {
11299    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11300    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11301    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11302 };
11303 
11304 #ifdef IMAP_STORAGE
11305    #define DATA_EXPORT_VM_USERS(USER)              \
11306       USER(ast_vm_user, context, AST_DATA_STRING)        \
11307       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11308       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11309       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11310       USER(ast_vm_user, email, AST_DATA_STRING)       \
11311       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11312       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11313       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11314       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11315       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11316       USER(ast_vm_user, language, AST_DATA_STRING)       \
11317       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11318       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11319       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11320       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11321       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11322       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11323       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11324       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11325       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11326       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11327       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11328       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11329       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11330       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11331       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11332 #else
11333    #define DATA_EXPORT_VM_USERS(USER)              \
11334       USER(ast_vm_user, context, AST_DATA_STRING)        \
11335       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11336       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11337       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11338       USER(ast_vm_user, email, AST_DATA_STRING)       \
11339       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11340       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11341       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11342       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11343       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11344       USER(ast_vm_user, language, AST_DATA_STRING)       \
11345       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11346       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11347       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11348       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11349       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11350       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11351       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11352       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11353       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11354       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11355       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11356       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11357 #endif
11358 
11359 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11360 
11361 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11362    ZONE(vm_zone, name, AST_DATA_STRING)      \
11363    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11364    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11365 
11366 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11367 
11368 /*!
11369  * \internal
11370  * \brief Add voicemail user to the data_root.
11371  * \param[in] search The search tree.
11372  * \param[in] data_root The main result node.
11373  * \param[in] user The voicemail user.
11374  */
11375 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11376     struct ast_data *data_root, struct ast_vm_user *user)
11377 {
11378    struct ast_data *data_user, *data_zone;
11379    struct ast_data *data_state;
11380    struct vm_zone *zone = NULL;
11381    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11382    char ext_context[256] = "";
11383 
11384    data_user = ast_data_add_node(data_root, "user");
11385    if (!data_user) {
11386       return -1;
11387    }
11388 
11389    ast_data_add_structure(ast_vm_user, data_user, user);
11390 
11391    AST_LIST_LOCK(&zones);
11392    AST_LIST_TRAVERSE(&zones, zone, list) {
11393       if (!strcmp(zone->name, user->zonetag)) {
11394          break;
11395       }
11396    }
11397    AST_LIST_UNLOCK(&zones);
11398 
11399    /* state */
11400    data_state = ast_data_add_node(data_user, "state");
11401    if (!data_state) {
11402       return -1;
11403    }
11404    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11405    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11406    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11407    ast_data_add_int(data_state, "newmsg", newmsg);
11408    ast_data_add_int(data_state, "oldmsg", oldmsg);
11409 
11410    if (zone) {
11411       data_zone = ast_data_add_node(data_user, "zone");
11412       ast_data_add_structure(vm_zone, data_zone, zone);
11413    }
11414 
11415    if (!ast_data_search_match(search, data_user)) {
11416       ast_data_remove_node(data_root, data_user);
11417    }
11418 
11419    return 0;
11420 }
11421 
11422 static int vm_users_data_provider_get(const struct ast_data_search *search,
11423    struct ast_data *data_root)
11424 {
11425    struct ast_vm_user *user;
11426 
11427    AST_LIST_LOCK(&users);
11428    AST_LIST_TRAVERSE(&users, user, list) {
11429       vm_users_data_provider_get_helper(search, data_root, user);
11430    }
11431    AST_LIST_UNLOCK(&users);
11432 
11433    return 0;
11434 }
11435 
11436 static const struct ast_data_handler vm_users_data_provider = {
11437    .version = AST_DATA_HANDLER_VERSION,
11438    .get = vm_users_data_provider_get
11439 };
11440 
11441 static const struct ast_data_entry vm_data_providers[] = {
11442    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11443 };
11444 
11445 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11446 {
11447    int new = 0, old = 0, urgent = 0;
11448 
11449    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11450 
11451    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11452       mwi_sub->old_urgent = urgent;
11453       mwi_sub->old_new = new;
11454       mwi_sub->old_old = old;
11455       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11456       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11457    }
11458 }
11459 
11460 static void poll_subscribed_mailboxes(void)
11461 {
11462    struct mwi_sub *mwi_sub;
11463 
11464    AST_RWLIST_RDLOCK(&mwi_subs);
11465    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11466       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11467          poll_subscribed_mailbox(mwi_sub);
11468       }
11469    }
11470    AST_RWLIST_UNLOCK(&mwi_subs);
11471 }
11472 
11473 static void *mb_poll_thread(void *data)
11474 {
11475    while (poll_thread_run) {
11476       struct timespec ts = { 0, };
11477       struct timeval wait;
11478 
11479       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11480       ts.tv_sec = wait.tv_sec;
11481       ts.tv_nsec = wait.tv_usec * 1000;
11482 
11483       ast_mutex_lock(&poll_lock);
11484       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11485       ast_mutex_unlock(&poll_lock);
11486 
11487       if (!poll_thread_run)
11488          break;
11489 
11490       poll_subscribed_mailboxes();
11491    }
11492 
11493    return NULL;
11494 }
11495 
11496 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11497 {
11498    ast_free(mwi_sub);
11499 }
11500 
11501 static int handle_unsubscribe(void *datap)
11502 {
11503    struct mwi_sub *mwi_sub;
11504    uint32_t *uniqueid = datap;
11505    
11506    AST_RWLIST_WRLOCK(&mwi_subs);
11507    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11508       if (mwi_sub->uniqueid == *uniqueid) {
11509          AST_LIST_REMOVE_CURRENT(entry);
11510          break;
11511       }
11512    }
11513    AST_RWLIST_TRAVERSE_SAFE_END
11514    AST_RWLIST_UNLOCK(&mwi_subs);
11515 
11516    if (mwi_sub)
11517       mwi_sub_destroy(mwi_sub);
11518 
11519    ast_free(uniqueid);  
11520    return 0;
11521 }
11522 
11523 static int handle_subscribe(void *datap)
11524 {
11525    unsigned int len;
11526    struct mwi_sub *mwi_sub;
11527    struct mwi_sub_task *p = datap;
11528 
11529    len = sizeof(*mwi_sub);
11530    if (!ast_strlen_zero(p->mailbox))
11531       len += strlen(p->mailbox);
11532 
11533    if (!ast_strlen_zero(p->context))
11534       len += strlen(p->context) + 1; /* Allow for seperator */
11535 
11536    if (!(mwi_sub = ast_calloc(1, len)))
11537       return -1;
11538 
11539    mwi_sub->uniqueid = p->uniqueid;
11540    if (!ast_strlen_zero(p->mailbox))
11541       strcpy(mwi_sub->mailbox, p->mailbox);
11542 
11543    if (!ast_strlen_zero(p->context)) {
11544       strcat(mwi_sub->mailbox, "@");
11545       strcat(mwi_sub->mailbox, p->context);
11546    }
11547 
11548    AST_RWLIST_WRLOCK(&mwi_subs);
11549    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11550    AST_RWLIST_UNLOCK(&mwi_subs);
11551    ast_free((void *) p->mailbox);
11552    ast_free((void *) p->context);
11553    ast_free(p);
11554    poll_subscribed_mailbox(mwi_sub);
11555    return 0;
11556 }
11557 
11558 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11559 {
11560    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11561 
11562    if (!uniqueid) {
11563       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11564       return;
11565    }
11566 
11567    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11568       ast_free(uniqueid);
11569       return;
11570    }
11571 
11572    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11573       ast_free(uniqueid);
11574       return;
11575    }
11576 
11577    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11578    *uniqueid = u;
11579    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11580       ast_free(uniqueid);
11581    }
11582 }
11583 
11584 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11585 {
11586    struct mwi_sub_task *mwist;
11587    
11588    if (ast_event_get_type(event) != AST_EVENT_SUB)
11589       return;
11590 
11591    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11592       return;
11593 
11594    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11595       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11596       return;
11597    }
11598    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11599    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11600    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11601    
11602    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11603       ast_free(mwist);
11604    }
11605 }
11606 
11607 static void start_poll_thread(void)
11608 {
11609    int errcode;
11610    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11611       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11612       AST_EVENT_IE_END);
11613 
11614    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11615       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11616       AST_EVENT_IE_END);
11617 
11618    if (mwi_sub_sub)
11619       ast_event_report_subs(mwi_sub_sub);
11620 
11621    poll_thread_run = 1;
11622 
11623    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11624       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11625    }
11626 }
11627 
11628 static void stop_poll_thread(void)
11629 {
11630    poll_thread_run = 0;
11631 
11632    if (mwi_sub_sub) {
11633       ast_event_unsubscribe(mwi_sub_sub);
11634       mwi_sub_sub = NULL;
11635    }
11636 
11637    if (mwi_unsub_sub) {
11638       ast_event_unsubscribe(mwi_unsub_sub);
11639       mwi_unsub_sub = NULL;
11640    }
11641 
11642    ast_mutex_lock(&poll_lock);
11643    ast_cond_signal(&poll_cond);
11644    ast_mutex_unlock(&poll_lock);
11645 
11646    pthread_join(poll_thread, NULL);
11647 
11648    poll_thread = AST_PTHREADT_NULL;
11649 }
11650 
11651 /*! \brief Manager list voicemail users command */
11652 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11653 {
11654    struct ast_vm_user *vmu = NULL;
11655    const char *id = astman_get_header(m, "ActionID");
11656    char actionid[128] = "";
11657 
11658    if (!ast_strlen_zero(id))
11659       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11660 
11661    AST_LIST_LOCK(&users);
11662 
11663    if (AST_LIST_EMPTY(&users)) {
11664       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11665       AST_LIST_UNLOCK(&users);
11666       return RESULT_SUCCESS;
11667    }
11668    
11669    astman_send_ack(s, m, "Voicemail user list will follow");
11670    
11671    AST_LIST_TRAVERSE(&users, vmu, list) {
11672       char dirname[256];
11673 
11674 #ifdef IMAP_STORAGE
11675       int new, old;
11676       inboxcount(vmu->mailbox, &new, &old);
11677 #endif
11678       
11679       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11680       astman_append(s,
11681          "%s"
11682          "Event: VoicemailUserEntry\r\n"
11683          "VMContext: %s\r\n"
11684          "VoiceMailbox: %s\r\n"
11685          "Fullname: %s\r\n"
11686          "Email: %s\r\n"
11687          "Pager: %s\r\n"
11688          "ServerEmail: %s\r\n"
11689          "MailCommand: %s\r\n"
11690          "Language: %s\r\n"
11691          "TimeZone: %s\r\n"
11692          "Callback: %s\r\n"
11693          "Dialout: %s\r\n"
11694          "UniqueID: %s\r\n"
11695          "ExitContext: %s\r\n"
11696          "SayDurationMinimum: %d\r\n"
11697          "SayEnvelope: %s\r\n"
11698          "SayCID: %s\r\n"
11699          "AttachMessage: %s\r\n"
11700          "AttachmentFormat: %s\r\n"
11701          "DeleteMessage: %s\r\n"
11702          "VolumeGain: %.2f\r\n"
11703          "CanReview: %s\r\n"
11704          "CallOperator: %s\r\n"
11705          "MaxMessageCount: %d\r\n"
11706          "MaxMessageLength: %d\r\n"
11707          "NewMessageCount: %d\r\n"
11708 #ifdef IMAP_STORAGE
11709          "OldMessageCount: %d\r\n"
11710          "IMAPUser: %s\r\n"
11711 #endif
11712          "\r\n",
11713          actionid,
11714          vmu->context,
11715          vmu->mailbox,
11716          vmu->fullname,
11717          vmu->email,
11718          vmu->pager,
11719          vmu->serveremail,
11720          vmu->mailcmd,
11721          vmu->language,
11722          vmu->zonetag,
11723          vmu->callback,
11724          vmu->dialout,
11725          vmu->uniqueid,
11726          vmu->exit,
11727          vmu->saydurationm,
11728          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11729          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11730          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11731          vmu->attachfmt,
11732          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11733          vmu->volgain,
11734          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11735          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11736          vmu->maxmsg,
11737          vmu->maxsecs,
11738 #ifdef IMAP_STORAGE
11739          new, old, vmu->imapuser
11740 #else
11741          count_messages(vmu, dirname)
11742 #endif
11743          );
11744    }     
11745    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11746 
11747    AST_LIST_UNLOCK(&users);
11748 
11749    return RESULT_SUCCESS;
11750 }
11751 
11752 /*! \brief Free the users structure. */
11753 static void free_vm_users(void) 
11754 {
11755    struct ast_vm_user *current;
11756    AST_LIST_LOCK(&users);
11757    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11758       ast_set_flag(current, VM_ALLOCED);
11759       free_user(current);
11760    }
11761    AST_LIST_UNLOCK(&users);
11762 }
11763 
11764 /*! \brief Free the zones structure. */
11765 static void free_vm_zones(void)
11766 {
11767    struct vm_zone *zcur;
11768    AST_LIST_LOCK(&zones);
11769    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11770       free_zone(zcur);
11771    AST_LIST_UNLOCK(&zones);
11772 }
11773 
11774 static const char *substitute_escapes(const char *value)
11775 {
11776    char *current;
11777 
11778    /* Add 16 for fudge factor */
11779    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11780 
11781    ast_str_reset(str);
11782    
11783    /* Substitute strings \r, \n, and \t into the appropriate characters */
11784    for (current = (char *) value; *current; current++) {
11785       if (*current == '\\') {
11786          current++;
11787          if (!*current) {
11788             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11789             break;
11790          }
11791          switch (*current) {
11792          case '\\':
11793             ast_str_append(&str, 0, "\\");
11794             break;
11795          case 'r':
11796             ast_str_append(&str, 0, "\r");
11797             break;
11798          case 'n':
11799 #ifdef IMAP_STORAGE
11800             if (!str->used || str->str[str->used - 1] != '\r') {
11801                ast_str_append(&str, 0, "\r");
11802             }
11803 #endif
11804             ast_str_append(&str, 0, "\n");
11805             break;
11806          case 't':
11807             ast_str_append(&str, 0, "\t");
11808             break;
11809          default:
11810             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11811             break;
11812          }
11813       } else {
11814          ast_str_append(&str, 0, "%c", *current);
11815       }
11816    }
11817 
11818    return ast_str_buffer(str);
11819 }
11820 
11821 static int load_config(int reload)
11822 {
11823    struct ast_config *cfg, *ucfg;
11824    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11825    int res;
11826 
11827    ast_unload_realtime("voicemail");
11828    ast_unload_realtime("voicemail_data");
11829 
11830    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11831       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11832          return 0;
11833       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11834          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11835          ucfg = NULL;
11836       }
11837       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11838       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11839          ast_config_destroy(ucfg);
11840          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11841          return 0;
11842       }
11843    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11844       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11845       return 0;
11846    } else {
11847       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11848       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11849          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11850          ucfg = NULL;
11851       }
11852    }
11853 
11854    res = actual_load_config(reload, cfg, ucfg);
11855 
11856    ast_config_destroy(cfg);
11857    ast_config_destroy(ucfg);
11858 
11859    return res;
11860 }
11861 
11862 #ifdef TEST_FRAMEWORK
11863 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11864 {
11865    ast_unload_realtime("voicemail");
11866    ast_unload_realtime("voicemail_data");
11867    return actual_load_config(reload, cfg, ucfg);
11868 }
11869 #endif
11870 
11871 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11872 {
11873    struct ast_vm_user *current;
11874    char *cat;
11875    struct ast_variable *var;
11876    const char *val;
11877    char *q, *stringp, *tmp;
11878    int x;
11879    int tmpadsi[4];
11880    char secretfn[PATH_MAX] = "";
11881 
11882 #ifdef IMAP_STORAGE
11883    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11884 #endif
11885    /* set audio control prompts */
11886    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11887    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11888    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11889    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11890    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11891 
11892    /* Free all the users structure */  
11893    free_vm_users();
11894 
11895    /* Free all the zones structure */
11896    free_vm_zones();
11897 
11898    AST_LIST_LOCK(&users);  
11899 
11900    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11901    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11902 
11903    if (cfg) {
11904       /* General settings */
11905 
11906       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11907          val = "default";
11908       ast_copy_string(userscontext, val, sizeof(userscontext));
11909       /* Attach voice message to mail message ? */
11910       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11911          val = "yes";
11912       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11913 
11914       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11915          val = "no";
11916       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11917 
11918       volgain = 0.0;
11919       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11920          sscanf(val, "%30lf", &volgain);
11921 
11922 #ifdef ODBC_STORAGE
11923       strcpy(odbc_database, "asterisk");
11924       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11925          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11926       }
11927       strcpy(odbc_table, "voicemessages");
11928       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11929          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11930       }
11931 #endif      
11932       /* Mail command */
11933       strcpy(mailcmd, SENDMAIL);
11934       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11935          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11936 
11937       maxsilence = 0;
11938       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11939          maxsilence = atoi(val);
11940          if (maxsilence > 0)
11941             maxsilence *= 1000;
11942       }
11943       
11944       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11945          maxmsg = MAXMSG;
11946       } else {
11947          maxmsg = atoi(val);
11948          if (maxmsg < 0) {
11949             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11950             maxmsg = MAXMSG;
11951          } else if (maxmsg > MAXMSGLIMIT) {
11952             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11953             maxmsg = MAXMSGLIMIT;
11954          }
11955       }
11956 
11957       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11958          maxdeletedmsg = 0;
11959       } else {
11960          if (sscanf(val, "%30d", &x) == 1)
11961             maxdeletedmsg = x;
11962          else if (ast_true(val))
11963             maxdeletedmsg = MAXMSG;
11964          else
11965             maxdeletedmsg = 0;
11966 
11967          if (maxdeletedmsg < 0) {
11968             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11969             maxdeletedmsg = MAXMSG;
11970          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11971             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11972             maxdeletedmsg = MAXMSGLIMIT;
11973          }
11974       }
11975 
11976       /* Load date format config for voicemail mail */
11977       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11978          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11979       }
11980 
11981       /* Load date format config for voicemail pager mail */
11982       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11983          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11984       }
11985 
11986       /* External password changing command */
11987       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11988          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11989          pwdchange = PWDCHANGE_EXTERNAL;
11990       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11991          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11992          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11993       }
11994  
11995       /* External password validation command */
11996       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11997          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11998          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11999       }
12000 
12001 #ifdef IMAP_STORAGE
12002       /* IMAP server address */
12003       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
12004          ast_copy_string(imapserver, val, sizeof(imapserver));
12005       } else {
12006          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
12007       }
12008       /* IMAP server port */
12009       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
12010          ast_copy_string(imapport, val, sizeof(imapport));
12011       } else {
12012          ast_copy_string(imapport, "143", sizeof(imapport));
12013       }
12014       /* IMAP server flags */
12015       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
12016          ast_copy_string(imapflags, val, sizeof(imapflags));
12017       }
12018       /* IMAP server master username */
12019       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12020          ast_copy_string(authuser, val, sizeof(authuser));
12021       }
12022       /* IMAP server master password */
12023       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12024          ast_copy_string(authpassword, val, sizeof(authpassword));
12025       }
12026       /* Expunge on exit */
12027       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12028          if (ast_false(val))
12029             expungeonhangup = 0;
12030          else
12031             expungeonhangup = 1;
12032       } else {
12033          expungeonhangup = 1;
12034       }
12035       /* IMAP voicemail folder */
12036       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12037          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12038       } else {
12039          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12040       }
12041       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12042          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12043       }
12044       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12045          imapgreetings = ast_true(val);
12046       } else {
12047          imapgreetings = 0;
12048       }
12049       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12050          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12051       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12052          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12053          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12054       } else {
12055          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12056       }
12057 
12058       /* There is some very unorthodox casting done here. This is due
12059        * to the way c-client handles the argument passed in. It expects a 
12060        * void pointer and casts the pointer directly to a long without
12061        * first dereferencing it. */
12062       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12063          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12064       } else {
12065          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12066       }
12067 
12068       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12069          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12070       } else {
12071          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12072       }
12073 
12074       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12075          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12076       } else {
12077          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12078       }
12079 
12080       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12081          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12082       } else {
12083          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12084       }
12085 
12086       /* Increment configuration version */
12087       imapversion++;
12088 #endif
12089       /* External voicemail notify application */
12090       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12091          ast_copy_string(externnotify, val, sizeof(externnotify));
12092          ast_debug(1, "found externnotify: %s\n", externnotify);
12093       } else {
12094          externnotify[0] = '\0';
12095       }
12096 
12097       /* SMDI voicemail notification */
12098       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12099          ast_debug(1, "Enabled SMDI voicemail notification\n");
12100          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12101             smdi_iface = ast_smdi_interface_find(val);
12102          } else {
12103             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12104             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12105          }
12106          if (!smdi_iface) {
12107             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12108          } 
12109       }
12110 
12111       /* Silence treshold */
12112       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12113       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12114          silencethreshold = atoi(val);
12115       
12116       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12117          val = ASTERISK_USERNAME;
12118       ast_copy_string(serveremail, val, sizeof(serveremail));
12119       
12120       vmmaxsecs = 0;
12121       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12122          if (sscanf(val, "%30d", &x) == 1) {
12123             vmmaxsecs = x;
12124          } else {
12125             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12126          }
12127       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12128          static int maxmessage_deprecate = 0;
12129          if (maxmessage_deprecate == 0) {
12130             maxmessage_deprecate = 1;
12131             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12132          }
12133          if (sscanf(val, "%30d", &x) == 1) {
12134             vmmaxsecs = x;
12135          } else {
12136             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12137          }
12138       }
12139 
12140       vmminsecs = 0;
12141       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12142          if (sscanf(val, "%30d", &x) == 1) {
12143             vmminsecs = x;
12144             if (maxsilence / 1000 >= vmminsecs) {
12145                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12146             }
12147          } else {
12148             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12149          }
12150       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12151          static int maxmessage_deprecate = 0;
12152          if (maxmessage_deprecate == 0) {
12153             maxmessage_deprecate = 1;
12154             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12155          }
12156          if (sscanf(val, "%30d", &x) == 1) {
12157             vmminsecs = x;
12158             if (maxsilence / 1000 >= vmminsecs) {
12159                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12160             }
12161          } else {
12162             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12163          }
12164       }
12165 
12166       val = ast_variable_retrieve(cfg, "general", "format");
12167       if (!val) {
12168          val = "wav";   
12169       } else {
12170          tmp = ast_strdupa(val);
12171          val = ast_format_str_reduce(tmp);
12172          if (!val) {
12173             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12174             val = "wav";
12175          }
12176       }
12177       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12178 
12179       skipms = 3000;
12180       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12181          if (sscanf(val, "%30d", &x) == 1) {
12182             maxgreet = x;
12183          } else {
12184             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12185          }
12186       }
12187 
12188       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12189          if (sscanf(val, "%30d", &x) == 1) {
12190             skipms = x;
12191          } else {
12192             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12193          }
12194       }
12195 
12196       maxlogins = 3;
12197       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12198          if (sscanf(val, "%30d", &x) == 1) {
12199             maxlogins = x;
12200          } else {
12201             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12202          }
12203       }
12204 
12205       minpassword = MINPASSWORD;
12206       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12207          if (sscanf(val, "%30d", &x) == 1) {
12208             minpassword = x;
12209          } else {
12210             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12211          }
12212       }
12213 
12214       /* Force new user to record name ? */
12215       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12216          val = "no";
12217       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12218 
12219       /* Force new user to record greetings ? */
12220       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12221          val = "no";
12222       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12223 
12224       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12225          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12226          stringp = ast_strdupa(val);
12227          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12228             if (!ast_strlen_zero(stringp)) {
12229                q = strsep(&stringp, ",");
12230                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12231                   q++;
12232                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12233                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12234             } else {
12235                cidinternalcontexts[x][0] = '\0';
12236             }
12237          }
12238       }
12239       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12240          ast_debug(1, "VM Review Option disabled globally\n");
12241          val = "no";
12242       }
12243       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12244 
12245       /* Temporary greeting reminder */
12246       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12247          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12248          val = "no";
12249       } else {
12250          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12251       }
12252       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12253       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12254          ast_debug(1, "VM next message wrap disabled globally\n");
12255          val = "no";
12256       }
12257       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12258 
12259       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12260          ast_debug(1, "VM Operator break disabled globally\n");
12261          val = "no";
12262       }
12263       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12264 
12265       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12266          ast_debug(1, "VM CID Info before msg disabled globally\n");
12267          val = "no";
12268       } 
12269       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12270 
12271       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12272          ast_debug(1, "Send Voicemail msg disabled globally\n");
12273          val = "no";
12274       }
12275       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12276    
12277       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12278          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12279          val = "yes";
12280       }
12281       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12282 
12283       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12284          ast_debug(1, "Move Heard enabled globally\n");
12285          val = "yes";
12286       }
12287       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12288 
12289       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12290          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12291          val = "no";
12292       }
12293       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12294 
12295       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12296          ast_debug(1, "Duration info before msg enabled globally\n");
12297          val = "yes";
12298       }
12299       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12300 
12301       saydurationminfo = 2;
12302       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12303          if (sscanf(val, "%30d", &x) == 1) {
12304             saydurationminfo = x;
12305          } else {
12306             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12307          }
12308       }
12309 
12310       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12311          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12312          val = "no";
12313       }
12314       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12315 
12316       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12317          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12318          ast_debug(1, "found dialout context: %s\n", dialcontext);
12319       } else {
12320          dialcontext[0] = '\0';  
12321       }
12322       
12323       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12324          ast_copy_string(callcontext, val, sizeof(callcontext));
12325          ast_debug(1, "found callback context: %s\n", callcontext);
12326       } else {
12327          callcontext[0] = '\0';
12328       }
12329 
12330       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12331          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12332          ast_debug(1, "found operator context: %s\n", exitcontext);
12333       } else {
12334          exitcontext[0] = '\0';
12335       }
12336       
12337       /* load password sounds configuration */
12338       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12339          ast_copy_string(vm_password, val, sizeof(vm_password));
12340       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12341          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12342       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12343          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12344       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12345          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12346       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12347          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12348       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12349          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12350       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12351          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12352       }
12353       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12354          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12355       }
12356       /* load configurable audio prompts */
12357       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12358          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12359       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12360          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12361       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12362          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12363       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12364          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12365       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12366          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12367 
12368       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12369          val = "no";
12370       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12371 
12372       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12373          val = "voicemail.conf";
12374       }
12375       if (!(strcmp(val, "spooldir"))) {
12376          passwordlocation = OPT_PWLOC_SPOOLDIR;
12377       } else {
12378          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12379       }
12380 
12381       poll_freq = DEFAULT_POLL_FREQ;
12382       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12383          if (sscanf(val, "%30u", &poll_freq) != 1) {
12384             poll_freq = DEFAULT_POLL_FREQ;
12385             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12386          }
12387       }
12388 
12389       poll_mailboxes = 0;
12390       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12391          poll_mailboxes = ast_true(val);
12392 
12393       memset(fromstring, 0, sizeof(fromstring));
12394       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12395       strcpy(charset, "ISO-8859-1");
12396       if (emailbody) {
12397          ast_free(emailbody);
12398          emailbody = NULL;
12399       }
12400       if (emailsubject) {
12401          ast_free(emailsubject);
12402          emailsubject = NULL;
12403       }
12404       if (pagerbody) {
12405          ast_free(pagerbody);
12406          pagerbody = NULL;
12407       }
12408       if (pagersubject) {
12409          ast_free(pagersubject);
12410          pagersubject = NULL;
12411       }
12412       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12413          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12414       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12415          ast_copy_string(fromstring, val, sizeof(fromstring));
12416       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12417          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12418       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12419          ast_copy_string(charset, val, sizeof(charset));
12420       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12421          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12422          for (x = 0; x < 4; x++) {
12423             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12424          }
12425       }
12426       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12427          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12428          for (x = 0; x < 4; x++) {
12429             memcpy(&adsisec[x], &tmpadsi[x], 1);
12430          }
12431       }
12432       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12433          if (atoi(val)) {
12434             adsiver = atoi(val);
12435          }
12436       }
12437       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12438          ast_copy_string(zonetag, val, sizeof(zonetag));
12439       }
12440       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12441          ast_copy_string(locale, val, sizeof(locale));
12442       }
12443       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12444          emailsubject = ast_strdup(substitute_escapes(val));
12445       }
12446       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12447          emailbody = ast_strdup(substitute_escapes(val));
12448       }
12449       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12450          pagersubject = ast_strdup(substitute_escapes(val));
12451       }
12452       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12453          pagerbody = ast_strdup(substitute_escapes(val));
12454       }
12455 
12456       /* load mailboxes from users.conf */
12457       if (ucfg) { 
12458          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12459             if (!strcasecmp(cat, "general")) {
12460                continue;
12461             }
12462             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12463                continue;
12464             if ((current = find_or_create(userscontext, cat))) {
12465                populate_defaults(current);
12466                apply_options_full(current, ast_variable_browse(ucfg, cat));
12467                ast_copy_string(current->context, userscontext, sizeof(current->context));
12468                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12469                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12470                }
12471 
12472                switch (current->passwordlocation) {
12473                case OPT_PWLOC_SPOOLDIR:
12474                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12475                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12476                }
12477             }
12478          }
12479       }
12480 
12481       /* load mailboxes from voicemail.conf */
12482       cat = ast_category_browse(cfg, NULL);
12483       while (cat) {
12484          if (strcasecmp(cat, "general")) {
12485             var = ast_variable_browse(cfg, cat);
12486             if (strcasecmp(cat, "zonemessages")) {
12487                /* Process mailboxes in this context */
12488                while (var) {
12489                   append_mailbox(cat, var->name, var->value);
12490                   var = var->next;
12491                }
12492             } else {
12493                /* Timezones in this context */
12494                while (var) {
12495                   struct vm_zone *z;
12496                   if ((z = ast_malloc(sizeof(*z)))) {
12497                      char *msg_format, *tzone;
12498                      msg_format = ast_strdupa(var->value);
12499                      tzone = strsep(&msg_format, "|,");
12500                      if (msg_format) {
12501                         ast_copy_string(z->name, var->name, sizeof(z->name));
12502                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12503                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12504                         AST_LIST_LOCK(&zones);
12505                         AST_LIST_INSERT_HEAD(&zones, z, list);
12506                         AST_LIST_UNLOCK(&zones);
12507                      } else {
12508                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12509                         ast_free(z);
12510                      }
12511                   } else {
12512                      AST_LIST_UNLOCK(&users);
12513                      return -1;
12514                   }
12515                   var = var->next;
12516                }
12517             }
12518          }
12519          cat = ast_category_browse(cfg, cat);
12520       }
12521 
12522       AST_LIST_UNLOCK(&users);
12523 
12524       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12525          start_poll_thread();
12526       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12527          stop_poll_thread();;
12528 
12529       return 0;
12530    } else {
12531       AST_LIST_UNLOCK(&users);
12532       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12533       return 0;
12534    }
12535 }
12536 
12537 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12538 {
12539    int res = -1;
12540    char dir[PATH_MAX];
12541    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12542    ast_debug(2, "About to try retrieving name file %s\n", dir);
12543    RETRIEVE(dir, -1, mailbox, context);
12544    if (ast_fileexists(dir, NULL, NULL)) {
12545       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12546    }
12547    DISPOSE(dir, -1);
12548    return res;
12549 }
12550 
12551 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12552    struct ast_config *pwconf;
12553    struct ast_flags config_flags = { 0 };
12554 
12555    pwconf = ast_config_load(secretfn, config_flags);
12556    if (pwconf) {
12557       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12558       if (val) {
12559          ast_copy_string(password, val, passwordlen);
12560          ast_config_destroy(pwconf);
12561          return;
12562       }
12563       ast_config_destroy(pwconf);
12564    }
12565    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12566 }
12567 
12568 static int write_password_to_file(const char *secretfn, const char *password) {
12569    struct ast_config *conf;
12570    struct ast_category *cat;
12571    struct ast_variable *var;
12572    int res = -1;
12573 
12574    if (!(conf = ast_config_new())) {
12575       ast_log(LOG_ERROR, "Error creating new config structure\n");
12576       return res;
12577    }
12578    if (!(cat = ast_category_new("general", "", 1))) {
12579       ast_log(LOG_ERROR, "Error creating new category structure\n");
12580       ast_config_destroy(conf);
12581       return res;
12582    }
12583    if (!(var = ast_variable_new("password", password, ""))) {
12584       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12585       ast_config_destroy(conf);
12586       ast_category_destroy(cat);
12587       return res;
12588    }
12589    ast_category_append(conf, cat);
12590    ast_variable_append(cat, var);
12591    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12592       res = 0;
12593    } else {
12594       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12595    }
12596 
12597    ast_config_destroy(conf);
12598    return res;
12599 }
12600 
12601 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12602 {
12603    char *context;
12604    char *args_copy;
12605    int res;
12606 
12607    if (ast_strlen_zero(data)) {
12608       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12609       return -1;
12610    }
12611 
12612    args_copy = ast_strdupa(data);
12613    if ((context = strchr(args_copy, '@'))) {
12614       *context++ = '\0';
12615    } else {
12616       context = "default";
12617    }
12618 
12619    if ((res = sayname(chan, args_copy, context) < 0)) {
12620       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12621       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12622       if (!res) {
12623          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12624       }
12625    }
12626 
12627    return res;
12628 }
12629 
12630 #ifdef TEST_FRAMEWORK
12631 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12632 {
12633    return 0;
12634 }
12635 
12636 static struct ast_frame *fake_read(struct ast_channel *ast)
12637 {
12638    return &ast_null_frame;
12639 }
12640 
12641 AST_TEST_DEFINE(test_voicemail_vmsayname)
12642 {
12643    char dir[PATH_MAX];
12644    char dir2[PATH_MAX];
12645    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12646    static const char TEST_EXTENSION[] = "1234";
12647 
12648    struct ast_channel *test_channel1 = NULL;
12649    int res = -1;
12650 
12651    static const struct ast_channel_tech fake_tech = {
12652       .write = fake_write,
12653       .read = fake_read,
12654    };
12655 
12656    switch (cmd) {
12657    case TEST_INIT:
12658       info->name = "vmsayname_exec";
12659       info->category = "/apps/app_voicemail/";
12660       info->summary = "Vmsayname unit test";
12661       info->description =
12662          "This tests passing various parameters to vmsayname";
12663       return AST_TEST_NOT_RUN;
12664    case TEST_EXECUTE:
12665       break;
12666    }
12667 
12668    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12669         NULL, NULL, 0, 0, "TestChannel1"))) {
12670       goto exit_vmsayname_test;
12671    }
12672 
12673    /* normally this is done in the channel driver */
12674    test_channel1->nativeformats = AST_FORMAT_GSM;
12675    test_channel1->writeformat = AST_FORMAT_GSM;
12676    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12677    test_channel1->readformat = AST_FORMAT_GSM;
12678    test_channel1->rawreadformat = AST_FORMAT_GSM;
12679    test_channel1->tech = &fake_tech;
12680 
12681    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12682    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12683    if (!(res = vmsayname_exec(test_channel1, dir))) {
12684       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12685       if (ast_fileexists(dir, NULL, NULL)) {
12686          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12687          res = -1;
12688          goto exit_vmsayname_test;
12689       } else {
12690          /* no greeting already exists as expected, let's create one to fully test sayname */
12691          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12692             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12693             goto exit_vmsayname_test;
12694          }
12695          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12696          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12697          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12698          if ((res = symlink(dir, dir2))) {
12699             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12700             goto exit_vmsayname_test;
12701          }
12702          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12703          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12704          res = vmsayname_exec(test_channel1, dir);
12705 
12706          /* TODO: there may be a better way to do this */
12707          unlink(dir2);
12708          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12709          rmdir(dir2);
12710          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12711          rmdir(dir2);
12712       }
12713    }
12714 
12715 exit_vmsayname_test:
12716 
12717    if (test_channel1) {
12718       ast_hangup(test_channel1);
12719    }
12720 
12721    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12722 }
12723 
12724 AST_TEST_DEFINE(test_voicemail_msgcount)
12725 {
12726    int i, j, res = AST_TEST_PASS, syserr;
12727    struct ast_vm_user *vmu;
12728    struct vm_state vms;
12729 #ifdef IMAP_STORAGE
12730    struct ast_channel *chan = NULL;
12731 #endif
12732    struct {
12733       char dir[256];
12734       char file[256];
12735       char txtfile[256];
12736    } tmp[3];
12737    char syscmd[256];
12738    const char origweasels[] = "tt-weasels";
12739    const char testcontext[] = "test";
12740    const char testmailbox[] = "00000000";
12741    const char testspec[] = "00000000@test";
12742    FILE *txt;
12743    int new, old, urgent;
12744    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12745    const int folder2mbox[3] = { 1, 11, 0 };
12746    const int expected_results[3][12] = {
12747       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12748       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12749       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12750       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12751    };
12752 
12753    switch (cmd) {
12754    case TEST_INIT:
12755       info->name = "test_voicemail_msgcount";
12756       info->category = "/apps/app_voicemail/";
12757       info->summary = "Test Voicemail status checks";
12758       info->description =
12759          "Verify that message counts are correct when retrieved through the public API";
12760       return AST_TEST_NOT_RUN;
12761    case TEST_EXECUTE:
12762       break;
12763    }
12764 
12765    /* Make sure the original path was completely empty */
12766    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12767    if ((syserr = ast_safe_system(syscmd))) {
12768       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12769          syserr > 0 ? strerror(syserr) : "unable to fork()");
12770       return AST_TEST_FAIL;
12771    }
12772 
12773 #ifdef IMAP_STORAGE
12774    if (!(chan = ast_dummy_channel_alloc())) {
12775       ast_test_status_update(test, "Unable to create dummy channel\n");
12776       return AST_TEST_FAIL;
12777    }
12778 #endif
12779 
12780    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12781       !(vmu = find_or_create(testcontext, testmailbox))) {
12782       ast_test_status_update(test, "Cannot create vmu structure\n");
12783       ast_unreplace_sigchld();
12784 #ifdef IMAP_STORAGE
12785       chan = ast_channel_unref(chan);
12786 #endif
12787       return AST_TEST_FAIL;
12788    }
12789 
12790    populate_defaults(vmu);
12791    memset(&vms, 0, sizeof(vms));
12792 
12793    /* Create temporary voicemail */
12794    for (i = 0; i < 3; i++) {
12795       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12796       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12797       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12798 
12799       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12800          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12801             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12802          if ((syserr = ast_safe_system(syscmd))) {
12803             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12804                syserr > 0 ? strerror(syserr) : "unable to fork()");
12805             ast_unreplace_sigchld();
12806 #ifdef IMAP_STORAGE
12807             chan = ast_channel_unref(chan);
12808 #endif
12809             return AST_TEST_FAIL;
12810          }
12811       }
12812 
12813       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12814          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12815          fclose(txt);
12816       } else {
12817          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12818          res = AST_TEST_FAIL;
12819          break;
12820       }
12821       open_mailbox(&vms, vmu, folder2mbox[i]);
12822       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12823 
12824       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12825       for (j = 0; j < 3; j++) {
12826          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12827          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12828             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12829                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12830             res = AST_TEST_FAIL;
12831          }
12832       }
12833 
12834       new = old = urgent = 0;
12835       if (ast_app_inboxcount(testspec, &new, &old)) {
12836          ast_test_status_update(test, "inboxcount returned failure\n");
12837          res = AST_TEST_FAIL;
12838       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12839          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12840             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12841          res = AST_TEST_FAIL;
12842       }
12843 
12844       new = old = urgent = 0;
12845       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12846          ast_test_status_update(test, "inboxcount2 returned failure\n");
12847          res = AST_TEST_FAIL;
12848       } else if (old != expected_results[i][6 + 0] ||
12849             urgent != expected_results[i][6 + 1] ||
12850                new != expected_results[i][6 + 2]    ) {
12851          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12852             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12853          res = AST_TEST_FAIL;
12854       }
12855 
12856       new = old = urgent = 0;
12857       for (j = 0; j < 3; j++) {
12858          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12859             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12860                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12861             res = AST_TEST_FAIL;
12862          }
12863       }
12864    }
12865 
12866    for (i = 0; i < 3; i++) {
12867       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12868        * server, in which case, the rm below will not affect the
12869        * voicemails. */
12870       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12871       DISPOSE(tmp[i].dir, 0);
12872    }
12873 
12874    if (vms.deleted) {
12875       ast_free(vms.deleted);
12876    }
12877    if (vms.heard) {
12878       ast_free(vms.heard);
12879    }
12880 
12881 #ifdef IMAP_STORAGE
12882    chan = ast_channel_unref(chan);
12883 #endif
12884 
12885    /* And remove test directory */
12886    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12887    if ((syserr = ast_safe_system(syscmd))) {
12888       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12889          syserr > 0 ? strerror(syserr) : "unable to fork()");
12890    }
12891 
12892    return res;
12893 }
12894 
12895 AST_TEST_DEFINE(test_voicemail_notify_endl)
12896 {
12897    int res = AST_TEST_PASS;
12898    char testcontext[] = "test";
12899    char testmailbox[] = "00000000";
12900    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12901    char attach[256], attach2[256];
12902    char buf[256] = ""; /* No line should actually be longer than 80 */
12903    struct ast_channel *chan = NULL;
12904    struct ast_vm_user *vmu, vmus = {
12905       .flags = 0,
12906    };
12907    FILE *file;
12908    struct {
12909       char *name;
12910       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12911       void *location;
12912       union {
12913          int intval;
12914          char *strval;
12915       } u;
12916    } test_items[] = {
12917       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12918       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12919       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12920       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12921       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12922       { "attach2", STRPTR, attach2, .u.strval = "" },
12923       { "attach", STRPTR, attach, .u.strval = "" },
12924    };
12925    int which;
12926 
12927    switch (cmd) {
12928    case TEST_INIT:
12929       info->name = "test_voicemail_notify_endl";
12930       info->category = "/apps/app_voicemail/";
12931       info->summary = "Test Voicemail notification end-of-line";
12932       info->description =
12933          "Verify that notification emails use a consistent end-of-line character";
12934       return AST_TEST_NOT_RUN;
12935    case TEST_EXECUTE:
12936       break;
12937    }
12938 
12939    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12940    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12941 
12942    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12943       !(vmu = find_or_create(testcontext, testmailbox))) {
12944       ast_test_status_update(test, "Cannot create vmu structure\n");
12945       return AST_TEST_NOT_RUN;
12946    }
12947 
12948    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12949       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12950       return AST_TEST_NOT_RUN;
12951    }
12952 
12953    populate_defaults(vmu);
12954    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12955 #ifdef IMAP_STORAGE
12956    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12957 #endif
12958 
12959    file = tmpfile();
12960    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12961       /* Kill previous test, if any */
12962       rewind(file);
12963       if (ftruncate(fileno(file), 0)) {
12964          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12965          res = AST_TEST_FAIL;
12966          break;
12967       }
12968 
12969       /* Make each change, in order, to the test mailbox */
12970       if (test_items[which].type == INT) {
12971          *((int *) test_items[which].location) = test_items[which].u.intval;
12972       } else if (test_items[which].type == FLAGVAL) {
12973          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12974             ast_clear_flag(vmu, test_items[which].u.intval);
12975          } else {
12976             ast_set_flag(vmu, test_items[which].u.intval);
12977          }
12978       } else if (test_items[which].type == STATIC) {
12979          strcpy(test_items[which].location, test_items[which].u.strval);
12980       } else if (test_items[which].type == STRPTR) {
12981          test_items[which].location = test_items[which].u.strval;
12982       }
12983 
12984       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12985       rewind(file);
12986       while (fgets(buf, sizeof(buf), file)) {
12987          if (
12988 #ifdef IMAP_STORAGE
12989          buf[strlen(buf) - 2] != '\r'
12990 #else
12991          buf[strlen(buf) - 2] == '\r'
12992 #endif
12993          || buf[strlen(buf) - 1] != '\n') {
12994             res = AST_TEST_FAIL;
12995          }
12996       }
12997    }
12998    fclose(file);
12999    return res;
13000 }
13001 
13002 AST_TEST_DEFINE(test_voicemail_load_config)
13003 {
13004    int res = AST_TEST_PASS;
13005    struct ast_vm_user *vmu;
13006    struct ast_config *cfg;
13007    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
13008    int fd;
13009    FILE *file;
13010    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
13011 
13012    switch (cmd) {
13013    case TEST_INIT:
13014       info->name = "test_voicemail_load_config";
13015       info->category = "/apps/app_voicemail/";
13016       info->summary = "Test loading Voicemail config";
13017       info->description =
13018          "Verify that configuration is loaded consistently. "
13019          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13020          "some options were loaded after the mailboxes were instantiated, causing "
13021          "those options not to be set correctly.";
13022       return AST_TEST_NOT_RUN;
13023    case TEST_EXECUTE:
13024       break;
13025    }
13026 
13027    /* build a config file by hand... */
13028    if ((fd = mkstemp(config_filename)) < 0) {
13029       return AST_TEST_FAIL;
13030    }
13031    if (!(file = fdopen(fd, "w"))) {
13032       close(fd);
13033       unlink(config_filename);
13034       return AST_TEST_FAIL;
13035    }
13036    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13037    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13038    fputs("00000002 => 9999,Mrs. Test\n", file);
13039    fclose(file);
13040 
13041    if (!(cfg = ast_config_load(config_filename, config_flags))) {
13042       res = AST_TEST_FAIL;
13043       goto cleanup;
13044    }
13045 
13046    load_config_from_memory(1, cfg, NULL);
13047    ast_config_destroy(cfg);
13048 
13049 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13050    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13051    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13052 
13053    AST_LIST_LOCK(&users);
13054    AST_LIST_TRAVERSE(&users, vmu, list) {
13055       if (!strcmp(vmu->mailbox, "00000001")) {
13056          if (0); /* trick to get CHECK to work */
13057          CHECK(vmu, callback, "othercontext")
13058          CHECK(vmu, locale, "nl_NL.UTF-8")
13059          CHECK(vmu, zonetag, "central")
13060       } else if (!strcmp(vmu->mailbox, "00000002")) {
13061          if (0); /* trick to get CHECK to work */
13062          CHECK(vmu, callback, "somecontext")
13063          CHECK(vmu, locale, "de_DE.UTF-8")
13064          CHECK(vmu, zonetag, "european")
13065       }
13066    }
13067    AST_LIST_UNLOCK(&users);
13068 
13069 #undef CHECK
13070 
13071    /* restore config */
13072    load_config(1); /* this might say "Failed to load configuration file." */
13073 
13074 cleanup:
13075    unlink(config_filename);
13076    return res;
13077 }
13078 
13079 #endif /* defined(TEST_FRAMEWORK) */
13080 
13081 static int reload(void)
13082 {
13083    return load_config(1);
13084 }
13085 
13086 static int unload_module(void)
13087 {
13088    int res;
13089 
13090    res = ast_unregister_application(app);
13091    res |= ast_unregister_application(app2);
13092    res |= ast_unregister_application(app3);
13093    res |= ast_unregister_application(app4);
13094    res |= ast_unregister_application(sayname_app);
13095    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13096    res |= ast_manager_unregister("VoicemailUsersList");
13097    res |= ast_data_unregister(NULL);
13098 #ifdef TEST_FRAMEWORK
13099    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13100    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13101    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13102    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13103    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13104 #endif
13105    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13106    ast_uninstall_vm_functions();
13107    ao2_ref(inprocess_container, -1);
13108 
13109    if (poll_thread != AST_PTHREADT_NULL)
13110       stop_poll_thread();
13111 
13112    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13113    ast_unload_realtime("voicemail");
13114    ast_unload_realtime("voicemail_data");
13115 
13116    free_vm_users();
13117    free_vm_zones();
13118    return res;
13119 }
13120 
13121 static int load_module(void)
13122 {
13123    int res;
13124    my_umask = umask(0);
13125    umask(my_umask);
13126 
13127    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13128       return AST_MODULE_LOAD_DECLINE;
13129    }
13130 
13131    /* compute the location of the voicemail spool directory */
13132    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13133    
13134    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13135       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13136    }
13137 
13138    if ((res = load_config(0)))
13139       return res;
13140 
13141    res = ast_register_application_xml(app, vm_exec);
13142    res |= ast_register_application_xml(app2, vm_execmain);
13143    res |= ast_register_application_xml(app3, vm_box_exists);
13144    res |= ast_register_application_xml(app4, vmauthenticate);
13145    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13146    res |= ast_custom_function_register(&mailbox_exists_acf);
13147    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13148 #ifdef TEST_FRAMEWORK
13149    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13150    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13151    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13152    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13153    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13154 #endif
13155 
13156    if (res)
13157       return res;
13158 
13159    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13160    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13161 
13162    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13163    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13164    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13165 
13166    return res;
13167 }
13168 
13169 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13170 {
13171    int cmd = 0;
13172    char destination[80] = "";
13173    int retries = 0;
13174 
13175    if (!num) {
13176       ast_verb(3, "Destination number will be entered manually\n");
13177       while (retries < 3 && cmd != 't') {
13178          destination[1] = '\0';
13179          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13180          if (!cmd)
13181             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13182          if (!cmd)
13183             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13184          if (!cmd) {
13185             cmd = ast_waitfordigit(chan, 6000);
13186             if (cmd)
13187                destination[0] = cmd;
13188          }
13189          if (!cmd) {
13190             retries++;
13191          } else {
13192 
13193             if (cmd < 0)
13194                return 0;
13195             if (cmd == '*') {
13196                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13197                return 0;
13198             }
13199             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13200                retries++;
13201             else
13202                cmd = 't';
13203          }
13204          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13205       }
13206       if (retries >= 3) {
13207          return 0;
13208       }
13209       
13210    } else {
13211       if (option_verbose > 2)
13212          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13213       ast_copy_string(destination, num, sizeof(destination));
13214    }
13215 
13216    if (!ast_strlen_zero(destination)) {
13217       if (destination[strlen(destination) -1 ] == '*')
13218          return 0; 
13219       if (option_verbose > 2)
13220          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13221       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13222       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13223       chan->priority = 0;
13224       return 9;
13225    }
13226    return 0;
13227 }
13228 
13229 /*!
13230  * \brief The advanced options within a message.
13231  * \param chan
13232  * \param vmu 
13233  * \param vms
13234  * \param msg
13235  * \param option
13236  * \param record_gain
13237  *
13238  * Provides handling for the play message envelope, call the person back, or reply to message. 
13239  *
13240  * \return zero on success, -1 on error.
13241  */
13242 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)
13243 {
13244    int res = 0;
13245    char filename[PATH_MAX];
13246    struct ast_config *msg_cfg = NULL;
13247    const char *origtime, *context;
13248    char *name, *num;
13249    int retries = 0;
13250    char *cid;
13251    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13252 
13253    vms->starting = 0; 
13254 
13255    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13256 
13257    /* Retrieve info from VM attribute file */
13258    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13259    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13260    msg_cfg = ast_config_load(filename, config_flags);
13261    DISPOSE(vms->curdir, vms->curmsg);
13262    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13263       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13264       return 0;
13265    }
13266 
13267    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13268       ast_config_destroy(msg_cfg);
13269       return 0;
13270    }
13271 
13272    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13273 
13274    context = ast_variable_retrieve(msg_cfg, "message", "context");
13275    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13276       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13277    switch (option) {
13278    case 3: /* Play message envelope */
13279       if (!res)
13280          res = play_message_datetime(chan, vmu, origtime, filename);
13281       if (!res)
13282          res = play_message_callerid(chan, vms, cid, context, 0);
13283 
13284       res = 't';
13285       break;
13286 
13287    case 2:  /* Call back */
13288 
13289       if (ast_strlen_zero(cid))
13290          break;
13291 
13292       ast_callerid_parse(cid, &name, &num);
13293       while ((res > -1) && (res != 't')) {
13294          switch (res) {
13295          case '1':
13296             if (num) {
13297                /* Dial the CID number */
13298                res = dialout(chan, vmu, num, vmu->callback);
13299                if (res) {
13300                   ast_config_destroy(msg_cfg);
13301                   return 9;
13302                }
13303             } else {
13304                res = '2';
13305             }
13306             break;
13307 
13308          case '2':
13309             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13310             if (!ast_strlen_zero(vmu->dialout)) {
13311                res = dialout(chan, vmu, NULL, vmu->dialout);
13312                if (res) {
13313                   ast_config_destroy(msg_cfg);
13314                   return 9;
13315                }
13316             } else {
13317                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13318                res = ast_play_and_wait(chan, "vm-sorry");
13319             }
13320             ast_config_destroy(msg_cfg);
13321             return res;
13322          case '*':
13323             res = 't';
13324             break;
13325          case '3':
13326          case '4':
13327          case '5':
13328          case '6':
13329          case '7':
13330          case '8':
13331          case '9':
13332          case '0':
13333 
13334             res = ast_play_and_wait(chan, "vm-sorry");
13335             retries++;
13336             break;
13337          default:
13338             if (num) {
13339                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13340                res = ast_play_and_wait(chan, "vm-num-i-have");
13341                if (!res)
13342                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13343                if (!res)
13344                   res = ast_play_and_wait(chan, "vm-tocallnum");
13345                /* Only prompt for a caller-specified number if there is a dialout context specified */
13346                if (!ast_strlen_zero(vmu->dialout)) {
13347                   if (!res)
13348                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13349                }
13350             } else {
13351                res = ast_play_and_wait(chan, "vm-nonumber");
13352                if (!ast_strlen_zero(vmu->dialout)) {
13353                   if (!res)
13354                      res = ast_play_and_wait(chan, "vm-toenternumber");
13355                }
13356             }
13357             if (!res) {
13358                res = ast_play_and_wait(chan, "vm-star-cancel");
13359             }
13360             if (!res) {
13361                res = ast_waitfordigit(chan, 6000);
13362             }
13363             if (!res) {
13364                retries++;
13365                if (retries > 3) {
13366                   res = 't';
13367                }
13368             }
13369             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13370             break; 
13371             
13372          }
13373          if (res == 't')
13374             res = 0;
13375          else if (res == '*')
13376             res = -1;
13377       }
13378       break;
13379       
13380    case 1:  /* Reply */
13381       /* Send reply directly to sender */
13382       if (ast_strlen_zero(cid))
13383          break;
13384 
13385       ast_callerid_parse(cid, &name, &num);
13386       if (!num) {
13387          ast_verb(3, "No CID number available, no reply sent\n");
13388          if (!res)
13389             res = ast_play_and_wait(chan, "vm-nonumber");
13390          ast_config_destroy(msg_cfg);
13391          return res;
13392       } else {
13393          struct ast_vm_user vmu2;
13394          if (find_user(&vmu2, vmu->context, num)) {
13395             struct leave_vm_options leave_options;
13396             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13397             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13398 
13399             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13400             
13401             memset(&leave_options, 0, sizeof(leave_options));
13402             leave_options.record_gain = record_gain;
13403             res = leave_voicemail(chan, mailbox, &leave_options);
13404             if (!res)
13405                res = 't';
13406             ast_config_destroy(msg_cfg);
13407             return res;
13408          } else {
13409             /* Sender has no mailbox, can't reply */
13410             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13411             ast_play_and_wait(chan, "vm-nobox");
13412             res = 't';
13413             ast_config_destroy(msg_cfg);
13414             return res;
13415          }
13416       } 
13417       res = 0;
13418 
13419       break;
13420    }
13421 
13422 #ifndef IMAP_STORAGE
13423    ast_config_destroy(msg_cfg);
13424 
13425    if (!res) {
13426       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13427       vms->heard[msg] = 1;
13428       res = wait_file(chan, vms, vms->fn);
13429    }
13430 #endif
13431    return res;
13432 }
13433 
13434 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13435          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13436          signed char record_gain, struct vm_state *vms, char *flag)
13437 {
13438    /* Record message & let caller review or re-record it, or set options if applicable */
13439    int res = 0;
13440    int cmd = 0;
13441    int max_attempts = 3;
13442    int attempts = 0;
13443    int recorded = 0;
13444    int msg_exists = 0;
13445    signed char zero_gain = 0;
13446    char tempfile[PATH_MAX];
13447    char *acceptdtmf = "#";
13448    char *canceldtmf = "";
13449    int canceleddtmf = 0;
13450 
13451    /* Note that urgent and private are for flagging messages as such in the future */
13452 
13453    /* barf if no pointer passed to store duration in */
13454    if (duration == NULL) {
13455       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13456       return -1;
13457    }
13458 
13459    if (!outsidecaller)
13460       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13461    else
13462       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13463 
13464    cmd = '3';  /* Want to start by recording */
13465 
13466    while ((cmd >= 0) && (cmd != 't')) {
13467       switch (cmd) {
13468       case '1':
13469          if (!msg_exists) {
13470             /* In this case, 1 is to record a message */
13471             cmd = '3';
13472             break;
13473          } else {
13474             /* Otherwise 1 is to save the existing message */
13475             ast_verb(3, "Saving message as is\n");
13476             if (!outsidecaller) 
13477                ast_filerename(tempfile, recordfile, NULL);
13478             ast_stream_and_wait(chan, "vm-msgsaved", "");
13479             if (!outsidecaller) {
13480                /* Saves to IMAP server only if imapgreeting=yes */
13481                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13482                DISPOSE(recordfile, -1);
13483             }
13484             cmd = 't';
13485             return res;
13486          }
13487       case '2':
13488          /* Review */
13489          ast_verb(3, "Reviewing the message\n");
13490          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13491          break;
13492       case '3':
13493          msg_exists = 0;
13494          /* Record */
13495          if (recorded == 1) 
13496             ast_verb(3, "Re-recording the message\n");
13497          else  
13498             ast_verb(3, "Recording the message\n");
13499          
13500          if (recorded && outsidecaller) {
13501             cmd = ast_play_and_wait(chan, INTRO);
13502             cmd = ast_play_and_wait(chan, "beep");
13503          }
13504          recorded = 1;
13505          /* 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 */
13506          if (record_gain)
13507             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13508          if (ast_test_flag(vmu, VM_OPERATOR))
13509             canceldtmf = "0";
13510          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13511          if (strchr(canceldtmf, cmd)) {
13512          /* need this flag here to distinguish between pressing '0' during message recording or after */
13513             canceleddtmf = 1;
13514          }
13515          if (record_gain)
13516             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13517          if (cmd == -1) {
13518             /* User has hung up, no options to give */
13519             if (!outsidecaller) {
13520                /* user was recording a greeting and they hung up, so let's delete the recording. */
13521                ast_filedelete(tempfile, NULL);
13522             }     
13523             return cmd;
13524          }
13525          if (cmd == '0') {
13526             break;
13527          } else if (cmd == '*') {
13528             break;
13529 #if 0
13530          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13531             /* Message is too short */
13532             ast_verb(3, "Message too short\n");
13533             cmd = ast_play_and_wait(chan, "vm-tooshort");
13534             cmd = ast_filedelete(tempfile, NULL);
13535             break;
13536          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13537             /* Message is all silence */
13538             ast_verb(3, "Nothing recorded\n");
13539             cmd = ast_filedelete(tempfile, NULL);
13540             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13541             if (!cmd)
13542                cmd = ast_play_and_wait(chan, "vm-speakup");
13543             break;
13544 #endif
13545          } else {
13546             /* If all is well, a message exists */
13547             msg_exists = 1;
13548             cmd = 0;
13549          }
13550          break;
13551       case '4':
13552          if (outsidecaller) {  /* only mark vm messages */
13553             /* Mark Urgent */
13554             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13555                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13556                res = ast_play_and_wait(chan, "vm-marked-urgent");
13557                strcpy(flag, "Urgent");
13558             } else if (flag) {
13559                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13560                res = ast_play_and_wait(chan, "vm-urgent-removed");
13561                strcpy(flag, "");
13562             } else {
13563                ast_play_and_wait(chan, "vm-sorry");
13564             }
13565             cmd = 0;
13566          } else {
13567             cmd = ast_play_and_wait(chan, "vm-sorry");
13568          }
13569          break;
13570       case '5':
13571       case '6':
13572       case '7':
13573       case '8':
13574       case '9':
13575       case '*':
13576       case '#':
13577          cmd = ast_play_and_wait(chan, "vm-sorry");
13578          break;
13579 #if 0 
13580 /*  XXX Commented out for the moment because of the dangers of deleting
13581     a message while recording (can put the message numbers out of sync) */
13582       case '*':
13583          /* Cancel recording, delete message, offer to take another message*/
13584          cmd = ast_play_and_wait(chan, "vm-deleted");
13585          cmd = ast_filedelete(tempfile, NULL);
13586          if (outsidecaller) {
13587             res = vm_exec(chan, NULL);
13588             return res;
13589          }
13590          else
13591             return 1;
13592 #endif
13593       case '0':
13594          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13595             cmd = ast_play_and_wait(chan, "vm-sorry");
13596             break;
13597          }
13598          if (msg_exists || recorded) {
13599             cmd = ast_play_and_wait(chan, "vm-saveoper");
13600             if (!cmd)
13601                cmd = ast_waitfordigit(chan, 3000);
13602             if (cmd == '1') {
13603                ast_filerename(tempfile, recordfile, NULL);
13604                ast_play_and_wait(chan, "vm-msgsaved");
13605                cmd = '0';
13606             } else if (cmd == '4') {
13607                if (flag) {
13608                   ast_play_and_wait(chan, "vm-marked-urgent");
13609                   strcpy(flag, "Urgent");
13610                }
13611                ast_play_and_wait(chan, "vm-msgsaved");
13612                cmd = '0';
13613             } else {
13614                ast_play_and_wait(chan, "vm-deleted");
13615                DELETE(tempfile, -1, tempfile, vmu);
13616                cmd = '0';
13617             }
13618          }
13619          return cmd;
13620       default:
13621          /* If the caller is an ouside caller, and the review option is enabled,
13622             allow them to review the message, but let the owner of the box review
13623             their OGM's */
13624          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13625             return cmd;
13626          if (msg_exists) {
13627             cmd = ast_play_and_wait(chan, "vm-review");
13628             if (!cmd && outsidecaller) {
13629                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13630                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13631                } else if (flag) {
13632                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13633                }
13634             }
13635          } else {
13636             cmd = ast_play_and_wait(chan, "vm-torerecord");
13637             if (!cmd)
13638                cmd = ast_waitfordigit(chan, 600);
13639          }
13640          
13641          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13642             cmd = ast_play_and_wait(chan, "vm-reachoper");
13643             if (!cmd)
13644                cmd = ast_waitfordigit(chan, 600);
13645          }
13646 #if 0
13647          if (!cmd)
13648             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13649 #endif
13650          if (!cmd)
13651             cmd = ast_waitfordigit(chan, 6000);
13652          if (!cmd) {
13653             attempts++;
13654          }
13655          if (attempts > max_attempts) {
13656             cmd = 't';
13657          }
13658       }
13659    }
13660    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13661       /* Hang up or timeout, so delete the recording. */
13662       ast_filedelete(tempfile, NULL);
13663    }
13664 
13665    if (cmd != 't' && outsidecaller)
13666       ast_play_and_wait(chan, "vm-goodbye");
13667 
13668    return cmd;
13669 }
13670 
13671 /* This is a workaround so that menuselect displays a proper description
13672  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13673  */
13674 
13675 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13676       .load = load_module,
13677       .unload = unload_module,
13678       .reload = reload,
13679       .nonoptreq = "res_adsi,res_smdi",
13680       );

Generated on Mon Oct 8 12:38:57 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7