Sat Mar 10 01:54:00 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: 352643 $")
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 (!(tmp = 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    if (!vms->dh_arraysize) {
01754       /* initial allocation */
01755       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01756          return -1;
01757       }
01758       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01759          return -1;
01760       }
01761       vms->dh_arraysize = arraysize;
01762    } else if (vms->dh_arraysize < arraysize) {
01763       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01764          return -1;
01765       }
01766       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01767          return -1;
01768       }
01769       memset(vms->deleted, 0, arraysize * sizeof(int));
01770       memset(vms->heard, 0, arraysize * sizeof(int));
01771       vms->dh_arraysize = arraysize;
01772    }
01773 
01774    return 0;
01775 }
01776 
01777 /* All IMAP-specific functions should go in this block. This
01778  * keeps them from being spread out all over the code */
01779 #ifdef IMAP_STORAGE
01780 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01781 {
01782    char arg[10];
01783    struct vm_state *vms;
01784    unsigned long messageNum;
01785 
01786    /* If greetings aren't stored in IMAP, just delete the file */
01787    if (msgnum < 0 && !imapgreetings) {
01788       ast_filedelete(file, NULL);
01789       return;
01790    }
01791 
01792    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01793       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);
01794       return;
01795    }
01796 
01797    /* find real message number based on msgnum */
01798    /* this may be an index into vms->msgArray based on the msgnum. */
01799    messageNum = vms->msgArray[msgnum];
01800    if (messageNum == 0) {
01801       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01802       return;
01803    }
01804    if (option_debug > 2)
01805       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01806    /* delete message */
01807    snprintf (arg, sizeof(arg), "%lu", messageNum);
01808    ast_mutex_lock(&vms->lock);
01809    mail_setflag (vms->mailstream, arg, "\\DELETED");
01810    mail_expunge(vms->mailstream);
01811    ast_mutex_unlock(&vms->lock);
01812 }
01813 
01814 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01815 {
01816    struct vm_state *vms_p;
01817    char *file, *filename;
01818    char *attachment;
01819    int i;
01820    BODY *body;
01821 
01822    /* This function is only used for retrieval of IMAP greetings
01823     * regular messages are not retrieved this way, nor are greetings
01824     * if they are stored locally*/
01825    if (msgnum > -1 || !imapgreetings) {
01826       return 0;
01827    } else {
01828       file = strrchr(ast_strdupa(dir), '/');
01829       if (file)
01830          *file++ = '\0';
01831       else {
01832          ast_debug (1, "Failed to procure file name from directory passed.\n");
01833          return -1;
01834       }
01835    }
01836 
01837    /* check if someone is accessing this box right now... */
01838    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01839       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01840       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01841       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01842       * that's all we need to do.
01843       */
01844       if (!(vms_p = create_vm_state_from_user(vmu))) {
01845          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01846          return -1;
01847       }
01848    }
01849 
01850    /* Greetings will never have a prepended message */
01851    *vms_p->introfn = '\0';
01852 
01853    ast_mutex_lock(&vms_p->lock);
01854    init_mailstream(vms_p, GREETINGS_FOLDER);
01855    if (!vms_p->mailstream) {
01856       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01857       ast_mutex_unlock(&vms_p->lock);
01858       return -1;
01859    }
01860 
01861    /*XXX Yuck, this could probably be done a lot better */
01862    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01863       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01864       /* We have the body, now we extract the file name of the first attachment. */
01865       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01866          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01867       } else {
01868          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01869          ast_mutex_unlock(&vms_p->lock);
01870          return -1;
01871       }
01872       filename = strsep(&attachment, ".");
01873       if (!strcmp(filename, file)) {
01874          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01875          vms_p->msgArray[vms_p->curmsg] = i + 1;
01876          save_body(body, vms_p, "2", attachment, 0);
01877          ast_mutex_unlock(&vms_p->lock);
01878          return 0;
01879       }
01880    }
01881    ast_mutex_unlock(&vms_p->lock);
01882 
01883    return -1;
01884 }
01885 
01886 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01887 {
01888    BODY *body;
01889    char *header_content;
01890    char *attachedfilefmt;
01891    char buf[80];
01892    struct vm_state *vms;
01893    char text_file[PATH_MAX];
01894    FILE *text_file_ptr;
01895    int res = 0;
01896    struct ast_vm_user *vmu;
01897 
01898    if (!(vmu = find_user(NULL, context, mailbox))) {
01899       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01900       return -1;
01901    }
01902    
01903    if (msgnum < 0) {
01904       if (imapgreetings) {
01905          res = imap_retrieve_greeting(dir, msgnum, vmu);
01906          goto exit;
01907       } else {
01908          res = 0;
01909          goto exit;
01910       }
01911    }
01912 
01913    /* Before anything can happen, we need a vm_state so that we can
01914     * actually access the imap server through the vms->mailstream
01915     */
01916    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01917       /* This should not happen. If it does, then I guess we'd
01918        * need to create the vm_state, extract which mailbox to
01919        * open, and then set up the msgArray so that the correct
01920        * IMAP message could be accessed. If I have seen correctly
01921        * though, the vms should be obtainable from the vmstates list
01922        * and should have its msgArray properly set up.
01923        */
01924       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01925       res = -1;
01926       goto exit;
01927    }
01928    
01929    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01930    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01931 
01932    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01933    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01934       res = 0;
01935       goto exit;
01936    }
01937 
01938    if (option_debug > 2)
01939       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01940    if (vms->msgArray[msgnum] == 0) {
01941       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01942       res = -1;
01943       goto exit;
01944    }
01945 
01946    /* This will only work for new messages... */
01947    ast_mutex_lock(&vms->lock);
01948    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01949    ast_mutex_unlock(&vms->lock);
01950    /* empty string means no valid header */
01951    if (ast_strlen_zero(header_content)) {
01952       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01953       res = -1;
01954       goto exit;
01955    }
01956 
01957    ast_mutex_lock(&vms->lock);
01958    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01959    ast_mutex_unlock(&vms->lock);
01960 
01961    /* We have the body, now we extract the file name of the first attachment. */
01962    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01963       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01964    } else {
01965       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01966       res = -1;
01967       goto exit;
01968    }
01969    
01970    /* Find the format of the attached file */
01971 
01972    strsep(&attachedfilefmt, ".");
01973    if (!attachedfilefmt) {
01974       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01975       res = -1;
01976       goto exit;
01977    }
01978    
01979    save_body(body, vms, "2", attachedfilefmt, 0);
01980    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01981       *vms->introfn = '\0';
01982    }
01983 
01984    /* Get info from headers!! */
01985    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01986 
01987    if (!(text_file_ptr = fopen(text_file, "w"))) {
01988       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01989    }
01990 
01991    fprintf(text_file_ptr, "%s\n", "[message]");
01992 
01993    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01994    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01995    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01996    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01997    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01998    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01999    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02000    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02001    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02002    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02003    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02004    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02005    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02006    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02007    fclose(text_file_ptr);
02008 
02009 exit:
02010    free_user(vmu);
02011    return res;
02012 }
02013 
02014 static int folder_int(const char *folder)
02015 {
02016    /*assume a NULL folder means INBOX*/
02017    if (!folder) {
02018       return 0;
02019    }
02020    if (!strcasecmp(folder, imapfolder)) {
02021       return 0;
02022    } else if (!strcasecmp(folder, "Old")) {
02023       return 1;
02024    } else if (!strcasecmp(folder, "Work")) {
02025       return 2;
02026    } else if (!strcasecmp(folder, "Family")) {
02027       return 3;
02028    } else if (!strcasecmp(folder, "Friends")) {
02029       return 4;
02030    } else if (!strcasecmp(folder, "Cust1")) {
02031       return 5;
02032    } else if (!strcasecmp(folder, "Cust2")) {
02033       return 6;
02034    } else if (!strcasecmp(folder, "Cust3")) {
02035       return 7;
02036    } else if (!strcasecmp(folder, "Cust4")) {
02037       return 8;
02038    } else if (!strcasecmp(folder, "Cust5")) {
02039       return 9;
02040    } else if (!strcasecmp(folder, "Urgent")) {
02041       return 11;
02042    } else { /*assume they meant INBOX if folder is not found otherwise*/
02043       return 0;
02044    }
02045 }
02046 
02047 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02048 {
02049    SEARCHPGM *pgm;
02050    SEARCHHEADER *hdr;
02051 
02052    struct ast_vm_user *vmu, vmus;
02053    struct vm_state *vms_p;
02054    int ret = 0;
02055    int fold = folder_int(folder);
02056    int urgent = 0;
02057    
02058    /* If URGENT, then look at INBOX */
02059    if (fold == 11) {
02060       fold = NEW_FOLDER;
02061       urgent = 1;
02062    }
02063 
02064    if (ast_strlen_zero(mailbox))
02065       return 0;
02066 
02067    /* We have to get the user before we can open the stream! */
02068    vmu = find_user(&vmus, context, mailbox);
02069    if (!vmu) {
02070       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02071       return -1;
02072    } else {
02073       /* No IMAP account available */
02074       if (vmu->imapuser[0] == '\0') {
02075          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02076          return -1;
02077       }
02078    }
02079    
02080    /* No IMAP account available */
02081    if (vmu->imapuser[0] == '\0') {
02082       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02083       free_user(vmu);
02084       return -1;
02085    }
02086 
02087    /* check if someone is accessing this box right now... */
02088    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02089    if (!vms_p) {
02090       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02091    }
02092    if (vms_p) {
02093       ast_debug(3, "Returning before search - user is logged in\n");
02094       if (fold == 0) { /* INBOX */
02095          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02096       }
02097       if (fold == 1) { /* Old messages */
02098          return vms_p->oldmessages;
02099       }
02100    }
02101 
02102    /* add one if not there... */
02103    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02104    if (!vms_p) {
02105       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02106    }
02107 
02108    if (!vms_p) {
02109       vms_p = create_vm_state_from_user(vmu);
02110    }
02111    ret = init_mailstream(vms_p, fold);
02112    if (!vms_p->mailstream) {
02113       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02114       return -1;
02115    }
02116    if (ret == 0) {
02117       ast_mutex_lock(&vms_p->lock);
02118       pgm = mail_newsearchpgm ();
02119       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02120       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02121       pgm->header = hdr;
02122       if (fold != OLD_FOLDER) {
02123          pgm->unseen = 1;
02124          pgm->seen = 0;
02125       }
02126       /* In the special case where fold is 1 (old messages) we have to do things a bit
02127        * differently. Old messages are stored in the INBOX but are marked as "seen"
02128        */
02129       else {
02130          pgm->unseen = 0;
02131          pgm->seen = 1;
02132       }
02133       /* look for urgent messages */
02134       if (fold == NEW_FOLDER) {
02135          if (urgent) {
02136             pgm->flagged = 1;
02137             pgm->unflagged = 0;
02138          } else {
02139             pgm->flagged = 0;
02140             pgm->unflagged = 1;
02141          }
02142       }
02143       pgm->undeleted = 1;
02144       pgm->deleted = 0;
02145 
02146       vms_p->vmArrayIndex = 0;
02147       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02148       if (fold == 0 && urgent == 0)
02149          vms_p->newmessages = vms_p->vmArrayIndex;
02150       if (fold == 1)
02151          vms_p->oldmessages = vms_p->vmArrayIndex;
02152       if (fold == 0 && urgent == 1)
02153          vms_p->urgentmessages = vms_p->vmArrayIndex;
02154       /*Freeing the searchpgm also frees the searchhdr*/
02155       mail_free_searchpgm(&pgm);
02156       ast_mutex_unlock(&vms_p->lock);
02157       vms_p->updated = 0;
02158       return vms_p->vmArrayIndex;
02159    } else {
02160       ast_mutex_lock(&vms_p->lock);
02161       mail_ping(vms_p->mailstream);
02162       ast_mutex_unlock(&vms_p->lock);
02163    }
02164    return 0;
02165 }
02166 
02167 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02168 {
02169    /* Check if mailbox is full */
02170    check_quota(vms, vmu->imapfolder);
02171    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02172       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02173       ast_play_and_wait(chan, "vm-mailboxfull");
02174       return -1;
02175    }
02176    
02177    /* Check if we have exceeded maxmsg */
02178    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));
02179    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02180       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02181       ast_play_and_wait(chan, "vm-mailboxfull");
02182       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02183       return -1;
02184    }
02185 
02186    return 0;
02187 }
02188 
02189 /*!
02190  * \brief Gets the number of messages that exist in a mailbox folder.
02191  * \param context
02192  * \param mailbox
02193  * \param folder
02194  * 
02195  * This method is used when IMAP backend is used.
02196  * \return The number of messages in this mailbox folder (zero or more).
02197  */
02198 static int messagecount(const char *context, const char *mailbox, const char *folder)
02199 {
02200    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02201       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02202    } else {
02203       return __messagecount(context, mailbox, folder);
02204    }
02205 }
02206 
02207 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)
02208 {
02209    char *myserveremail = serveremail;
02210    char fn[PATH_MAX];
02211    char introfn[PATH_MAX];
02212    char mailbox[256];
02213    char *stringp;
02214    FILE *p = NULL;
02215    char tmp[80] = "/tmp/astmail-XXXXXX";
02216    long len;
02217    void *buf;
02218    int tempcopy = 0;
02219    STRING str;
02220    int ret; /* for better error checking */
02221    char *imap_flags = NIL;
02222    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02223    int box = NEW_FOLDER;
02224 
02225    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02226    if (msgnum < 0) {
02227       if(!imapgreetings) {
02228          return 0;
02229       } else {
02230          box = GREETINGS_FOLDER;
02231       }
02232    }
02233    
02234    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02235       return -1;
02236    }
02237 
02238    /* Set urgent flag for IMAP message */
02239    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02240       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02241       imap_flags = "\\FLAGGED";
02242    }
02243    
02244    /* Attach only the first format */
02245    fmt = ast_strdupa(fmt);
02246    stringp = fmt;
02247    strsep(&stringp, "|");
02248 
02249    if (!ast_strlen_zero(vmu->serveremail))
02250       myserveremail = vmu->serveremail;
02251 
02252    if (msgnum > -1)
02253       make_file(fn, sizeof(fn), dir, msgnum);
02254    else
02255       ast_copy_string (fn, dir, sizeof(fn));
02256 
02257    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02258    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02259       *introfn = '\0';
02260    }
02261    
02262    if (ast_strlen_zero(vmu->email)) {
02263       /* We need the vmu->email to be set when we call make_email_file, but
02264        * if we keep it set, a duplicate e-mail will be created. So at the end
02265        * of this function, we will revert back to an empty string if tempcopy
02266        * is 1.
02267        */
02268       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02269       tempcopy = 1;
02270    }
02271 
02272    if (!strcmp(fmt, "wav49"))
02273       fmt = "WAV";
02274    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02275 
02276    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02277       command hangs. */
02278    if (!(p = vm_mkftemp(tmp))) {
02279       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02280       if (tempcopy)
02281          *(vmu->email) = '\0';
02282       return -1;
02283    }
02284 
02285    if (msgnum < 0 && imapgreetings) {
02286       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02287          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02288          return -1;
02289       }
02290       imap_delete_old_greeting(fn, vms);
02291    }
02292 
02293    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02294       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02295       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02296       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02297    /* read mail file to memory */
02298    len = ftell(p);
02299    rewind(p);
02300    if (!(buf = ast_malloc(len + 1))) {
02301       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02302       fclose(p);
02303       if (tempcopy)
02304          *(vmu->email) = '\0';
02305       return -1;
02306    }
02307    if (fread(buf, len, 1, p) < len) {
02308       if (ferror(p)) {
02309          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02310          return -1;
02311       }
02312    }
02313    ((char *) buf)[len] = '\0';
02314    INIT(&str, mail_string, buf, len);
02315    ret = init_mailstream(vms, box);
02316    if (ret == 0) {
02317       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02318       ast_mutex_lock(&vms->lock);
02319       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02320          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02321       ast_mutex_unlock(&vms->lock);
02322       fclose(p);
02323       unlink(tmp);
02324       ast_free(buf);
02325    } else {
02326       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02327       fclose(p);
02328       unlink(tmp);
02329       ast_free(buf);
02330       return -1;
02331    }
02332    ast_debug(3, "%s stored\n", fn);
02333    
02334    if (tempcopy)
02335       *(vmu->email) = '\0';
02336    inprocess_count(vmu->mailbox, vmu->context, -1);
02337    return 0;
02338 
02339 }
02340 
02341 /*!
02342  * \brief Gets the number of messages that exist in the inbox folder.
02343  * \param mailbox_context
02344  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02345  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02346  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02347  * 
02348  * This method is used when IMAP backend is used.
02349  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02350  *
02351  * \return zero on success, -1 on error.
02352  */
02353 
02354 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02355 {
02356    char tmp[PATH_MAX] = "";
02357    char *mailboxnc;
02358    char *context;
02359    char *mb;
02360    char *cur;
02361    if (newmsgs)
02362       *newmsgs = 0;
02363    if (oldmsgs)
02364       *oldmsgs = 0;
02365    if (urgentmsgs)
02366       *urgentmsgs = 0;
02367 
02368    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02369    /* If no mailbox, return immediately */
02370    if (ast_strlen_zero(mailbox_context))
02371       return 0;
02372    
02373    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02374    context = strchr(tmp, '@');
02375    if (strchr(mailbox_context, ',')) {
02376       int tmpnew, tmpold, tmpurgent;
02377       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02378       mb = tmp;
02379       while ((cur = strsep(&mb, ", "))) {
02380          if (!ast_strlen_zero(cur)) {
02381             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02382                return -1;
02383             else {
02384                if (newmsgs)
02385                   *newmsgs += tmpnew; 
02386                if (oldmsgs)
02387                   *oldmsgs += tmpold;
02388                if (urgentmsgs)
02389                   *urgentmsgs += tmpurgent;
02390             }
02391          }
02392       }
02393       return 0;
02394    }
02395    if (context) {
02396       *context = '\0';
02397       mailboxnc = tmp;
02398       context++;
02399    } else {
02400       context = "default";
02401       mailboxnc = (char *) mailbox_context;
02402    }
02403 
02404    if (newmsgs) {
02405       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02406       if (!vmu) {
02407          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02408          return -1;
02409       }
02410       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02411          return -1;
02412       }
02413    }
02414    if (oldmsgs) {
02415       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02416          return -1;
02417       }
02418    }
02419    if (urgentmsgs) {
02420       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02421          return -1;
02422       }
02423    }
02424    return 0;
02425 }
02426 
02427 /** 
02428  * \brief Determines if the given folder has messages.
02429  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02430  * \param folder the folder to look in
02431  *
02432  * This function is used when the mailbox is stored in an IMAP back end.
02433  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02434  * \return 1 if the folder has one or more messages. zero otherwise.
02435  */
02436 
02437 static int has_voicemail(const char *mailbox, const char *folder)
02438 {
02439    char tmp[256], *tmp2, *box, *context;
02440    ast_copy_string(tmp, mailbox, sizeof(tmp));
02441    tmp2 = tmp;
02442    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02443       while ((box = strsep(&tmp2, ",&"))) {
02444          if (!ast_strlen_zero(box)) {
02445             if (has_voicemail(box, folder)) {
02446                return 1;
02447             }
02448          }
02449       }
02450    }
02451    if ((context = strchr(tmp, '@'))) {
02452       *context++ = '\0';
02453    } else {
02454       context = "default";
02455    }
02456    return __messagecount(context, tmp, folder) ? 1 : 0;
02457 }
02458 
02459 /*!
02460  * \brief Copies a message from one mailbox to another.
02461  * \param chan
02462  * \param vmu
02463  * \param imbox
02464  * \param msgnum
02465  * \param duration
02466  * \param recip
02467  * \param fmt
02468  * \param dir
02469  *
02470  * This works with IMAP storage based mailboxes.
02471  *
02472  * \return zero on success, -1 on error.
02473  */
02474 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)
02475 {
02476    struct vm_state *sendvms = NULL, *destvms = NULL;
02477    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02478    if (msgnum >= recip->maxmsg) {
02479       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02480       return -1;
02481    }
02482    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02483       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02484       return -1;
02485    }
02486    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02487       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02488       return -1;
02489    }
02490    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02491    ast_mutex_lock(&sendvms->lock);
02492    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02493       ast_mutex_unlock(&sendvms->lock);
02494       return 0;
02495    }
02496    ast_mutex_unlock(&sendvms->lock);
02497    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02498    return -1;
02499 }
02500 
02501 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02502 {
02503    char tmp[256], *t = tmp;
02504    size_t left = sizeof(tmp);
02505    
02506    if (box == OLD_FOLDER) {
02507       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02508    } else {
02509       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02510    }
02511 
02512    if (box == NEW_FOLDER) {
02513       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02514    } else {
02515       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02516    }
02517 
02518    /* Build up server information */
02519    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02520 
02521    /* Add authentication user if present */
02522    if (!ast_strlen_zero(authuser))
02523       ast_build_string(&t, &left, "/authuser=%s", authuser);
02524 
02525    /* Add flags if present */
02526    if (!ast_strlen_zero(imapflags))
02527       ast_build_string(&t, &left, "/%s", imapflags);
02528 
02529    /* End with username */
02530 #if 1
02531    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02532 #else
02533    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02534 #endif
02535    if (box == NEW_FOLDER || box == OLD_FOLDER)
02536       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02537    else if (box == GREETINGS_FOLDER)
02538       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02539    else {   /* Other folders such as Friends, Family, etc... */
02540       if (!ast_strlen_zero(imapparentfolder)) {
02541          /* imapparentfolder would typically be set to INBOX */
02542          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02543       } else {
02544          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02545       }
02546    }
02547 }
02548 
02549 static int init_mailstream(struct vm_state *vms, int box)
02550 {
02551    MAILSTREAM *stream = NIL;
02552    long debug;
02553    char tmp[256];
02554    
02555    if (!vms) {
02556       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02557       return -1;
02558    }
02559    if (option_debug > 2)
02560       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02561    if (vms->mailstream == NIL || !vms->mailstream) {
02562       if (option_debug)
02563          ast_log(LOG_DEBUG, "mailstream not set.\n");
02564    } else {
02565       stream = vms->mailstream;
02566    }
02567    /* debug = T;  user wants protocol telemetry? */
02568    debug = NIL;  /* NO protocol telemetry? */
02569 
02570    if (delimiter == '\0') {      /* did not probe the server yet */
02571       char *cp;
02572 #ifdef USE_SYSTEM_IMAP
02573 #include <imap/linkage.c>
02574 #elif defined(USE_SYSTEM_CCLIENT)
02575 #include <c-client/linkage.c>
02576 #else
02577 #include "linkage.c"
02578 #endif
02579       /* Connect to INBOX first to get folders delimiter */
02580       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02581       ast_mutex_lock(&vms->lock);
02582       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02583       ast_mutex_unlock(&vms->lock);
02584       if (stream == NIL) {
02585          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02586          return -1;
02587       }
02588       get_mailbox_delimiter(stream);
02589       /* update delimiter in imapfolder */
02590       for (cp = vms->imapfolder; *cp; cp++)
02591          if (*cp == '/')
02592             *cp = delimiter;
02593    }
02594    /* Now connect to the target folder */
02595    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02596    if (option_debug > 2)
02597       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02598    ast_mutex_lock(&vms->lock);
02599    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02600    ast_mutex_unlock(&vms->lock);
02601    if (vms->mailstream == NIL) {
02602       return -1;
02603    } else {
02604       return 0;
02605    }
02606 }
02607 
02608 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02609 {
02610    SEARCHPGM *pgm;
02611    SEARCHHEADER *hdr;
02612    int ret, urgent = 0;
02613 
02614    /* If Urgent, then look at INBOX */
02615    if (box == 11) {
02616       box = NEW_FOLDER;
02617       urgent = 1;
02618    }
02619 
02620    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02621    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02622    vms->imapversion = vmu->imapversion;
02623    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02624 
02625    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02626       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02627       return -1;
02628    }
02629    
02630    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02631    
02632    /* Check Quota */
02633    if  (box == 0)  {
02634       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02635       check_quota(vms, (char *) mbox(vmu, box));
02636    }
02637 
02638    ast_mutex_lock(&vms->lock);
02639    pgm = mail_newsearchpgm();
02640 
02641    /* Check IMAP folder for Asterisk messages only... */
02642    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02643    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02644    pgm->header = hdr;
02645    pgm->deleted = 0;
02646    pgm->undeleted = 1;
02647 
02648    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02649    if (box == NEW_FOLDER && urgent == 1) {
02650       pgm->unseen = 1;
02651       pgm->seen = 0;
02652       pgm->flagged = 1;
02653       pgm->unflagged = 0;
02654    } else if (box == NEW_FOLDER && urgent == 0) {
02655       pgm->unseen = 1;
02656       pgm->seen = 0;
02657       pgm->flagged = 0;
02658       pgm->unflagged = 1;
02659    } else if (box == OLD_FOLDER) {
02660       pgm->seen = 1;
02661       pgm->unseen = 0;
02662    }
02663 
02664    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02665 
02666    vms->vmArrayIndex = 0;
02667    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02668    vms->lastmsg = vms->vmArrayIndex - 1;
02669    mail_free_searchpgm(&pgm);
02670    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02671     * ensure to allocate enough space to account for all of them. Warn if old messages
02672     * have not been checked first as that is required.
02673     */
02674    if (box == 0 && !vms->dh_arraysize) {
02675       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02676    }
02677    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02678       ast_mutex_unlock(&vms->lock);
02679       return -1;
02680    }
02681 
02682    ast_mutex_unlock(&vms->lock);
02683    return 0;
02684 }
02685 
02686 static void write_file(char *filename, char *buffer, unsigned long len)
02687 {
02688    FILE *output;
02689 
02690    output = fopen (filename, "w");
02691    if (fwrite(buffer, len, 1, output) != 1) {
02692       if (ferror(output)) {
02693          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02694       }
02695    }
02696    fclose (output);
02697 }
02698 
02699 static void update_messages_by_imapuser(const char *user, unsigned long number)
02700 {
02701    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02702 
02703    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02704       return;
02705    }
02706 
02707    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02708    vms->msgArray[vms->vmArrayIndex++] = number;
02709 }
02710 
02711 void mm_searched(MAILSTREAM *stream, unsigned long number)
02712 {
02713    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02714 
02715    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02716       return;
02717 
02718    update_messages_by_imapuser(user, number);
02719 }
02720 
02721 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02722 {
02723    struct ast_variable *var;
02724    struct ast_vm_user *vmu;
02725 
02726    vmu = ast_calloc(1, sizeof *vmu);
02727    if (!vmu)
02728       return NULL;
02729    ast_set_flag(vmu, VM_ALLOCED);
02730    populate_defaults(vmu);
02731 
02732    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02733    if (var) {
02734       apply_options_full(vmu, var);
02735       ast_variables_destroy(var);
02736       return vmu;
02737    } else {
02738       ast_free(vmu);
02739       return NULL;
02740    }
02741 }
02742 
02743 /* Interfaces to C-client */
02744 
02745 void mm_exists(MAILSTREAM * stream, unsigned long number)
02746 {
02747    /* mail_ping will callback here if new mail! */
02748    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02749    if (number == 0) return;
02750    set_update(stream);
02751 }
02752 
02753 
02754 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02755 {
02756    /* mail_ping will callback here if expunged mail! */
02757    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02758    if (number == 0) return;
02759    set_update(stream);
02760 }
02761 
02762 
02763 void mm_flags(MAILSTREAM * stream, unsigned long number)
02764 {
02765    /* mail_ping will callback here if read mail! */
02766    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02767    if (number == 0) return;
02768    set_update(stream);
02769 }
02770 
02771 
02772 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02773 {
02774    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02775    mm_log (string, errflg);
02776 }
02777 
02778 
02779 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02780 {
02781    if (delimiter == '\0') {
02782       delimiter = delim;
02783    }
02784 
02785    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02786    if (attributes & LATT_NOINFERIORS)
02787       ast_debug(5, "no inferiors\n");
02788    if (attributes & LATT_NOSELECT)
02789       ast_debug(5, "no select\n");
02790    if (attributes & LATT_MARKED)
02791       ast_debug(5, "marked\n");
02792    if (attributes & LATT_UNMARKED)
02793       ast_debug(5, "unmarked\n");
02794 }
02795 
02796 
02797 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02798 {
02799    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02800    if (attributes & LATT_NOINFERIORS)
02801       ast_debug(5, "no inferiors\n");
02802    if (attributes & LATT_NOSELECT)
02803       ast_debug(5, "no select\n");
02804    if (attributes & LATT_MARKED)
02805       ast_debug(5, "marked\n");
02806    if (attributes & LATT_UNMARKED)
02807       ast_debug(5, "unmarked\n");
02808 }
02809 
02810 
02811 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02812 {
02813    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02814    if (status->flags & SA_MESSAGES)
02815       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02816    if (status->flags & SA_RECENT)
02817       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02818    if (status->flags & SA_UNSEEN)
02819       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02820    if (status->flags & SA_UIDVALIDITY)
02821       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02822    if (status->flags & SA_UIDNEXT)
02823       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02824    ast_log(AST_LOG_NOTICE, "\n");
02825 }
02826 
02827 
02828 void mm_log(char *string, long errflg)
02829 {
02830    switch ((short) errflg) {
02831       case NIL:
02832          ast_debug(1, "IMAP Info: %s\n", string);
02833          break;
02834       case PARSE:
02835       case WARN:
02836          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02837          break;
02838       case ERROR:
02839          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02840          break;
02841    }
02842 }
02843 
02844 
02845 void mm_dlog(char *string)
02846 {
02847    ast_log(AST_LOG_NOTICE, "%s\n", string);
02848 }
02849 
02850 
02851 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02852 {
02853    struct ast_vm_user *vmu;
02854 
02855    ast_debug(4, "Entering callback mm_login\n");
02856 
02857    ast_copy_string(user, mb->user, MAILTMPLEN);
02858 
02859    /* We should only do this when necessary */
02860    if (!ast_strlen_zero(authpassword)) {
02861       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02862    } else {
02863       AST_LIST_TRAVERSE(&users, vmu, list) {
02864          if (!strcasecmp(mb->user, vmu->imapuser)) {
02865             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02866             break;
02867          }
02868       }
02869       if (!vmu) {
02870          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02871             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02872             free_user(vmu);
02873          }
02874       }
02875    }
02876 }
02877 
02878 
02879 void mm_critical(MAILSTREAM * stream)
02880 {
02881 }
02882 
02883 
02884 void mm_nocritical(MAILSTREAM * stream)
02885 {
02886 }
02887 
02888 
02889 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02890 {
02891    kill (getpid (), SIGSTOP);
02892    return NIL;
02893 }
02894 
02895 
02896 void mm_fatal(char *string)
02897 {
02898    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02899 }
02900 
02901 /* C-client callback to handle quota */
02902 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02903 {
02904    struct vm_state *vms;
02905    char *mailbox = stream->mailbox, *user;
02906    char buf[1024] = "";
02907    unsigned long usage = 0, limit = 0;
02908    
02909    while (pquota) {
02910       usage = pquota->usage;
02911       limit = pquota->limit;
02912       pquota = pquota->next;
02913    }
02914    
02915    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)))) {
02916       ast_log(AST_LOG_ERROR, "No state found.\n");
02917       return;
02918    }
02919 
02920    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02921 
02922    vms->quota_usage = usage;
02923    vms->quota_limit = limit;
02924 }
02925 
02926 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02927 {
02928    char *start, *eol_pnt;
02929    int taglen;
02930 
02931    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02932       return NULL;
02933 
02934    taglen = strlen(tag) + 1;
02935    if (taglen < 1)
02936       return NULL;
02937 
02938    if (!(start = strstr(header, tag)))
02939       return NULL;
02940 
02941    /* Since we can be called multiple times we should clear our buffer */
02942    memset(buf, 0, len);
02943 
02944    ast_copy_string(buf, start+taglen, len);
02945    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02946       *eol_pnt = '\0';
02947    return buf;
02948 }
02949 
02950 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02951 {
02952    char *start, *quote, *eol_pnt;
02953 
02954    if (ast_strlen_zero(mailbox))
02955       return NULL;
02956 
02957    if (!(start = strstr(mailbox, "/user=")))
02958       return NULL;
02959 
02960    ast_copy_string(buf, start+6, len);
02961 
02962    if (!(quote = strchr(buf, '\"'))) {
02963       if (!(eol_pnt = strchr(buf, '/')))
02964          eol_pnt = strchr(buf,'}');
02965       *eol_pnt = '\0';
02966       return buf;
02967    } else {
02968       eol_pnt = strchr(buf+1,'\"');
02969       *eol_pnt = '\0';
02970       return buf+1;
02971    }
02972 }
02973 
02974 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02975 {
02976    struct vm_state *vms_p;
02977 
02978    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02979    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02980       return vms_p;
02981    }
02982    if (option_debug > 4)
02983       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02984    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02985       return NULL;
02986    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02987    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02988    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02989    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02990    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02991    vms_p->imapversion = vmu->imapversion;
02992    if (option_debug > 4)
02993       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
02994    vms_p->updated = 1;
02995    /* set mailbox to INBOX! */
02996    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
02997    init_vm_state(vms_p);
02998    vmstate_insert(vms_p);
02999    return vms_p;
03000 }
03001 
03002 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03003 {
03004    struct vmstate *vlist = NULL;
03005 
03006    if (interactive) {
03007       struct vm_state *vms;
03008       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03009       vms = pthread_getspecific(ts_vmstate.key);
03010       return vms;
03011    }
03012 
03013    AST_LIST_LOCK(&vmstates);
03014    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03015       if (!vlist->vms) {
03016          ast_debug(3, "error: vms is NULL for %s\n", user);
03017          continue;
03018       }
03019       if (vlist->vms->imapversion != imapversion) {
03020          continue;
03021       }
03022       if (!vlist->vms->imapuser) {
03023          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03024          continue;
03025       }
03026 
03027       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03028          AST_LIST_UNLOCK(&vmstates);
03029          return vlist->vms;
03030       }
03031    }
03032    AST_LIST_UNLOCK(&vmstates);
03033 
03034    ast_debug(3, "%s not found in vmstates\n", user);
03035 
03036    return NULL;
03037 }
03038 
03039 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03040 {
03041 
03042    struct vmstate *vlist = NULL;
03043    const char *local_context = S_OR(context, "default");
03044 
03045    if (interactive) {
03046       struct vm_state *vms;
03047       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03048       vms = pthread_getspecific(ts_vmstate.key);
03049       return vms;
03050    }
03051 
03052    AST_LIST_LOCK(&vmstates);
03053    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03054       if (!vlist->vms) {
03055          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03056          continue;
03057       }
03058       if (vlist->vms->imapversion != imapversion) {
03059          continue;
03060       }
03061       if (!vlist->vms->username || !vlist->vms->context) {
03062          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03063          continue;
03064       }
03065 
03066       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);
03067       
03068       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03069          ast_debug(3, "Found it!\n");
03070          AST_LIST_UNLOCK(&vmstates);
03071          return vlist->vms;
03072       }
03073    }
03074    AST_LIST_UNLOCK(&vmstates);
03075 
03076    ast_debug(3, "%s not found in vmstates\n", mailbox);
03077 
03078    return NULL;
03079 }
03080 
03081 static void vmstate_insert(struct vm_state *vms) 
03082 {
03083    struct vmstate *v;
03084    struct vm_state *altvms;
03085 
03086    /* If interactive, it probably already exists, and we should
03087       use the one we already have since it is more up to date.
03088       We can compare the username to find the duplicate */
03089    if (vms->interactive == 1) {
03090       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03091       if (altvms) {  
03092          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03093          vms->newmessages = altvms->newmessages;
03094          vms->oldmessages = altvms->oldmessages;
03095          vms->vmArrayIndex = altvms->vmArrayIndex;
03096          vms->lastmsg = altvms->lastmsg;
03097          vms->curmsg = altvms->curmsg;
03098          /* get a pointer to the persistent store */
03099          vms->persist_vms = altvms;
03100          /* Reuse the mailstream? */
03101 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03102          vms->mailstream = altvms->mailstream;
03103 #else
03104          vms->mailstream = NIL;
03105 #endif
03106       }
03107       return;
03108    }
03109 
03110    if (!(v = ast_calloc(1, sizeof(*v))))
03111       return;
03112    
03113    v->vms = vms;
03114 
03115    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03116 
03117    AST_LIST_LOCK(&vmstates);
03118    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03119    AST_LIST_UNLOCK(&vmstates);
03120 }
03121 
03122 static void vmstate_delete(struct vm_state *vms) 
03123 {
03124    struct vmstate *vc = NULL;
03125    struct vm_state *altvms = NULL;
03126 
03127    /* If interactive, we should copy pertinent info
03128       back to the persistent state (to make update immediate) */
03129    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03130       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03131       altvms->newmessages = vms->newmessages;
03132       altvms->oldmessages = vms->oldmessages;
03133       altvms->updated = 1;
03134       vms->mailstream = mail_close(vms->mailstream);
03135 
03136       /* Interactive states are not stored within the persistent list */
03137       return;
03138    }
03139    
03140    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03141    
03142    AST_LIST_LOCK(&vmstates);
03143    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03144       if (vc->vms == vms) {
03145          AST_LIST_REMOVE_CURRENT(list);
03146          break;
03147       }
03148    }
03149    AST_LIST_TRAVERSE_SAFE_END
03150    AST_LIST_UNLOCK(&vmstates);
03151    
03152    if (vc) {
03153       ast_mutex_destroy(&vc->vms->lock);
03154       ast_free(vc);
03155    }
03156    else
03157       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03158 }
03159 
03160 static void set_update(MAILSTREAM * stream) 
03161 {
03162    struct vm_state *vms;
03163    char *mailbox = stream->mailbox, *user;
03164    char buf[1024] = "";
03165 
03166    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03167       if (user && option_debug > 2)
03168          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03169       return;
03170    }
03171 
03172    ast_debug(3, "User %s mailbox set for update.\n", user);
03173 
03174    vms->updated = 1; /* Set updated flag since mailbox changed */
03175 }
03176 
03177 static void init_vm_state(struct vm_state *vms) 
03178 {
03179    int x;
03180    vms->vmArrayIndex = 0;
03181    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03182       vms->msgArray[x] = 0;
03183    }
03184    ast_mutex_init(&vms->lock);
03185 }
03186 
03187 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03188 {
03189    char *body_content;
03190    char *body_decoded;
03191    char *fn = is_intro ? vms->introfn : vms->fn;
03192    unsigned long len;
03193    unsigned long newlen;
03194    char filename[256];
03195    
03196    if (!body || body == NIL)
03197       return -1;
03198 
03199    ast_mutex_lock(&vms->lock);
03200    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03201    ast_mutex_unlock(&vms->lock);
03202    if (body_content != NIL) {
03203       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03204       /* ast_debug(1,body_content); */
03205       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03206       /* If the body of the file is empty, return an error */
03207       if (!newlen) {
03208          return -1;
03209       }
03210       write_file(filename, (char *) body_decoded, newlen);
03211    } else {
03212       ast_debug(5, "Body of message is NULL.\n");
03213       return -1;
03214    }
03215    return 0;
03216 }
03217 
03218 /*! 
03219  * \brief Get delimiter via mm_list callback 
03220  * \param stream
03221  *
03222  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03223  */
03224 /* MUTEX should already be held */
03225 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03226    char tmp[50];
03227    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03228    mail_list(stream, tmp, "*");
03229 }
03230 
03231 /*! 
03232  * \brief Check Quota for user 
03233  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03234  * \param mailbox the mailbox to check the quota for.
03235  *
03236  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03237  */
03238 static void check_quota(struct vm_state *vms, char *mailbox) {
03239    ast_mutex_lock(&vms->lock);
03240    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03241    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03242    if (vms && vms->mailstream != NULL) {
03243       imap_getquotaroot(vms->mailstream, mailbox);
03244    } else {
03245       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03246    }
03247    ast_mutex_unlock(&vms->lock);
03248 }
03249 
03250 #endif /* IMAP_STORAGE */
03251 
03252 /*! \brief Lock file path
03253  * only return failure if ast_lock_path returns 'timeout',
03254  * not if the path does not exist or any other reason
03255  */
03256 static int vm_lock_path(const char *path)
03257 {
03258    switch (ast_lock_path(path)) {
03259    case AST_LOCK_TIMEOUT:
03260       return -1;
03261    default:
03262       return 0;
03263    }
03264 }
03265 
03266 
03267 #ifdef ODBC_STORAGE
03268 struct generic_prepare_struct {
03269    char *sql;
03270    int argc;
03271    char **argv;
03272 };
03273 
03274 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03275 {
03276    struct generic_prepare_struct *gps = data;
03277    int res, i;
03278    SQLHSTMT stmt;
03279 
03280    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03281    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03282       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03283       return NULL;
03284    }
03285    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03286    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03287       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03288       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03289       return NULL;
03290    }
03291    for (i = 0; i < gps->argc; i++)
03292       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03293 
03294    return stmt;
03295 }
03296 
03297 /*!
03298  * \brief Retrieves a file from an ODBC data store.
03299  * \param dir the path to the file to be retreived.
03300  * \param msgnum the message number, such as within a mailbox folder.
03301  * 
03302  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03303  * 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.
03304  *
03305  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03306  * The output is the message information file with the name msgnum and the extension .txt
03307  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03308  * 
03309  * \return 0 on success, -1 on error.
03310  */
03311 static int retrieve_file(char *dir, int msgnum)
03312 {
03313    int x = 0;
03314    int res;
03315    int fd = -1;
03316    size_t fdlen = 0;
03317    void *fdm = MAP_FAILED;
03318    SQLSMALLINT colcount = 0;
03319    SQLHSTMT stmt;
03320    char sql[PATH_MAX];
03321    char fmt[80]="";
03322    char *c;
03323    char coltitle[256];
03324    SQLSMALLINT collen;
03325    SQLSMALLINT datatype;
03326    SQLSMALLINT decimaldigits;
03327    SQLSMALLINT nullable;
03328    SQLULEN colsize;
03329    SQLLEN colsize2;
03330    FILE *f = NULL;
03331    char rowdata[80];
03332    char fn[PATH_MAX];
03333    char full_fn[PATH_MAX];
03334    char msgnums[80];
03335    char *argv[] = { dir, msgnums };
03336    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03337 
03338    struct odbc_obj *obj;
03339    obj = ast_odbc_request_obj(odbc_database, 0);
03340    if (obj) {
03341       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03342       c = strchr(fmt, '|');
03343       if (c)
03344          *c = '\0';
03345       if (!strcasecmp(fmt, "wav49"))
03346          strcpy(fmt, "WAV");
03347       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03348       if (msgnum > -1)
03349          make_file(fn, sizeof(fn), dir, msgnum);
03350       else
03351          ast_copy_string(fn, dir, sizeof(fn));
03352 
03353       /* Create the information file */
03354       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03355       
03356       if (!(f = fopen(full_fn, "w+"))) {
03357          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03358          goto yuck;
03359       }
03360       
03361       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03362       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03363       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03364       if (!stmt) {
03365          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03366          ast_odbc_release_obj(obj);
03367          goto yuck;
03368       }
03369       res = SQLFetch(stmt);
03370       if (res == SQL_NO_DATA) {
03371          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03372          ast_odbc_release_obj(obj);
03373          goto yuck;
03374       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03375          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03376          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03377          ast_odbc_release_obj(obj);
03378          goto yuck;
03379       }
03380       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03381       if (fd < 0) {
03382          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03383          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03384          ast_odbc_release_obj(obj);
03385          goto yuck;
03386       }
03387       res = SQLNumResultCols(stmt, &colcount);
03388       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03389          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03390          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03391          ast_odbc_release_obj(obj);
03392          goto yuck;
03393       }
03394       if (f) 
03395          fprintf(f, "[message]\n");
03396       for (x = 0; x < colcount; x++) {
03397          rowdata[0] = '\0';
03398          colsize = 0;
03399          collen = sizeof(coltitle);
03400          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03401                   &datatype, &colsize, &decimaldigits, &nullable);
03402          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03403             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03404             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03405             ast_odbc_release_obj(obj);
03406             goto yuck;
03407          }
03408          if (!strcasecmp(coltitle, "recording")) {
03409             off_t offset;
03410             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03411             fdlen = colsize2;
03412             if (fd > -1) {
03413                char tmp[1]="";
03414                lseek(fd, fdlen - 1, SEEK_SET);
03415                if (write(fd, tmp, 1) != 1) {
03416                   close(fd);
03417                   fd = -1;
03418                   continue;
03419                }
03420                /* Read out in small chunks */
03421                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03422                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03423                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03424                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03425                      ast_odbc_release_obj(obj);
03426                      goto yuck;
03427                   } else {
03428                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03429                      munmap(fdm, CHUNKSIZE);
03430                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03431                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03432                         unlink(full_fn);
03433                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03434                         ast_odbc_release_obj(obj);
03435                         goto yuck;
03436                      }
03437                   }
03438                }
03439                if (truncate(full_fn, fdlen) < 0) {
03440                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03441                }
03442             }
03443          } else {
03444             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03445             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03446                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03447                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03448                ast_odbc_release_obj(obj);
03449                goto yuck;
03450             }
03451             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03452                fprintf(f, "%s=%s\n", coltitle, rowdata);
03453          }
03454       }
03455       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03456       ast_odbc_release_obj(obj);
03457    } else
03458       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03459 yuck:
03460    if (f)
03461       fclose(f);
03462    if (fd > -1)
03463       close(fd);
03464    return x - 1;
03465 }
03466 
03467 /*!
03468  * \brief Determines the highest message number in use for a given user and mailbox folder.
03469  * \param vmu 
03470  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03471  *
03472  * This method is used when mailboxes are stored in an ODBC back end.
03473  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03474  *
03475  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03476 
03477  */
03478 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03479 {
03480    int x = 0;
03481    int res;
03482    SQLHSTMT stmt;
03483    char sql[PATH_MAX];
03484    char rowdata[20];
03485    char *argv[] = { dir };
03486    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03487 
03488    struct odbc_obj *obj;
03489    obj = ast_odbc_request_obj(odbc_database, 0);
03490    if (obj) {
03491       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03492 
03493       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03494       if (!stmt) {
03495          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03496          ast_odbc_release_obj(obj);
03497          goto yuck;
03498       }
03499       res = SQLFetch(stmt);
03500       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03501          if (res == SQL_NO_DATA) {
03502             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03503          } else {
03504             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03505          }
03506 
03507          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03508          ast_odbc_release_obj(obj);
03509          goto yuck;
03510       }
03511       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03512       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03513          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03514          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03515          ast_odbc_release_obj(obj);
03516          goto yuck;
03517       }
03518       if (sscanf(rowdata, "%30d", &x) != 1)
03519          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03520       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03521       ast_odbc_release_obj(obj);
03522       return x;
03523    } else
03524       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03525 yuck:
03526    return x - 1;
03527 }
03528 
03529 /*!
03530  * \brief Determines if the specified message exists.
03531  * \param dir the folder the mailbox folder to look for messages. 
03532  * \param msgnum the message index to query for.
03533  *
03534  * This method is used when mailboxes are stored in an ODBC back end.
03535  *
03536  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03537  */
03538 static int message_exists(char *dir, int msgnum)
03539 {
03540    int x = 0;
03541    int res;
03542    SQLHSTMT stmt;
03543    char sql[PATH_MAX];
03544    char rowdata[20];
03545    char msgnums[20];
03546    char *argv[] = { dir, msgnums };
03547    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03548 
03549    struct odbc_obj *obj;
03550    obj = ast_odbc_request_obj(odbc_database, 0);
03551    if (obj) {
03552       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03553       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03554       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03555       if (!stmt) {
03556          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03557          ast_odbc_release_obj(obj);
03558          goto yuck;
03559       }
03560       res = SQLFetch(stmt);
03561       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03562          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03563          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03564          ast_odbc_release_obj(obj);
03565          goto yuck;
03566       }
03567       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03568       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03569          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03570          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03571          ast_odbc_release_obj(obj);
03572          goto yuck;
03573       }
03574       if (sscanf(rowdata, "%30d", &x) != 1)
03575          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03576       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03577       ast_odbc_release_obj(obj);
03578    } else
03579       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03580 yuck:
03581    return x;
03582 }
03583 
03584 /*!
03585  * \brief returns the number of messages found.
03586  * \param vmu
03587  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03588  *
03589  * This method is used when mailboxes are stored in an ODBC back end.
03590  *
03591  * \return The count of messages being zero or more, less than zero on error.
03592  */
03593 static int count_messages(struct ast_vm_user *vmu, char *dir)
03594 {
03595    int x = 0;
03596    int res;
03597    SQLHSTMT stmt;
03598    char sql[PATH_MAX];
03599    char rowdata[20];
03600    char *argv[] = { dir };
03601    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03602 
03603    struct odbc_obj *obj;
03604    obj = ast_odbc_request_obj(odbc_database, 0);
03605    if (obj) {
03606       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03607       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03608       if (!stmt) {
03609          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03610          ast_odbc_release_obj(obj);
03611          goto yuck;
03612       }
03613       res = SQLFetch(stmt);
03614       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03615          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03616          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03617          ast_odbc_release_obj(obj);
03618          goto yuck;
03619       }
03620       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03621       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03622          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03623          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03624          ast_odbc_release_obj(obj);
03625          goto yuck;
03626       }
03627       if (sscanf(rowdata, "%30d", &x) != 1)
03628          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03629       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03630       ast_odbc_release_obj(obj);
03631       return x;
03632    } else
03633       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03634 yuck:
03635    return x - 1;
03636 
03637 }
03638 
03639 /*!
03640  * \brief Deletes a message from the mailbox folder.
03641  * \param sdir The mailbox folder to work in.
03642  * \param smsg The message index to be deleted.
03643  *
03644  * This method is used when mailboxes are stored in an ODBC back end.
03645  * The specified message is directly deleted from the database 'voicemessages' table.
03646  * 
03647  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03648  */
03649 static void delete_file(const char *sdir, int smsg)
03650 {
03651    SQLHSTMT stmt;
03652    char sql[PATH_MAX];
03653    char msgnums[20];
03654    char *argv[] = { NULL, msgnums };
03655    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03656    struct odbc_obj *obj;
03657 
03658    argv[0] = ast_strdupa(sdir);
03659 
03660    obj = ast_odbc_request_obj(odbc_database, 0);
03661    if (obj) {
03662       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03663       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03664       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03665       if (!stmt)
03666          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03667       else
03668          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03669       ast_odbc_release_obj(obj);
03670    } else
03671       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03672    return;  
03673 }
03674 
03675 /*!
03676  * \brief Copies a voicemail from one mailbox to another.
03677  * \param sdir the folder for which to look for the message to be copied.
03678  * \param smsg the index of the message to be copied.
03679  * \param ddir the destination folder to copy the message into.
03680  * \param dmsg the index to be used for the copied message.
03681  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03682  * \param dmailboxcontext The context for the destination user.
03683  *
03684  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03685  */
03686 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03687 {
03688    SQLHSTMT stmt;
03689    char sql[512];
03690    char msgnums[20];
03691    char msgnumd[20];
03692    struct odbc_obj *obj;
03693    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03694    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03695 
03696    delete_file(ddir, dmsg);
03697    obj = ast_odbc_request_obj(odbc_database, 0);
03698    if (obj) {
03699       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03700       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03701       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);
03702       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03703       if (!stmt)
03704          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03705       else
03706          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03707       ast_odbc_release_obj(obj);
03708    } else
03709       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03710    return;  
03711 }
03712 
03713 struct insert_data {
03714    char *sql;
03715    const char *dir;
03716    const char *msgnums;
03717    void *data;
03718    SQLLEN datalen;
03719    SQLLEN indlen;
03720    const char *context;
03721    const char *macrocontext;
03722    const char *callerid;
03723    const char *origtime;
03724    const char *duration;
03725    const char *mailboxuser;
03726    const char *mailboxcontext;
03727    const char *category;
03728    const char *flag;
03729 };
03730 
03731 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03732 {
03733    struct insert_data *data = vdata;
03734    int res;
03735    SQLHSTMT stmt;
03736 
03737    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03738    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03739       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03740       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03741       return NULL;
03742    }
03743 
03744    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03745    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03746    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03747    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03748    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03749    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03750    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03751    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03752    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03753    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03754    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03755    if (!ast_strlen_zero(data->category)) {
03756       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03757    }
03758    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03759    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03760       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03761       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03762       return NULL;
03763    }
03764 
03765    return stmt;
03766 }
03767 
03768 /*!
03769  * \brief Stores a voicemail into the database.
03770  * \param dir the folder the mailbox folder to store the message.
03771  * \param mailboxuser the user owning the mailbox folder.
03772  * \param mailboxcontext
03773  * \param msgnum the message index for the message to be stored.
03774  *
03775  * This method is used when mailboxes are stored in an ODBC back end.
03776  * The message sound file and information file is looked up on the file system. 
03777  * A SQL query is invoked to store the message into the (MySQL) database.
03778  *
03779  * \return the zero on success -1 on error.
03780  */
03781 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03782 {
03783    int res = 0;
03784    int fd = -1;
03785    void *fdm = MAP_FAILED;
03786    size_t fdlen = -1;
03787    SQLHSTMT stmt;
03788    char sql[PATH_MAX];
03789    char msgnums[20];
03790    char fn[PATH_MAX];
03791    char full_fn[PATH_MAX];
03792    char fmt[80]="";
03793    char *c;
03794    struct ast_config *cfg = NULL;
03795    struct odbc_obj *obj;
03796    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03797       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03798    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03799 
03800    delete_file(dir, msgnum);
03801    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03802       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03803       return -1;
03804    }
03805 
03806    do {
03807       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03808       c = strchr(fmt, '|');
03809       if (c)
03810          *c = '\0';
03811       if (!strcasecmp(fmt, "wav49"))
03812          strcpy(fmt, "WAV");
03813       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03814       if (msgnum > -1)
03815          make_file(fn, sizeof(fn), dir, msgnum);
03816       else
03817          ast_copy_string(fn, dir, sizeof(fn));
03818       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03819       cfg = ast_config_load(full_fn, config_flags);
03820       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03821       fd = open(full_fn, O_RDWR);
03822       if (fd < 0) {
03823          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03824          res = -1;
03825          break;
03826       }
03827       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03828          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03829             idata.context = "";
03830          }
03831          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03832             idata.macrocontext = "";
03833          }
03834          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03835             idata.callerid = "";
03836          }
03837          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03838             idata.origtime = "";
03839          }
03840          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03841             idata.duration = "";
03842          }
03843          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03844             idata.category = "";
03845          }
03846          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03847             idata.flag = "";
03848          }
03849       }
03850       fdlen = lseek(fd, 0, SEEK_END);
03851       lseek(fd, 0, SEEK_SET);
03852       printf("Length is %zd\n", fdlen);
03853       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03854       if (fdm == MAP_FAILED) {
03855          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03856          res = -1;
03857          break;
03858       } 
03859       idata.data = fdm;
03860       idata.datalen = idata.indlen = fdlen;
03861 
03862       if (!ast_strlen_zero(idata.category)) 
03863          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03864       else
03865          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03866 
03867       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03868          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03869       } else {
03870          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03871          res = -1;
03872       }
03873    } while (0);
03874    if (obj) {
03875       ast_odbc_release_obj(obj);
03876    }
03877    if (cfg)
03878       ast_config_destroy(cfg);
03879    if (fdm != MAP_FAILED)
03880       munmap(fdm, fdlen);
03881    if (fd > -1)
03882       close(fd);
03883    return res;
03884 }
03885 
03886 /*!
03887  * \brief Renames a message in a mailbox folder.
03888  * \param sdir The folder of the message to be renamed.
03889  * \param smsg The index of the message to be renamed.
03890  * \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.
03891  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03892  * \param ddir The destination folder for the message to be renamed into
03893  * \param dmsg The destination message for the message to be renamed.
03894  *
03895  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03896  * 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.
03897  * 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.
03898  */
03899 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03900 {
03901    SQLHSTMT stmt;
03902    char sql[PATH_MAX];
03903    char msgnums[20];
03904    char msgnumd[20];
03905    struct odbc_obj *obj;
03906    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03907    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03908 
03909    delete_file(ddir, dmsg);
03910    obj = ast_odbc_request_obj(odbc_database, 0);
03911    if (obj) {
03912       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03913       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03914       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03915       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03916       if (!stmt)
03917          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03918       else
03919          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03920       ast_odbc_release_obj(obj);
03921    } else
03922       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03923    return;  
03924 }
03925 
03926 /*!
03927  * \brief Removes a voicemail message file.
03928  * \param dir the path to the message file.
03929  * \param msgnum the unique number for the message within the mailbox.
03930  *
03931  * Removes the message content file and the information file.
03932  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03933  * Typical use is to clean up after a RETRIEVE operation. 
03934  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03935  * \return zero on success, -1 on error.
03936  */
03937 static int remove_file(char *dir, int msgnum)
03938 {
03939    char fn[PATH_MAX];
03940    char full_fn[PATH_MAX];
03941    char msgnums[80];
03942    
03943    if (msgnum > -1) {
03944       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03945       make_file(fn, sizeof(fn), dir, msgnum);
03946    } else
03947       ast_copy_string(fn, dir, sizeof(fn));
03948    ast_filedelete(fn, NULL);  
03949    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03950    unlink(full_fn);
03951    return 0;
03952 }
03953 #else
03954 #ifndef IMAP_STORAGE
03955 /*!
03956  * \brief Find all .txt files - even if they are not in sequence from 0000.
03957  * \param vmu
03958  * \param dir
03959  *
03960  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03961  *
03962  * \return the count of messages, zero or more.
03963  */
03964 static int count_messages(struct ast_vm_user *vmu, char *dir)
03965 {
03966 
03967    int vmcount = 0;
03968    DIR *vmdir = NULL;
03969    struct dirent *vment = NULL;
03970 
03971    if (vm_lock_path(dir))
03972       return ERROR_LOCK_PATH;
03973 
03974    if ((vmdir = opendir(dir))) {
03975       while ((vment = readdir(vmdir))) {
03976          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03977             vmcount++;
03978          }
03979       }
03980       closedir(vmdir);
03981    }
03982    ast_unlock_path(dir);
03983    
03984    return vmcount;
03985 }
03986 
03987 /*!
03988  * \brief Renames a message in a mailbox folder.
03989  * \param sfn The path to the mailbox information and data file to be renamed.
03990  * \param dfn The path for where the message data and information files will be renamed to.
03991  *
03992  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03993  */
03994 static void rename_file(char *sfn, char *dfn)
03995 {
03996    char stxt[PATH_MAX];
03997    char dtxt[PATH_MAX];
03998    ast_filerename(sfn, dfn, NULL);
03999    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04000    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04001    if (ast_check_realtime("voicemail_data")) {
04002       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04003    }
04004    rename(stxt, dtxt);
04005 }
04006 
04007 /*! 
04008  * \brief Determines the highest message number in use for a given user and mailbox folder.
04009  * \param vmu 
04010  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04011  *
04012  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04013  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04014  *
04015  * \note Should always be called with a lock already set on dir.
04016  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04017  */
04018 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04019 {
04020    int x;
04021    unsigned char map[MAXMSGLIMIT] = "";
04022    DIR *msgdir;
04023    struct dirent *msgdirent;
04024    int msgdirint;
04025    char extension[4];
04026    int stopcount = 0;
04027 
04028    /* Reading the entire directory into a file map scales better than
04029     * doing a stat repeatedly on a predicted sequence.  I suspect this
04030     * is partially due to stat(2) internally doing a readdir(2) itself to
04031     * find each file. */
04032    if (!(msgdir = opendir(dir))) {
04033       return -1;
04034    }
04035 
04036    while ((msgdirent = readdir(msgdir))) {
04037       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04038          map[msgdirint] = 1;
04039          stopcount++;
04040          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04041       }
04042    }
04043    closedir(msgdir);
04044 
04045    for (x = 0; x < vmu->maxmsg; x++) {
04046       if (map[x] == 1) {
04047          stopcount--;
04048       } else if (map[x] == 0 && !stopcount) {
04049          break;
04050       }
04051    }
04052 
04053    return x - 1;
04054 }
04055 
04056 #endif /* #ifndef IMAP_STORAGE */
04057 #endif /* #else of #ifdef ODBC_STORAGE */
04058 #ifndef IMAP_STORAGE
04059 /*!
04060  * \brief Utility function to copy a file.
04061  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04062  * \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.
04063  *
04064  * 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.
04065  * The copy operation copies up to 4096 bytes at once.
04066  *
04067  * \return zero on success, -1 on error.
04068  */
04069 static int copy(char *infile, char *outfile)
04070 {
04071    int ifd;
04072    int ofd;
04073    int res;
04074    int len;
04075    char buf[4096];
04076 
04077 #ifdef HARDLINK_WHEN_POSSIBLE
04078    /* Hard link if possible; saves disk space & is faster */
04079    if (link(infile, outfile)) {
04080 #endif
04081       if ((ifd = open(infile, O_RDONLY)) < 0) {
04082          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04083          return -1;
04084       }
04085       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04086          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04087          close(ifd);
04088          return -1;
04089       }
04090       do {
04091          len = read(ifd, buf, sizeof(buf));
04092          if (len < 0) {
04093             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04094             close(ifd);
04095             close(ofd);
04096             unlink(outfile);
04097          }
04098          if (len) {
04099             res = write(ofd, buf, len);
04100             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04101                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04102                close(ifd);
04103                close(ofd);
04104                unlink(outfile);
04105             }
04106          }
04107       } while (len);
04108       close(ifd);
04109       close(ofd);
04110       return 0;
04111 #ifdef HARDLINK_WHEN_POSSIBLE
04112    } else {
04113       /* Hard link succeeded */
04114       return 0;
04115    }
04116 #endif
04117 }
04118 
04119 /*!
04120  * \brief Copies a voicemail information (envelope) file.
04121  * \param frompath
04122  * \param topath 
04123  *
04124  * Every voicemail has the data (.wav) file, and the information file.
04125  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04126  * This is used by the COPY macro when not using IMAP storage.
04127  */
04128 static void copy_plain_file(char *frompath, char *topath)
04129 {
04130    char frompath2[PATH_MAX], topath2[PATH_MAX];
04131    struct ast_variable *tmp,*var = NULL;
04132    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04133    ast_filecopy(frompath, topath, NULL);
04134    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04135    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04136    if (ast_check_realtime("voicemail_data")) {
04137       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04138       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04139       for (tmp = var; tmp; tmp = tmp->next) {
04140          if (!strcasecmp(tmp->name, "origmailbox")) {
04141             origmailbox = tmp->value;
04142          } else if (!strcasecmp(tmp->name, "context")) {
04143             context = tmp->value;
04144          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04145             macrocontext = tmp->value;
04146          } else if (!strcasecmp(tmp->name, "exten")) {
04147             exten = tmp->value;
04148          } else if (!strcasecmp(tmp->name, "priority")) {
04149             priority = tmp->value;
04150          } else if (!strcasecmp(tmp->name, "callerchan")) {
04151             callerchan = tmp->value;
04152          } else if (!strcasecmp(tmp->name, "callerid")) {
04153             callerid = tmp->value;
04154          } else if (!strcasecmp(tmp->name, "origdate")) {
04155             origdate = tmp->value;
04156          } else if (!strcasecmp(tmp->name, "origtime")) {
04157             origtime = tmp->value;
04158          } else if (!strcasecmp(tmp->name, "category")) {
04159             category = tmp->value;
04160          } else if (!strcasecmp(tmp->name, "duration")) {
04161             duration = tmp->value;
04162          }
04163       }
04164       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);
04165    }
04166    copy(frompath2, topath2);
04167    ast_variables_destroy(var);
04168 }
04169 #endif
04170 
04171 /*! 
04172  * \brief Removes the voicemail sound and information file.
04173  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04174  *
04175  * This is used by the DELETE macro when voicemails are stored on the file system.
04176  *
04177  * \return zero on success, -1 on error.
04178  */
04179 static int vm_delete(char *file)
04180 {
04181    char *txt;
04182    int txtsize = 0;
04183 
04184    txtsize = (strlen(file) + 5)*sizeof(char);
04185    txt = alloca(txtsize);
04186    /* Sprintf here would safe because we alloca'd exactly the right length,
04187     * but trying to eliminate all sprintf's anyhow
04188     */
04189    if (ast_check_realtime("voicemail_data")) {
04190       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04191    }
04192    snprintf(txt, txtsize, "%s.txt", file);
04193    unlink(txt);
04194    return ast_filedelete(file, NULL);
04195 }
04196 
04197 /*!
04198  * \brief utility used by inchar(), for base_encode()
04199  */
04200 static int inbuf(struct baseio *bio, FILE *fi)
04201 {
04202    int l;
04203 
04204    if (bio->ateof)
04205       return 0;
04206 
04207    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04208       if (ferror(fi))
04209          return -1;
04210 
04211       bio->ateof = 1;
04212       return 0;
04213    }
04214 
04215    bio->iolen = l;
04216    bio->iocp = 0;
04217 
04218    return 1;
04219 }
04220 
04221 /*!
04222  * \brief utility used by base_encode()
04223  */
04224 static int inchar(struct baseio *bio, FILE *fi)
04225 {
04226    if (bio->iocp>=bio->iolen) {
04227       if (!inbuf(bio, fi))
04228          return EOF;
04229    }
04230 
04231    return bio->iobuf[bio->iocp++];
04232 }
04233 
04234 /*!
04235  * \brief utility used by base_encode()
04236  */
04237 static int ochar(struct baseio *bio, int c, FILE *so)
04238 {
04239    if (bio->linelength >= BASELINELEN) {
04240       if (fputs(ENDL, so) == EOF) {
04241          return -1;
04242       }
04243 
04244       bio->linelength = 0;
04245    }
04246 
04247    if (putc(((unsigned char) c), so) == EOF) {
04248       return -1;
04249    }
04250 
04251    bio->linelength++;
04252 
04253    return 1;
04254 }
04255 
04256 /*!
04257  * \brief Performs a base 64 encode algorithm on the contents of a File
04258  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04259  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04260  *
04261  * 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 ?
04262  *
04263  * \return zero on success, -1 on error.
04264  */
04265 static int base_encode(char *filename, FILE *so)
04266 {
04267    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04268       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04269       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04270       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04271    int i, hiteof = 0;
04272    FILE *fi;
04273    struct baseio bio;
04274 
04275    memset(&bio, 0, sizeof(bio));
04276    bio.iocp = BASEMAXINLINE;
04277 
04278    if (!(fi = fopen(filename, "rb"))) {
04279       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04280       return -1;
04281    }
04282 
04283    while (!hiteof){
04284       unsigned char igroup[3], ogroup[4];
04285       int c, n;
04286 
04287       memset(igroup, 0, sizeof(igroup));
04288 
04289       for (n = 0; n < 3; n++) {
04290          if ((c = inchar(&bio, fi)) == EOF) {
04291             hiteof = 1;
04292             break;
04293          }
04294 
04295          igroup[n] = (unsigned char) c;
04296       }
04297 
04298       if (n > 0) {
04299          ogroup[0]= dtable[igroup[0] >> 2];
04300          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04301          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04302          ogroup[3]= dtable[igroup[2] & 0x3F];
04303 
04304          if (n < 3) {
04305             ogroup[3] = '=';
04306 
04307             if (n < 2)
04308                ogroup[2] = '=';
04309          }
04310 
04311          for (i = 0; i < 4; i++)
04312             ochar(&bio, ogroup[i], so);
04313       }
04314    }
04315 
04316    fclose(fi);
04317    
04318    if (fputs(ENDL, so) == EOF) {
04319       return 0;
04320    }
04321 
04322    return 1;
04323 }
04324 
04325 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)
04326 {
04327    char callerid[256];
04328    char num[12];
04329    char fromdir[256], fromfile[256];
04330    struct ast_config *msg_cfg;
04331    const char *origcallerid, *origtime;
04332    char origcidname[80], origcidnum[80], origdate[80];
04333    int inttime;
04334    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04335 
04336    /* Prepare variables for substitution in email body and subject */
04337    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04338    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04339    snprintf(num, sizeof(num), "%d", msgnum);
04340    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04341    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04342    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04343    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04344       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04345    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04346    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04347    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04348    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04349    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04350 
04351    /* Retrieve info from VM attribute file */
04352    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04353    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04354    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04355       strcat(fromfile, ".txt");
04356    }
04357    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04358       if (option_debug > 0) {
04359          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04360       }
04361       return;
04362    }
04363 
04364    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04365       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04366       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04367       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04368       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04369    }
04370 
04371    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04372       struct timeval tv = { inttime, };
04373       struct ast_tm tm;
04374       ast_localtime(&tv, &tm, NULL);
04375       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04376       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04377    }
04378    ast_config_destroy(msg_cfg);
04379 }
04380 
04381 /*!
04382  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04383  * \param from The string to work with.
04384  * \param buf The buffer into which to write the modified quoted string.
04385  * \param maxlen Always zero, but see \see ast_str
04386  * 
04387  * \return The destination string with quotes wrapped on it (the to field).
04388  */
04389 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04390 {
04391    const char *ptr;
04392 
04393    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04394    ast_str_set(buf, maxlen, "\"");
04395    for (ptr = from; *ptr; ptr++) {
04396       if (*ptr == '"' || *ptr == '\\') {
04397          ast_str_append(buf, maxlen, "\\%c", *ptr);
04398       } else {
04399          ast_str_append(buf, maxlen, "%c", *ptr);
04400       }
04401    }
04402    ast_str_append(buf, maxlen, "\"");
04403 
04404    return ast_str_buffer(*buf);
04405 }
04406 
04407 /*! \brief
04408  * fill in *tm for current time according to the proper timezone, if any.
04409  * \return tm so it can be used as a function argument.
04410  */
04411 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04412 {
04413    const struct vm_zone *z = NULL;
04414    struct timeval t = ast_tvnow();
04415 
04416    /* Does this user have a timezone specified? */
04417    if (!ast_strlen_zero(vmu->zonetag)) {
04418       /* Find the zone in the list */
04419       AST_LIST_LOCK(&zones);
04420       AST_LIST_TRAVERSE(&zones, z, list) {
04421          if (!strcmp(z->name, vmu->zonetag))
04422             break;
04423       }
04424       AST_LIST_UNLOCK(&zones);
04425    }
04426    ast_localtime(&t, tm, z ? z->timezone : NULL);
04427    return tm;
04428 }
04429 
04430 /*!\brief Check if the string would need encoding within the MIME standard, to
04431  * avoid confusing certain mail software that expects messages to be 7-bit
04432  * clean.
04433  */
04434 static int check_mime(const char *str)
04435 {
04436    for (; *str; str++) {
04437       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04438          return 1;
04439       }
04440    }
04441    return 0;
04442 }
04443 
04444 /*!\brief Encode a string according to the MIME rules for encoding strings
04445  * that are not 7-bit clean or contain control characters.
04446  *
04447  * Additionally, if the encoded string would exceed the MIME limit of 76
04448  * characters per line, then the encoding will be broken up into multiple
04449  * sections, separated by a space character, in order to facilitate
04450  * breaking up the associated header across multiple lines.
04451  *
04452  * \param end An expandable buffer for holding the result
04453  * \param maxlen Always zero, but see \see ast_str
04454  * \param start A string to be encoded
04455  * \param preamble The length of the first line already used for this string,
04456  * to ensure that each line maintains a maximum length of 76 chars.
04457  * \param postamble the length of any additional characters appended to the
04458  * line, used to ensure proper field wrapping.
04459  * \retval The encoded string.
04460  */
04461 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04462 {
04463    struct ast_str *tmp = ast_str_alloca(80);
04464    int first_section = 1;
04465 
04466    ast_str_reset(*end);
04467    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04468    for (; *start; start++) {
04469       int need_encoding = 0;
04470       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04471          need_encoding = 1;
04472       }
04473       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04474          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04475          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04476          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04477          /* Start new line */
04478          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04479          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04480          first_section = 0;
04481       }
04482       if (need_encoding && *start == ' ') {
04483          ast_str_append(&tmp, -1, "_");
04484       } else if (need_encoding) {
04485          ast_str_append(&tmp, -1, "=%hhX", *start);
04486       } else {
04487          ast_str_append(&tmp, -1, "%c", *start);
04488       }
04489    }
04490    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04491    return ast_str_buffer(*end);
04492 }
04493 
04494 /*!
04495  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04496  * \param p The output file to generate the email contents into.
04497  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04498  * \param vmu The voicemail user who is sending the voicemail.
04499  * \param msgnum The message index in the mailbox folder.
04500  * \param context 
04501  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04502  * \param fromfolder
04503  * \param cidnum The caller ID number.
04504  * \param cidname The caller ID name.
04505  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04506  * \param attach2 
04507  * \param format The message sound file format. i.e. .wav
04508  * \param duration The time of the message content, in seconds.
04509  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04510  * \param chan
04511  * \param category
04512  * \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.
04513  * \param flag
04514  *
04515  * 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.
04516  */
04517 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)
04518 {
04519    char date[256];
04520    char host[MAXHOSTNAMELEN] = "";
04521    char who[256];
04522    char bound[256];
04523    char dur[256];
04524    struct ast_tm tm;
04525    char enc_cidnum[256] = "", enc_cidname[256] = "";
04526    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04527    char *greeting_attachment; 
04528    char filename[256];
04529 
04530    if (!str1 || !str2) {
04531       ast_free(str1);
04532       ast_free(str2);
04533       return;
04534    }
04535 
04536    if (cidnum) {
04537       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04538    }
04539    if (cidname) {
04540       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04541    }
04542    gethostname(host, sizeof(host) - 1);
04543 
04544    if (strchr(srcemail, '@')) {
04545       ast_copy_string(who, srcemail, sizeof(who));
04546    } else {
04547       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04548    }
04549 
04550    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04551    if (greeting_attachment) {
04552       *greeting_attachment++ = '\0';
04553    }
04554 
04555    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04556    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04557    fprintf(p, "Date: %s" ENDL, date);
04558 
04559    /* Set date format for voicemail mail */
04560    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04561 
04562    if (!ast_strlen_zero(fromstring)) {
04563       struct ast_channel *ast;
04564       if ((ast = ast_dummy_channel_alloc())) {
04565          char *ptr;
04566          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04567          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04568 
04569          if (check_mime(ast_str_buffer(str1))) {
04570             int first_line = 1;
04571             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04572             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04573                *ptr = '\0';
04574                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04575                first_line = 0;
04576                /* Substring is smaller, so this will never grow */
04577                ast_str_set(&str2, 0, "%s", ptr + 1);
04578             }
04579             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04580          } else {
04581             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04582          }
04583          ast = ast_channel_unref(ast);
04584       } else {
04585          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04586       }
04587    } else {
04588       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04589    }
04590 
04591    if (check_mime(vmu->fullname)) {
04592       int first_line = 1;
04593       char *ptr;
04594       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04595       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04596          *ptr = '\0';
04597          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04598          first_line = 0;
04599          /* Substring is smaller, so this will never grow */
04600          ast_str_set(&str2, 0, "%s", ptr + 1);
04601       }
04602       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04603    } else {
04604       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04605    }
04606 
04607    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04608       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04609       struct ast_channel *ast;
04610       if ((ast = ast_dummy_channel_alloc())) {
04611          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04612          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04613          if (check_mime(ast_str_buffer(str1))) {
04614             int first_line = 1;
04615             char *ptr;
04616             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04617             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04618                *ptr = '\0';
04619                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04620                first_line = 0;
04621                /* Substring is smaller, so this will never grow */
04622                ast_str_set(&str2, 0, "%s", ptr + 1);
04623             }
04624             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04625          } else {
04626             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04627          }
04628          ast = ast_channel_unref(ast);
04629       } else {
04630          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04631       }
04632    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04633       if (ast_strlen_zero(flag)) {
04634          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04635       } else {
04636          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04637       }
04638    } else {
04639       if (ast_strlen_zero(flag)) {
04640          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04641       } else {
04642          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04643       }
04644    }
04645 
04646    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04647       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04648    if (imap) {
04649       /* additional information needed for IMAP searching */
04650       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04651       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04652       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04653       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04654 #ifdef IMAP_STORAGE
04655       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04656 #else
04657       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04658 #endif
04659       /* flag added for Urgent */
04660       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04661       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04662       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04663       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04664       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04665       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04666       if (!ast_strlen_zero(category)) {
04667          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04668       } else {
04669          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04670       }
04671       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04672       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04673       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04674    }
04675    if (!ast_strlen_zero(cidnum)) {
04676       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04677    }
04678    if (!ast_strlen_zero(cidname)) {
04679       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04680    }
04681    fprintf(p, "MIME-Version: 1.0" ENDL);
04682    if (attach_user_voicemail) {
04683       /* Something unique. */
04684       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04685          (int) getpid(), (unsigned int) ast_random());
04686 
04687       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04688       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04689       fprintf(p, "--%s" ENDL, bound);
04690    }
04691    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04692    if (emailbody || vmu->emailbody) {
04693       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04694       struct ast_channel *ast;
04695       if ((ast = ast_dummy_channel_alloc())) {
04696          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04697          ast_str_substitute_variables(&str1, 0, ast, e_body);
04698 #ifdef IMAP_STORAGE
04699             {
04700                /* Convert body to native line terminators for IMAP backend */
04701                char *line = ast_str_buffer(str1), *next;
04702                do {
04703                   /* Terminate line before outputting it to the file */
04704                   if ((next = strchr(line, '\n'))) {
04705                      *next++ = '\0';
04706                   }
04707                   fprintf(p, "%s" ENDL, line);
04708                   line = next;
04709                } while (!ast_strlen_zero(line));
04710             }
04711 #else
04712          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04713 #endif
04714          ast = ast_channel_unref(ast);
04715       } else {
04716          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04717       }
04718    } else if (msgnum > -1) {
04719       if (strcmp(vmu->mailbox, mailbox)) {
04720          /* Forwarded type */
04721          struct ast_config *msg_cfg;
04722          const char *v;
04723          int inttime;
04724          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04725          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04726          /* Retrieve info from VM attribute file */
04727          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04728          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04729          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04730             strcat(fromfile, ".txt");
04731          }
04732          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04733             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04734                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04735             }
04736 
04737             /* You might be tempted to do origdate, except that a) it's in the wrong
04738              * format, and b) it's missing for IMAP recordings. */
04739             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04740                struct timeval tv = { inttime, };
04741                struct ast_tm tm;
04742                ast_localtime(&tv, &tm, NULL);
04743                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04744             }
04745             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04746                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04747                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04748                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04749                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04750                date, origcallerid, origdate);
04751             ast_config_destroy(msg_cfg);
04752          } else {
04753             goto plain_message;
04754          }
04755       } else {
04756 plain_message:
04757          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04758             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04759             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04760             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04761             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04762       }
04763    } else {
04764       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04765             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04766    }
04767 
04768    if (imap || attach_user_voicemail) {
04769       if (!ast_strlen_zero(attach2)) {
04770          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04771          ast_debug(5, "creating second attachment filename %s\n", filename);
04772          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04773          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04774          ast_debug(5, "creating attachment filename %s\n", filename);
04775          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04776       } else {
04777          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04778          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04779          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04780       }
04781    }
04782    ast_free(str1);
04783    ast_free(str2);
04784 }
04785 
04786 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)
04787 {
04788    char tmpdir[256], newtmp[256];
04789    char fname[256];
04790    char tmpcmd[256];
04791    int tmpfd = -1;
04792    int soxstatus = 0;
04793 
04794    /* Eww. We want formats to tell us their own MIME type */
04795    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04796 
04797    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04798       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04799       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04800       tmpfd = mkstemp(newtmp);
04801       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04802       ast_debug(3, "newtmp: %s\n", newtmp);
04803       if (tmpfd > -1) {
04804          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04805          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04806             attach = newtmp;
04807             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04808          } else {
04809             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04810                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04811             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04812          }
04813       }
04814    }
04815    fprintf(p, "--%s" ENDL, bound);
04816    if (msgnum > -1)
04817       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04818    else
04819       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04820    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04821    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04822    if (msgnum > -1)
04823       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04824    else
04825       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04826    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04827    base_encode(fname, p);
04828    if (last)
04829       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04830    if (tmpfd > -1) {
04831       if (soxstatus == 0) {
04832          unlink(fname);
04833       }
04834       close(tmpfd);
04835       unlink(newtmp);
04836    }
04837    return 0;
04838 }
04839 
04840 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)
04841 {
04842    FILE *p = NULL;
04843    char tmp[80] = "/tmp/astmail-XXXXXX";
04844    char tmp2[256];
04845    char *stringp;
04846 
04847    if (vmu && ast_strlen_zero(vmu->email)) {
04848       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04849       return(0);
04850    }
04851 
04852    /* Mail only the first format */
04853    format = ast_strdupa(format);
04854    stringp = format;
04855    strsep(&stringp, "|");
04856 
04857    if (!strcmp(format, "wav49"))
04858       format = "WAV";
04859    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));
04860    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04861       command hangs */
04862    if ((p = vm_mkftemp(tmp)) == NULL) {
04863       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04864       return -1;
04865    } else {
04866       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04867       fclose(p);
04868       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04869       ast_safe_system(tmp2);
04870       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04871    }
04872    return 0;
04873 }
04874 
04875 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)
04876 {
04877    char enc_cidnum[256], enc_cidname[256];
04878    char date[256];
04879    char host[MAXHOSTNAMELEN] = "";
04880    char who[256];
04881    char dur[PATH_MAX];
04882    char tmp[80] = "/tmp/astmail-XXXXXX";
04883    char tmp2[PATH_MAX];
04884    struct ast_tm tm;
04885    FILE *p;
04886    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04887 
04888    if (!str1 || !str2) {
04889       ast_free(str1);
04890       ast_free(str2);
04891       return -1;
04892    }
04893 
04894    if (cidnum) {
04895       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04896    }
04897    if (cidname) {
04898       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04899    }
04900 
04901    if ((p = vm_mkftemp(tmp)) == NULL) {
04902       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04903       ast_free(str1);
04904       ast_free(str2);
04905       return -1;
04906    }
04907    gethostname(host, sizeof(host)-1);
04908    if (strchr(srcemail, '@')) {
04909       ast_copy_string(who, srcemail, sizeof(who));
04910    } else {
04911       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04912    }
04913    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04914    ast_strftime_locale(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04915    fprintf(p, "Date: %s\n", date);
04916 
04917    /* Reformat for custom pager format */
04918    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04919 
04920    if (!ast_strlen_zero(pagerfromstring)) {
04921       struct ast_channel *ast;
04922       if ((ast = ast_dummy_channel_alloc())) {
04923          char *ptr;
04924          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04925          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04926 
04927          if (check_mime(ast_str_buffer(str1))) {
04928             int first_line = 1;
04929             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04930             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04931                *ptr = '\0';
04932                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04933                first_line = 0;
04934                /* Substring is smaller, so this will never grow */
04935                ast_str_set(&str2, 0, "%s", ptr + 1);
04936             }
04937             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04938          } else {
04939             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04940          }
04941          ast = ast_channel_unref(ast);
04942       } else {
04943          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04944       }
04945    } else {
04946       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04947    }
04948 
04949    if (check_mime(vmu->fullname)) {
04950       int first_line = 1;
04951       char *ptr;
04952       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04953       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04954          *ptr = '\0';
04955          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04956          first_line = 0;
04957          /* Substring is smaller, so this will never grow */
04958          ast_str_set(&str2, 0, "%s", ptr + 1);
04959       }
04960       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04961    } else {
04962       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04963    }
04964 
04965    if (!ast_strlen_zero(pagersubject)) {
04966       struct ast_channel *ast;
04967       if ((ast = ast_dummy_channel_alloc())) {
04968          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04969          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04970          if (check_mime(ast_str_buffer(str1))) {
04971             int first_line = 1;
04972             char *ptr;
04973             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04974             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04975                *ptr = '\0';
04976                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04977                first_line = 0;
04978                /* Substring is smaller, so this will never grow */
04979                ast_str_set(&str2, 0, "%s", ptr + 1);
04980             }
04981             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04982          } else {
04983             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04984          }
04985          ast = ast_channel_unref(ast);
04986       } else {
04987          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04988       }
04989    } else {
04990       if (ast_strlen_zero(flag)) {
04991          fprintf(p, "Subject: New VM\n\n");
04992       } else {
04993          fprintf(p, "Subject: New %s VM\n\n", flag);
04994       }
04995    }
04996 
04997    if (pagerbody) {
04998       struct ast_channel *ast;
04999       if ((ast = ast_dummy_channel_alloc())) {
05000          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05001          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05002          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05003          ast = ast_channel_unref(ast);
05004       } else {
05005          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05006       }
05007    } else {
05008       fprintf(p, "New %s long %s msg in box %s\n"
05009             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05010    }
05011 
05012    fclose(p);
05013    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05014    ast_safe_system(tmp2);
05015    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05016    ast_free(str1);
05017    ast_free(str2);
05018    return 0;
05019 }
05020 
05021 /*!
05022  * \brief Gets the current date and time, as formatted string.
05023  * \param s The buffer to hold the output formatted date.
05024  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05025  * 
05026  * The date format string used is "%a %b %e %r UTC %Y".
05027  * 
05028  * \return zero on success, -1 on error.
05029  */
05030 static int get_date(char *s, int len)
05031 {
05032    struct ast_tm tm;
05033    struct timeval t = ast_tvnow();
05034    
05035    ast_localtime(&t, &tm, "UTC");
05036 
05037    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05038 }
05039 
05040 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05041 {
05042    int res;
05043    char fn[PATH_MAX];
05044    char dest[PATH_MAX];
05045 
05046    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05047 
05048    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05049       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05050       return -1;
05051    }
05052 
05053    RETRIEVE(fn, -1, ext, context);
05054    if (ast_fileexists(fn, NULL, NULL) > 0) {
05055       res = ast_stream_and_wait(chan, fn, ecodes);
05056       if (res) {
05057          DISPOSE(fn, -1);
05058          return res;
05059       }
05060    } else {
05061       /* Dispose just in case */
05062       DISPOSE(fn, -1);
05063       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05064       if (res)
05065          return res;
05066       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05067       if (res)
05068          return res;
05069    }
05070    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05071    return res;
05072 }
05073 
05074 static void free_zone(struct vm_zone *z)
05075 {
05076    ast_free(z);
05077 }
05078 
05079 #ifdef ODBC_STORAGE
05080 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05081 {
05082    int x = -1;
05083    int res;
05084    SQLHSTMT stmt = NULL;
05085    char sql[PATH_MAX];
05086    char rowdata[20];
05087    char tmp[PATH_MAX] = "";
05088    struct odbc_obj *obj = NULL;
05089    char *context;
05090    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05091 
05092    if (newmsgs)
05093       *newmsgs = 0;
05094    if (oldmsgs)
05095       *oldmsgs = 0;
05096    if (urgentmsgs)
05097       *urgentmsgs = 0;
05098 
05099    /* If no mailbox, return immediately */
05100    if (ast_strlen_zero(mailbox))
05101       return 0;
05102 
05103    ast_copy_string(tmp, mailbox, sizeof(tmp));
05104 
05105    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05106       int u, n, o;
05107       char *next, *remaining = tmp;
05108       while ((next = strsep(&remaining, " ,"))) {
05109          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05110             return -1;
05111          }
05112          if (urgentmsgs) {
05113             *urgentmsgs += u;
05114          }
05115          if (newmsgs) {
05116             *newmsgs += n;
05117          }
05118          if (oldmsgs) {
05119             *oldmsgs += o;
05120          }
05121       }
05122       return 0;
05123    }
05124 
05125    context = strchr(tmp, '@');
05126    if (context) {
05127       *context = '\0';
05128       context++;
05129    } else
05130       context = "default";
05131 
05132    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05133       do {
05134          if (newmsgs) {
05135             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05136             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05137                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05138                break;
05139             }
05140             res = SQLFetch(stmt);
05141             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05142                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05143                break;
05144             }
05145             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05146             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05147                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05148                break;
05149             }
05150             *newmsgs = atoi(rowdata);
05151             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05152          }
05153 
05154          if (oldmsgs) {
05155             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05156             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05157                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05158                break;
05159             }
05160             res = SQLFetch(stmt);
05161             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05162                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05163                break;
05164             }
05165             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05166             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05167                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05168                break;
05169             }
05170             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05171             *oldmsgs = atoi(rowdata);
05172          }
05173 
05174          if (urgentmsgs) {
05175             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05176             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05177                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05178                break;
05179             }
05180             res = SQLFetch(stmt);
05181             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05182                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05183                break;
05184             }
05185             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05186             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05187                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05188                break;
05189             }
05190             *urgentmsgs = atoi(rowdata);
05191          }
05192 
05193          x = 0;
05194       } while (0);
05195    } else {
05196       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05197    }
05198 
05199    if (stmt) {
05200       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05201    }
05202    if (obj) {
05203       ast_odbc_release_obj(obj);
05204    }
05205    return x;
05206 }
05207 
05208 /*!
05209  * \brief Gets the number of messages that exist in a mailbox folder.
05210  * \param context
05211  * \param mailbox
05212  * \param folder
05213  * 
05214  * This method is used when ODBC backend is used.
05215  * \return The number of messages in this mailbox folder (zero or more).
05216  */
05217 static int messagecount(const char *context, const char *mailbox, const char *folder)
05218 {
05219    struct odbc_obj *obj = NULL;
05220    int nummsgs = 0;
05221    int res;
05222    SQLHSTMT stmt = NULL;
05223    char sql[PATH_MAX];
05224    char rowdata[20];
05225    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05226    if (!folder)
05227       folder = "INBOX";
05228    /* If no mailbox, return immediately */
05229    if (ast_strlen_zero(mailbox))
05230       return 0;
05231 
05232    obj = ast_odbc_request_obj(odbc_database, 0);
05233    if (obj) {
05234       if (!strcmp(folder, "INBOX")) {
05235          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);
05236       } else {
05237          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05238       }
05239       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05240       if (!stmt) {
05241          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05242          goto yuck;
05243       }
05244       res = SQLFetch(stmt);
05245       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05246          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05247          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05248          goto yuck;
05249       }
05250       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05251       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05252          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05253          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05254          goto yuck;
05255       }
05256       nummsgs = atoi(rowdata);
05257       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05258    } else
05259       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05260 
05261 yuck:
05262    if (obj)
05263       ast_odbc_release_obj(obj);
05264    return nummsgs;
05265 }
05266 
05267 /** 
05268  * \brief Determines if the given folder has messages.
05269  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05270  * 
05271  * This function is used when the mailbox is stored in an ODBC back end.
05272  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05273  * \return 1 if the folder has one or more messages. zero otherwise.
05274  */
05275 static int has_voicemail(const char *mailbox, const char *folder)
05276 {
05277    char tmp[256], *tmp2 = tmp, *box, *context;
05278    ast_copy_string(tmp, mailbox, sizeof(tmp));
05279    while ((context = box = strsep(&tmp2, ",&"))) {
05280       strsep(&context, "@");
05281       if (ast_strlen_zero(context))
05282          context = "default";
05283       if (messagecount(context, box, folder))
05284          return 1;
05285    }
05286    return 0;
05287 }
05288 #endif
05289 #ifndef IMAP_STORAGE
05290 /*! 
05291  * \brief Copies a message from one mailbox to another.
05292  * \param chan
05293  * \param vmu
05294  * \param imbox
05295  * \param msgnum
05296  * \param duration
05297  * \param recip
05298  * \param fmt
05299  * \param dir
05300  * \param flag
05301  *
05302  * This is only used by file storage based mailboxes.
05303  *
05304  * \return zero on success, -1 on error.
05305  */
05306 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)
05307 {
05308    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05309    const char *frombox = mbox(vmu, imbox);
05310    const char *userfolder;
05311    int recipmsgnum;
05312    int res = 0;
05313 
05314    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05315 
05316    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05317       userfolder = "Urgent";
05318    } else {
05319       userfolder = "INBOX";
05320    }
05321 
05322    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05323 
05324    if (!dir)
05325       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05326    else
05327       ast_copy_string(fromdir, dir, sizeof(fromdir));
05328 
05329    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05330    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05331 
05332    if (vm_lock_path(todir))
05333       return ERROR_LOCK_PATH;
05334 
05335    recipmsgnum = last_message_index(recip, todir) + 1;
05336    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05337       make_file(topath, sizeof(topath), todir, recipmsgnum);
05338 #ifndef ODBC_STORAGE
05339       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05340          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05341       } else {
05342 #endif
05343          /* If we are prepending a message for ODBC, then the message already
05344           * exists in the database, but we want to force copying from the
05345           * filesystem (since only the FS contains the prepend). */
05346          copy_plain_file(frompath, topath);
05347          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05348          vm_delete(topath);
05349 #ifndef ODBC_STORAGE
05350       }
05351 #endif
05352    } else {
05353       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05354       res = -1;
05355    }
05356    ast_unlock_path(todir);
05357    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05358       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05359       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05360       flag);
05361    
05362    return res;
05363 }
05364 #endif
05365 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05366 
05367 static int messagecount(const char *context, const char *mailbox, const char *folder)
05368 {
05369    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05370 }
05371 
05372 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05373 {
05374    DIR *dir;
05375    struct dirent *de;
05376    char fn[256];
05377    int ret = 0;
05378 
05379    /* If no mailbox, return immediately */
05380    if (ast_strlen_zero(mailbox))
05381       return 0;
05382 
05383    if (ast_strlen_zero(folder))
05384       folder = "INBOX";
05385    if (ast_strlen_zero(context))
05386       context = "default";
05387 
05388    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05389 
05390    if (!(dir = opendir(fn)))
05391       return 0;
05392 
05393    while ((de = readdir(dir))) {
05394       if (!strncasecmp(de->d_name, "msg", 3)) {
05395          if (shortcircuit) {
05396             ret = 1;
05397             break;
05398          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05399             ret++;
05400          }
05401       }
05402    }
05403 
05404    closedir(dir);
05405 
05406    return ret;
05407 }
05408 
05409 /** 
05410  * \brief Determines if the given folder has messages.
05411  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05412  * \param folder the folder to look in
05413  *
05414  * This function is used when the mailbox is stored in a filesystem back end.
05415  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05416  * \return 1 if the folder has one or more messages. zero otherwise.
05417  */
05418 static int has_voicemail(const char *mailbox, const char *folder)
05419 {
05420    char tmp[256], *tmp2 = tmp, *box, *context;
05421    ast_copy_string(tmp, mailbox, sizeof(tmp));
05422    if (ast_strlen_zero(folder)) {
05423       folder = "INBOX";
05424    }
05425    while ((box = strsep(&tmp2, ",&"))) {
05426       if ((context = strchr(box, '@')))
05427          *context++ = '\0';
05428       else
05429          context = "default";
05430       if (__has_voicemail(context, box, folder, 1))
05431          return 1;
05432       /* If we are checking INBOX, we should check Urgent as well */
05433       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05434          return 1;
05435       }
05436    }
05437    return 0;
05438 }
05439 
05440 
05441 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05442 {
05443    char tmp[256];
05444    char *context;
05445 
05446    /* If no mailbox, return immediately */
05447    if (ast_strlen_zero(mailbox))
05448       return 0;
05449 
05450    if (newmsgs)
05451       *newmsgs = 0;
05452    if (oldmsgs)
05453       *oldmsgs = 0;
05454    if (urgentmsgs)
05455       *urgentmsgs = 0;
05456 
05457    if (strchr(mailbox, ',')) {
05458       int tmpnew, tmpold, tmpurgent;
05459       char *mb, *cur;
05460 
05461       ast_copy_string(tmp, mailbox, sizeof(tmp));
05462       mb = tmp;
05463       while ((cur = strsep(&mb, ", "))) {
05464          if (!ast_strlen_zero(cur)) {
05465             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05466                return -1;
05467             else {
05468                if (newmsgs)
05469                   *newmsgs += tmpnew; 
05470                if (oldmsgs)
05471                   *oldmsgs += tmpold;
05472                if (urgentmsgs)
05473                   *urgentmsgs += tmpurgent;
05474             }
05475          }
05476       }
05477       return 0;
05478    }
05479 
05480    ast_copy_string(tmp, mailbox, sizeof(tmp));
05481    
05482    if ((context = strchr(tmp, '@')))
05483       *context++ = '\0';
05484    else
05485       context = "default";
05486 
05487    if (newmsgs)
05488       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05489    if (oldmsgs)
05490       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05491    if (urgentmsgs)
05492       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05493 
05494    return 0;
05495 }
05496 
05497 #endif
05498 
05499 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05500 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05501 {
05502    int urgentmsgs = 0;
05503    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05504    if (newmsgs) {
05505       *newmsgs += urgentmsgs;
05506    }
05507    return res;
05508 }
05509 
05510 static void run_externnotify(char *context, char *extension, const char *flag)
05511 {
05512    char arguments[255];
05513    char ext_context[256] = "";
05514    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05515    struct ast_smdi_mwi_message *mwi_msg;
05516 
05517    if (!ast_strlen_zero(context))
05518       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05519    else
05520       ast_copy_string(ext_context, extension, sizeof(ext_context));
05521 
05522    if (smdi_iface) {
05523       if (ast_app_has_voicemail(ext_context, NULL)) 
05524          ast_smdi_mwi_set(smdi_iface, extension);
05525       else
05526          ast_smdi_mwi_unset(smdi_iface, extension);
05527 
05528       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05529          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05530          if (!strncmp(mwi_msg->cause, "INV", 3))
05531             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05532          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05533             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05534          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05535          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05536       } else {
05537          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05538       }
05539    }
05540 
05541    if (!ast_strlen_zero(externnotify)) {
05542       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05543          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05544       } else {
05545          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05546          ast_debug(1, "Executing %s\n", arguments);
05547          ast_safe_system(arguments);
05548       }
05549    }
05550 }
05551 
05552 /*!
05553  * \brief Variables used for saving a voicemail.
05554  *
05555  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05556  */
05557 struct leave_vm_options {
05558    unsigned int flags;
05559    signed char record_gain;
05560    char *exitcontext;
05561 };
05562 
05563 /*!
05564  * \brief Prompts the user and records a voicemail to a mailbox.
05565  * \param chan
05566  * \param ext
05567  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05568  * 
05569  * 
05570  * 
05571  * \return zero on success, -1 on error.
05572  */
05573 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05574 {
05575 #ifdef IMAP_STORAGE
05576    int newmsgs, oldmsgs;
05577 #else
05578    char urgdir[PATH_MAX];
05579 #endif
05580    char txtfile[PATH_MAX];
05581    char tmptxtfile[PATH_MAX];
05582    struct vm_state *vms = NULL;
05583    char callerid[256];
05584    FILE *txt;
05585    char date[256];
05586    int txtdes;
05587    int res = 0;
05588    int msgnum;
05589    int duration = 0;
05590    int sound_duration = 0;
05591    int ausemacro = 0;
05592    int ousemacro = 0;
05593    int ouseexten = 0;
05594    char tmpdur[16];
05595    char priority[16];
05596    char origtime[16];
05597    char dir[PATH_MAX];
05598    char tmpdir[PATH_MAX];
05599    char fn[PATH_MAX];
05600    char prefile[PATH_MAX] = "";
05601    char tempfile[PATH_MAX] = "";
05602    char ext_context[256] = "";
05603    char fmt[80];
05604    char *context;
05605    char ecodes[17] = "#";
05606    struct ast_str *tmp = ast_str_create(16);
05607    char *tmpptr;
05608    struct ast_vm_user *vmu;
05609    struct ast_vm_user svm;
05610    const char *category = NULL;
05611    const char *code;
05612    const char *alldtmf = "0123456789ABCD*#";
05613    char flag[80];
05614 
05615    if (!tmp) {
05616       return -1;
05617    }
05618 
05619    ast_str_set(&tmp, 0, "%s", ext);
05620    ext = ast_str_buffer(tmp);
05621    if ((context = strchr(ext, '@'))) {
05622       *context++ = '\0';
05623       tmpptr = strchr(context, '&');
05624    } else {
05625       tmpptr = strchr(ext, '&');
05626    }
05627 
05628    if (tmpptr)
05629       *tmpptr++ = '\0';
05630 
05631    ast_channel_lock(chan);
05632    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05633       category = ast_strdupa(category);
05634    }
05635    ast_channel_unlock(chan);
05636 
05637    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05638       ast_copy_string(flag, "Urgent", sizeof(flag));
05639    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05640       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05641    } else {
05642       flag[0] = '\0';
05643    }
05644 
05645    ast_debug(3, "Before find_user\n");
05646    if (!(vmu = find_user(&svm, context, ext))) {
05647       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05648       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05649       ast_free(tmp);
05650       return res;
05651    }
05652    /* Setup pre-file if appropriate */
05653    if (strcmp(vmu->context, "default"))
05654       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05655    else
05656       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05657 
05658    /* Set the path to the prefile. Will be one of 
05659       VM_SPOOL_DIRcontext/ext/busy
05660       VM_SPOOL_DIRcontext/ext/unavail
05661       Depending on the flag set in options.
05662    */
05663    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05664       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05665    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05666       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05667    }
05668    /* Set the path to the tmpfile as
05669       VM_SPOOL_DIR/context/ext/temp
05670       and attempt to create the folder structure.
05671    */
05672    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05673    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05674       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05675       ast_free(tmp);
05676       return -1;
05677    }
05678    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05679    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05680       ast_copy_string(prefile, tempfile, sizeof(prefile));
05681 
05682    DISPOSE(tempfile, -1);
05683    /* It's easier just to try to make it than to check for its existence */
05684 #ifndef IMAP_STORAGE
05685    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05686 #else
05687    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05688    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05689       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05690    }
05691 #endif
05692 
05693    /* Check current or macro-calling context for special extensions */
05694    if (ast_test_flag(vmu, VM_OPERATOR)) {
05695       if (!ast_strlen_zero(vmu->exit)) {
05696          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05697             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05698             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05699             ouseexten = 1;
05700          }
05701       } else if (ast_exists_extension(chan, chan->context, "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       } else if (!ast_strlen_zero(chan->macrocontext)
05706          && ast_exists_extension(chan, chan->macrocontext, "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          ousemacro = 1;
05710       }
05711    }
05712 
05713    if (!ast_strlen_zero(vmu->exit)) {
05714       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05715          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05716          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05717       }
05718    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05719       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05720       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05721    } else if (!ast_strlen_zero(chan->macrocontext)
05722       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05723          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05724       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05725       ausemacro = 1;
05726    }
05727 
05728    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05729       for (code = alldtmf; *code; code++) {
05730          char e[2] = "";
05731          e[0] = *code;
05732          if (strchr(ecodes, e[0]) == NULL
05733             && ast_canmatch_extension(chan, chan->context, e, 1,
05734                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05735             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05736          }
05737       }
05738    }
05739 
05740    /* Play the beginning intro if desired */
05741    if (!ast_strlen_zero(prefile)) {
05742 #ifdef ODBC_STORAGE
05743       int success = 
05744 #endif
05745          RETRIEVE(prefile, -1, ext, context);
05746       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05747          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05748             res = ast_waitstream(chan, ecodes);
05749 #ifdef ODBC_STORAGE
05750          if (success == -1) {
05751             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05752             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05753             store_file(prefile, vmu->mailbox, vmu->context, -1);
05754          }
05755 #endif
05756       } else {
05757          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05758          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05759       }
05760       DISPOSE(prefile, -1);
05761       if (res < 0) {
05762          ast_debug(1, "Hang up during prefile playback\n");
05763          free_user(vmu);
05764          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05765          ast_free(tmp);
05766          return -1;
05767       }
05768    }
05769    if (res == '#') {
05770       /* On a '#' we skip the instructions */
05771       ast_set_flag(options, OPT_SILENT);
05772       res = 0;
05773    }
05774    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05775    if (vmu->maxmsg == 0) {
05776       if (option_debug > 2)
05777          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05778       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05779       goto leave_vm_out;
05780    }
05781    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05782       res = ast_stream_and_wait(chan, INTRO, ecodes);
05783       if (res == '#') {
05784          ast_set_flag(options, OPT_SILENT);
05785          res = 0;
05786       }
05787    }
05788    if (res > 0)
05789       ast_stopstream(chan);
05790    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05791     other than the operator -- an automated attendant or mailbox login for example */
05792    if (res == '*') {
05793       chan->exten[0] = 'a';
05794       chan->exten[1] = '\0';
05795       if (!ast_strlen_zero(vmu->exit)) {
05796          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05797       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05798          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05799       }
05800       chan->priority = 0;
05801       free_user(vmu);
05802       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05803       ast_free(tmp);
05804       return 0;
05805    }
05806 
05807    /* Check for a '0' here */
05808    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05809    transfer:
05810       if (ouseexten || ousemacro) {
05811          chan->exten[0] = 'o';
05812          chan->exten[1] = '\0';
05813          if (!ast_strlen_zero(vmu->exit)) {
05814             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05815          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05816             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05817          }
05818          ast_play_and_wait(chan, "transfer");
05819          chan->priority = 0;
05820          free_user(vmu);
05821          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05822       }
05823       ast_free(tmp);
05824       return OPERATOR_EXIT;
05825    }
05826 
05827    /* Allow all other digits to exit Voicemail and return to the dialplan */
05828    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05829       if (!ast_strlen_zero(options->exitcontext))
05830          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05831       free_user(vmu);
05832       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05833       ast_free(tmp);
05834       return res;
05835    }
05836 
05837    if (res < 0) {
05838       free_user(vmu);
05839       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05840       ast_free(tmp);
05841       return -1;
05842    }
05843    /* The meat of recording the message...  All the announcements and beeps have been played*/
05844    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05845    if (!ast_strlen_zero(fmt)) {
05846       msgnum = 0;
05847 
05848 #ifdef IMAP_STORAGE
05849       /* Is ext a mailbox? */
05850       /* must open stream for this user to get info! */
05851       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05852       if (res < 0) {
05853          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05854          ast_free(tmp);
05855          return -1;
05856       }
05857       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05858       /* It is possible under certain circumstances that inboxcount did not
05859        * create a vm_state when it was needed. This is a catchall which will
05860        * rarely be used.
05861        */
05862          if (!(vms = create_vm_state_from_user(vmu))) {
05863             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05864             ast_free(tmp);
05865             return -1;
05866          }
05867       }
05868       vms->newmessages++;
05869       
05870       /* here is a big difference! We add one to it later */
05871       msgnum = newmsgs + oldmsgs;
05872       ast_debug(3, "Messagecount set to %d\n", msgnum);
05873       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05874       /* set variable for compatibility */
05875       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05876 
05877       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05878          goto leave_vm_out;
05879       }
05880 #else
05881       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05882          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05883          if (!res)
05884             res = ast_waitstream(chan, "");
05885          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05886          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05887          inprocess_count(vmu->mailbox, vmu->context, -1);
05888          goto leave_vm_out;
05889       }
05890 
05891 #endif
05892       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05893       txtdes = mkstemp(tmptxtfile);
05894       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05895       if (txtdes < 0) {
05896          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05897          if (!res)
05898             res = ast_waitstream(chan, "");
05899          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05900          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05901          inprocess_count(vmu->mailbox, vmu->context, -1);
05902          goto leave_vm_out;
05903       }
05904 
05905       /* Now play the beep once we have the message number for our next message. */
05906       if (res >= 0) {
05907          /* Unless we're *really* silent, try to send the beep */
05908          res = ast_stream_and_wait(chan, "beep", "");
05909       }
05910             
05911       /* Store information in real-time storage */
05912       if (ast_check_realtime("voicemail_data")) {
05913          snprintf(priority, sizeof(priority), "%d", chan->priority);
05914          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05915          get_date(date, sizeof(date));
05916          ast_callerid_merge(callerid, sizeof(callerid),
05917             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05918             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05919             "Unknown");
05920          ast_store_realtime("voicemail_data",
05921             "origmailbox", ext,
05922             "context", chan->context,
05923             "macrocontext", chan->macrocontext,
05924             "exten", chan->exten,
05925             "priority", priority,
05926             "callerchan", chan->name,
05927             "callerid", callerid,
05928             "origdate", date,
05929             "origtime", origtime,
05930             "category", S_OR(category, ""),
05931             "filename", tmptxtfile,
05932             SENTINEL);
05933       }
05934 
05935       /* Store information */
05936       txt = fdopen(txtdes, "w+");
05937       if (txt) {
05938          get_date(date, sizeof(date));
05939          ast_callerid_merge(callerid, sizeof(callerid),
05940             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05941             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05942             "Unknown");
05943          fprintf(txt, 
05944             ";\n"
05945             "; Message Information file\n"
05946             ";\n"
05947             "[message]\n"
05948             "origmailbox=%s\n"
05949             "context=%s\n"
05950             "macrocontext=%s\n"
05951             "exten=%s\n"
05952             "rdnis=%s\n"
05953             "priority=%d\n"
05954             "callerchan=%s\n"
05955             "callerid=%s\n"
05956             "origdate=%s\n"
05957             "origtime=%ld\n"
05958             "category=%s\n",
05959             ext,
05960             chan->context,
05961             chan->macrocontext, 
05962             chan->exten,
05963             S_COR(chan->redirecting.from.number.valid,
05964                chan->redirecting.from.number.str, "unknown"),
05965             chan->priority,
05966             chan->name,
05967             callerid,
05968             date, (long) time(NULL),
05969             category ? category : "");
05970       } else {
05971          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05972          inprocess_count(vmu->mailbox, vmu->context, -1);
05973          if (ast_check_realtime("voicemail_data")) {
05974             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05975          }
05976          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05977          goto leave_vm_out;
05978       }
05979       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
05980 
05981       if (txt) {
05982          fprintf(txt, "flag=%s\n", flag);
05983          if (sound_duration < vmu->minsecs) {
05984             fclose(txt);
05985             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
05986             ast_filedelete(tmptxtfile, NULL);
05987             unlink(tmptxtfile);
05988             if (ast_check_realtime("voicemail_data")) {
05989                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05990             }
05991             inprocess_count(vmu->mailbox, vmu->context, -1);
05992          } else {
05993             fprintf(txt, "duration=%d\n", duration);
05994             fclose(txt);
05995             if (vm_lock_path(dir)) {
05996                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05997                /* Delete files */
05998                ast_filedelete(tmptxtfile, NULL);
05999                unlink(tmptxtfile);
06000                inprocess_count(vmu->mailbox, vmu->context, -1);
06001             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06002                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06003                unlink(tmptxtfile);
06004                ast_unlock_path(dir);
06005                inprocess_count(vmu->mailbox, vmu->context, -1);
06006                if (ast_check_realtime("voicemail_data")) {
06007                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06008                }
06009             } else {
06010 #ifndef IMAP_STORAGE
06011                msgnum = last_message_index(vmu, dir) + 1;
06012 #endif
06013                make_file(fn, sizeof(fn), dir, msgnum);
06014 
06015                /* assign a variable with the name of the voicemail file */ 
06016 #ifndef IMAP_STORAGE
06017                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06018 #else
06019                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06020 #endif
06021 
06022                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06023                ast_filerename(tmptxtfile, fn, NULL);
06024                rename(tmptxtfile, txtfile);
06025                inprocess_count(vmu->mailbox, vmu->context, -1);
06026 
06027                /* Properly set permissions on voicemail text descriptor file.
06028                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06029                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06030                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06031 
06032                ast_unlock_path(dir);
06033                if (ast_check_realtime("voicemail_data")) {
06034                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06035                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06036                }
06037                /* We must store the file first, before copying the message, because
06038                 * ODBC storage does the entire copy with SQL.
06039                 */
06040                if (ast_fileexists(fn, NULL, NULL) > 0) {
06041                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06042                }
06043 
06044                /* Are there to be more recipients of this message? */
06045                while (tmpptr) {
06046                   struct ast_vm_user recipu, *recip;
06047                   char *exten, *cntx;
06048 
06049                   exten = strsep(&tmpptr, "&");
06050                   cntx = strchr(exten, '@');
06051                   if (cntx) {
06052                      *cntx = '\0';
06053                      cntx++;
06054                   }
06055                   if ((recip = find_user(&recipu, cntx, exten))) {
06056                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06057                      free_user(recip);
06058                   }
06059                }
06060 #ifndef IMAP_STORAGE
06061                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06062                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06063                   char sfn[PATH_MAX];
06064                   char dfn[PATH_MAX];
06065                   int x;
06066                   /* It's easier just to try to make it than to check for its existence */
06067                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06068                   x = last_message_index(vmu, urgdir) + 1;
06069                   make_file(sfn, sizeof(sfn), dir, msgnum);
06070                   make_file(dfn, sizeof(dfn), urgdir, x);
06071                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06072                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06073                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06074                   ast_copy_string(fn, dfn, sizeof(fn));
06075                   msgnum = x;
06076                }
06077 #endif
06078                /* Notification needs to happen after the copy, though. */
06079                if (ast_fileexists(fn, NULL, NULL)) {
06080 #ifdef IMAP_STORAGE
06081                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06082                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06083                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06084                      flag);
06085 #else
06086                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06087                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06088                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06089                      flag);
06090 #endif
06091                }
06092 
06093                /* Disposal needs to happen after the optional move and copy */
06094                if (ast_fileexists(fn, NULL, NULL)) {
06095                   DISPOSE(dir, msgnum);
06096                }
06097             }
06098          }
06099       } else {
06100          inprocess_count(vmu->mailbox, vmu->context, -1);
06101       }
06102       if (res == '0') {
06103          goto transfer;
06104       } else if (res > 0 && res != 't')
06105          res = 0;
06106 
06107       if (sound_duration < vmu->minsecs)
06108          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06109          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06110       else
06111          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06112    } else
06113       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06114 leave_vm_out:
06115    free_user(vmu);
06116 
06117 #ifdef IMAP_STORAGE
06118    /* expunge message - use UID Expunge if supported on IMAP server*/
06119    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06120    if (expungeonhangup == 1) {
06121       ast_mutex_lock(&vms->lock);
06122 #ifdef HAVE_IMAP_TK2006
06123       if (LEVELUIDPLUS (vms->mailstream)) {
06124          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06125       } else 
06126 #endif
06127          mail_expunge(vms->mailstream);
06128       ast_mutex_unlock(&vms->lock);
06129    }
06130 #endif
06131 
06132    ast_free(tmp);
06133    return res;
06134 }
06135 
06136 #if !defined(IMAP_STORAGE)
06137 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06138 {
06139    /* we know the actual number of messages, so stop process when number is hit */
06140 
06141    int x, dest;
06142    char sfn[PATH_MAX];
06143    char dfn[PATH_MAX];
06144 
06145    if (vm_lock_path(dir)) {
06146       return ERROR_LOCK_PATH;
06147    }
06148 
06149    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06150       make_file(sfn, sizeof(sfn), dir, x);
06151       if (EXISTS(dir, x, sfn, NULL)) {
06152 
06153          if (x != dest) {
06154             make_file(dfn, sizeof(dfn), dir, dest);
06155             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06156          }
06157 
06158          dest++;
06159       }
06160    }
06161    ast_unlock_path(dir);
06162 
06163    return dest;
06164 }
06165 #endif
06166 
06167 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06168 {
06169    int d;
06170    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06171    return d;
06172 }
06173 
06174 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06175 {
06176 #ifdef IMAP_STORAGE
06177    /* we must use mbox(x) folder names, and copy the message there */
06178    /* simple. huh? */
06179    char sequence[10];
06180    char mailbox[256];
06181    int res;
06182 
06183    /* get the real IMAP message number for this message */
06184    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06185    
06186    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06187    ast_mutex_lock(&vms->lock);
06188    /* if save to Old folder, put in INBOX as read */
06189    if (box == OLD_FOLDER) {
06190       mail_setflag(vms->mailstream, sequence, "\\Seen");
06191       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06192    } else if (box == NEW_FOLDER) {
06193       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06194       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06195    }
06196    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06197       ast_mutex_unlock(&vms->lock);
06198       return 0;
06199    }
06200    /* Create the folder if it don't exist */
06201    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06202    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06203    if (mail_create(vms->mailstream, mailbox) == NIL) 
06204       ast_debug(5, "Folder exists.\n");
06205    else
06206       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06207    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06208    ast_mutex_unlock(&vms->lock);
06209    return res;
06210 #else
06211    char *dir = vms->curdir;
06212    char *username = vms->username;
06213    char *context = vmu->context;
06214    char sfn[PATH_MAX];
06215    char dfn[PATH_MAX];
06216    char ddir[PATH_MAX];
06217    const char *dbox = mbox(vmu, box);
06218    int x, i;
06219    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06220 
06221    if (vm_lock_path(ddir))
06222       return ERROR_LOCK_PATH;
06223 
06224    x = last_message_index(vmu, ddir) + 1;
06225 
06226    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06227       x--;
06228       for (i = 1; i <= x; i++) {
06229          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06230          make_file(sfn, sizeof(sfn), ddir, i);
06231          make_file(dfn, sizeof(dfn), ddir, i - 1);
06232          if (EXISTS(ddir, i, sfn, NULL)) {
06233             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06234          } else
06235             break;
06236       }
06237    } else {
06238       if (x >= vmu->maxmsg) {
06239          ast_unlock_path(ddir);
06240          return -1;
06241       }
06242    }
06243    make_file(sfn, sizeof(sfn), dir, msg);
06244    make_file(dfn, sizeof(dfn), ddir, x);
06245    if (strcmp(sfn, dfn)) {
06246       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06247    }
06248    ast_unlock_path(ddir);
06249 #endif
06250    return 0;
06251 }
06252 
06253 static int adsi_logo(unsigned char *buf)
06254 {
06255    int bytes = 0;
06256    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06257    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06258    return bytes;
06259 }
06260 
06261 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06262 {
06263    unsigned char buf[256];
06264    int bytes = 0;
06265    int x;
06266    char num[5];
06267 
06268    *useadsi = 0;
06269    bytes += ast_adsi_data_mode(buf + bytes);
06270    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06271 
06272    bytes = 0;
06273    bytes += adsi_logo(buf);
06274    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06275 #ifdef DISPLAY
06276    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06277 #endif
06278    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06279    bytes += ast_adsi_data_mode(buf + bytes);
06280    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06281 
06282    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06283       bytes = 0;
06284       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06285       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06286       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06287       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06288       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06289       return 0;
06290    }
06291 
06292 #ifdef DISPLAY
06293    /* Add a dot */
06294    bytes = 0;
06295    bytes += ast_adsi_logo(buf);
06296    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06297    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06298    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06299    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06300 #endif
06301    bytes = 0;
06302    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06303    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06304    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06305    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06306    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06307    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06308    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06309 
06310 #ifdef DISPLAY
06311    /* Add another dot */
06312    bytes = 0;
06313    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06314    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06315 
06316    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06317    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06318 #endif
06319 
06320    bytes = 0;
06321    /* These buttons we load but don't use yet */
06322    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06323    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06324    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06325    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06326    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06327    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06328    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06329 
06330 #ifdef DISPLAY
06331    /* Add another dot */
06332    bytes = 0;
06333    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06334    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06335    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06336 #endif
06337 
06338    bytes = 0;
06339    for (x = 0; x < 5; x++) {
06340       snprintf(num, sizeof(num), "%d", x);
06341       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06342    }
06343    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06344    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06345 
06346 #ifdef DISPLAY
06347    /* Add another dot */
06348    bytes = 0;
06349    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06350    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06351    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06352 #endif
06353 
06354    if (ast_adsi_end_download(chan)) {
06355       bytes = 0;
06356       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06357       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06358       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06359       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06360       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06361       return 0;
06362    }
06363    bytes = 0;
06364    bytes += ast_adsi_download_disconnect(buf + bytes);
06365    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06366    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06367 
06368    ast_debug(1, "Done downloading scripts...\n");
06369 
06370 #ifdef DISPLAY
06371    /* Add last dot */
06372    bytes = 0;
06373    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06374    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06375 #endif
06376    ast_debug(1, "Restarting session...\n");
06377 
06378    bytes = 0;
06379    /* Load the session now */
06380    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06381       *useadsi = 1;
06382       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06383    } else
06384       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06385 
06386    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06387    return 0;
06388 }
06389 
06390 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06391 {
06392    int x;
06393    if (!ast_adsi_available(chan))
06394       return;
06395    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06396    if (x < 0)
06397       return;
06398    if (!x) {
06399       if (adsi_load_vmail(chan, useadsi)) {
06400          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06401          return;
06402       }
06403    } else
06404       *useadsi = 1;
06405 }
06406 
06407 static void adsi_login(struct ast_channel *chan)
06408 {
06409    unsigned char buf[256];
06410    int bytes = 0;
06411    unsigned char keys[8];
06412    int x;
06413    if (!ast_adsi_available(chan))
06414       return;
06415 
06416    for (x = 0; x < 8; x++)
06417       keys[x] = 0;
06418    /* Set one key for next */
06419    keys[3] = ADSI_KEY_APPS + 3;
06420 
06421    bytes += adsi_logo(buf + bytes);
06422    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06423    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06424    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06425    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06426    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06427    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06428    bytes += ast_adsi_set_keys(buf + bytes, keys);
06429    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06430    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06431 }
06432 
06433 static void adsi_password(struct ast_channel *chan)
06434 {
06435    unsigned char buf[256];
06436    int bytes = 0;
06437    unsigned char keys[8];
06438    int x;
06439    if (!ast_adsi_available(chan))
06440       return;
06441 
06442    for (x = 0; x < 8; x++)
06443       keys[x] = 0;
06444    /* Set one key for next */
06445    keys[3] = ADSI_KEY_APPS + 3;
06446 
06447    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06448    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06449    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06450    bytes += ast_adsi_set_keys(buf + bytes, keys);
06451    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06452    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06453 }
06454 
06455 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06456 {
06457    unsigned char buf[256];
06458    int bytes = 0;
06459    unsigned char keys[8];
06460    int x, y;
06461 
06462    if (!ast_adsi_available(chan))
06463       return;
06464 
06465    for (x = 0; x < 5; x++) {
06466       y = ADSI_KEY_APPS + 12 + start + x;
06467       if (y > ADSI_KEY_APPS + 12 + 4)
06468          y = 0;
06469       keys[x] = ADSI_KEY_SKT | y;
06470    }
06471    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06472    keys[6] = 0;
06473    keys[7] = 0;
06474 
06475    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06476    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06477    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06478    bytes += ast_adsi_set_keys(buf + bytes, keys);
06479    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06480 
06481    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06482 }
06483 
06484 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06485 {
06486    int bytes = 0;
06487    unsigned char buf[256]; 
06488    char buf1[256], buf2[256];
06489    char fn2[PATH_MAX];
06490 
06491    char cid[256] = "";
06492    char *val;
06493    char *name, *num;
06494    char datetime[21] = "";
06495    FILE *f;
06496 
06497    unsigned char keys[8];
06498 
06499    int x;
06500 
06501    if (!ast_adsi_available(chan))
06502       return;
06503 
06504    /* Retrieve important info */
06505    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06506    f = fopen(fn2, "r");
06507    if (f) {
06508       while (!feof(f)) {   
06509          if (!fgets((char *) buf, sizeof(buf), f)) {
06510             continue;
06511          }
06512          if (!feof(f)) {
06513             char *stringp = NULL;
06514             stringp = (char *) buf;
06515             strsep(&stringp, "=");
06516             val = strsep(&stringp, "=");
06517             if (!ast_strlen_zero(val)) {
06518                if (!strcmp((char *) buf, "callerid"))
06519                   ast_copy_string(cid, val, sizeof(cid));
06520                if (!strcmp((char *) buf, "origdate"))
06521                   ast_copy_string(datetime, val, sizeof(datetime));
06522             }
06523          }
06524       }
06525       fclose(f);
06526    }
06527    /* New meaning for keys */
06528    for (x = 0; x < 5; x++)
06529       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06530    keys[6] = 0x0;
06531    keys[7] = 0x0;
06532 
06533    if (!vms->curmsg) {
06534       /* No prev key, provide "Folder" instead */
06535       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06536    }
06537    if (vms->curmsg >= vms->lastmsg) {
06538       /* If last message ... */
06539       if (vms->curmsg) {
06540          /* but not only message, provide "Folder" instead */
06541          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06542          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06543 
06544       } else {
06545          /* Otherwise if only message, leave blank */
06546          keys[3] = 1;
06547       }
06548    }
06549 
06550    if (!ast_strlen_zero(cid)) {
06551       ast_callerid_parse(cid, &name, &num);
06552       if (!name)
06553          name = num;
06554    } else
06555       name = "Unknown Caller";
06556 
06557    /* If deleted, show "undeleted" */
06558 
06559    if (vms->deleted[vms->curmsg])
06560       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06561 
06562    /* Except "Exit" */
06563    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06564    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06565       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06566    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06567 
06568    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06569    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06570    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06571    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06572    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06573    bytes += ast_adsi_set_keys(buf + bytes, keys);
06574    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06575 
06576    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06577 }
06578 
06579 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06580 {
06581    int bytes = 0;
06582    unsigned char buf[256];
06583    unsigned char keys[8];
06584 
06585    int x;
06586 
06587    if (!ast_adsi_available(chan))
06588       return;
06589 
06590    /* New meaning for keys */
06591    for (x = 0; x < 5; x++)
06592       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06593 
06594    keys[6] = 0x0;
06595    keys[7] = 0x0;
06596 
06597    if (!vms->curmsg) {
06598       /* No prev key, provide "Folder" instead */
06599       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06600    }
06601    if (vms->curmsg >= vms->lastmsg) {
06602       /* If last message ... */
06603       if (vms->curmsg) {
06604          /* but not only message, provide "Folder" instead */
06605          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06606       } else {
06607          /* Otherwise if only message, leave blank */
06608          keys[3] = 1;
06609       }
06610    }
06611 
06612    /* If deleted, show "undeleted" */
06613    if (vms->deleted[vms->curmsg]) 
06614       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06615 
06616    /* Except "Exit" */
06617    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06618    bytes += ast_adsi_set_keys(buf + bytes, keys);
06619    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06620 
06621    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06622 }
06623 
06624 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06625 {
06626    unsigned char buf[256] = "";
06627    char buf1[256] = "", buf2[256] = "";
06628    int bytes = 0;
06629    unsigned char keys[8];
06630    int x;
06631 
06632    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06633    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06634    if (!ast_adsi_available(chan))
06635       return;
06636    if (vms->newmessages) {
06637       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06638       if (vms->oldmessages) {
06639          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06640          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06641       } else {
06642          snprintf(buf2, sizeof(buf2), "%s.", newm);
06643       }
06644    } else if (vms->oldmessages) {
06645       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06646       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06647    } else {
06648       strcpy(buf1, "You have no messages.");
06649       buf2[0] = ' ';
06650       buf2[1] = '\0';
06651    }
06652    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06653    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06654    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06655 
06656    for (x = 0; x < 6; x++)
06657       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06658    keys[6] = 0;
06659    keys[7] = 0;
06660 
06661    /* Don't let them listen if there are none */
06662    if (vms->lastmsg < 0)
06663       keys[0] = 1;
06664    bytes += ast_adsi_set_keys(buf + bytes, keys);
06665 
06666    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06667 
06668    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06669 }
06670 
06671 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06672 {
06673    unsigned char buf[256] = "";
06674    char buf1[256] = "", buf2[256] = "";
06675    int bytes = 0;
06676    unsigned char keys[8];
06677    int x;
06678 
06679    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06680 
06681    if (!ast_adsi_available(chan))
06682       return;
06683 
06684    /* Original command keys */
06685    for (x = 0; x < 6; x++)
06686       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06687 
06688    keys[6] = 0;
06689    keys[7] = 0;
06690 
06691    if ((vms->lastmsg + 1) < 1)
06692       keys[0] = 0;
06693 
06694    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06695       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06696 
06697    if (vms->lastmsg + 1)
06698       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06699    else
06700       strcpy(buf2, "no messages.");
06701    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06702    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06703    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06704    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06705    bytes += ast_adsi_set_keys(buf + bytes, keys);
06706 
06707    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06708 
06709    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06710    
06711 }
06712 
06713 /*
06714 static void adsi_clear(struct ast_channel *chan)
06715 {
06716    char buf[256];
06717    int bytes=0;
06718    if (!ast_adsi_available(chan))
06719       return;
06720    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06721    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06722 
06723    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06724 }
06725 */
06726 
06727 static void adsi_goodbye(struct ast_channel *chan)
06728 {
06729    unsigned char buf[256];
06730    int bytes = 0;
06731 
06732    if (!ast_adsi_available(chan))
06733       return;
06734    bytes += adsi_logo(buf + bytes);
06735    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06736    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06737    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06738    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06739 
06740    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06741 }
06742 
06743 /*!\brief get_folder: Folder menu
06744  * Plays "press 1 for INBOX messages" etc.
06745  * Should possibly be internationalized
06746  */
06747 static int get_folder(struct ast_channel *chan, int start)
06748 {
06749    int x;
06750    int d;
06751    char fn[PATH_MAX];
06752    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06753    if (d)
06754       return d;
06755    for (x = start; x < 5; x++) { /* For all folders */
06756       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06757          return d;
06758       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06759       if (d)
06760          return d;
06761       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06762 
06763       /* The inbox folder can have its name changed under certain conditions
06764        * so this checks if the sound file exists for the inbox folder name and
06765        * if it doesn't, plays the default name instead. */
06766       if (x == 0) {
06767          if (ast_fileexists(fn, NULL, NULL)) {
06768             d = vm_play_folder_name(chan, fn);
06769          } else {
06770             ast_verb(1, "failed to find %s\n", fn);
06771             d = vm_play_folder_name(chan, "vm-INBOX");
06772          }
06773       } else {
06774          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06775          d = vm_play_folder_name(chan, fn);
06776       }
06777 
06778       if (d)
06779          return d;
06780       d = ast_waitfordigit(chan, 500);
06781       if (d)
06782          return d;
06783    }
06784 
06785    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06786    if (d)
06787       return d;
06788    d = ast_waitfordigit(chan, 4000);
06789    return d;
06790 }
06791 
06792 /*!
06793  * \brief plays a prompt and waits for a keypress.
06794  * \param chan
06795  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06796  * \param start Does not appear to be used at this time.
06797  *
06798  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06799  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06800  * prompting for the number inputs that correspond to the available folders.
06801  * 
06802  * \return zero on success, or -1 on error.
06803  */
06804 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06805 {
06806    int res = 0;
06807    int loops = 0;
06808 
06809    res = ast_play_and_wait(chan, fn);  /* Folder name */
06810    while (((res < '0') || (res > '9')) &&
06811          (res != '#') && (res >= 0) &&
06812          loops < 4) {
06813       res = get_folder(chan, 0);
06814       loops++;
06815    }
06816    if (loops == 4) { /* give up */
06817       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06818       return '#';
06819    }
06820    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06821    return res;
06822 }
06823 
06824 /*!
06825  * \brief presents the option to prepend to an existing message when forwarding it.
06826  * \param chan
06827  * \param vmu
06828  * \param curdir
06829  * \param curmsg
06830  * \param vm_fmts
06831  * \param context
06832  * \param record_gain
06833  * \param duration
06834  * \param vms
06835  * \param flag 
06836  *
06837  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06838  *
06839  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06840  * \return zero on success, -1 on error.
06841  */
06842 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06843          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06844 {
06845    int cmd = 0;
06846    int retries = 0, prepend_duration = 0, already_recorded = 0;
06847    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06848    char textfile[PATH_MAX];
06849    struct ast_config *msg_cfg;
06850    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06851 #ifndef IMAP_STORAGE
06852    signed char zero_gain = 0;
06853 #endif
06854    const char *duration_str;
06855 
06856    /* Must always populate duration correctly */
06857    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06858    strcpy(textfile, msgfile);
06859    strcpy(backup, msgfile);
06860    strcpy(backup_textfile, msgfile);
06861    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06862    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06863    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06864 
06865    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06866       *duration = atoi(duration_str);
06867    } else {
06868       *duration = 0;
06869    }
06870 
06871    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06872       if (cmd)
06873          retries = 0;
06874       switch (cmd) {
06875       case '1': 
06876 
06877 #ifdef IMAP_STORAGE
06878          /* Record new intro file */
06879          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06880          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06881          ast_play_and_wait(chan, INTRO);
06882          ast_play_and_wait(chan, "beep");
06883          play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06884          cmd = 't';
06885 #else
06886 
06887          /* prepend a message to the current message, update the metadata and return */
06888 
06889          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06890          strcpy(textfile, msgfile);
06891          strncat(textfile, ".txt", sizeof(textfile) - 1);
06892          *duration = 0;
06893 
06894          /* if we can't read the message metadata, stop now */
06895          if (!msg_cfg) {
06896             cmd = 0;
06897             break;
06898          }
06899 
06900          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06901 #ifndef IMAP_STORAGE
06902          if (already_recorded) {
06903             ast_filecopy(backup, msgfile, NULL);
06904             copy(backup_textfile, textfile);
06905          }
06906          else {
06907             ast_filecopy(msgfile, backup, NULL);
06908             copy(textfile, backup_textfile);
06909          }
06910 #endif
06911          already_recorded = 1;
06912 
06913          if (record_gain)
06914             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06915 
06916          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06917 
06918          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06919             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06920             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06921             ast_filerename(backup, msgfile, NULL);
06922          }
06923 
06924          if (record_gain)
06925             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06926 
06927          
06928          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06929             *duration = atoi(duration_str);
06930 
06931          if (prepend_duration) {
06932             struct ast_category *msg_cat;
06933             /* need enough space for a maximum-length message duration */
06934             char duration_buf[12];
06935 
06936             *duration += prepend_duration;
06937             msg_cat = ast_category_get(msg_cfg, "message");
06938             snprintf(duration_buf, 11, "%ld", *duration);
06939             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06940                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06941             }
06942          }
06943 
06944 #endif
06945          break;
06946       case '2': 
06947          /* NULL out introfile so we know there is no intro! */
06948 #ifdef IMAP_STORAGE
06949          *vms->introfn = '\0';
06950 #endif
06951          cmd = 't';
06952          break;
06953       case '*':
06954          cmd = '*';
06955          break;
06956       default: 
06957          /* If time_out and return to menu, reset already_recorded */
06958          already_recorded = 0;
06959 
06960          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06961             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06962          if (!cmd) {
06963             cmd = ast_play_and_wait(chan, "vm-starmain");
06964             /* "press star to return to the main menu" */
06965          }
06966          if (!cmd) {
06967             cmd = ast_waitfordigit(chan, 6000);
06968          }
06969          if (!cmd) {
06970             retries++;
06971          }
06972          if (retries > 3) {
06973             cmd = '*'; /* Let's cancel this beast */
06974          }
06975          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
06976       }
06977    }
06978 
06979    if (msg_cfg)
06980       ast_config_destroy(msg_cfg);
06981    if (prepend_duration)
06982       *duration = prepend_duration;
06983 
06984    if (already_recorded && cmd == -1) {
06985       /* restore original message if prepention cancelled */
06986       ast_filerename(backup, msgfile, NULL);
06987       rename(backup_textfile, textfile);
06988    }
06989 
06990    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
06991       cmd = 0;
06992    return cmd;
06993 }
06994 
06995 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06996 {
06997    struct ast_event *event;
06998    char *mailbox, *context;
06999 
07000    /* Strip off @default */
07001    context = mailbox = ast_strdupa(box);
07002    strsep(&context, "@");
07003    if (ast_strlen_zero(context))
07004       context = "default";
07005 
07006    if (!(event = ast_event_new(AST_EVENT_MWI,
07007          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07008          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07009          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07010          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07011          AST_EVENT_IE_END))) {
07012       return;
07013    }
07014 
07015    ast_event_queue_and_cache(event);
07016 }
07017 
07018 /*!
07019  * \brief Sends email notification that a user has a new voicemail waiting for them.
07020  * \param chan
07021  * \param vmu
07022  * \param vms
07023  * \param msgnum
07024  * \param duration
07025  * \param fmt
07026  * \param cidnum The Caller ID phone number value.
07027  * \param cidname The Caller ID name value.
07028  * \param flag
07029  *
07030  * \return zero on success, -1 on error.
07031  */
07032 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)
07033 {
07034    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07035    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07036    const char *category;
07037    char *myserveremail = serveremail;
07038 
07039    ast_channel_lock(chan);
07040    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07041       category = ast_strdupa(category);
07042    }
07043    ast_channel_unlock(chan);
07044 
07045 #ifndef IMAP_STORAGE
07046    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07047 #else
07048    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07049 #endif
07050    make_file(fn, sizeof(fn), todir, msgnum);
07051    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07052 
07053    if (!ast_strlen_zero(vmu->attachfmt)) {
07054       if (strstr(fmt, vmu->attachfmt))
07055          fmt = vmu->attachfmt;
07056       else
07057          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);
07058    }
07059 
07060    /* Attach only the first format */
07061    fmt = ast_strdupa(fmt);
07062    stringp = fmt;
07063    strsep(&stringp, "|");
07064 
07065    if (!ast_strlen_zero(vmu->serveremail))
07066       myserveremail = vmu->serveremail;
07067 
07068    if (!ast_strlen_zero(vmu->email)) {
07069       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07070 
07071       if (attach_user_voicemail)
07072          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07073 
07074       /* XXX possible imap issue, should category be NULL XXX */
07075       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07076 
07077       if (attach_user_voicemail)
07078          DISPOSE(todir, msgnum);
07079    }
07080 
07081    if (!ast_strlen_zero(vmu->pager)) {
07082       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07083    }
07084 
07085    if (ast_test_flag(vmu, VM_DELETE))
07086       DELETE(todir, msgnum, fn, vmu);
07087 
07088    /* Leave voicemail for someone */
07089    if (ast_app_has_voicemail(ext_context, NULL)) 
07090       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07091 
07092    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07093 
07094    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);
07095    run_externnotify(vmu->context, vmu->mailbox, flag);
07096 
07097 #ifdef IMAP_STORAGE
07098    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07099    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07100       vm_imap_delete(NULL, vms->curmsg, vmu);
07101       vms->newmessages--;  /* Fix new message count */
07102    }
07103 #endif
07104 
07105    return 0;
07106 }
07107 
07108 /*!
07109  * \brief Sends a voicemail message to a mailbox recipient.
07110  * \param chan
07111  * \param context
07112  * \param vms
07113  * \param sender
07114  * \param fmt
07115  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07116  *             Will be 0 when called to forward an existing message (option 8)
07117  *             Will be 1 when called to leave a message (option 3->5)
07118  * \param record_gain 
07119  * \param urgent
07120  *
07121  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07122  * 
07123  * When in the leave message mode (is_new_message == 1):
07124  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07125  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07126  *
07127  * When in the forward message mode (is_new_message == 0):
07128  *   - retreives the current message to be forwarded
07129  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07130  *   - determines the target mailbox and folders
07131  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07132  *
07133  * \return zero on success, -1 on error.
07134  */
07135 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)
07136 {
07137 #ifdef IMAP_STORAGE
07138    int todircount = 0;
07139    struct vm_state *dstvms;
07140 #endif
07141    char username[70]="";
07142    char fn[PATH_MAX]; /* for playback of name greeting */
07143    char ecodes[16] = "#";
07144    int res = 0, cmd = 0;
07145    struct ast_vm_user *receiver = NULL, *vmtmp;
07146    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07147    char *stringp;
07148    const char *s;
07149    int saved_messages = 0;
07150    int valid_extensions = 0;
07151    char *dir;
07152    int curmsg;
07153    char urgent_str[7] = "";
07154    int prompt_played = 0;
07155 #ifndef IMAP_STORAGE
07156    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07157 #endif
07158    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07159       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07160    }
07161 
07162    if (vms == NULL) return -1;
07163    dir = vms->curdir;
07164    curmsg = vms->curmsg;
07165 
07166    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07167    while (!res && !valid_extensions) {
07168       int use_directory = 0;
07169       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07170          int done = 0;
07171          int retries = 0;
07172          cmd = 0;
07173          while ((cmd >= 0) && !done ){
07174             if (cmd)
07175                retries = 0;
07176             switch (cmd) {
07177             case '1': 
07178                use_directory = 0;
07179                done = 1;
07180                break;
07181             case '2': 
07182                use_directory = 1;
07183                done = 1;
07184                break;
07185             case '*': 
07186                cmd = 't';
07187                done = 1;
07188                break;
07189             default: 
07190                /* Press 1 to enter an extension press 2 to use the directory */
07191                cmd = ast_play_and_wait(chan, "vm-forward");
07192                if (!cmd) {
07193                   cmd = ast_waitfordigit(chan, 3000);
07194                }
07195                if (!cmd) {
07196                   retries++;
07197                }
07198                if (retries > 3) {
07199                   cmd = 't';
07200                   done = 1;
07201                }
07202                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07203             }
07204          }
07205          if (cmd < 0 || cmd == 't')
07206             break;
07207       }
07208       
07209       if (use_directory) {
07210          /* use app_directory */
07211          
07212          char old_context[sizeof(chan->context)];
07213          char old_exten[sizeof(chan->exten)];
07214          int old_priority;
07215          struct ast_app* directory_app;
07216 
07217          directory_app = pbx_findapp("Directory");
07218          if (directory_app) {
07219             char vmcontext[256];
07220             /* make backup copies */
07221             memcpy(old_context, chan->context, sizeof(chan->context));
07222             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07223             old_priority = chan->priority;
07224             
07225             /* call the the Directory, changes the channel */
07226             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07227             res = pbx_exec(chan, directory_app, vmcontext);
07228             
07229             ast_copy_string(username, chan->exten, sizeof(username));
07230             
07231             /* restore the old context, exten, and priority */
07232             memcpy(chan->context, old_context, sizeof(chan->context));
07233             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07234             chan->priority = old_priority;
07235          } else {
07236             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07237             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07238          }
07239       } else {
07240          /* Ask for an extension */
07241          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07242          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07243          prompt_played++;
07244          if (res || prompt_played > 4)
07245             break;
07246          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07247             break;
07248       }
07249       
07250       /* start all over if no username */
07251       if (ast_strlen_zero(username))
07252          continue;
07253       stringp = username;
07254       s = strsep(&stringp, "*");
07255       /* start optimistic */
07256       valid_extensions = 1;
07257       while (s) {
07258          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07259             int oldmsgs;
07260             int newmsgs;
07261             int capacity;
07262             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07263                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07264                /* Shouldn't happen, but allow trying another extension if it does */
07265                res = ast_play_and_wait(chan, "pbx-invalid");
07266                valid_extensions = 0;
07267                break;
07268             }
07269             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07270             if ((newmsgs + oldmsgs) >= capacity) {
07271                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07272                res = ast_play_and_wait(chan, "vm-mailboxfull");
07273                valid_extensions = 0;
07274                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07275                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07276                   free_user(vmtmp);
07277                }
07278                inprocess_count(receiver->mailbox, receiver->context, -1);
07279                break;
07280             }
07281             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07282          } else {
07283             /* XXX Optimization for the future.  When we encounter a single bad extension,
07284              * bailing out on all of the extensions may not be the way to go.  We should
07285              * probably just bail on that single extension, then allow the user to enter
07286              * several more. XXX
07287              */
07288             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07289                free_user(receiver);
07290             }
07291             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07292             /* "I am sorry, that's not a valid extension.  Please try again." */
07293             res = ast_play_and_wait(chan, "pbx-invalid");
07294             valid_extensions = 0;
07295             break;
07296          }
07297 
07298          /* play name if available, else play extension number */
07299          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07300          RETRIEVE(fn, -1, s, receiver->context);
07301          if (ast_fileexists(fn, NULL, NULL) > 0) {
07302             res = ast_stream_and_wait(chan, fn, ecodes);
07303             if (res) {
07304                DISPOSE(fn, -1);
07305                return res;
07306             }
07307          } else {
07308             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07309          }
07310          DISPOSE(fn, -1);
07311 
07312          s = strsep(&stringp, "*");
07313       }
07314       /* break from the loop of reading the extensions */
07315       if (valid_extensions)
07316          break;
07317    }
07318    /* check if we're clear to proceed */
07319    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07320       return res;
07321    if (is_new_message == 1) {
07322       struct leave_vm_options leave_options;
07323       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07324       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07325 
07326       /* Send VoiceMail */
07327       memset(&leave_options, 0, sizeof(leave_options));
07328       leave_options.record_gain = record_gain;
07329       cmd = leave_voicemail(chan, mailbox, &leave_options);
07330    } else {
07331       /* Forward VoiceMail */
07332       long duration = 0;
07333       struct vm_state vmstmp;
07334       int copy_msg_result = 0;
07335       memcpy(&vmstmp, vms, sizeof(vmstmp));
07336 
07337       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07338 
07339       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07340       if (!cmd) {
07341          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07342 #ifdef IMAP_STORAGE
07343             int attach_user_voicemail;
07344             char *myserveremail = serveremail;
07345             
07346             /* get destination mailbox */
07347             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07348             if (!dstvms) {
07349                dstvms = create_vm_state_from_user(vmtmp);
07350             }
07351             if (dstvms) {
07352                init_mailstream(dstvms, 0);
07353                if (!dstvms->mailstream) {
07354                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07355                } else {
07356                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07357                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07358                }
07359             } else {
07360                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07361             }
07362             if (!ast_strlen_zero(vmtmp->serveremail))
07363                myserveremail = vmtmp->serveremail;
07364             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07365             /* NULL category for IMAP storage */
07366             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07367                dstvms->curbox,
07368                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07369                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07370                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07371                NULL, urgent_str);
07372 #else
07373             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07374 #endif
07375             saved_messages++;
07376             AST_LIST_REMOVE_CURRENT(list);
07377             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07378             free_user(vmtmp);
07379             if (res)
07380                break;
07381          }
07382          AST_LIST_TRAVERSE_SAFE_END;
07383          if (saved_messages > 0 && !copy_msg_result) {
07384             /* give confirmation that the message was saved */
07385             /* commented out since we can't forward batches yet
07386             if (saved_messages == 1)
07387                res = ast_play_and_wait(chan, "vm-message");
07388             else
07389                res = ast_play_and_wait(chan, "vm-messages");
07390             if (!res)
07391                res = ast_play_and_wait(chan, "vm-saved"); */
07392 #ifdef IMAP_STORAGE
07393             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07394             if (ast_strlen_zero(vmstmp.introfn))
07395 #endif
07396             res = ast_play_and_wait(chan, "vm-msgsaved");
07397          }
07398 #ifndef IMAP_STORAGE
07399          else {
07400             /* with IMAP, mailbox full warning played by imap_check_limits */
07401             res = ast_play_and_wait(chan, "vm-mailboxfull");
07402          }
07403          /* Restore original message without prepended message if backup exists */
07404          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07405          strcpy(textfile, msgfile);
07406          strcpy(backup, msgfile);
07407          strcpy(backup_textfile, msgfile);
07408          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07409          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07410          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07411          if (ast_fileexists(backup, NULL, NULL) > 0) {
07412             ast_filerename(backup, msgfile, NULL);
07413             rename(backup_textfile, textfile);
07414          }
07415 #endif
07416       }
07417       DISPOSE(dir, curmsg);
07418 #ifndef IMAP_STORAGE
07419       if (cmd) { /* assuming hangup, cleanup backup file */
07420          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07421          strcpy(textfile, msgfile);
07422          strcpy(backup_textfile, msgfile);
07423          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07424          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07425          rename(backup_textfile, textfile);
07426       }
07427 #endif
07428    }
07429 
07430    /* If anything failed above, we still have this list to free */
07431    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07432       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07433       free_user(vmtmp);
07434    }
07435    return res ? res : cmd;
07436 }
07437 
07438 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07439 {
07440    int res;
07441    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07442       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07443    return res;
07444 }
07445 
07446 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07447 {
07448    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07449    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);
07450 }
07451 
07452 static int play_message_category(struct ast_channel *chan, const char *category)
07453 {
07454    int res = 0;
07455 
07456    if (!ast_strlen_zero(category))
07457       res = ast_play_and_wait(chan, category);
07458 
07459    if (res) {
07460       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07461       res = 0;
07462    }
07463 
07464    return res;
07465 }
07466 
07467 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07468 {
07469    int res = 0;
07470    struct vm_zone *the_zone = NULL;
07471    time_t t;
07472 
07473    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07474       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07475       return 0;
07476    }
07477 
07478    /* Does this user have a timezone specified? */
07479    if (!ast_strlen_zero(vmu->zonetag)) {
07480       /* Find the zone in the list */
07481       struct vm_zone *z;
07482       AST_LIST_LOCK(&zones);
07483       AST_LIST_TRAVERSE(&zones, z, list) {
07484          if (!strcmp(z->name, vmu->zonetag)) {
07485             the_zone = z;
07486             break;
07487          }
07488       }
07489       AST_LIST_UNLOCK(&zones);
07490    }
07491 
07492 /* No internal variable parsing for now, so we'll comment it out for the time being */
07493 #if 0
07494    /* Set the DIFF_* variables */
07495    ast_localtime(&t, &time_now, NULL);
07496    tv_now = ast_tvnow();
07497    ast_localtime(&tv_now, &time_then, NULL);
07498 
07499    /* Day difference */
07500    if (time_now.tm_year == time_then.tm_year)
07501       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07502    else
07503       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07504    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07505 
07506    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07507 #endif
07508    if (the_zone) {
07509       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07510    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07511       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07512    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07513       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07514    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07515       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);
07516    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07517       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07518    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07519       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07520    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07521       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07522    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07523       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);
07524    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07525       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07526    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07527       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07528    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07529       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);
07530    } else {
07531       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07532    }
07533 #if 0
07534    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07535 #endif
07536    return res;
07537 }
07538 
07539 
07540 
07541 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07542 {
07543    int res = 0;
07544    int i;
07545    char *callerid, *name;
07546    char prefile[PATH_MAX] = "";
07547    
07548 
07549    /* If voicemail cid is not enabled, or we didn't get cid or context from
07550     * the attribute file, leave now.
07551     *
07552     * TODO Still need to change this so that if this function is called by the
07553     * message envelope (and someone is explicitly requesting to hear the CID),
07554     * it does not check to see if CID is enabled in the config file.
07555     */
07556    if ((cid == NULL)||(context == NULL))
07557       return res;
07558 
07559    /* Strip off caller ID number from name */
07560    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07561    ast_callerid_parse(cid, &name, &callerid);
07562    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07563       /* Check for internal contexts and only */
07564       /* say extension when the call didn't come from an internal context in the list */
07565       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07566          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07567          if ((strcmp(cidinternalcontexts[i], context) == 0))
07568             break;
07569       }
07570       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07571          if (!res) {
07572             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07573             if (!ast_strlen_zero(prefile)) {
07574             /* See if we can find a recorded name for this person instead of their extension number */
07575                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07576                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07577                   if (!callback)
07578                      res = wait_file2(chan, vms, "vm-from");
07579                   res = ast_stream_and_wait(chan, prefile, "");
07580                } else {
07581                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07582                   /* Say "from extension" as one saying to sound smoother */
07583                   if (!callback)
07584                      res = wait_file2(chan, vms, "vm-from-extension");
07585                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07586                }
07587             }
07588          }
07589       } else if (!res) {
07590          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07591          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07592          if (!callback)
07593             res = wait_file2(chan, vms, "vm-from-phonenumber");
07594          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07595       }
07596    } else {
07597       /* Number unknown */
07598       ast_debug(1, "VM-CID: From an unknown number\n");
07599       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07600       res = wait_file2(chan, vms, "vm-unknown-caller");
07601    }
07602    return res;
07603 }
07604 
07605 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07606 {
07607    int res = 0;
07608    int durationm;
07609    int durations;
07610    /* Verify that we have a duration for the message */
07611    if (duration == NULL)
07612       return res;
07613 
07614    /* Convert from seconds to minutes */
07615    durations = atoi(duration);
07616    durationm = (durations / 60);
07617 
07618    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07619 
07620    if ((!res) && (durationm >= minduration)) {
07621       res = wait_file2(chan, vms, "vm-duration");
07622 
07623       /* POLISH syntax */
07624       if (!strncasecmp(chan->language, "pl", 2)) {
07625          div_t num = div(durationm, 10);
07626 
07627          if (durationm == 1) {
07628             res = ast_play_and_wait(chan, "digits/1z");
07629             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07630          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07631             if (num.rem == 2) {
07632                if (!num.quot) {
07633                   res = ast_play_and_wait(chan, "digits/2-ie");
07634                } else {
07635                   res = say_and_wait(chan, durationm - 2 , chan->language);
07636                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07637                }
07638             } else {
07639                res = say_and_wait(chan, durationm, chan->language);
07640             }
07641             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07642          } else {
07643             res = say_and_wait(chan, durationm, chan->language);
07644             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07645          }
07646       /* DEFAULT syntax */
07647       } else {
07648          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07649          res = wait_file2(chan, vms, "vm-minutes");
07650       }
07651    }
07652    return res;
07653 }
07654 
07655 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07656 {
07657    int res = 0;
07658    char filename[256], *cid;
07659    const char *origtime, *context, *category, *duration, *flag;
07660    struct ast_config *msg_cfg;
07661    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07662 
07663    vms->starting = 0;
07664    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07665    adsi_message(chan, vms);
07666    if (!vms->curmsg) {
07667       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07668    } else if (vms->curmsg == vms->lastmsg) {
07669       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07670    }
07671 
07672    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07673    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07674    msg_cfg = ast_config_load(filename, config_flags);
07675    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07676       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07677       return 0;
07678    }
07679    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07680 
07681    /* Play the word urgent if we are listening to urgent messages */
07682    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07683       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07684    }
07685 
07686    if (!res) {
07687       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07688       /* POLISH syntax */
07689       if (!strncasecmp(chan->language, "pl", 2)) {
07690          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07691             int ten, one;
07692             char nextmsg[256];
07693             ten = (vms->curmsg + 1) / 10;
07694             one = (vms->curmsg + 1) % 10;
07695 
07696             if (vms->curmsg < 20) {
07697                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07698                res = wait_file2(chan, vms, nextmsg);
07699             } else {
07700                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07701                res = wait_file2(chan, vms, nextmsg);
07702                if (one > 0) {
07703                   if (!res) {
07704                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07705                      res = wait_file2(chan, vms, nextmsg);
07706                   }
07707                }
07708             }
07709          }
07710          if (!res)
07711             res = wait_file2(chan, vms, "vm-message");
07712       /* HEBREW syntax */
07713       } else if (!strncasecmp(chan->language, "he", 2)) {
07714          if (!vms->curmsg) {
07715             res = wait_file2(chan, vms, "vm-message");
07716             res = wait_file2(chan, vms, "vm-first");
07717          } else if (vms->curmsg == vms->lastmsg) {
07718             res = wait_file2(chan, vms, "vm-message");
07719             res = wait_file2(chan, vms, "vm-last");
07720          } else {
07721             res = wait_file2(chan, vms, "vm-message");
07722             res = wait_file2(chan, vms, "vm-number");
07723             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07724          }
07725       /* VIETNAMESE syntax */
07726       } else if (!strncasecmp(chan->language, "vi", 2)) {
07727          if (!vms->curmsg) {
07728             res = wait_file2(chan, vms, "vm-message");
07729             res = wait_file2(chan, vms, "vm-first");
07730          } else if (vms->curmsg == vms->lastmsg) {
07731             res = wait_file2(chan, vms, "vm-message");
07732             res = wait_file2(chan, vms, "vm-last");
07733          } else {
07734             res = wait_file2(chan, vms, "vm-message");
07735             res = wait_file2(chan, vms, "vm-number");
07736             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07737          }
07738       } else {
07739          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07740             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07741          } else { /* DEFAULT syntax */
07742             res = wait_file2(chan, vms, "vm-message");
07743          }
07744          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07745             if (!res) {
07746                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07747                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07748             }
07749          }
07750       }
07751    }
07752 
07753    if (!msg_cfg) {
07754       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07755       return 0;
07756    }
07757 
07758    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07759       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07760       DISPOSE(vms->curdir, vms->curmsg);
07761       ast_config_destroy(msg_cfg);
07762       return 0;
07763    }
07764 
07765    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07766    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07767    category = ast_variable_retrieve(msg_cfg, "message", "category");
07768 
07769    context = ast_variable_retrieve(msg_cfg, "message", "context");
07770    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07771       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07772    if (!res) {
07773       res = play_message_category(chan, category);
07774    }
07775    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07776       res = play_message_datetime(chan, vmu, origtime, filename);
07777    }
07778    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07779       res = play_message_callerid(chan, vms, cid, context, 0);
07780    }
07781    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07782       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07783    }
07784    /* Allow pressing '1' to skip envelope / callerid */
07785    if (res == '1') {
07786       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07787       res = 0;
07788    }
07789    ast_config_destroy(msg_cfg);
07790 
07791    if (!res) {
07792       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07793       vms->heard[vms->curmsg] = 1;
07794 #ifdef IMAP_STORAGE
07795       /*IMAP storage stores any prepended message from a forward
07796        * as a separate file from the rest of the message
07797        */
07798       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07799          wait_file(chan, vms, vms->introfn);
07800       }
07801 #endif
07802       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07803          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07804          res = 0;
07805       }
07806       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07807    }
07808    DISPOSE(vms->curdir, vms->curmsg);
07809    return res;
07810 }
07811 
07812 #ifdef IMAP_STORAGE
07813 static int imap_remove_file(char *dir, int msgnum)
07814 {
07815    char fn[PATH_MAX];
07816    char full_fn[PATH_MAX];
07817    char intro[PATH_MAX] = {0,};
07818    
07819    if (msgnum > -1) {
07820       make_file(fn, sizeof(fn), dir, msgnum);
07821       snprintf(intro, sizeof(intro), "%sintro", fn);
07822    } else
07823       ast_copy_string(fn, dir, sizeof(fn));
07824    
07825    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07826       ast_filedelete(fn, NULL);
07827       if (!ast_strlen_zero(intro)) {
07828          ast_filedelete(intro, NULL);
07829       }
07830       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07831       unlink(full_fn);
07832    }
07833    return 0;
07834 }
07835 
07836 
07837 
07838 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07839 {
07840    char *file, *filename;
07841    char *attachment;
07842    char arg[10];
07843    int i;
07844    BODY* body;
07845 
07846    file = strrchr(ast_strdupa(dir), '/');
07847    if (file) {
07848       *file++ = '\0';
07849    } else {
07850       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07851       return -1;
07852    }
07853 
07854    ast_mutex_lock(&vms->lock);
07855    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07856       mail_fetchstructure(vms->mailstream, i + 1, &body);
07857       /* We have the body, now we extract the file name of the first attachment. */
07858       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07859          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07860       } else {
07861          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07862          ast_mutex_unlock(&vms->lock);
07863          return -1;
07864       }
07865       filename = strsep(&attachment, ".");
07866       if (!strcmp(filename, file)) {
07867          sprintf(arg, "%d", i + 1);
07868          mail_setflag(vms->mailstream, arg, "\\DELETED");
07869       }
07870    }
07871    mail_expunge(vms->mailstream);
07872    ast_mutex_unlock(&vms->lock);
07873    return 0;
07874 }
07875 
07876 #elif !defined(IMAP_STORAGE)
07877 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07878 {
07879    int count_msg, last_msg;
07880 
07881    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07882 
07883    /* Rename the member vmbox HERE so that we don't try to return before
07884     * we know what's going on.
07885     */
07886    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07887 
07888    /* Faster to make the directory than to check if it exists. */
07889    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07890 
07891    /* traverses directory using readdir (or select query for ODBC) */
07892    count_msg = count_messages(vmu, vms->curdir);
07893    if (count_msg < 0) {
07894       return count_msg;
07895    } else {
07896       vms->lastmsg = count_msg - 1;
07897    }
07898 
07899    if (vm_allocate_dh(vms, vmu, count_msg)) {
07900       return -1;
07901    }
07902 
07903    /*
07904    The following test is needed in case sequencing gets messed up.
07905    There appears to be more than one way to mess up sequence, so
07906    we will not try to find all of the root causes--just fix it when
07907    detected.
07908    */
07909 
07910    if (vm_lock_path(vms->curdir)) {
07911       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07912       return ERROR_LOCK_PATH;
07913    }
07914 
07915    /* for local storage, checks directory for messages up to maxmsg limit */
07916    last_msg = last_message_index(vmu, vms->curdir);
07917    ast_unlock_path(vms->curdir);
07918 
07919    if (last_msg < -1) {
07920       return last_msg;
07921    } else if (vms->lastmsg != last_msg) {
07922       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);
07923       resequence_mailbox(vmu, vms->curdir, count_msg);
07924    }
07925 
07926    return 0;
07927 }
07928 #endif
07929 
07930 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07931 {
07932    int x = 0;
07933 
07934 #ifndef IMAP_STORAGE
07935    int last_msg_idx;
07936    int res = 0, nummsg;
07937    char fn2[PATH_MAX];
07938 #endif
07939 
07940    if (vms->lastmsg <= -1) {
07941       goto done;
07942    }
07943 
07944    vms->curmsg = -1;
07945 #ifndef IMAP_STORAGE
07946    /* Get the deleted messages fixed */
07947    if (vm_lock_path(vms->curdir)) {
07948       return ERROR_LOCK_PATH;
07949    }
07950 
07951    /* update count as message may have arrived while we've got mailbox open */
07952    last_msg_idx = last_message_index(vmu, vms->curdir);
07953    if (last_msg_idx != vms->lastmsg) {
07954       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07955    }
07956 
07957    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07958    for (x = 0; x < last_msg_idx + 1; x++) {
07959       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07960          /* Save this message.  It's not in INBOX or hasn't been heard */
07961          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07962          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07963             break;
07964          }
07965          vms->curmsg++;
07966          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
07967          if (strcmp(vms->fn, fn2)) {
07968             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07969          }
07970       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
07971          /* Move to old folder before deleting */
07972          res = save_to_folder(vmu, vms, x, 1);
07973          if (res == ERROR_LOCK_PATH) {
07974             /* If save failed do not delete the message */
07975             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07976             vms->deleted[x] = 0;
07977             vms->heard[x] = 0;
07978             --x;
07979          }
07980       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07981          /* Move to deleted folder */
07982          res = save_to_folder(vmu, vms, x, 10);
07983          if (res == ERROR_LOCK_PATH) {
07984             /* If save failed do not delete the message */
07985             vms->deleted[x] = 0;
07986             vms->heard[x] = 0;
07987             --x;
07988          }
07989       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07990          /* If realtime storage enabled - we should explicitly delete this message,
07991          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07992          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07993          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
07994             DELETE(vms->curdir, x, vms->fn, vmu);
07995          }
07996       }
07997    }
07998 
07999    /* Delete ALL remaining messages */
08000    nummsg = x - 1;
08001    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08002       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08003       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08004          DELETE(vms->curdir, x, vms->fn, vmu);
08005       }
08006    }
08007    ast_unlock_path(vms->curdir);
08008 #else /* defined(IMAP_STORAGE) */
08009    if (vms->deleted) {
08010       /* Since we now expunge after each delete, deleting in reverse order
08011        * ensures that no reordering occurs between each step. */
08012       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
08013          if (vms->deleted[x]) {
08014             ast_debug(3, "IMAP delete of %d\n", x);
08015             DELETE(vms->curdir, x, vms->fn, vmu);
08016          }
08017       }
08018    }
08019 #endif
08020 
08021 done:
08022    if (vms->deleted && vmu->maxmsg) {
08023       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int));
08024    }
08025    if (vms->heard && vmu->maxmsg) {
08026       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int));
08027    }
08028 
08029    return 0;
08030 }
08031 
08032 /* In Greek even though we CAN use a syntax like "friends messages"
08033  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08034  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08035  * syntax for the above three categories which is more elegant.
08036  */
08037 
08038 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08039 {
08040    int cmd;
08041    char *buf;
08042 
08043    buf = alloca(strlen(box) + 2);
08044    strcpy(buf, box);
08045    strcat(buf, "s");
08046 
08047    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08048       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08049       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08050    } else {
08051       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08052       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08053    }
08054 }
08055 
08056 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08057 {
08058    int cmd;
08059 
08060    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08061       if (!strcasecmp(box, "vm-INBOX"))
08062          cmd = ast_play_and_wait(chan, "vm-new-e");
08063       else
08064          cmd = ast_play_and_wait(chan, "vm-old-e");
08065       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08066    } else {
08067       cmd = ast_play_and_wait(chan, "vm-messages");
08068       return cmd ? cmd : ast_play_and_wait(chan, box);
08069    }
08070 }
08071 
08072 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08073 {
08074    int cmd;
08075 
08076    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08077       cmd = ast_play_and_wait(chan, "vm-messages");
08078       return cmd ? cmd : ast_play_and_wait(chan, box);
08079    } else {
08080       cmd = ast_play_and_wait(chan, box);
08081       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08082    }
08083 }
08084 
08085 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08086 {
08087    int cmd;
08088 
08089    if (  !strncasecmp(chan->language, "it", 2) ||
08090         !strncasecmp(chan->language, "es", 2) ||
08091         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08092       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08093       return cmd ? cmd : ast_play_and_wait(chan, box);
08094    } else if (!strncasecmp(chan->language, "gr", 2)) {
08095       return vm_play_folder_name_gr(chan, box);
08096    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08097       return ast_play_and_wait(chan, box);
08098    } else if (!strncasecmp(chan->language, "pl", 2)) {
08099       return vm_play_folder_name_pl(chan, box);
08100    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08101       return vm_play_folder_name_ua(chan, box);
08102    } else if (!strncasecmp(chan->language, "vi", 2)) {
08103       return ast_play_and_wait(chan, box);
08104    } else {  /* Default English */
08105       cmd = ast_play_and_wait(chan, box);
08106       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08107    }
08108 }
08109 
08110 /* GREEK SYNTAX
08111    In greek the plural for old/new is
08112    different so we need the following files
08113    We also need vm-denExeteMynhmata because
08114    this syntax is different.
08115 
08116    -> vm-Olds.wav : "Palia"
08117    -> vm-INBOXs.wav : "Nea"
08118    -> vm-denExeteMynhmata : "den exete mynhmata"
08119 */
08120 
08121 
08122 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08123 {
08124    int res = 0;
08125 
08126    if (vms->newmessages) {
08127       res = ast_play_and_wait(chan, "vm-youhave");
08128       if (!res) 
08129          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08130       if (!res) {
08131          if ((vms->newmessages == 1)) {
08132             res = ast_play_and_wait(chan, "vm-INBOX");
08133             if (!res)
08134                res = ast_play_and_wait(chan, "vm-message");
08135          } else {
08136             res = ast_play_and_wait(chan, "vm-INBOXs");
08137             if (!res)
08138                res = ast_play_and_wait(chan, "vm-messages");
08139          }
08140       }
08141    } else if (vms->oldmessages){
08142       res = ast_play_and_wait(chan, "vm-youhave");
08143       if (!res)
08144          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08145       if ((vms->oldmessages == 1)){
08146          res = ast_play_and_wait(chan, "vm-Old");
08147          if (!res)
08148             res = ast_play_and_wait(chan, "vm-message");
08149       } else {
08150          res = ast_play_and_wait(chan, "vm-Olds");
08151          if (!res)
08152             res = ast_play_and_wait(chan, "vm-messages");
08153       }
08154    } else if (!vms->oldmessages && !vms->newmessages) 
08155       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08156    return res;
08157 }
08158 
08159 /* Version of vm_intro() designed to work for many languages.
08160  *
08161  * It is hoped that this function can prevent the proliferation of 
08162  * language-specific vm_intro() functions and in time replace the language-
08163  * specific functions which already exist.  An examination of the language-
08164  * specific functions revealed that they all corrected the same deficiencies
08165  * in vm_intro_en() (which was the default function). Namely:
08166  *
08167  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08168  *     wording of the voicemail greeting hides this problem.  For example,
08169  *     vm-INBOX contains only the word "new".  This means that both of these
08170  *     sequences produce valid utterances:
08171  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08172  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08173  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08174  *     in many languages) the first utterance becomes "you have 1 the new message".
08175  *  2) The function contains hardcoded rules for pluralizing the word "message".
08176  *     These rules are correct for English, but not for many other languages.
08177  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08178  *     required in many languages.
08179  *  4) The gender of the word for "message" is not specified. This is a problem
08180  *     because in many languages the gender of the number in phrases such
08181  *     as "you have one new message" must match the gender of the word
08182  *     meaning "message".
08183  *
08184  * Fixing these problems for each new language has meant duplication of effort.
08185  * This new function solves the problems in the following general ways:
08186  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08187  *     and vm-Old respectively for those languages where it makes sense.
08188  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08189  *     on vm-message.
08190  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08191  *     prefix on vm-new and vm-old (none for English).
08192  *  4) Pass the gender of the language's word for "message" as an agument to
08193  *     this function which is can in turn pass on to the functions which 
08194  *     say numbers and put endings on nounds and adjectives.
08195  *
08196  * All languages require these messages:
08197  *  vm-youhave    "You have..."
08198  *  vm-and     "and"
08199  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08200  *
08201  * To use it for English, you will need these additional sound files:
08202  *  vm-new     "new"
08203  *  vm-message    "message", singular
08204  *  vm-messages      "messages", plural
08205  *
08206  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08207  *
08208  *  vm-newn    "novoye" (singular, neuter)
08209  *  vm-newx    "novikh" (counting plural form, genative plural)
08210  *  vm-message    "sobsheniye" (singular form)
08211  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08212  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08213  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08214  *  digits/2n     "dva" (neuter singular)
08215  */
08216 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08217 {
08218    int res;
08219    int lastnum = 0;
08220 
08221    res = ast_play_and_wait(chan, "vm-youhave");
08222 
08223    if (!res && vms->newmessages) {
08224       lastnum = vms->newmessages;
08225 
08226       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08227          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08228       }
08229 
08230       if (!res && vms->oldmessages) {
08231          res = ast_play_and_wait(chan, "vm-and");
08232       }
08233    }
08234 
08235    if (!res && vms->oldmessages) {
08236       lastnum = vms->oldmessages;
08237 
08238       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08239          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08240       }
08241    }
08242 
08243    if (!res) {
08244       if (lastnum == 0) {
08245          res = ast_play_and_wait(chan, "vm-no");
08246       }
08247       if (!res) {
08248          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08249       }
08250    }
08251 
08252    return res;
08253 }
08254 
08255 /* Default Hebrew syntax */
08256 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08257 {
08258    int res = 0;
08259 
08260    /* Introduce messages they have */
08261    if (!res) {
08262       if ((vms->newmessages) || (vms->oldmessages)) {
08263          res = ast_play_and_wait(chan, "vm-youhave");
08264       }
08265       /*
08266        * The word "shtei" refers to the number 2 in hebrew when performing a count
08267        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08268        * an element, this is one of them.
08269        */
08270       if (vms->newmessages) {
08271          if (!res) {
08272             if (vms->newmessages == 1) {
08273                res = ast_play_and_wait(chan, "vm-INBOX1");
08274             } else {
08275                if (vms->newmessages == 2) {
08276                   res = ast_play_and_wait(chan, "vm-shtei");
08277                } else {
08278                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08279                }
08280                res = ast_play_and_wait(chan, "vm-INBOX");
08281             }
08282          }
08283          if (vms->oldmessages && !res) {
08284             res = ast_play_and_wait(chan, "vm-and");
08285             if (vms->oldmessages == 1) {
08286                res = ast_play_and_wait(chan, "vm-Old1");
08287             } else {
08288                if (vms->oldmessages == 2) {
08289                   res = ast_play_and_wait(chan, "vm-shtei");
08290                } else {
08291                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08292                }
08293                res = ast_play_and_wait(chan, "vm-Old");
08294             }
08295          }
08296       }
08297       if (!res && vms->oldmessages && !vms->newmessages) {
08298          if (!res) {
08299             if (vms->oldmessages == 1) {
08300                res = ast_play_and_wait(chan, "vm-Old1");
08301             } else {
08302                if (vms->oldmessages == 2) {
08303                   res = ast_play_and_wait(chan, "vm-shtei");
08304                } else {
08305                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08306                }
08307                res = ast_play_and_wait(chan, "vm-Old");
08308             }
08309          }
08310       }
08311       if (!res) {
08312          if (!vms->oldmessages && !vms->newmessages) {
08313             if (!res) {
08314                res = ast_play_and_wait(chan, "vm-nomessages");
08315             }
08316          }
08317       }
08318    }
08319    return res;
08320 }
08321    
08322 /* Default English syntax */
08323 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08324 {
08325    int res;
08326 
08327    /* Introduce messages they have */
08328    res = ast_play_and_wait(chan, "vm-youhave");
08329    if (!res) {
08330       if (vms->urgentmessages) {
08331          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08332          if (!res)
08333             res = ast_play_and_wait(chan, "vm-Urgent");
08334          if ((vms->oldmessages || vms->newmessages) && !res) {
08335             res = ast_play_and_wait(chan, "vm-and");
08336          } else if (!res) {
08337             if ((vms->urgentmessages == 1))
08338                res = ast_play_and_wait(chan, "vm-message");
08339             else
08340                res = ast_play_and_wait(chan, "vm-messages");
08341          }
08342       }
08343       if (vms->newmessages) {
08344          res = say_and_wait(chan, vms->newmessages, chan->language);
08345          if (!res)
08346             res = ast_play_and_wait(chan, "vm-INBOX");
08347          if (vms->oldmessages && !res)
08348             res = ast_play_and_wait(chan, "vm-and");
08349          else if (!res) {
08350             if ((vms->newmessages == 1))
08351                res = ast_play_and_wait(chan, "vm-message");
08352             else
08353                res = ast_play_and_wait(chan, "vm-messages");
08354          }
08355             
08356       }
08357       if (!res && vms->oldmessages) {
08358          res = say_and_wait(chan, vms->oldmessages, chan->language);
08359          if (!res)
08360             res = ast_play_and_wait(chan, "vm-Old");
08361          if (!res) {
08362             if (vms->oldmessages == 1)
08363                res = ast_play_and_wait(chan, "vm-message");
08364             else
08365                res = ast_play_and_wait(chan, "vm-messages");
08366          }
08367       }
08368       if (!res) {
08369          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08370             res = ast_play_and_wait(chan, "vm-no");
08371             if (!res)
08372                res = ast_play_and_wait(chan, "vm-messages");
08373          }
08374       }
08375    }
08376    return res;
08377 }
08378 
08379 /* ITALIAN syntax */
08380 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08381 {
08382    /* Introduce messages they have */
08383    int res;
08384    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08385       res = ast_play_and_wait(chan, "vm-no") ||
08386          ast_play_and_wait(chan, "vm-message");
08387    else
08388       res = ast_play_and_wait(chan, "vm-youhave");
08389    if (!res && vms->newmessages) {
08390       res = (vms->newmessages == 1) ?
08391          ast_play_and_wait(chan, "digits/un") ||
08392          ast_play_and_wait(chan, "vm-nuovo") ||
08393          ast_play_and_wait(chan, "vm-message") :
08394          /* 2 or more new messages */
08395          say_and_wait(chan, vms->newmessages, chan->language) ||
08396          ast_play_and_wait(chan, "vm-nuovi") ||
08397          ast_play_and_wait(chan, "vm-messages");
08398       if (!res && vms->oldmessages)
08399          res = ast_play_and_wait(chan, "vm-and");
08400    }
08401    if (!res && vms->oldmessages) {
08402       res = (vms->oldmessages == 1) ?
08403          ast_play_and_wait(chan, "digits/un") ||
08404          ast_play_and_wait(chan, "vm-vecchio") ||
08405          ast_play_and_wait(chan, "vm-message") :
08406          /* 2 or more old messages */
08407          say_and_wait(chan, vms->oldmessages, chan->language) ||
08408          ast_play_and_wait(chan, "vm-vecchi") ||
08409          ast_play_and_wait(chan, "vm-messages");
08410    }
08411    return res;
08412 }
08413 
08414 /* POLISH syntax */
08415 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08416 {
08417    /* Introduce messages they have */
08418    int res;
08419    div_t num;
08420 
08421    if (!vms->oldmessages && !vms->newmessages) {
08422       res = ast_play_and_wait(chan, "vm-no");
08423       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08424       return res;
08425    } else {
08426       res = ast_play_and_wait(chan, "vm-youhave");
08427    }
08428 
08429    if (vms->newmessages) {
08430       num = div(vms->newmessages, 10);
08431       if (vms->newmessages == 1) {
08432          res = ast_play_and_wait(chan, "digits/1-a");
08433          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08434          res = res ? res : ast_play_and_wait(chan, "vm-message");
08435       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08436          if (num.rem == 2) {
08437             if (!num.quot) {
08438                res = ast_play_and_wait(chan, "digits/2-ie");
08439             } else {
08440                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08441                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08442             }
08443          } else {
08444             res = say_and_wait(chan, vms->newmessages, chan->language);
08445          }
08446          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08447          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08448       } else {
08449          res = say_and_wait(chan, vms->newmessages, chan->language);
08450          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08451          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08452       }
08453       if (!res && vms->oldmessages)
08454          res = ast_play_and_wait(chan, "vm-and");
08455    }
08456    if (!res && vms->oldmessages) {
08457       num = div(vms->oldmessages, 10);
08458       if (vms->oldmessages == 1) {
08459          res = ast_play_and_wait(chan, "digits/1-a");
08460          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08461          res = res ? res : ast_play_and_wait(chan, "vm-message");
08462       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08463          if (num.rem == 2) {
08464             if (!num.quot) {
08465                res = ast_play_and_wait(chan, "digits/2-ie");
08466             } else {
08467                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08468                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08469             }
08470          } else {
08471             res = say_and_wait(chan, vms->oldmessages, chan->language);
08472          }
08473          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08474          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08475       } else {
08476          res = say_and_wait(chan, vms->oldmessages, chan->language);
08477          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08478          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08479       }
08480    }
08481 
08482    return res;
08483 }
08484 
08485 /* SWEDISH syntax */
08486 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08487 {
08488    /* Introduce messages they have */
08489    int res;
08490 
08491    res = ast_play_and_wait(chan, "vm-youhave");
08492    if (res)
08493       return res;
08494 
08495    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08496       res = ast_play_and_wait(chan, "vm-no");
08497       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08498       return res;
08499    }
08500 
08501    if (vms->newmessages) {
08502       if ((vms->newmessages == 1)) {
08503          res = ast_play_and_wait(chan, "digits/ett");
08504          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08505          res = res ? res : ast_play_and_wait(chan, "vm-message");
08506       } else {
08507          res = say_and_wait(chan, vms->newmessages, chan->language);
08508          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08509          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08510       }
08511       if (!res && vms->oldmessages)
08512          res = ast_play_and_wait(chan, "vm-and");
08513    }
08514    if (!res && vms->oldmessages) {
08515       if (vms->oldmessages == 1) {
08516          res = ast_play_and_wait(chan, "digits/ett");
08517          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08518          res = res ? res : ast_play_and_wait(chan, "vm-message");
08519       } else {
08520          res = say_and_wait(chan, vms->oldmessages, chan->language);
08521          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08522          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08523       }
08524    }
08525 
08526    return res;
08527 }
08528 
08529 /* NORWEGIAN syntax */
08530 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08531 {
08532    /* Introduce messages they have */
08533    int res;
08534 
08535    res = ast_play_and_wait(chan, "vm-youhave");
08536    if (res)
08537       return res;
08538 
08539    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08540       res = ast_play_and_wait(chan, "vm-no");
08541       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08542       return res;
08543    }
08544 
08545    if (vms->newmessages) {
08546       if ((vms->newmessages == 1)) {
08547          res = ast_play_and_wait(chan, "digits/1");
08548          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08549          res = res ? res : ast_play_and_wait(chan, "vm-message");
08550       } else {
08551          res = say_and_wait(chan, vms->newmessages, chan->language);
08552          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08553          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08554       }
08555       if (!res && vms->oldmessages)
08556          res = ast_play_and_wait(chan, "vm-and");
08557    }
08558    if (!res && vms->oldmessages) {
08559       if (vms->oldmessages == 1) {
08560          res = ast_play_and_wait(chan, "digits/1");
08561          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08562          res = res ? res : ast_play_and_wait(chan, "vm-message");
08563       } else {
08564          res = say_and_wait(chan, vms->oldmessages, chan->language);
08565          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08566          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08567       }
08568    }
08569 
08570    return res;
08571 }
08572 
08573 /* GERMAN syntax */
08574 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08575 {
08576    /* Introduce messages they have */
08577    int res;
08578    res = ast_play_and_wait(chan, "vm-youhave");
08579    if (!res) {
08580       if (vms->newmessages) {
08581          if ((vms->newmessages == 1))
08582             res = ast_play_and_wait(chan, "digits/1F");
08583          else
08584             res = say_and_wait(chan, vms->newmessages, chan->language);
08585          if (!res)
08586             res = ast_play_and_wait(chan, "vm-INBOX");
08587          if (vms->oldmessages && !res)
08588             res = ast_play_and_wait(chan, "vm-and");
08589          else if (!res) {
08590             if ((vms->newmessages == 1))
08591                res = ast_play_and_wait(chan, "vm-message");
08592             else
08593                res = ast_play_and_wait(chan, "vm-messages");
08594          }
08595             
08596       }
08597       if (!res && vms->oldmessages) {
08598          if (vms->oldmessages == 1)
08599             res = ast_play_and_wait(chan, "digits/1F");
08600          else
08601             res = say_and_wait(chan, vms->oldmessages, chan->language);
08602          if (!res)
08603             res = ast_play_and_wait(chan, "vm-Old");
08604          if (!res) {
08605             if (vms->oldmessages == 1)
08606                res = ast_play_and_wait(chan, "vm-message");
08607             else
08608                res = ast_play_and_wait(chan, "vm-messages");
08609          }
08610       }
08611       if (!res) {
08612          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08613             res = ast_play_and_wait(chan, "vm-no");
08614             if (!res)
08615                res = ast_play_and_wait(chan, "vm-messages");
08616          }
08617       }
08618    }
08619    return res;
08620 }
08621 
08622 /* SPANISH syntax */
08623 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08624 {
08625    /* Introduce messages they have */
08626    int res;
08627    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08628       res = ast_play_and_wait(chan, "vm-youhaveno");
08629       if (!res)
08630          res = ast_play_and_wait(chan, "vm-messages");
08631    } else {
08632       res = ast_play_and_wait(chan, "vm-youhave");
08633    }
08634    if (!res) {
08635       if (vms->newmessages) {
08636          if (!res) {
08637             if ((vms->newmessages == 1)) {
08638                res = ast_play_and_wait(chan, "digits/1M");
08639                if (!res)
08640                   res = ast_play_and_wait(chan, "vm-message");
08641                if (!res)
08642                   res = ast_play_and_wait(chan, "vm-INBOXs");
08643             } else {
08644                res = say_and_wait(chan, vms->newmessages, chan->language);
08645                if (!res)
08646                   res = ast_play_and_wait(chan, "vm-messages");
08647                if (!res)
08648                   res = ast_play_and_wait(chan, "vm-INBOX");
08649             }
08650          }
08651          if (vms->oldmessages && !res)
08652             res = ast_play_and_wait(chan, "vm-and");
08653       }
08654       if (vms->oldmessages) {
08655          if (!res) {
08656             if (vms->oldmessages == 1) {
08657                res = ast_play_and_wait(chan, "digits/1M");
08658                if (!res)
08659                   res = ast_play_and_wait(chan, "vm-message");
08660                if (!res)
08661                   res = ast_play_and_wait(chan, "vm-Olds");
08662             } else {
08663                res = say_and_wait(chan, vms->oldmessages, chan->language);
08664                if (!res)
08665                   res = ast_play_and_wait(chan, "vm-messages");
08666                if (!res)
08667                   res = ast_play_and_wait(chan, "vm-Old");
08668             }
08669          }
08670       }
08671    }
08672 return res;
08673 }
08674 
08675 /* BRAZILIAN PORTUGUESE syntax */
08676 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08677    /* Introduce messages they have */
08678    int res;
08679    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08680       res = ast_play_and_wait(chan, "vm-nomessages");
08681       return res;
08682    } else {
08683       res = ast_play_and_wait(chan, "vm-youhave");
08684    }
08685    if (vms->newmessages) {
08686       if (!res)
08687          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08688       if ((vms->newmessages == 1)) {
08689          if (!res)
08690             res = ast_play_and_wait(chan, "vm-message");
08691          if (!res)
08692             res = ast_play_and_wait(chan, "vm-INBOXs");
08693       } else {
08694          if (!res)
08695             res = ast_play_and_wait(chan, "vm-messages");
08696          if (!res)
08697             res = ast_play_and_wait(chan, "vm-INBOX");
08698       }
08699       if (vms->oldmessages && !res)
08700          res = ast_play_and_wait(chan, "vm-and");
08701    }
08702    if (vms->oldmessages) {
08703       if (!res)
08704          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08705       if (vms->oldmessages == 1) {
08706          if (!res)
08707             res = ast_play_and_wait(chan, "vm-message");
08708          if (!res)
08709             res = ast_play_and_wait(chan, "vm-Olds");
08710       } else {
08711          if (!res)
08712             res = ast_play_and_wait(chan, "vm-messages");
08713          if (!res)
08714             res = ast_play_and_wait(chan, "vm-Old");
08715       }
08716    }
08717    return res;
08718 }
08719 
08720 /* FRENCH syntax */
08721 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08722 {
08723    /* Introduce messages they have */
08724    int res;
08725    res = ast_play_and_wait(chan, "vm-youhave");
08726    if (!res) {
08727       if (vms->newmessages) {
08728          res = say_and_wait(chan, vms->newmessages, chan->language);
08729          if (!res)
08730             res = ast_play_and_wait(chan, "vm-INBOX");
08731          if (vms->oldmessages && !res)
08732             res = ast_play_and_wait(chan, "vm-and");
08733          else if (!res) {
08734             if ((vms->newmessages == 1))
08735                res = ast_play_and_wait(chan, "vm-message");
08736             else
08737                res = ast_play_and_wait(chan, "vm-messages");
08738          }
08739             
08740       }
08741       if (!res && vms->oldmessages) {
08742          res = say_and_wait(chan, vms->oldmessages, chan->language);
08743          if (!res)
08744             res = ast_play_and_wait(chan, "vm-Old");
08745          if (!res) {
08746             if (vms->oldmessages == 1)
08747                res = ast_play_and_wait(chan, "vm-message");
08748             else
08749                res = ast_play_and_wait(chan, "vm-messages");
08750          }
08751       }
08752       if (!res) {
08753          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08754             res = ast_play_and_wait(chan, "vm-no");
08755             if (!res)
08756                res = ast_play_and_wait(chan, "vm-messages");
08757          }
08758       }
08759    }
08760    return res;
08761 }
08762 
08763 /* DUTCH syntax */
08764 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08765 {
08766    /* Introduce messages they have */
08767    int res;
08768    res = ast_play_and_wait(chan, "vm-youhave");
08769    if (!res) {
08770       if (vms->newmessages) {
08771          res = say_and_wait(chan, vms->newmessages, chan->language);
08772          if (!res) {
08773             if (vms->newmessages == 1)
08774                res = ast_play_and_wait(chan, "vm-INBOXs");
08775             else
08776                res = ast_play_and_wait(chan, "vm-INBOX");
08777          }
08778          if (vms->oldmessages && !res)
08779             res = ast_play_and_wait(chan, "vm-and");
08780          else if (!res) {
08781             if ((vms->newmessages == 1))
08782                res = ast_play_and_wait(chan, "vm-message");
08783             else
08784                res = ast_play_and_wait(chan, "vm-messages");
08785          }
08786             
08787       }
08788       if (!res && vms->oldmessages) {
08789          res = say_and_wait(chan, vms->oldmessages, chan->language);
08790          if (!res) {
08791             if (vms->oldmessages == 1)
08792                res = ast_play_and_wait(chan, "vm-Olds");
08793             else
08794                res = ast_play_and_wait(chan, "vm-Old");
08795          }
08796          if (!res) {
08797             if (vms->oldmessages == 1)
08798                res = ast_play_and_wait(chan, "vm-message");
08799             else
08800                res = ast_play_and_wait(chan, "vm-messages");
08801          }
08802       }
08803       if (!res) {
08804          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08805             res = ast_play_and_wait(chan, "vm-no");
08806             if (!res)
08807                res = ast_play_and_wait(chan, "vm-messages");
08808          }
08809       }
08810    }
08811    return res;
08812 }
08813 
08814 /* PORTUGUESE syntax */
08815 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08816 {
08817    /* Introduce messages they have */
08818    int res;
08819    res = ast_play_and_wait(chan, "vm-youhave");
08820    if (!res) {
08821       if (vms->newmessages) {
08822          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08823          if (!res) {
08824             if ((vms->newmessages == 1)) {
08825                res = ast_play_and_wait(chan, "vm-message");
08826                if (!res)
08827                   res = ast_play_and_wait(chan, "vm-INBOXs");
08828             } else {
08829                res = ast_play_and_wait(chan, "vm-messages");
08830                if (!res)
08831                   res = ast_play_and_wait(chan, "vm-INBOX");
08832             }
08833          }
08834          if (vms->oldmessages && !res)
08835             res = ast_play_and_wait(chan, "vm-and");
08836       }
08837       if (!res && vms->oldmessages) {
08838          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08839          if (!res) {
08840             if (vms->oldmessages == 1) {
08841                res = ast_play_and_wait(chan, "vm-message");
08842                if (!res)
08843                   res = ast_play_and_wait(chan, "vm-Olds");
08844             } else {
08845                res = ast_play_and_wait(chan, "vm-messages");
08846                if (!res)
08847                   res = ast_play_and_wait(chan, "vm-Old");
08848             }
08849          }
08850       }
08851       if (!res) {
08852          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08853             res = ast_play_and_wait(chan, "vm-no");
08854             if (!res)
08855                res = ast_play_and_wait(chan, "vm-messages");
08856          }
08857       }
08858    }
08859    return res;
08860 }
08861 
08862 
08863 /* CZECH syntax */
08864 /* in czech there must be declension of word new and message
08865  * czech        : english        : czech      : english
08866  * --------------------------------------------------------
08867  * vm-youhave   : you have 
08868  * vm-novou     : one new        : vm-zpravu  : message
08869  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08870  * vm-novych    : 5-infinite new : vm-zprav   : messages
08871  * vm-starou   : one old
08872  * vm-stare     : 2-4 old 
08873  * vm-starych   : 5-infinite old
08874  * jednu        : one   - falling 4. 
08875  * vm-no        : no  ( no messages )
08876  */
08877 
08878 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08879 {
08880    int res;
08881    res = ast_play_and_wait(chan, "vm-youhave");
08882    if (!res) {
08883       if (vms->newmessages) {
08884          if (vms->newmessages == 1) {
08885             res = ast_play_and_wait(chan, "digits/jednu");
08886          } else {
08887             res = say_and_wait(chan, vms->newmessages, chan->language);
08888          }
08889          if (!res) {
08890             if ((vms->newmessages == 1))
08891                res = ast_play_and_wait(chan, "vm-novou");
08892             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08893                res = ast_play_and_wait(chan, "vm-nove");
08894             if (vms->newmessages > 4)
08895                res = ast_play_and_wait(chan, "vm-novych");
08896          }
08897          if (vms->oldmessages && !res)
08898             res = ast_play_and_wait(chan, "vm-and");
08899          else if (!res) {
08900             if ((vms->newmessages == 1))
08901                res = ast_play_and_wait(chan, "vm-zpravu");
08902             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08903                res = ast_play_and_wait(chan, "vm-zpravy");
08904             if (vms->newmessages > 4)
08905                res = ast_play_and_wait(chan, "vm-zprav");
08906          }
08907       }
08908       if (!res && vms->oldmessages) {
08909          res = say_and_wait(chan, vms->oldmessages, chan->language);
08910          if (!res) {
08911             if ((vms->oldmessages == 1))
08912                res = ast_play_and_wait(chan, "vm-starou");
08913             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08914                res = ast_play_and_wait(chan, "vm-stare");
08915             if (vms->oldmessages > 4)
08916                res = ast_play_and_wait(chan, "vm-starych");
08917          }
08918          if (!res) {
08919             if ((vms->oldmessages == 1))
08920                res = ast_play_and_wait(chan, "vm-zpravu");
08921             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08922                res = ast_play_and_wait(chan, "vm-zpravy");
08923             if (vms->oldmessages > 4)
08924                res = ast_play_and_wait(chan, "vm-zprav");
08925          }
08926       }
08927       if (!res) {
08928          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08929             res = ast_play_and_wait(chan, "vm-no");
08930             if (!res)
08931                res = ast_play_and_wait(chan, "vm-zpravy");
08932          }
08933       }
08934    }
08935    return res;
08936 }
08937 
08938 /* CHINESE (Taiwan) syntax */
08939 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08940 {
08941    int res;
08942    /* Introduce messages they have */
08943    res = ast_play_and_wait(chan, "vm-you");
08944 
08945    if (!res && vms->newmessages) {
08946       res = ast_play_and_wait(chan, "vm-have");
08947       if (!res)
08948          res = say_and_wait(chan, vms->newmessages, chan->language);
08949       if (!res)
08950          res = ast_play_and_wait(chan, "vm-tong");
08951       if (!res)
08952          res = ast_play_and_wait(chan, "vm-INBOX");
08953       if (vms->oldmessages && !res)
08954          res = ast_play_and_wait(chan, "vm-and");
08955       else if (!res) 
08956          res = ast_play_and_wait(chan, "vm-messages");
08957    }
08958    if (!res && vms->oldmessages) {
08959       res = ast_play_and_wait(chan, "vm-have");
08960       if (!res)
08961          res = say_and_wait(chan, vms->oldmessages, chan->language);
08962       if (!res)
08963          res = ast_play_and_wait(chan, "vm-tong");
08964       if (!res)
08965          res = ast_play_and_wait(chan, "vm-Old");
08966       if (!res)
08967          res = ast_play_and_wait(chan, "vm-messages");
08968    }
08969    if (!res && !vms->oldmessages && !vms->newmessages) {
08970       res = ast_play_and_wait(chan, "vm-haveno");
08971       if (!res)
08972          res = ast_play_and_wait(chan, "vm-messages");
08973    }
08974    return res;
08975 }
08976 
08977 /* Vietnamese syntax */
08978 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
08979 {
08980    int res;
08981 
08982    /* Introduce messages they have */
08983    res = ast_play_and_wait(chan, "vm-youhave");
08984    if (!res) {
08985       if (vms->newmessages) {
08986          res = say_and_wait(chan, vms->newmessages, chan->language);
08987          if (!res)
08988             res = ast_play_and_wait(chan, "vm-INBOX");
08989          if (vms->oldmessages && !res)
08990             res = ast_play_and_wait(chan, "vm-and");
08991       }
08992       if (!res && vms->oldmessages) {
08993          res = say_and_wait(chan, vms->oldmessages, chan->language);
08994          if (!res)
08995             res = ast_play_and_wait(chan, "vm-Old");        
08996       }
08997       if (!res) {
08998          if (!vms->oldmessages && !vms->newmessages) {
08999             res = ast_play_and_wait(chan, "vm-no");
09000             if (!res)
09001                res = ast_play_and_wait(chan, "vm-message");
09002          }
09003       }
09004    }
09005    return res;
09006 }
09007 
09008 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09009 {
09010    char prefile[256];
09011    
09012    /* Notify the user that the temp greeting is set and give them the option to remove it */
09013    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09014    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09015       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09016       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09017          ast_play_and_wait(chan, "vm-tempgreetactive");
09018       }
09019       DISPOSE(prefile, -1);
09020    }
09021 
09022    /* Play voicemail intro - syntax is different for different languages */
09023    if (0) {
09024       return 0;
09025    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09026       return vm_intro_cs(chan, vms);
09027    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09028       static int deprecation_warning = 0;
09029       if (deprecation_warning++ % 10 == 0) {
09030          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09031       }
09032       return vm_intro_cs(chan, vms);
09033    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09034       return vm_intro_de(chan, vms);
09035    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09036       return vm_intro_es(chan, vms);
09037    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09038       return vm_intro_fr(chan, vms);
09039    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09040       return vm_intro_gr(chan, vms);
09041    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09042       return vm_intro_he(chan, vms);
09043    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09044       return vm_intro_it(chan, vms);
09045    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09046       return vm_intro_nl(chan, vms);
09047    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09048       return vm_intro_no(chan, vms);
09049    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09050       return vm_intro_pl(chan, vms);
09051    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09052       return vm_intro_pt_BR(chan, vms);
09053    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09054       return vm_intro_pt(chan, vms);
09055    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09056       return vm_intro_multilang(chan, vms, "n");
09057    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09058       return vm_intro_se(chan, vms);
09059    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09060       return vm_intro_multilang(chan, vms, "n");
09061    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09062       return vm_intro_vi(chan, vms);
09063    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09064       return vm_intro_zh(chan, vms);
09065    } else {                                             /* Default to ENGLISH */
09066       return vm_intro_en(chan, vms);
09067    }
09068 }
09069 
09070 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09071 {
09072    int res = 0;
09073    /* Play instructions and wait for new command */
09074    while (!res) {
09075       if (vms->starting) {
09076          if (vms->lastmsg > -1) {
09077             if (skipadvanced)
09078                res = ast_play_and_wait(chan, "vm-onefor-full");
09079             else
09080                res = ast_play_and_wait(chan, "vm-onefor");
09081             if (!res)
09082                res = vm_play_folder_name(chan, vms->vmbox);
09083          }
09084          if (!res) {
09085             if (skipadvanced)
09086                res = ast_play_and_wait(chan, "vm-opts-full");
09087             else
09088                res = ast_play_and_wait(chan, "vm-opts");
09089          }
09090       } else {
09091          /* Added for additional help */
09092          if (skipadvanced) {
09093             res = ast_play_and_wait(chan, "vm-onefor-full");
09094             if (!res)
09095                res = vm_play_folder_name(chan, vms->vmbox);
09096             res = ast_play_and_wait(chan, "vm-opts-full");
09097          }
09098          /* Logic:
09099           * If the current message is not the first OR
09100           * if we're listening to the first new message and there are
09101           * also urgent messages, then prompt for navigation to the
09102           * previous message
09103           */
09104          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09105             res = ast_play_and_wait(chan, "vm-prev");
09106          }
09107          if (!res && !skipadvanced)
09108             res = ast_play_and_wait(chan, "vm-advopts");
09109          if (!res)
09110             res = ast_play_and_wait(chan, "vm-repeat");
09111          /* Logic:
09112           * If we're not listening to the last message OR
09113           * we're listening to the last urgent message and there are
09114           * also new non-urgent messages, then prompt for navigation
09115           * to the next message
09116           */
09117          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09118             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09119             res = ast_play_and_wait(chan, "vm-next");
09120          }
09121          if (!res) {
09122             if (!vms->deleted[vms->curmsg])
09123                res = ast_play_and_wait(chan, "vm-delete");
09124             else
09125                res = ast_play_and_wait(chan, "vm-undelete");
09126             if (!res)
09127                res = ast_play_and_wait(chan, "vm-toforward");
09128             if (!res)
09129                res = ast_play_and_wait(chan, "vm-savemessage");
09130          }
09131       }
09132       if (!res) {
09133          res = ast_play_and_wait(chan, "vm-helpexit");
09134       }
09135       if (!res)
09136          res = ast_waitfordigit(chan, 6000);
09137       if (!res) {
09138          vms->repeats++;
09139          if (vms->repeats > 2) {
09140             res = 't';
09141          }
09142       }
09143    }
09144    return res;
09145 }
09146 
09147 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09148 {
09149    int res = 0;
09150    /* Play instructions and wait for new command */
09151    while (!res) {
09152       if (vms->lastmsg > -1) {
09153          res = ast_play_and_wait(chan, "vm-listen");
09154          if (!res)
09155             res = vm_play_folder_name(chan, vms->vmbox);
09156          if (!res)
09157             res = ast_play_and_wait(chan, "press");
09158          if (!res)
09159             res = ast_play_and_wait(chan, "digits/1");
09160       }
09161       if (!res)
09162          res = ast_play_and_wait(chan, "vm-opts");
09163       if (!res) {
09164          vms->starting = 0;
09165          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09166       }
09167    }
09168    return res;
09169 }
09170 
09171 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09172 {
09173    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09174       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09175    } else {             /* Default to ENGLISH */
09176       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09177    }
09178 }
09179 
09180 
09181 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09182 {
09183    int cmd = 0;
09184    int duration = 0;
09185    int tries = 0;
09186    char newpassword[80] = "";
09187    char newpassword2[80] = "";
09188    char prefile[PATH_MAX] = "";
09189    unsigned char buf[256];
09190    int bytes = 0;
09191 
09192    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09193    if (ast_adsi_available(chan)) {
09194       bytes += adsi_logo(buf + bytes);
09195       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09196       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09197       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09198       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09199       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09200    }
09201 
09202    /* If forcename is set, have the user record their name */
09203    if (ast_test_flag(vmu, VM_FORCENAME)) {
09204       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09205       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09206          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09207          if (cmd < 0 || cmd == 't' || cmd == '#')
09208             return cmd;
09209       }
09210    }
09211 
09212    /* If forcegreetings is set, have the user record their greetings */
09213    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09214       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09215       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09216          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09217          if (cmd < 0 || cmd == 't' || cmd == '#')
09218             return cmd;
09219       }
09220 
09221       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09222       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09223          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09224          if (cmd < 0 || cmd == 't' || cmd == '#')
09225             return cmd;
09226       }
09227    }
09228 
09229    /*
09230     * Change the password last since new users will be able to skip over any steps this one comes before
09231     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09232     */
09233    for (;;) {
09234       newpassword[1] = '\0';
09235       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09236       if (cmd == '#')
09237          newpassword[0] = '\0';
09238       if (cmd < 0 || cmd == 't' || cmd == '#')
09239          return cmd;
09240       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09241       if (cmd < 0 || cmd == 't' || cmd == '#')
09242          return cmd;
09243       cmd = check_password(vmu, newpassword); /* perform password validation */
09244       if (cmd != 0) {
09245          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09246          cmd = ast_play_and_wait(chan, vm_invalid_password);
09247       } else {
09248          newpassword2[1] = '\0';
09249          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09250          if (cmd == '#')
09251             newpassword2[0] = '\0';
09252          if (cmd < 0 || cmd == 't' || cmd == '#')
09253             return cmd;
09254          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09255          if (cmd < 0 || cmd == 't' || cmd == '#')
09256             return cmd;
09257          if (!strcmp(newpassword, newpassword2))
09258             break;
09259          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09260          cmd = ast_play_and_wait(chan, vm_mismatch);
09261       }
09262       if (++tries == 3)
09263          return -1;
09264       if (cmd != 0) {
09265          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09266       }
09267    }
09268    if (pwdchange & PWDCHANGE_INTERNAL)
09269       vm_change_password(vmu, newpassword);
09270    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09271       vm_change_password_shell(vmu, newpassword);
09272 
09273    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09274    cmd = ast_play_and_wait(chan, vm_passchanged);
09275 
09276    return cmd;
09277 }
09278 
09279 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09280 {
09281    int cmd = 0;
09282    int retries = 0;
09283    int duration = 0;
09284    char newpassword[80] = "";
09285    char newpassword2[80] = "";
09286    char prefile[PATH_MAX] = "";
09287    unsigned char buf[256];
09288    int bytes = 0;
09289 
09290    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09291    if (ast_adsi_available(chan)) {
09292       bytes += adsi_logo(buf + bytes);
09293       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09294       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09295       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09296       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09297       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09298    }
09299    while ((cmd >= 0) && (cmd != 't')) {
09300       if (cmd)
09301          retries = 0;
09302       switch (cmd) {
09303       case '1': /* Record your unavailable message */
09304          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09305          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09306          break;
09307       case '2':  /* Record your busy message */
09308          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09309          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09310          break;
09311       case '3': /* Record greeting */
09312          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09313          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09314          break;
09315       case '4':  /* manage the temporary greeting */
09316          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09317          break;
09318       case '5': /* change password */
09319          if (vmu->password[0] == '-') {
09320             cmd = ast_play_and_wait(chan, "vm-no");
09321             break;
09322          }
09323          newpassword[1] = '\0';
09324          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09325          if (cmd == '#')
09326             newpassword[0] = '\0';
09327          else {
09328             if (cmd < 0)
09329                break;
09330             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09331                break;
09332             }
09333          }
09334          cmd = check_password(vmu, newpassword); /* perform password validation */
09335          if (cmd != 0) {
09336             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09337             cmd = ast_play_and_wait(chan, vm_invalid_password);
09338             if (!cmd) {
09339                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09340             }
09341             break;
09342          }
09343          newpassword2[1] = '\0';
09344          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09345          if (cmd == '#')
09346             newpassword2[0] = '\0';
09347          else {
09348             if (cmd < 0)
09349                break;
09350 
09351             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09352                break;
09353             }
09354          }
09355          if (strcmp(newpassword, newpassword2)) {
09356             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09357             cmd = ast_play_and_wait(chan, vm_mismatch);
09358             if (!cmd) {
09359                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09360             }
09361             break;
09362          }
09363 
09364          if (pwdchange & PWDCHANGE_INTERNAL) {
09365             vm_change_password(vmu, newpassword);
09366          }
09367          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09368             vm_change_password_shell(vmu, newpassword);
09369          }
09370 
09371          ast_debug(1, "User %s set password to %s of length %d\n",
09372             vms->username, newpassword, (int) strlen(newpassword));
09373          cmd = ast_play_and_wait(chan, vm_passchanged);
09374          break;
09375       case '*': 
09376          cmd = 't';
09377          break;
09378       default: 
09379          cmd = 0;
09380          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09381          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09382          if (ast_fileexists(prefile, NULL, NULL)) {
09383             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09384          }
09385          DISPOSE(prefile, -1);
09386          if (!cmd) {
09387             cmd = ast_play_and_wait(chan, "vm-options");
09388          }
09389          if (!cmd) {
09390             cmd = ast_waitfordigit(chan, 6000);
09391          }
09392          if (!cmd) {
09393             retries++;
09394          }
09395          if (retries > 3) {
09396             cmd = 't';
09397          }
09398          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09399       }
09400    }
09401    if (cmd == 't')
09402       cmd = 0;
09403    return cmd;
09404 }
09405 
09406 /*!
09407  * \brief The handler for 'record a temporary greeting'. 
09408  * \param chan
09409  * \param vmu
09410  * \param vms
09411  * \param fmtc
09412  * \param record_gain
09413  *
09414  * This is option 4 from the mailbox options menu.
09415  * This function manages the following promptings:
09416  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09417  * 2: remove (delete) the temporary greeting.
09418  * *: return to the main menu.
09419  *
09420  * \return zero on success, -1 on error.
09421  */
09422 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09423 {
09424    int cmd = 0;
09425    int retries = 0;
09426    int duration = 0;
09427    char prefile[PATH_MAX] = "";
09428    unsigned char buf[256];
09429    int bytes = 0;
09430 
09431    if (ast_adsi_available(chan)) {
09432       bytes += adsi_logo(buf + bytes);
09433       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09434       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09435       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09436       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09437       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09438    }
09439 
09440    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09441    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09442    while ((cmd >= 0) && (cmd != 't')) {
09443       if (cmd)
09444          retries = 0;
09445       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09446       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09447          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09448          cmd = 't';  
09449       } else {
09450          switch (cmd) {
09451          case '1':
09452             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09453             break;
09454          case '2':
09455             DELETE(prefile, -1, prefile, vmu);
09456             ast_play_and_wait(chan, "vm-tempremoved");
09457             cmd = 't';  
09458             break;
09459          case '*': 
09460             cmd = 't';
09461             break;
09462          default:
09463             cmd = ast_play_and_wait(chan,
09464                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09465                   "vm-tempgreeting2" : "vm-tempgreeting");
09466             if (!cmd) {
09467                cmd = ast_waitfordigit(chan, 6000);
09468             }
09469             if (!cmd) {
09470                retries++;
09471             }
09472             if (retries > 3) {
09473                cmd = 't';
09474             }
09475             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09476          }
09477       }
09478       DISPOSE(prefile, -1);
09479    }
09480    if (cmd == 't')
09481       cmd = 0;
09482    return cmd;
09483 }
09484 
09485 /*!
09486  * \brief Greek syntax for 'You have N messages' greeting.
09487  * \param chan
09488  * \param vms
09489  * \param vmu
09490  *
09491  * \return zero on success, -1 on error.
09492  */   
09493 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09494 {
09495    int cmd = 0;
09496 
09497    if (vms->lastmsg > -1) {
09498       cmd = play_message(chan, vmu, vms);
09499    } else {
09500       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09501       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09502          if (!cmd) {
09503             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09504             cmd = ast_play_and_wait(chan, vms->fn);
09505          }
09506          if (!cmd)
09507             cmd = ast_play_and_wait(chan, "vm-messages");
09508       } else {
09509          if (!cmd)
09510             cmd = ast_play_and_wait(chan, "vm-messages");
09511          if (!cmd) {
09512             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09513             cmd = ast_play_and_wait(chan, vms->fn);
09514          }
09515       }
09516    } 
09517    return cmd;
09518 }
09519 
09520 /* Hebrew Syntax */
09521 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09522 {
09523    int cmd = 0;
09524 
09525    if (vms->lastmsg > -1) {
09526       cmd = play_message(chan, vmu, vms);
09527    } else {
09528       if (!strcasecmp(vms->fn, "INBOX")) {
09529          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09530       } else {
09531          cmd = ast_play_and_wait(chan, "vm-nomessages");
09532       }
09533    }
09534    return cmd;
09535 }
09536 
09537 /*! 
09538  * \brief Default English syntax for 'You have N messages' greeting.
09539  * \param chan
09540  * \param vms
09541  * \param vmu
09542  *
09543  * \return zero on success, -1 on error.
09544  */
09545 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09546 {
09547    int cmd = 0;
09548 
09549    if (vms->lastmsg > -1) {
09550       cmd = play_message(chan, vmu, vms);
09551    } else {
09552       cmd = ast_play_and_wait(chan, "vm-youhave");
09553       if (!cmd) 
09554          cmd = ast_play_and_wait(chan, "vm-no");
09555       if (!cmd) {
09556          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09557          cmd = ast_play_and_wait(chan, vms->fn);
09558       }
09559       if (!cmd)
09560          cmd = ast_play_and_wait(chan, "vm-messages");
09561    }
09562    return cmd;
09563 }
09564 
09565 /*! 
09566  *\brief Italian syntax for 'You have N messages' greeting.
09567  * \param chan
09568  * \param vms
09569  * \param vmu
09570  *
09571  * \return zero on success, -1 on error.
09572  */
09573 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09574 {
09575    int cmd;
09576 
09577    if (vms->lastmsg > -1) {
09578       cmd = play_message(chan, vmu, vms);
09579    } else {
09580       cmd = ast_play_and_wait(chan, "vm-no");
09581       if (!cmd)
09582          cmd = ast_play_and_wait(chan, "vm-message");
09583       if (!cmd) {
09584          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09585          cmd = ast_play_and_wait(chan, vms->fn);
09586       }
09587    }
09588    return cmd;
09589 }
09590 
09591 /*! 
09592  * \brief Spanish syntax for 'You have N messages' greeting.
09593  * \param chan
09594  * \param vms
09595  * \param vmu
09596  *
09597  * \return zero on success, -1 on error.
09598  */
09599 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09600 {
09601    int cmd;
09602 
09603    if (vms->lastmsg > -1) {
09604       cmd = play_message(chan, vmu, vms);
09605    } else {
09606       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09607       if (!cmd)
09608          cmd = ast_play_and_wait(chan, "vm-messages");
09609       if (!cmd) {
09610          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09611          cmd = ast_play_and_wait(chan, vms->fn);
09612       }
09613    }
09614    return cmd;
09615 }
09616 
09617 /*! 
09618  * \brief Portuguese syntax for 'You have N messages' greeting.
09619  * \param chan
09620  * \param vms
09621  * \param vmu
09622  *
09623  * \return zero on success, -1 on error.
09624  */
09625 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09626 {
09627    int cmd;
09628 
09629    if (vms->lastmsg > -1) {
09630       cmd = play_message(chan, vmu, vms);
09631    } else {
09632       cmd = ast_play_and_wait(chan, "vm-no");
09633       if (!cmd) {
09634          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09635          cmd = ast_play_and_wait(chan, vms->fn);
09636       }
09637       if (!cmd)
09638          cmd = ast_play_and_wait(chan, "vm-messages");
09639    }
09640    return cmd;
09641 }
09642 
09643 /*! 
09644  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09645  * \param chan
09646  * \param vms
09647  * \param vmu
09648  *
09649  * \return zero on success, -1 on error.
09650  */
09651 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09652 {
09653    int cmd;
09654 
09655    if (vms->lastmsg > -1) {
09656       cmd = play_message(chan, vmu, vms);
09657    } else {
09658       cmd = ast_play_and_wait(chan, "vm-you");
09659       if (!cmd) 
09660          cmd = ast_play_and_wait(chan, "vm-haveno");
09661       if (!cmd)
09662          cmd = ast_play_and_wait(chan, "vm-messages");
09663       if (!cmd) {
09664          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09665          cmd = ast_play_and_wait(chan, vms->fn);
09666       }
09667    }
09668    return cmd;
09669 }
09670 
09671 /*! 
09672  * \brief Vietnamese syntax for 'You have N messages' greeting.
09673  * \param chan
09674  * \param vms
09675  * \param vmu
09676  *
09677  * \return zero on success, -1 on error.
09678  */
09679 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09680 {
09681    int cmd = 0;
09682 
09683    if (vms->lastmsg > -1) {
09684       cmd = play_message(chan, vmu, vms);
09685    } else {
09686       cmd = ast_play_and_wait(chan, "vm-no");
09687       if (!cmd) {
09688          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09689          cmd = ast_play_and_wait(chan, vms->fn);
09690       }
09691    }
09692    return cmd;
09693 }
09694 
09695 /*!
09696  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09697  * \param chan The channel for the current user. We read the language property from this.
09698  * \param vms passed into the language-specific vm_browse_messages function.
09699  * \param vmu passed into the language-specific vm_browse_messages function.
09700  * 
09701  * The method to be invoked is determined by the value of language code property in the user's channel.
09702  * The default (when unable to match) is to use english.
09703  *
09704  * \return zero on success, -1 on error.
09705  */
09706 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09707 {
09708    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09709       return vm_browse_messages_es(chan, vms, vmu);
09710    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09711       return vm_browse_messages_gr(chan, vms, vmu);
09712    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09713       return vm_browse_messages_he(chan, vms, vmu);
09714    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09715       return vm_browse_messages_it(chan, vms, vmu);
09716    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09717       return vm_browse_messages_pt(chan, vms, vmu);
09718    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09719       return vm_browse_messages_vi(chan, vms, vmu);
09720    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09721       return vm_browse_messages_zh(chan, vms, vmu);
09722    } else {                                             /* Default to English syntax */
09723       return vm_browse_messages_en(chan, vms, vmu);
09724    }
09725 }
09726 
09727 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09728          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09729          int skipuser, int max_logins, int silent)
09730 {
09731    int useadsi = 0, valid = 0, logretries = 0;
09732    char password[AST_MAX_EXTENSION]="", *passptr;
09733    struct ast_vm_user vmus, *vmu = NULL;
09734 
09735    /* If ADSI is supported, setup login screen */
09736    adsi_begin(chan, &useadsi);
09737    if (!skipuser && useadsi)
09738       adsi_login(chan);
09739    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09740    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09741       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09742       return -1;
09743    }
09744 
09745    /* Authenticate them and get their mailbox/password */
09746 
09747    while (!valid && (logretries < max_logins)) {
09748       /* Prompt for, and read in the username */
09749       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09750          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09751          return -1;
09752       }
09753       if (ast_strlen_zero(mailbox)) {
09754          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09755             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09756          } else {
09757             ast_verb(3, "Username not entered\n"); 
09758             return -1;
09759          }
09760       } else if (mailbox[0] == '*') {
09761          /* user entered '*' */
09762          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09763          if (ast_exists_extension(chan, chan->context, "a", 1,
09764             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09765             return -1;
09766          }
09767          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09768          mailbox[0] = '\0';
09769       }
09770 
09771       if (useadsi)
09772          adsi_password(chan);
09773 
09774       if (!ast_strlen_zero(prefix)) {
09775          char fullusername[80] = "";
09776          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09777          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09778          ast_copy_string(mailbox, fullusername, mailbox_size);
09779       }
09780 
09781       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09782       vmu = find_user(&vmus, context, mailbox);
09783       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09784          /* saved password is blank, so don't bother asking */
09785          password[0] = '\0';
09786       } else {
09787          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09788          if (ast_streamfile(chan, vm_password, chan->language)) {
09789             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09790             return -1;
09791          }
09792          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09793             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09794             return -1;
09795          } else if (password[0] == '*') {
09796             /* user entered '*' */
09797             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09798             if (ast_exists_extension(chan, chan->context, "a", 1,
09799                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09800                mailbox[0] = '*';
09801                return -1;
09802             }
09803             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09804             mailbox[0] = '\0';
09805             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09806             vmu = NULL;
09807          }
09808       }
09809 
09810       if (vmu) {
09811          passptr = vmu->password;
09812          if (passptr[0] == '-') passptr++;
09813       }
09814       if (vmu && !strcmp(passptr, password))
09815          valid++;
09816       else {
09817          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09818          if (!ast_strlen_zero(prefix))
09819             mailbox[0] = '\0';
09820       }
09821       logretries++;
09822       if (!valid) {
09823          if (skipuser || logretries >= max_logins) {
09824             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09825             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09826                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09827                return -1;
09828             }
09829          } else {
09830             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09831             if (useadsi)
09832                adsi_login(chan);
09833             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09834                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09835                return -1;
09836             }
09837          }
09838          if (ast_waitstream(chan, "")) /* Channel is hung up */
09839             return -1;
09840       }
09841    }
09842    if (!valid && (logretries >= max_logins)) {
09843       ast_stopstream(chan);
09844       ast_play_and_wait(chan, "vm-goodbye");
09845       return -1;
09846    }
09847    if (vmu && !skipuser) {
09848       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09849    }
09850    return 0;
09851 }
09852 
09853 static int vm_execmain(struct ast_channel *chan, const char *data)
09854 {
09855    /* XXX This is, admittedly, some pretty horrendous code.  For some
09856       reason it just seemed a lot easier to do with GOTO's.  I feel
09857       like I'm back in my GWBASIC days. XXX */
09858    int res = -1;
09859    int cmd = 0;
09860    int valid = 0;
09861    char prefixstr[80] ="";
09862    char ext_context[256]="";
09863    int box;
09864    int useadsi = 0;
09865    int skipuser = 0;
09866    struct vm_state vms;
09867    struct ast_vm_user *vmu = NULL, vmus;
09868    char *context = NULL;
09869    int silentexit = 0;
09870    struct ast_flags flags = { 0 };
09871    signed char record_gain = 0;
09872    int play_auto = 0;
09873    int play_folder = 0;
09874    int in_urgent = 0;
09875 #ifdef IMAP_STORAGE
09876    int deleted = 0;
09877 #endif
09878 
09879    /* Add the vm_state to the active list and keep it active */
09880    memset(&vms, 0, sizeof(vms));
09881 
09882    vms.lastmsg = -1;
09883 
09884    memset(&vmus, 0, sizeof(vmus));
09885 
09886    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09887    if (chan->_state != AST_STATE_UP) {
09888       ast_debug(1, "Before ast_answer\n");
09889       ast_answer(chan);
09890    }
09891 
09892    if (!ast_strlen_zero(data)) {
09893       char *opts[OPT_ARG_ARRAY_SIZE];
09894       char *parse;
09895       AST_DECLARE_APP_ARGS(args,
09896          AST_APP_ARG(argv0);
09897          AST_APP_ARG(argv1);
09898       );
09899 
09900       parse = ast_strdupa(data);
09901 
09902       AST_STANDARD_APP_ARGS(args, parse);
09903 
09904       if (args.argc == 2) {
09905          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09906             return -1;
09907          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09908             int gain;
09909             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09910                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09911                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09912                   return -1;
09913                } else {
09914                   record_gain = (signed char) gain;
09915                }
09916             } else {
09917                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09918             }
09919          }
09920          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09921             play_auto = 1;
09922             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09923                /* See if it is a folder name first */
09924                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09925                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09926                      play_folder = -1;
09927                   }
09928                } else {
09929                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09930                }
09931             } else {
09932                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09933             }
09934             if (play_folder > 9 || play_folder < 0) {
09935                ast_log(AST_LOG_WARNING,
09936                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09937                   opts[OPT_ARG_PLAYFOLDER]);
09938                play_folder = 0;
09939             }
09940          }
09941       } else {
09942          /* old style options parsing */
09943          while (*(args.argv0)) {
09944             if (*(args.argv0) == 's')
09945                ast_set_flag(&flags, OPT_SILENT);
09946             else if (*(args.argv0) == 'p')
09947                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09948             else 
09949                break;
09950             (args.argv0)++;
09951          }
09952 
09953       }
09954 
09955       valid = ast_test_flag(&flags, OPT_SILENT);
09956 
09957       if ((context = strchr(args.argv0, '@')))
09958          *context++ = '\0';
09959 
09960       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09961          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09962       else
09963          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09964 
09965       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09966          skipuser++;
09967       else
09968          valid = 0;
09969    }
09970 
09971    if (!valid)
09972       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09973 
09974    ast_debug(1, "After vm_authenticate\n");
09975 
09976    if (vms.username[0] == '*') {
09977       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
09978 
09979       /* user entered '*' */
09980       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
09981          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
09982          res = 0; /* prevent hangup */
09983          goto out;
09984       }
09985    }
09986 
09987    if (!res) {
09988       valid = 1;
09989       if (!skipuser)
09990          vmu = &vmus;
09991    } else {
09992       res = 0;
09993    }
09994 
09995    /* If ADSI is supported, setup login screen */
09996    adsi_begin(chan, &useadsi);
09997 
09998    ast_test_suite_assert(valid);
09999    if (!valid) {
10000       goto out;
10001    }
10002    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10003 
10004 #ifdef IMAP_STORAGE
10005    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10006    pthread_setspecific(ts_vmstate.key, &vms);
10007 
10008    vms.interactive = 1;
10009    vms.updated = 1;
10010    if (vmu)
10011       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10012    vmstate_insert(&vms);
10013    init_vm_state(&vms);
10014 #endif
10015    /* Avoid allocating a buffer of 0 bytes, because some platforms really don't like that. */
10016    if (!(vms.deleted = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
10017       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
10018       cmd = ast_play_and_wait(chan, "an-error-has-occured");
10019       return -1;
10020    }
10021    if (!(vms.heard = ast_calloc(vmu->maxmsg ? vmu->maxmsg : 1, sizeof(int)))) {
10022       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
10023       cmd = ast_play_and_wait(chan, "an-error-has-occured");
10024       return -1;
10025    }
10026    
10027    /* Set language from config to override channel language */
10028    if (!ast_strlen_zero(vmu->language))
10029       ast_string_field_set(chan, language, vmu->language);
10030 
10031    /* Retrieve urgent, old and new message counts */
10032    ast_debug(1, "Before open_mailbox\n");
10033    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10034    if (res < 0)
10035       goto out;
10036    vms.oldmessages = vms.lastmsg + 1;
10037    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10038    /* check INBOX */
10039    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10040    if (res < 0)
10041       goto out;
10042    vms.newmessages = vms.lastmsg + 1;
10043    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10044    /* Start in Urgent */
10045    in_urgent = 1;
10046    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10047    if (res < 0)
10048       goto out;
10049    vms.urgentmessages = vms.lastmsg + 1;
10050    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10051 
10052    /* Select proper mailbox FIRST!! */
10053    if (play_auto) {
10054       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10055       if (vms.urgentmessages) {
10056          in_urgent = 1;
10057          res = open_mailbox(&vms, vmu, 11);
10058       } else {
10059          in_urgent = 0;
10060          res = open_mailbox(&vms, vmu, play_folder);
10061       }
10062       if (res < 0)
10063          goto out;
10064 
10065       /* If there are no new messages, inform the user and hangup */
10066       if (vms.lastmsg == -1) {
10067          in_urgent = 0;
10068          cmd = vm_browse_messages(chan, &vms, vmu);
10069          res = 0;
10070          goto out;
10071       }
10072    } else {
10073       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10074          /* If we only have old messages start here */
10075          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10076          in_urgent = 0;
10077          play_folder = 1;
10078          if (res < 0)
10079             goto out;
10080       } else if (!vms.urgentmessages && vms.newmessages) {
10081          /* If we have new messages but none are urgent */
10082          in_urgent = 0;
10083          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10084          if (res < 0)
10085             goto out;
10086       }
10087    }
10088 
10089    if (useadsi)
10090       adsi_status(chan, &vms);
10091    res = 0;
10092 
10093    /* Check to see if this is a new user */
10094    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10095       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10096       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10097          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10098       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10099       if ((cmd == 't') || (cmd == '#')) {
10100          /* Timeout */
10101          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10102          res = 0;
10103          goto out;
10104       } else if (cmd < 0) {
10105          /* Hangup */
10106          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10107          res = -1;
10108          goto out;
10109       }
10110    }
10111 #ifdef IMAP_STORAGE
10112       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10113       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10114          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10115          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10116       }
10117       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10118       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10119          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10120          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10121       }
10122 #endif
10123 
10124    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10125    if (play_auto) {
10126       cmd = '1';
10127    } else {
10128       cmd = vm_intro(chan, vmu, &vms);
10129    }
10130    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10131 
10132    vms.repeats = 0;
10133    vms.starting = 1;
10134    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10135       /* Run main menu */
10136       switch (cmd) {
10137       case '1': /* First message */
10138          vms.curmsg = 0;
10139          /* Fall through */
10140       case '5': /* Play current message */
10141          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10142          cmd = vm_browse_messages(chan, &vms, vmu);
10143          break;
10144       case '2': /* Change folders */
10145          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10146          if (useadsi)
10147             adsi_folders(chan, 0, "Change to folder...");
10148 
10149          cmd = get_folder2(chan, "vm-changeto", 0);
10150          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10151          if (cmd == '#') {
10152             cmd = 0;
10153          } else if (cmd > 0) {
10154             cmd = cmd - '0';
10155             res = close_mailbox(&vms, vmu);
10156             if (res == ERROR_LOCK_PATH)
10157                goto out;
10158             /* If folder is not urgent, set in_urgent to zero! */
10159             if (cmd != 11) in_urgent = 0;
10160             res = open_mailbox(&vms, vmu, cmd);
10161             if (res < 0)
10162                goto out;
10163             play_folder = cmd;
10164             cmd = 0;
10165          }
10166          if (useadsi)
10167             adsi_status2(chan, &vms);
10168 
10169          if (!cmd) {
10170             cmd = vm_play_folder_name(chan, vms.vmbox);
10171          }
10172 
10173          vms.starting = 1;
10174          break;
10175       case '3': /* Advanced options */
10176          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10177          cmd = 0;
10178          vms.repeats = 0;
10179          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10180             switch (cmd) {
10181             case '1': /* Reply */
10182                if (vms.lastmsg > -1 && !vms.starting) {
10183                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10184                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10185                      res = cmd;
10186                      goto out;
10187                   }
10188                } else {
10189                   cmd = ast_play_and_wait(chan, "vm-sorry");
10190                }
10191                cmd = 't';
10192                break;
10193             case '2': /* Callback */
10194                if (!vms.starting)
10195                   ast_verb(3, "Callback Requested\n");
10196                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10197                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10198                   if (cmd == 9) {
10199                      silentexit = 1;
10200                      goto out;
10201                   } else if (cmd == ERROR_LOCK_PATH) {
10202                      res = cmd;
10203                      goto out;
10204                   }
10205                } else {
10206                   cmd = ast_play_and_wait(chan, "vm-sorry");
10207                }
10208                cmd = 't';
10209                break;
10210             case '3': /* Envelope */
10211                if (vms.lastmsg > -1 && !vms.starting) {
10212                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10213                   if (cmd == ERROR_LOCK_PATH) {
10214                      res = cmd;
10215                      goto out;
10216                   }
10217                } else {
10218                   cmd = ast_play_and_wait(chan, "vm-sorry");
10219                }
10220                cmd = 't';
10221                break;
10222             case '4': /* Dialout */
10223                if (!ast_strlen_zero(vmu->dialout)) {
10224                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10225                   if (cmd == 9) {
10226                      silentexit = 1;
10227                      goto out;
10228                   }
10229                } else {
10230                   cmd = ast_play_and_wait(chan, "vm-sorry");
10231                }
10232                cmd = 't';
10233                break;
10234 
10235             case '5': /* Leave VoiceMail */
10236                if (ast_test_flag(vmu, VM_SVMAIL)) {
10237                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10238                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10239                      res = cmd;
10240                      goto out;
10241                   }
10242                } else {
10243                   cmd = ast_play_and_wait(chan, "vm-sorry");
10244                }
10245                cmd = 't';
10246                break;
10247 
10248             case '*': /* Return to main menu */
10249                cmd = 't';
10250                break;
10251 
10252             default:
10253                cmd = 0;
10254                if (!vms.starting) {
10255                   cmd = ast_play_and_wait(chan, "vm-toreply");
10256                }
10257                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10258                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10259                }
10260                if (!cmd && !vms.starting) {
10261                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10262                }
10263                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10264                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10265                }
10266                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10267                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10268                }
10269                if (!cmd) {
10270                   cmd = ast_play_and_wait(chan, "vm-starmain");
10271                }
10272                if (!cmd) {
10273                   cmd = ast_waitfordigit(chan, 6000);
10274                }
10275                if (!cmd) {
10276                   vms.repeats++;
10277                }
10278                if (vms.repeats > 3) {
10279                   cmd = 't';
10280                }
10281                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10282             }
10283          }
10284          if (cmd == 't') {
10285             cmd = 0;
10286             vms.repeats = 0;
10287          }
10288          break;
10289       case '4': /* Go to the previous message */
10290          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10291          if (vms.curmsg > 0) {
10292             vms.curmsg--;
10293             cmd = play_message(chan, vmu, &vms);
10294          } else {
10295             /* Check if we were listening to new
10296                messages.  If so, go to Urgent messages
10297                instead of saying "no more messages"
10298             */
10299             if (in_urgent == 0 && vms.urgentmessages > 0) {
10300                /* Check for Urgent messages */
10301                in_urgent = 1;
10302                res = close_mailbox(&vms, vmu);
10303                if (res == ERROR_LOCK_PATH)
10304                   goto out;
10305                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10306                if (res < 0)
10307                   goto out;
10308                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10309                vms.curmsg = vms.lastmsg;
10310                if (vms.lastmsg < 0) {
10311                   cmd = ast_play_and_wait(chan, "vm-nomore");
10312                }
10313             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10314                vms.curmsg = vms.lastmsg;
10315                cmd = play_message(chan, vmu, &vms);
10316             } else {
10317                cmd = ast_play_and_wait(chan, "vm-nomore");
10318             }
10319          }
10320          break;
10321       case '6': /* Go to the next message */
10322          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10323          if (vms.curmsg < vms.lastmsg) {
10324             vms.curmsg++;
10325             cmd = play_message(chan, vmu, &vms);
10326          } else {
10327             if (in_urgent && vms.newmessages > 0) {
10328                /* Check if we were listening to urgent
10329                 * messages.  If so, go to regular new messages
10330                 * instead of saying "no more messages"
10331                 */
10332                in_urgent = 0;
10333                res = close_mailbox(&vms, vmu);
10334                if (res == ERROR_LOCK_PATH)
10335                   goto out;
10336                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10337                if (res < 0)
10338                   goto out;
10339                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10340                vms.curmsg = -1;
10341                if (vms.lastmsg < 0) {
10342                   cmd = ast_play_and_wait(chan, "vm-nomore");
10343                }
10344             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10345                vms.curmsg = 0;
10346                cmd = play_message(chan, vmu, &vms);
10347             } else {
10348                cmd = ast_play_and_wait(chan, "vm-nomore");
10349             }
10350          }
10351          break;
10352       case '7': /* Delete the current message */
10353          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10354             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10355             if (useadsi)
10356                adsi_delete(chan, &vms);
10357             if (vms.deleted[vms.curmsg]) {
10358                if (play_folder == 0) {
10359                   if (in_urgent) {
10360                      vms.urgentmessages--;
10361                   } else {
10362                      vms.newmessages--;
10363                   }
10364                }
10365                else if (play_folder == 1)
10366                   vms.oldmessages--;
10367                cmd = ast_play_and_wait(chan, "vm-deleted");
10368             } else {
10369                if (play_folder == 0) {
10370                   if (in_urgent) {
10371                      vms.urgentmessages++;
10372                   } else {
10373                      vms.newmessages++;
10374                   }
10375                }
10376                else if (play_folder == 1)
10377                   vms.oldmessages++;
10378                cmd = ast_play_and_wait(chan, "vm-undeleted");
10379             }
10380             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10381                if (vms.curmsg < vms.lastmsg) {
10382                   vms.curmsg++;
10383                   cmd = play_message(chan, vmu, &vms);
10384                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10385                   vms.curmsg = 0;
10386                   cmd = play_message(chan, vmu, &vms);
10387                } else {
10388                   /* Check if we were listening to urgent
10389                      messages.  If so, go to regular new messages
10390                      instead of saying "no more messages"
10391                   */
10392                   if (in_urgent == 1) {
10393                      /* Check for new messages */
10394                      in_urgent = 0;
10395                      res = close_mailbox(&vms, vmu);
10396                      if (res == ERROR_LOCK_PATH)
10397                         goto out;
10398                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10399                      if (res < 0)
10400                         goto out;
10401                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10402                      vms.curmsg = -1;
10403                      if (vms.lastmsg < 0) {
10404                         cmd = ast_play_and_wait(chan, "vm-nomore");
10405                      }
10406                   } else {
10407                      cmd = ast_play_and_wait(chan, "vm-nomore");
10408                   }
10409                }
10410             }
10411          } else /* Delete not valid if we haven't selected a message */
10412             cmd = 0;
10413 #ifdef IMAP_STORAGE
10414          deleted = 1;
10415 #endif
10416          break;
10417    
10418       case '8': /* Forward the current message */
10419          if (vms.lastmsg > -1) {
10420             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10421             if (cmd == ERROR_LOCK_PATH) {
10422                res = cmd;
10423                goto out;
10424             }
10425          } else {
10426             /* Check if we were listening to urgent
10427                messages.  If so, go to regular new messages
10428                instead of saying "no more messages"
10429             */
10430             if (in_urgent == 1 && vms.newmessages > 0) {
10431                /* Check for new messages */
10432                in_urgent = 0;
10433                res = close_mailbox(&vms, vmu);
10434                if (res == ERROR_LOCK_PATH)
10435                   goto out;
10436                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10437                if (res < 0)
10438                   goto out;
10439                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10440                vms.curmsg = -1;
10441                if (vms.lastmsg < 0) {
10442                   cmd = ast_play_and_wait(chan, "vm-nomore");
10443                }
10444             } else {
10445                cmd = ast_play_and_wait(chan, "vm-nomore");
10446             }
10447          }
10448          break;
10449       case '9': /* Save message to folder */
10450          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10451          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10452             /* No message selected */
10453             cmd = 0;
10454             break;
10455          }
10456          if (useadsi)
10457             adsi_folders(chan, 1, "Save to folder...");
10458          cmd = get_folder2(chan, "vm-savefolder", 1);
10459          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10460          box = 0; /* Shut up compiler */
10461          if (cmd == '#') {
10462             cmd = 0;
10463             break;
10464          } else if (cmd > 0) {
10465             box = cmd = cmd - '0';
10466             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10467             if (cmd == ERROR_LOCK_PATH) {
10468                res = cmd;
10469                goto out;
10470 #ifndef IMAP_STORAGE
10471             } else if (!cmd) {
10472                vms.deleted[vms.curmsg] = 1;
10473 #endif
10474             } else {
10475                vms.deleted[vms.curmsg] = 0;
10476                vms.heard[vms.curmsg] = 0;
10477             }
10478          }
10479          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10480          if (useadsi)
10481             adsi_message(chan, &vms);
10482          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10483          if (!cmd) {
10484             cmd = ast_play_and_wait(chan, "vm-message");
10485             if (!cmd) 
10486                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10487             if (!cmd)
10488                cmd = ast_play_and_wait(chan, "vm-savedto");
10489             if (!cmd)
10490                cmd = vm_play_folder_name(chan, vms.fn);
10491          } else {
10492             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10493          }
10494          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10495             if (vms.curmsg < vms.lastmsg) {
10496                vms.curmsg++;
10497                cmd = play_message(chan, vmu, &vms);
10498             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10499                vms.curmsg = 0;
10500                cmd = play_message(chan, vmu, &vms);
10501             } else {
10502                /* Check if we were listening to urgent
10503                   messages.  If so, go to regular new messages
10504                   instead of saying "no more messages"
10505                */
10506                if (in_urgent == 1 && vms.newmessages > 0) {
10507                   /* Check for new messages */
10508                   in_urgent = 0;
10509                   res = close_mailbox(&vms, vmu);
10510                   if (res == ERROR_LOCK_PATH)
10511                      goto out;
10512                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10513                   if (res < 0)
10514                      goto out;
10515                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10516                   vms.curmsg = -1;
10517                   if (vms.lastmsg < 0) {
10518                      cmd = ast_play_and_wait(chan, "vm-nomore");
10519                   }
10520                } else {
10521                   cmd = ast_play_and_wait(chan, "vm-nomore");
10522                }
10523             }
10524          }
10525          break;
10526       case '*': /* Help */
10527          if (!vms.starting) {
10528             cmd = ast_play_and_wait(chan, "vm-onefor");
10529             if (!strncasecmp(chan->language, "he", 2)) {
10530                cmd = ast_play_and_wait(chan, "vm-for");
10531             }
10532             if (!cmd)
10533                cmd = vm_play_folder_name(chan, vms.vmbox);
10534             if (!cmd)
10535                cmd = ast_play_and_wait(chan, "vm-opts");
10536             if (!cmd)
10537                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10538          } else
10539             cmd = 0;
10540          break;
10541       case '0': /* Mailbox options */
10542          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10543          if (useadsi)
10544             adsi_status(chan, &vms);
10545          break;
10546       default: /* Nothing */
10547          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10548          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10549          break;
10550       }
10551    }
10552    if ((cmd == 't') || (cmd == '#')) {
10553       /* Timeout */
10554       res = 0;
10555    } else {
10556       /* Hangup */
10557       res = -1;
10558    }
10559 
10560 out:
10561    if (res > -1) {
10562       ast_stopstream(chan);
10563       adsi_goodbye(chan);
10564       if (valid && res != OPERATOR_EXIT) {
10565          if (silentexit)
10566             res = ast_play_and_wait(chan, "vm-dialout");
10567          else 
10568             res = ast_play_and_wait(chan, "vm-goodbye");
10569       }
10570       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10571          res = 0;
10572       }
10573       if (useadsi)
10574          ast_adsi_unload_session(chan);
10575    }
10576    if (vmu)
10577       close_mailbox(&vms, vmu);
10578    if (valid) {
10579       int new = 0, old = 0, urgent = 0;
10580       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10581       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10582       /* Urgent flag not passwd to externnotify here */
10583       run_externnotify(vmu->context, vmu->mailbox, NULL);
10584       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10585       queue_mwi_event(ext_context, urgent, new, old);
10586    }
10587 #ifdef IMAP_STORAGE
10588    /* expunge message - use UID Expunge if supported on IMAP server*/
10589    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10590    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10591       ast_mutex_lock(&vms.lock);
10592 #ifdef HAVE_IMAP_TK2006
10593       if (LEVELUIDPLUS (vms.mailstream)) {
10594          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10595       } else 
10596 #endif
10597          mail_expunge(vms.mailstream);
10598       ast_mutex_unlock(&vms.lock);
10599    }
10600    /*  before we delete the state, we should copy pertinent info
10601     *  back to the persistent model */
10602    if (vmu) {
10603       vmstate_delete(&vms);
10604    }
10605 #endif
10606    if (vmu)
10607       free_user(vmu);
10608    if (vms.deleted)
10609       ast_free(vms.deleted);
10610    if (vms.heard)
10611       ast_free(vms.heard);
10612 
10613 #ifdef IMAP_STORAGE
10614    pthread_setspecific(ts_vmstate.key, NULL);
10615 #endif
10616    return res;
10617 }
10618 
10619 static int vm_exec(struct ast_channel *chan, const char *data)
10620 {
10621    int res = 0;
10622    char *tmp;
10623    struct leave_vm_options leave_options;
10624    struct ast_flags flags = { 0 };
10625    char *opts[OPT_ARG_ARRAY_SIZE];
10626    AST_DECLARE_APP_ARGS(args,
10627       AST_APP_ARG(argv0);
10628       AST_APP_ARG(argv1);
10629    );
10630    
10631    memset(&leave_options, 0, sizeof(leave_options));
10632 
10633    if (chan->_state != AST_STATE_UP)
10634       ast_answer(chan);
10635 
10636    if (!ast_strlen_zero(data)) {
10637       tmp = ast_strdupa(data);
10638       AST_STANDARD_APP_ARGS(args, tmp);
10639       if (args.argc == 2) {
10640          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10641             return -1;
10642          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10643          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10644             int gain;
10645 
10646             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10647                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10648                return -1;
10649             } else {
10650                leave_options.record_gain = (signed char) gain;
10651             }
10652          }
10653          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10654             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10655                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10656          }
10657       }
10658    } else {
10659       char temp[256];
10660       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10661       if (res < 0)
10662          return res;
10663       if (ast_strlen_zero(temp))
10664          return 0;
10665       args.argv0 = ast_strdupa(temp);
10666    }
10667 
10668    res = leave_voicemail(chan, args.argv0, &leave_options);
10669    if (res == 't') {
10670       ast_play_and_wait(chan, "vm-goodbye");
10671       res = 0;
10672    }
10673 
10674    if (res == OPERATOR_EXIT) {
10675       res = 0;
10676    }
10677 
10678    if (res == ERROR_LOCK_PATH) {
10679       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10680       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10681       res = 0;
10682    }
10683 
10684    return res;
10685 }
10686 
10687 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10688 {
10689    struct ast_vm_user *vmu;
10690 
10691    if (!ast_strlen_zero(box) && box[0] == '*') {
10692       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10693             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10694             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10695             "\n\tand will be ignored.\n", box, context);
10696       return NULL;
10697    }
10698 
10699    AST_LIST_TRAVERSE(&users, vmu, list) {
10700       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10701          if (strcasecmp(vmu->context, context)) {
10702             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10703                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10704                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10705                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10706          }
10707          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10708          return NULL;
10709       }
10710       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10711          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10712          return NULL;
10713       }
10714    }
10715    
10716    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10717       return NULL;
10718    
10719    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10720    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10721 
10722    AST_LIST_INSERT_TAIL(&users, vmu, list);
10723    
10724    return vmu;
10725 }
10726 
10727 static int append_mailbox(const char *context, const char *box, const char *data)
10728 {
10729    /* Assumes lock is already held */
10730    char *tmp;
10731    char *stringp;
10732    char *s;
10733    struct ast_vm_user *vmu;
10734    char *mailbox_full;
10735    int new = 0, old = 0, urgent = 0;
10736    char secretfn[PATH_MAX] = "";
10737 
10738    tmp = ast_strdupa(data);
10739 
10740    if (!(vmu = find_or_create(context, box)))
10741       return -1;
10742 
10743    populate_defaults(vmu);
10744 
10745    stringp = tmp;
10746    if ((s = strsep(&stringp, ","))) {
10747       if (!ast_strlen_zero(s) && s[0] == '*') {
10748          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10749             "\n\tmust be reset in voicemail.conf.\n", box);
10750       }
10751       /* assign password regardless of validity to prevent NULL password from being assigned */
10752       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10753    }
10754    if (stringp && (s = strsep(&stringp, ","))) {
10755       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10756    }
10757    if (stringp && (s = strsep(&stringp, ","))) {
10758       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10759    }
10760    if (stringp && (s = strsep(&stringp, ","))) {
10761       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10762    }
10763    if (stringp && (s = strsep(&stringp, ","))) {
10764       apply_options(vmu, s);
10765    }
10766 
10767    switch (vmu->passwordlocation) {
10768    case OPT_PWLOC_SPOOLDIR:
10769       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10770       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10771    }
10772 
10773    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10774    strcpy(mailbox_full, box);
10775    strcat(mailbox_full, "@");
10776    strcat(mailbox_full, context);
10777 
10778    inboxcount2(mailbox_full, &urgent, &new, &old);
10779    queue_mwi_event(mailbox_full, urgent, new, old);
10780 
10781    return 0;
10782 }
10783 
10784 AST_TEST_DEFINE(test_voicemail_vmuser)
10785 {
10786    int res = 0;
10787    struct ast_vm_user *vmu;
10788    /* language parameter seems to only be used for display in manager action */
10789    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10790       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10791       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10792       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10793       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10794       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10795       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10796       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10797       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10798 #ifdef IMAP_STORAGE
10799    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10800       "imapfolder=INBOX|imapvmshareid=6000";
10801 #endif
10802 
10803    switch (cmd) {
10804    case TEST_INIT:
10805       info->name = "vmuser";
10806       info->category = "/apps/app_voicemail/";
10807       info->summary = "Vmuser unit test";
10808       info->description =
10809          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10810       return AST_TEST_NOT_RUN;
10811    case TEST_EXECUTE:
10812       break;
10813    }
10814 
10815    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10816       return AST_TEST_NOT_RUN;
10817    }
10818    ast_set_flag(vmu, VM_ALLOCED);
10819    populate_defaults(vmu);
10820 
10821    apply_options(vmu, options_string);
10822 
10823    if (!ast_test_flag(vmu, VM_ATTACH)) {
10824       ast_test_status_update(test, "Parse failure for attach option\n");
10825       res = 1;
10826    }
10827    if (strcasecmp(vmu->attachfmt, "wav49")) {
10828       ast_test_status_update(test, "Parse failure for attachftm option\n");
10829       res = 1;
10830    }
10831    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10832       ast_test_status_update(test, "Parse failure for serveremail option\n");
10833       res = 1;
10834    }
10835    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10836       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10837       res = 1;
10838    }
10839    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10840       ast_test_status_update(test, "Parse failure for emailbody option\n");
10841       res = 1;
10842    }
10843    if (strcasecmp(vmu->zonetag, "central")) {
10844       ast_test_status_update(test, "Parse failure for tz option\n");
10845       res = 1;
10846    }
10847    if (!ast_test_flag(vmu, VM_DELETE)) {
10848       ast_test_status_update(test, "Parse failure for delete option\n");
10849       res = 1;
10850    }
10851    if (!ast_test_flag(vmu, VM_SAYCID)) {
10852       ast_test_status_update(test, "Parse failure for saycid option\n");
10853       res = 1;
10854    }
10855    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10856       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10857       res = 1;
10858    }
10859    if (!ast_test_flag(vmu, VM_REVIEW)) {
10860       ast_test_status_update(test, "Parse failure for review option\n");
10861       res = 1;
10862    }
10863    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10864       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10865       res = 1;
10866    }
10867    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10868       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10869       res = 1;
10870    }
10871    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10872       ast_test_status_update(test, "Parse failure for operator option\n");
10873       res = 1;
10874    }
10875    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10876       ast_test_status_update(test, "Parse failure for envelope option\n");
10877       res = 1;
10878    }
10879    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10880       ast_test_status_update(test, "Parse failure for moveheard option\n");
10881       res = 1;
10882    }
10883    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10884       ast_test_status_update(test, "Parse failure for sayduration option\n");
10885       res = 1;
10886    }
10887    if (vmu->saydurationm != 5) {
10888       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10889       res = 1;
10890    }
10891    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10892       ast_test_status_update(test, "Parse failure for forcename option\n");
10893       res = 1;
10894    }
10895    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10896       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10897       res = 1;
10898    }
10899    if (strcasecmp(vmu->callback, "somecontext")) {
10900       ast_test_status_update(test, "Parse failure for callbacks option\n");
10901       res = 1;
10902    }
10903    if (strcasecmp(vmu->dialout, "somecontext2")) {
10904       ast_test_status_update(test, "Parse failure for dialout option\n");
10905       res = 1;
10906    }
10907    if (strcasecmp(vmu->exit, "somecontext3")) {
10908       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10909       res = 1;
10910    }
10911    if (vmu->minsecs != 10) {
10912       ast_test_status_update(test, "Parse failure for minsecs option\n");
10913       res = 1;
10914    }
10915    if (vmu->maxsecs != 100) {
10916       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10917       res = 1;
10918    }
10919    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10920       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10921       res = 1;
10922    }
10923    if (vmu->maxdeletedmsg != 50) {
10924       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10925       res = 1;
10926    }
10927    if (vmu->volgain != 1.3) {
10928       ast_test_status_update(test, "Parse failure for volgain option\n");
10929       res = 1;
10930    }
10931    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10932       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10933       res = 1;
10934    }
10935 #ifdef IMAP_STORAGE
10936    apply_options(vmu, option_string2);
10937 
10938    if (strcasecmp(vmu->imapuser, "imapuser")) {
10939       ast_test_status_update(test, "Parse failure for imapuser option\n");
10940       res = 1;
10941    }
10942    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10943       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10944       res = 1;
10945    }
10946    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10947       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10948       res = 1;
10949    }
10950    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10951       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10952       res = 1;
10953    }
10954 #endif
10955 
10956    free_user(vmu);
10957    return res ? AST_TEST_FAIL : AST_TEST_PASS;
10958 }
10959 
10960 static int vm_box_exists(struct ast_channel *chan, const char *data) 
10961 {
10962    struct ast_vm_user svm;
10963    char *context, *box;
10964    AST_DECLARE_APP_ARGS(args,
10965       AST_APP_ARG(mbox);
10966       AST_APP_ARG(options);
10967    );
10968    static int dep_warning = 0;
10969 
10970    if (ast_strlen_zero(data)) {
10971       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10972       return -1;
10973    }
10974 
10975    if (!dep_warning) {
10976       dep_warning = 1;
10977       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10978    }
10979 
10980    box = ast_strdupa(data);
10981 
10982    AST_STANDARD_APP_ARGS(args, box);
10983 
10984    if (args.options) {
10985    }
10986 
10987    if ((context = strchr(args.mbox, '@'))) {
10988       *context = '\0';
10989       context++;
10990    }
10991 
10992    if (find_user(&svm, context, args.mbox)) {
10993       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10994    } else
10995       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10996 
10997    return 0;
10998 }
10999 
11000 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11001 {
11002    struct ast_vm_user svm;
11003    AST_DECLARE_APP_ARGS(arg,
11004       AST_APP_ARG(mbox);
11005       AST_APP_ARG(context);
11006    );
11007 
11008    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11009 
11010    if (ast_strlen_zero(arg.mbox)) {
11011       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11012       return -1;
11013    }
11014 
11015    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11016    return 0;
11017 }
11018 
11019 static struct ast_custom_function mailbox_exists_acf = {
11020    .name = "MAILBOX_EXISTS",
11021    .read = acf_mailbox_exists,
11022 };
11023 
11024 static int vmauthenticate(struct ast_channel *chan, const char *data)
11025 {
11026    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11027    struct ast_vm_user vmus;
11028    char *options = NULL;
11029    int silent = 0, skipuser = 0;
11030    int res = -1;
11031    
11032    if (data) {
11033       s = ast_strdupa(data);
11034       user = strsep(&s, ",");
11035       options = strsep(&s, ",");
11036       if (user) {
11037          s = user;
11038          user = strsep(&s, "@");
11039          context = strsep(&s, "");
11040          if (!ast_strlen_zero(user))
11041             skipuser++;
11042          ast_copy_string(mailbox, user, sizeof(mailbox));
11043       }
11044    }
11045 
11046    if (options) {
11047       silent = (strchr(options, 's')) != NULL;
11048    }
11049 
11050    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11051       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11052       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11053       ast_play_and_wait(chan, "auth-thankyou");
11054       res = 0;
11055    } else if (mailbox[0] == '*') {
11056       /* user entered '*' */
11057       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11058          res = 0; /* prevent hangup */
11059       }
11060    }
11061 
11062    return res;
11063 }
11064 
11065 static char *show_users_realtime(int fd, const char *context)
11066 {
11067    struct ast_config *cfg;
11068    const char *cat = NULL;
11069 
11070    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11071       "context", context, SENTINEL))) {
11072       return CLI_FAILURE;
11073    }
11074 
11075    ast_cli(fd,
11076       "\n"
11077       "=============================================================\n"
11078       "=== Configured Voicemail Users ==============================\n"
11079       "=============================================================\n"
11080       "===\n");
11081 
11082    while ((cat = ast_category_browse(cfg, cat))) {
11083       struct ast_variable *var = NULL;
11084       ast_cli(fd,
11085          "=== Mailbox ...\n"
11086          "===\n");
11087       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11088          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11089       ast_cli(fd,
11090          "===\n"
11091          "=== ---------------------------------------------------------\n"
11092          "===\n");
11093    }
11094 
11095    ast_cli(fd,
11096       "=============================================================\n"
11097       "\n");
11098 
11099    ast_config_destroy(cfg);
11100 
11101    return CLI_SUCCESS;
11102 }
11103 
11104 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11105 {
11106    int which = 0;
11107    int wordlen;
11108    struct ast_vm_user *vmu;
11109    const char *context = "";
11110 
11111    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11112    if (pos > 4)
11113       return NULL;
11114    if (pos == 3)
11115       return (state == 0) ? ast_strdup("for") : NULL;
11116    wordlen = strlen(word);
11117    AST_LIST_TRAVERSE(&users, vmu, list) {
11118       if (!strncasecmp(word, vmu->context, wordlen)) {
11119          if (context && strcmp(context, vmu->context) && ++which > state)
11120             return ast_strdup(vmu->context);
11121          /* ignore repeated contexts ? */
11122          context = vmu->context;
11123       }
11124    }
11125    return NULL;
11126 }
11127 
11128 /*! \brief Show a list of voicemail users in the CLI */
11129 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11130 {
11131    struct ast_vm_user *vmu;
11132 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11133    const char *context = NULL;
11134    int users_counter = 0;
11135 
11136    switch (cmd) {
11137    case CLI_INIT:
11138       e->command = "voicemail show users";
11139       e->usage =
11140          "Usage: voicemail show users [for <context>]\n"
11141          "       Lists all mailboxes currently set up\n";
11142       return NULL;
11143    case CLI_GENERATE:
11144       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11145    }  
11146 
11147    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11148       return CLI_SHOWUSAGE;
11149    if (a->argc == 5) {
11150       if (strcmp(a->argv[3],"for"))
11151          return CLI_SHOWUSAGE;
11152       context = a->argv[4];
11153    }
11154 
11155    if (ast_check_realtime("voicemail")) {
11156       if (!context) {
11157          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11158          return CLI_SHOWUSAGE;
11159       }
11160       return show_users_realtime(a->fd, context);
11161    }
11162 
11163    AST_LIST_LOCK(&users);
11164    if (AST_LIST_EMPTY(&users)) {
11165       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11166       AST_LIST_UNLOCK(&users);
11167       return CLI_FAILURE;
11168    }
11169    if (a->argc == 3)
11170       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11171    else {
11172       int count = 0;
11173       AST_LIST_TRAVERSE(&users, vmu, list) {
11174          if (!strcmp(context, vmu->context))
11175             count++;
11176       }
11177       if (count) {
11178          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11179       } else {
11180          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11181          AST_LIST_UNLOCK(&users);
11182          return CLI_FAILURE;
11183       }
11184    }
11185    AST_LIST_TRAVERSE(&users, vmu, list) {
11186       int newmsgs = 0, oldmsgs = 0;
11187       char count[12], tmp[256] = "";
11188 
11189       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
11190          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11191          inboxcount(tmp, &newmsgs, &oldmsgs);
11192          snprintf(count, sizeof(count), "%d", newmsgs);
11193          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11194          users_counter++;
11195       }
11196    }
11197    AST_LIST_UNLOCK(&users);
11198    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11199    return CLI_SUCCESS;
11200 }
11201 
11202 /*! \brief Show a list of voicemail zones in the CLI */
11203 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11204 {
11205    struct vm_zone *zone;
11206 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11207    char *res = CLI_SUCCESS;
11208 
11209    switch (cmd) {
11210    case CLI_INIT:
11211       e->command = "voicemail show zones";
11212       e->usage =
11213          "Usage: voicemail show zones\n"
11214          "       Lists zone message formats\n";
11215       return NULL;
11216    case CLI_GENERATE:
11217       return NULL;
11218    }
11219 
11220    if (a->argc != 3)
11221       return CLI_SHOWUSAGE;
11222 
11223    AST_LIST_LOCK(&zones);
11224    if (!AST_LIST_EMPTY(&zones)) {
11225       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11226       AST_LIST_TRAVERSE(&zones, zone, list) {
11227          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11228       }
11229    } else {
11230       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11231       res = CLI_FAILURE;
11232    }
11233    AST_LIST_UNLOCK(&zones);
11234 
11235    return res;
11236 }
11237 
11238 /*! \brief Reload voicemail configuration from the CLI */
11239 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11240 {
11241    switch (cmd) {
11242    case CLI_INIT:
11243       e->command = "voicemail reload";
11244       e->usage =
11245          "Usage: voicemail reload\n"
11246          "       Reload voicemail configuration\n";
11247       return NULL;
11248    case CLI_GENERATE:
11249       return NULL;
11250    }
11251 
11252    if (a->argc != 2)
11253       return CLI_SHOWUSAGE;
11254 
11255    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11256    load_config(1);
11257    
11258    return CLI_SUCCESS;
11259 }
11260 
11261 static struct ast_cli_entry cli_voicemail[] = {
11262    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11263    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11264    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11265 };
11266 
11267 #ifdef IMAP_STORAGE
11268    #define DATA_EXPORT_VM_USERS(USER)              \
11269       USER(ast_vm_user, context, AST_DATA_STRING)        \
11270       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11271       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11272       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11273       USER(ast_vm_user, email, AST_DATA_STRING)       \
11274       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11275       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11276       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11277       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11278       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11279       USER(ast_vm_user, language, AST_DATA_STRING)       \
11280       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11281       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11282       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11283       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11284       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11285       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11286       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11287       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11288       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11289       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11290       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11291       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11292       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11293       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11294       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11295 #else
11296    #define DATA_EXPORT_VM_USERS(USER)              \
11297       USER(ast_vm_user, context, AST_DATA_STRING)        \
11298       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11299       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11300       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11301       USER(ast_vm_user, email, AST_DATA_STRING)       \
11302       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11303       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11304       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11305       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11306       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11307       USER(ast_vm_user, language, AST_DATA_STRING)       \
11308       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11309       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11310       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11311       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11312       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11313       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11314       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11315       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11316       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11317       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11318       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11319       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11320 #endif
11321 
11322 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11323 
11324 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11325    ZONE(vm_zone, name, AST_DATA_STRING)      \
11326    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11327    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11328 
11329 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11330 
11331 /*!
11332  * \internal
11333  * \brief Add voicemail user to the data_root.
11334  * \param[in] search The search tree.
11335  * \param[in] data_root The main result node.
11336  * \param[in] user The voicemail user.
11337  */
11338 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11339     struct ast_data *data_root, struct ast_vm_user *user)
11340 {
11341    struct ast_data *data_user, *data_zone;
11342    struct ast_data *data_state;
11343    struct vm_zone *zone = NULL;
11344    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11345    char ext_context[256] = "";
11346 
11347    data_user = ast_data_add_node(data_root, "user");
11348    if (!data_user) {
11349       return -1;
11350    }
11351 
11352    ast_data_add_structure(ast_vm_user, data_user, user);
11353 
11354    AST_LIST_LOCK(&zones);
11355    AST_LIST_TRAVERSE(&zones, zone, list) {
11356       if (!strcmp(zone->name, user->zonetag)) {
11357          break;
11358       }
11359    }
11360    AST_LIST_UNLOCK(&zones);
11361 
11362    /* state */
11363    data_state = ast_data_add_node(data_user, "state");
11364    if (!data_state) {
11365       return -1;
11366    }
11367    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11368    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11369    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11370    ast_data_add_int(data_state, "newmsg", newmsg);
11371    ast_data_add_int(data_state, "oldmsg", oldmsg);
11372 
11373    if (zone) {
11374       data_zone = ast_data_add_node(data_user, "zone");
11375       ast_data_add_structure(vm_zone, data_zone, zone);
11376    }
11377 
11378    if (!ast_data_search_match(search, data_user)) {
11379       ast_data_remove_node(data_root, data_user);
11380    }
11381 
11382    return 0;
11383 }
11384 
11385 static int vm_users_data_provider_get(const struct ast_data_search *search,
11386    struct ast_data *data_root)
11387 {
11388    struct ast_vm_user *user;
11389 
11390    AST_LIST_LOCK(&users);
11391    AST_LIST_TRAVERSE(&users, user, list) {
11392       vm_users_data_provider_get_helper(search, data_root, user);
11393    }
11394    AST_LIST_UNLOCK(&users);
11395 
11396    return 0;
11397 }
11398 
11399 static const struct ast_data_handler vm_users_data_provider = {
11400    .version = AST_DATA_HANDLER_VERSION,
11401    .get = vm_users_data_provider_get
11402 };
11403 
11404 static const struct ast_data_entry vm_data_providers[] = {
11405    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11406 };
11407 
11408 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11409 {
11410    int new = 0, old = 0, urgent = 0;
11411 
11412    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11413 
11414    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11415       mwi_sub->old_urgent = urgent;
11416       mwi_sub->old_new = new;
11417       mwi_sub->old_old = old;
11418       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11419       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11420    }
11421 }
11422 
11423 static void poll_subscribed_mailboxes(void)
11424 {
11425    struct mwi_sub *mwi_sub;
11426 
11427    AST_RWLIST_RDLOCK(&mwi_subs);
11428    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11429       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11430          poll_subscribed_mailbox(mwi_sub);
11431       }
11432    }
11433    AST_RWLIST_UNLOCK(&mwi_subs);
11434 }
11435 
11436 static void *mb_poll_thread(void *data)
11437 {
11438    while (poll_thread_run) {
11439       struct timespec ts = { 0, };
11440       struct timeval wait;
11441 
11442       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11443       ts.tv_sec = wait.tv_sec;
11444       ts.tv_nsec = wait.tv_usec * 1000;
11445 
11446       ast_mutex_lock(&poll_lock);
11447       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11448       ast_mutex_unlock(&poll_lock);
11449 
11450       if (!poll_thread_run)
11451          break;
11452 
11453       poll_subscribed_mailboxes();
11454    }
11455 
11456    return NULL;
11457 }
11458 
11459 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11460 {
11461    ast_free(mwi_sub);
11462 }
11463 
11464 static int handle_unsubscribe(void *datap)
11465 {
11466    struct mwi_sub *mwi_sub;
11467    uint32_t *uniqueid = datap;
11468    
11469    AST_RWLIST_WRLOCK(&mwi_subs);
11470    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11471       if (mwi_sub->uniqueid == *uniqueid) {
11472          AST_LIST_REMOVE_CURRENT(entry);
11473          break;
11474       }
11475    }
11476    AST_RWLIST_TRAVERSE_SAFE_END
11477    AST_RWLIST_UNLOCK(&mwi_subs);
11478 
11479    if (mwi_sub)
11480       mwi_sub_destroy(mwi_sub);
11481 
11482    ast_free(uniqueid);  
11483    return 0;
11484 }
11485 
11486 static int handle_subscribe(void *datap)
11487 {
11488    unsigned int len;
11489    struct mwi_sub *mwi_sub;
11490    struct mwi_sub_task *p = datap;
11491 
11492    len = sizeof(*mwi_sub);
11493    if (!ast_strlen_zero(p->mailbox))
11494       len += strlen(p->mailbox);
11495 
11496    if (!ast_strlen_zero(p->context))
11497       len += strlen(p->context) + 1; /* Allow for seperator */
11498 
11499    if (!(mwi_sub = ast_calloc(1, len)))
11500       return -1;
11501 
11502    mwi_sub->uniqueid = p->uniqueid;
11503    if (!ast_strlen_zero(p->mailbox))
11504       strcpy(mwi_sub->mailbox, p->mailbox);
11505 
11506    if (!ast_strlen_zero(p->context)) {
11507       strcat(mwi_sub->mailbox, "@");
11508       strcat(mwi_sub->mailbox, p->context);
11509    }
11510 
11511    AST_RWLIST_WRLOCK(&mwi_subs);
11512    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11513    AST_RWLIST_UNLOCK(&mwi_subs);
11514    ast_free((void *) p->mailbox);
11515    ast_free((void *) p->context);
11516    ast_free(p);
11517    poll_subscribed_mailbox(mwi_sub);
11518    return 0;
11519 }
11520 
11521 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11522 {
11523    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11524    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
11525       return;
11526 
11527    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11528       return;
11529 
11530    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11531    *uniqueid = u;
11532    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11533       ast_free(uniqueid);
11534    }
11535 }
11536 
11537 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11538 {
11539    struct mwi_sub_task *mwist;
11540    
11541    if (ast_event_get_type(event) != AST_EVENT_SUB)
11542       return;
11543 
11544    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11545       return;
11546 
11547    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11548       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11549       return;
11550    }
11551    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11552    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11553    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11554    
11555    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11556       ast_free(mwist);
11557    }
11558 }
11559 
11560 static void start_poll_thread(void)
11561 {
11562    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11563       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11564       AST_EVENT_IE_END);
11565 
11566    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11567       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11568       AST_EVENT_IE_END);
11569 
11570    if (mwi_sub_sub)
11571       ast_event_report_subs(mwi_sub_sub);
11572 
11573    poll_thread_run = 1;
11574 
11575    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
11576 }
11577 
11578 static void stop_poll_thread(void)
11579 {
11580    poll_thread_run = 0;
11581 
11582    if (mwi_sub_sub) {
11583       ast_event_unsubscribe(mwi_sub_sub);
11584       mwi_sub_sub = NULL;
11585    }
11586 
11587    if (mwi_unsub_sub) {
11588       ast_event_unsubscribe(mwi_unsub_sub);
11589       mwi_unsub_sub = NULL;
11590    }
11591 
11592    ast_mutex_lock(&poll_lock);
11593    ast_cond_signal(&poll_cond);
11594    ast_mutex_unlock(&poll_lock);
11595 
11596    pthread_join(poll_thread, NULL);
11597 
11598    poll_thread = AST_PTHREADT_NULL;
11599 }
11600 
11601 /*! \brief Manager list voicemail users command */
11602 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11603 {
11604    struct ast_vm_user *vmu = NULL;
11605    const char *id = astman_get_header(m, "ActionID");
11606    char actionid[128] = "";
11607 
11608    if (!ast_strlen_zero(id))
11609       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11610 
11611    AST_LIST_LOCK(&users);
11612 
11613    if (AST_LIST_EMPTY(&users)) {
11614       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11615       AST_LIST_UNLOCK(&users);
11616       return RESULT_SUCCESS;
11617    }
11618    
11619    astman_send_ack(s, m, "Voicemail user list will follow");
11620    
11621    AST_LIST_TRAVERSE(&users, vmu, list) {
11622       char dirname[256];
11623 
11624 #ifdef IMAP_STORAGE
11625       int new, old;
11626       inboxcount(vmu->mailbox, &new, &old);
11627 #endif
11628       
11629       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11630       astman_append(s,
11631          "%s"
11632          "Event: VoicemailUserEntry\r\n"
11633          "VMContext: %s\r\n"
11634          "VoiceMailbox: %s\r\n"
11635          "Fullname: %s\r\n"
11636          "Email: %s\r\n"
11637          "Pager: %s\r\n"
11638          "ServerEmail: %s\r\n"
11639          "MailCommand: %s\r\n"
11640          "Language: %s\r\n"
11641          "TimeZone: %s\r\n"
11642          "Callback: %s\r\n"
11643          "Dialout: %s\r\n"
11644          "UniqueID: %s\r\n"
11645          "ExitContext: %s\r\n"
11646          "SayDurationMinimum: %d\r\n"
11647          "SayEnvelope: %s\r\n"
11648          "SayCID: %s\r\n"
11649          "AttachMessage: %s\r\n"
11650          "AttachmentFormat: %s\r\n"
11651          "DeleteMessage: %s\r\n"
11652          "VolumeGain: %.2f\r\n"
11653          "CanReview: %s\r\n"
11654          "CallOperator: %s\r\n"
11655          "MaxMessageCount: %d\r\n"
11656          "MaxMessageLength: %d\r\n"
11657          "NewMessageCount: %d\r\n"
11658 #ifdef IMAP_STORAGE
11659          "OldMessageCount: %d\r\n"
11660          "IMAPUser: %s\r\n"
11661 #endif
11662          "\r\n",
11663          actionid,
11664          vmu->context,
11665          vmu->mailbox,
11666          vmu->fullname,
11667          vmu->email,
11668          vmu->pager,
11669          vmu->serveremail,
11670          vmu->mailcmd,
11671          vmu->language,
11672          vmu->zonetag,
11673          vmu->callback,
11674          vmu->dialout,
11675          vmu->uniqueid,
11676          vmu->exit,
11677          vmu->saydurationm,
11678          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11679          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11680          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11681          vmu->attachfmt,
11682          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11683          vmu->volgain,
11684          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11685          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11686          vmu->maxmsg,
11687          vmu->maxsecs,
11688 #ifdef IMAP_STORAGE
11689          new, old, vmu->imapuser
11690 #else
11691          count_messages(vmu, dirname)
11692 #endif
11693          );
11694    }     
11695    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11696 
11697    AST_LIST_UNLOCK(&users);
11698 
11699    return RESULT_SUCCESS;
11700 }
11701 
11702 /*! \brief Free the users structure. */
11703 static void free_vm_users(void) 
11704 {
11705    struct ast_vm_user *current;
11706    AST_LIST_LOCK(&users);
11707    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11708       ast_set_flag(current, VM_ALLOCED);
11709       free_user(current);
11710    }
11711    AST_LIST_UNLOCK(&users);
11712 }
11713 
11714 /*! \brief Free the zones structure. */
11715 static void free_vm_zones(void)
11716 {
11717    struct vm_zone *zcur;
11718    AST_LIST_LOCK(&zones);
11719    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11720       free_zone(zcur);
11721    AST_LIST_UNLOCK(&zones);
11722 }
11723 
11724 static const char *substitute_escapes(const char *value)
11725 {
11726    char *current;
11727 
11728    /* Add 16 for fudge factor */
11729    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11730 
11731    ast_str_reset(str);
11732    
11733    /* Substitute strings \r, \n, and \t into the appropriate characters */
11734    for (current = (char *) value; *current; current++) {
11735       if (*current == '\\') {
11736          current++;
11737          if (!*current) {
11738             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11739             break;
11740          }
11741          switch (*current) {
11742          case '\\':
11743             ast_str_append(&str, 0, "\\");
11744             break;
11745          case 'r':
11746             ast_str_append(&str, 0, "\r");
11747             break;
11748          case 'n':
11749 #ifdef IMAP_STORAGE
11750             if (!str->used || str->str[str->used - 1] != '\r') {
11751                ast_str_append(&str, 0, "\r");
11752             }
11753 #endif
11754             ast_str_append(&str, 0, "\n");
11755             break;
11756          case 't':
11757             ast_str_append(&str, 0, "\t");
11758             break;
11759          default:
11760             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11761             break;
11762          }
11763       } else {
11764          ast_str_append(&str, 0, "%c", *current);
11765       }
11766    }
11767 
11768    return ast_str_buffer(str);
11769 }
11770 
11771 static int load_config(int reload)
11772 {
11773    struct ast_config *cfg, *ucfg;
11774    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11775    int res;
11776 
11777    ast_unload_realtime("voicemail");
11778    ast_unload_realtime("voicemail_data");
11779 
11780    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11781       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11782          return 0;
11783       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11784          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11785          ucfg = NULL;
11786       }
11787       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11788       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11789          ast_config_destroy(ucfg);
11790          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11791          return 0;
11792       }
11793    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11794       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11795       return 0;
11796    } else {
11797       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11798       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11799          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11800          ucfg = NULL;
11801       }
11802    }
11803 
11804    res = actual_load_config(reload, cfg, ucfg);
11805 
11806    ast_config_destroy(cfg);
11807    ast_config_destroy(ucfg);
11808 
11809    return res;
11810 }
11811 
11812 #ifdef TEST_FRAMEWORK
11813 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11814 {
11815    ast_unload_realtime("voicemail");
11816    ast_unload_realtime("voicemail_data");
11817    return actual_load_config(reload, cfg, ucfg);
11818 }
11819 #endif
11820 
11821 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11822 {
11823    struct ast_vm_user *current;
11824    char *cat;
11825    struct ast_variable *var;
11826    const char *val;
11827    char *q, *stringp, *tmp;
11828    int x;
11829    int tmpadsi[4];
11830    char secretfn[PATH_MAX] = "";
11831 
11832 #ifdef IMAP_STORAGE
11833    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11834 #endif
11835    /* set audio control prompts */
11836    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11837    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11838    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11839    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11840    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11841 
11842    /* Free all the users structure */  
11843    free_vm_users();
11844 
11845    /* Free all the zones structure */
11846    free_vm_zones();
11847 
11848    AST_LIST_LOCK(&users);  
11849 
11850    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11851    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11852 
11853    if (cfg) {
11854       /* General settings */
11855 
11856       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11857          val = "default";
11858       ast_copy_string(userscontext, val, sizeof(userscontext));
11859       /* Attach voice message to mail message ? */
11860       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11861          val = "yes";
11862       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11863 
11864       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11865          val = "no";
11866       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11867 
11868       volgain = 0.0;
11869       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11870          sscanf(val, "%30lf", &volgain);
11871 
11872 #ifdef ODBC_STORAGE
11873       strcpy(odbc_database, "asterisk");
11874       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11875          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11876       }
11877       strcpy(odbc_table, "voicemessages");
11878       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11879          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11880       }
11881 #endif      
11882       /* Mail command */
11883       strcpy(mailcmd, SENDMAIL);
11884       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11885          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11886 
11887       maxsilence = 0;
11888       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11889          maxsilence = atoi(val);
11890          if (maxsilence > 0)
11891             maxsilence *= 1000;
11892       }
11893       
11894       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11895          maxmsg = MAXMSG;
11896       } else {
11897          maxmsg = atoi(val);
11898          if (maxmsg < 0) {
11899             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11900             maxmsg = MAXMSG;
11901          } else if (maxmsg > MAXMSGLIMIT) {
11902             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11903             maxmsg = MAXMSGLIMIT;
11904          }
11905       }
11906 
11907       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11908          maxdeletedmsg = 0;
11909       } else {
11910          if (sscanf(val, "%30d", &x) == 1)
11911             maxdeletedmsg = x;
11912          else if (ast_true(val))
11913             maxdeletedmsg = MAXMSG;
11914          else
11915             maxdeletedmsg = 0;
11916 
11917          if (maxdeletedmsg < 0) {
11918             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11919             maxdeletedmsg = MAXMSG;
11920          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11921             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11922             maxdeletedmsg = MAXMSGLIMIT;
11923          }
11924       }
11925 
11926       /* Load date format config for voicemail mail */
11927       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11928          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11929       }
11930 
11931       /* Load date format config for voicemail pager mail */
11932       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11933          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11934       }
11935 
11936       /* External password changing command */
11937       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11938          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11939          pwdchange = PWDCHANGE_EXTERNAL;
11940       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11941          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11942          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11943       }
11944  
11945       /* External password validation command */
11946       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11947          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11948          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11949       }
11950 
11951 #ifdef IMAP_STORAGE
11952       /* IMAP server address */
11953       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11954          ast_copy_string(imapserver, val, sizeof(imapserver));
11955       } else {
11956          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11957       }
11958       /* IMAP server port */
11959       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11960          ast_copy_string(imapport, val, sizeof(imapport));
11961       } else {
11962          ast_copy_string(imapport, "143", sizeof(imapport));
11963       }
11964       /* IMAP server flags */
11965       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11966          ast_copy_string(imapflags, val, sizeof(imapflags));
11967       }
11968       /* IMAP server master username */
11969       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11970          ast_copy_string(authuser, val, sizeof(authuser));
11971       }
11972       /* IMAP server master password */
11973       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11974          ast_copy_string(authpassword, val, sizeof(authpassword));
11975       }
11976       /* Expunge on exit */
11977       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11978          if (ast_false(val))
11979             expungeonhangup = 0;
11980          else
11981             expungeonhangup = 1;
11982       } else {
11983          expungeonhangup = 1;
11984       }
11985       /* IMAP voicemail folder */
11986       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11987          ast_copy_string(imapfolder, val, sizeof(imapfolder));
11988       } else {
11989          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
11990       }
11991       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11992          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11993       }
11994       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11995          imapgreetings = ast_true(val);
11996       } else {
11997          imapgreetings = 0;
11998       }
11999       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12000          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12001       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12002          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12003          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12004       } else {
12005          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12006       }
12007 
12008       /* There is some very unorthodox casting done here. This is due
12009        * to the way c-client handles the argument passed in. It expects a 
12010        * void pointer and casts the pointer directly to a long without
12011        * first dereferencing it. */
12012       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12013          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12014       } else {
12015          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12016       }
12017 
12018       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12019          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12020       } else {
12021          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12022       }
12023 
12024       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12025          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12026       } else {
12027          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12028       }
12029 
12030       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12031          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12032       } else {
12033          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12034       }
12035 
12036       /* Increment configuration version */
12037       imapversion++;
12038 #endif
12039       /* External voicemail notify application */
12040       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12041          ast_copy_string(externnotify, val, sizeof(externnotify));
12042          ast_debug(1, "found externnotify: %s\n", externnotify);
12043       } else {
12044          externnotify[0] = '\0';
12045       }
12046 
12047       /* SMDI voicemail notification */
12048       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12049          ast_debug(1, "Enabled SMDI voicemail notification\n");
12050          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12051             smdi_iface = ast_smdi_interface_find(val);
12052          } else {
12053             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12054             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12055          }
12056          if (!smdi_iface) {
12057             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12058          } 
12059       }
12060 
12061       /* Silence treshold */
12062       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12063       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12064          silencethreshold = atoi(val);
12065       
12066       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12067          val = ASTERISK_USERNAME;
12068       ast_copy_string(serveremail, val, sizeof(serveremail));
12069       
12070       vmmaxsecs = 0;
12071       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12072          if (sscanf(val, "%30d", &x) == 1) {
12073             vmmaxsecs = x;
12074          } else {
12075             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12076          }
12077       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12078          static int maxmessage_deprecate = 0;
12079          if (maxmessage_deprecate == 0) {
12080             maxmessage_deprecate = 1;
12081             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12082          }
12083          if (sscanf(val, "%30d", &x) == 1) {
12084             vmmaxsecs = x;
12085          } else {
12086             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12087          }
12088       }
12089 
12090       vmminsecs = 0;
12091       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12092          if (sscanf(val, "%30d", &x) == 1) {
12093             vmminsecs = x;
12094             if (maxsilence / 1000 >= vmminsecs) {
12095                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12096             }
12097          } else {
12098             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12099          }
12100       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12101          static int maxmessage_deprecate = 0;
12102          if (maxmessage_deprecate == 0) {
12103             maxmessage_deprecate = 1;
12104             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12105          }
12106          if (sscanf(val, "%30d", &x) == 1) {
12107             vmminsecs = x;
12108             if (maxsilence / 1000 >= vmminsecs) {
12109                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12110             }
12111          } else {
12112             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12113          }
12114       }
12115 
12116       val = ast_variable_retrieve(cfg, "general", "format");
12117       if (!val) {
12118          val = "wav";   
12119       } else {
12120          tmp = ast_strdupa(val);
12121          val = ast_format_str_reduce(tmp);
12122          if (!val) {
12123             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12124             val = "wav";
12125          }
12126       }
12127       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12128 
12129       skipms = 3000;
12130       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12131          if (sscanf(val, "%30d", &x) == 1) {
12132             maxgreet = x;
12133          } else {
12134             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12135          }
12136       }
12137 
12138       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12139          if (sscanf(val, "%30d", &x) == 1) {
12140             skipms = x;
12141          } else {
12142             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12143          }
12144       }
12145 
12146       maxlogins = 3;
12147       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12148          if (sscanf(val, "%30d", &x) == 1) {
12149             maxlogins = x;
12150          } else {
12151             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12152          }
12153       }
12154 
12155       minpassword = MINPASSWORD;
12156       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12157          if (sscanf(val, "%30d", &x) == 1) {
12158             minpassword = x;
12159          } else {
12160             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12161          }
12162       }
12163 
12164       /* Force new user to record name ? */
12165       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12166          val = "no";
12167       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12168 
12169       /* Force new user to record greetings ? */
12170       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12171          val = "no";
12172       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12173 
12174       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12175          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12176          stringp = ast_strdupa(val);
12177          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12178             if (!ast_strlen_zero(stringp)) {
12179                q = strsep(&stringp, ",");
12180                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12181                   q++;
12182                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12183                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12184             } else {
12185                cidinternalcontexts[x][0] = '\0';
12186             }
12187          }
12188       }
12189       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12190          ast_debug(1, "VM Review Option disabled globally\n");
12191          val = "no";
12192       }
12193       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12194 
12195       /* Temporary greeting reminder */
12196       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12197          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12198          val = "no";
12199       } else {
12200          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12201       }
12202       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12203       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12204          ast_debug(1, "VM next message wrap disabled globally\n");
12205          val = "no";
12206       }
12207       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12208 
12209       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12210          ast_debug(1, "VM Operator break disabled globally\n");
12211          val = "no";
12212       }
12213       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12214 
12215       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12216          ast_debug(1, "VM CID Info before msg disabled globally\n");
12217          val = "no";
12218       } 
12219       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12220 
12221       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12222          ast_debug(1, "Send Voicemail msg disabled globally\n");
12223          val = "no";
12224       }
12225       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12226    
12227       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12228          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12229          val = "yes";
12230       }
12231       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12232 
12233       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12234          ast_debug(1, "Move Heard enabled globally\n");
12235          val = "yes";
12236       }
12237       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12238 
12239       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12240          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12241          val = "no";
12242       }
12243       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12244 
12245       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12246          ast_debug(1, "Duration info before msg enabled globally\n");
12247          val = "yes";
12248       }
12249       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12250 
12251       saydurationminfo = 2;
12252       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12253          if (sscanf(val, "%30d", &x) == 1) {
12254             saydurationminfo = x;
12255          } else {
12256             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12257          }
12258       }
12259 
12260       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12261          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12262          val = "no";
12263       }
12264       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12265 
12266       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12267          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12268          ast_debug(1, "found dialout context: %s\n", dialcontext);
12269       } else {
12270          dialcontext[0] = '\0';  
12271       }
12272       
12273       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12274          ast_copy_string(callcontext, val, sizeof(callcontext));
12275          ast_debug(1, "found callback context: %s\n", callcontext);
12276       } else {
12277          callcontext[0] = '\0';
12278       }
12279 
12280       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12281          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12282          ast_debug(1, "found operator context: %s\n", exitcontext);
12283       } else {
12284          exitcontext[0] = '\0';
12285       }
12286       
12287       /* load password sounds configuration */
12288       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12289          ast_copy_string(vm_password, val, sizeof(vm_password));
12290       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12291          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12292       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12293          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12294       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12295          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12296       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12297          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12298       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12299          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12300       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12301          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12302       }
12303       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12304          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12305       }
12306       /* load configurable audio prompts */
12307       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12308          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12309       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12310          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12311       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12312          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12313       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12314          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12315       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12316          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12317 
12318       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12319          val = "no";
12320       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12321 
12322       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12323          val = "voicemail.conf";
12324       }
12325       if (!(strcmp(val, "spooldir"))) {
12326          passwordlocation = OPT_PWLOC_SPOOLDIR;
12327       } else {
12328          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12329       }
12330 
12331       poll_freq = DEFAULT_POLL_FREQ;
12332       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12333          if (sscanf(val, "%30u", &poll_freq) != 1) {
12334             poll_freq = DEFAULT_POLL_FREQ;
12335             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12336          }
12337       }
12338 
12339       poll_mailboxes = 0;
12340       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12341          poll_mailboxes = ast_true(val);
12342 
12343       memset(fromstring, 0, sizeof(fromstring));
12344       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12345       strcpy(charset, "ISO-8859-1");
12346       if (emailbody) {
12347          ast_free(emailbody);
12348          emailbody = NULL;
12349       }
12350       if (emailsubject) {
12351          ast_free(emailsubject);
12352          emailsubject = NULL;
12353       }
12354       if (pagerbody) {
12355          ast_free(pagerbody);
12356          pagerbody = NULL;
12357       }
12358       if (pagersubject) {
12359          ast_free(pagersubject);
12360          pagersubject = NULL;
12361       }
12362       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12363          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12364       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12365          ast_copy_string(fromstring, val, sizeof(fromstring));
12366       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12367          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12368       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12369          ast_copy_string(charset, val, sizeof(charset));
12370       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12371          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12372          for (x = 0; x < 4; x++) {
12373             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12374          }
12375       }
12376       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12377          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12378          for (x = 0; x < 4; x++) {
12379             memcpy(&adsisec[x], &tmpadsi[x], 1);
12380          }
12381       }
12382       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12383          if (atoi(val)) {
12384             adsiver = atoi(val);
12385          }
12386       }
12387       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12388          ast_copy_string(zonetag, val, sizeof(zonetag));
12389       }
12390       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12391          ast_copy_string(locale, val, sizeof(locale));
12392       }
12393       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12394          emailsubject = ast_strdup(substitute_escapes(val));
12395       }
12396       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12397          emailbody = ast_strdup(substitute_escapes(val));
12398       }
12399       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12400          pagersubject = ast_strdup(substitute_escapes(val));
12401       }
12402       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12403          pagerbody = ast_strdup(substitute_escapes(val));
12404       }
12405 
12406       /* load mailboxes from users.conf */
12407       if (ucfg) { 
12408          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12409             if (!strcasecmp(cat, "general")) {
12410                continue;
12411             }
12412             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12413                continue;
12414             if ((current = find_or_create(userscontext, cat))) {
12415                populate_defaults(current);
12416                apply_options_full(current, ast_variable_browse(ucfg, cat));
12417                ast_copy_string(current->context, userscontext, sizeof(current->context));
12418                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12419                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12420                }
12421 
12422                switch (current->passwordlocation) {
12423                case OPT_PWLOC_SPOOLDIR:
12424                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12425                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12426                }
12427             }
12428          }
12429       }
12430 
12431       /* load mailboxes from voicemail.conf */
12432       cat = ast_category_browse(cfg, NULL);
12433       while (cat) {
12434          if (strcasecmp(cat, "general")) {
12435             var = ast_variable_browse(cfg, cat);
12436             if (strcasecmp(cat, "zonemessages")) {
12437                /* Process mailboxes in this context */
12438                while (var) {
12439                   append_mailbox(cat, var->name, var->value);
12440                   var = var->next;
12441                }
12442             } else {
12443                /* Timezones in this context */
12444                while (var) {
12445                   struct vm_zone *z;
12446                   if ((z = ast_malloc(sizeof(*z)))) {
12447                      char *msg_format, *tzone;
12448                      msg_format = ast_strdupa(var->value);
12449                      tzone = strsep(&msg_format, "|,");
12450                      if (msg_format) {
12451                         ast_copy_string(z->name, var->name, sizeof(z->name));
12452                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12453                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12454                         AST_LIST_LOCK(&zones);
12455                         AST_LIST_INSERT_HEAD(&zones, z, list);
12456                         AST_LIST_UNLOCK(&zones);
12457                      } else {
12458                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12459                         ast_free(z);
12460                      }
12461                   } else {
12462                      AST_LIST_UNLOCK(&users);
12463                      return -1;
12464                   }
12465                   var = var->next;
12466                }
12467             }
12468          }
12469          cat = ast_category_browse(cfg, cat);
12470       }
12471 
12472       AST_LIST_UNLOCK(&users);
12473 
12474       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12475          start_poll_thread();
12476       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12477          stop_poll_thread();;
12478 
12479       return 0;
12480    } else {
12481       AST_LIST_UNLOCK(&users);
12482       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12483       return 0;
12484    }
12485 }
12486 
12487 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12488 {
12489    int res = -1;
12490    char dir[PATH_MAX];
12491    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12492    ast_debug(2, "About to try retrieving name file %s\n", dir);
12493    RETRIEVE(dir, -1, mailbox, context);
12494    if (ast_fileexists(dir, NULL, NULL)) {
12495       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12496    }
12497    DISPOSE(dir, -1);
12498    return res;
12499 }
12500 
12501 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12502    struct ast_config *pwconf;
12503    struct ast_flags config_flags = { 0 };
12504 
12505    pwconf = ast_config_load(secretfn, config_flags);
12506    if (pwconf) {
12507       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12508       if (val) {
12509          ast_copy_string(password, val, passwordlen);
12510          return;
12511       }
12512    }
12513    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12514 }
12515 
12516 static int write_password_to_file(const char *secretfn, const char *password) {
12517    struct ast_config *conf;
12518    struct ast_category *cat;
12519    struct ast_variable *var;
12520 
12521    if (!(conf=ast_config_new())) {
12522       ast_log(LOG_ERROR, "Error creating new config structure\n");
12523       return -1;
12524    }
12525    if (!(cat=ast_category_new("general","",1))) {
12526       ast_log(LOG_ERROR, "Error creating new category structure\n");
12527       return -1;
12528    }
12529    if (!(var=ast_variable_new("password",password,""))) {
12530       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12531       return -1;
12532    }
12533    ast_category_append(conf,cat);
12534    ast_variable_append(cat,var);
12535    if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12536       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12537       return -1;
12538    }
12539    return 0;
12540 }
12541 
12542 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12543 {
12544    char *context;
12545    char *args_copy;
12546    int res;
12547 
12548    if (ast_strlen_zero(data)) {
12549       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context");
12550       return -1;
12551    }
12552 
12553    args_copy = ast_strdupa(data);
12554    if ((context = strchr(args_copy, '@'))) {
12555       *context++ = '\0';
12556    } else {
12557       context = "default";
12558    }
12559 
12560    if ((res = sayname(chan, args_copy, context) < 0)) {
12561       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12562       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12563       if (!res) {
12564          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12565       }
12566    }
12567 
12568    return res;
12569 }
12570 
12571 #ifdef TEST_FRAMEWORK
12572 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12573 {
12574    return 0;
12575 }
12576 
12577 static struct ast_frame *fake_read(struct ast_channel *ast)
12578 {
12579    return &ast_null_frame;
12580 }
12581 
12582 AST_TEST_DEFINE(test_voicemail_vmsayname)
12583 {
12584    char dir[PATH_MAX];
12585    char dir2[PATH_MAX];
12586    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12587    static const char TEST_EXTENSION[] = "1234";
12588 
12589    struct ast_channel *test_channel1 = NULL;
12590    int res = -1;
12591 
12592    static const struct ast_channel_tech fake_tech = {
12593       .write = fake_write,
12594       .read = fake_read,
12595    };
12596 
12597    switch (cmd) {
12598    case TEST_INIT:
12599       info->name = "vmsayname_exec";
12600       info->category = "/apps/app_voicemail/";
12601       info->summary = "Vmsayname unit test";
12602       info->description =
12603          "This tests passing various parameters to vmsayname";
12604       return AST_TEST_NOT_RUN;
12605    case TEST_EXECUTE:
12606       break;
12607    }
12608 
12609    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12610         NULL, NULL, 0, 0, "TestChannel1"))) {
12611       goto exit_vmsayname_test;
12612    }
12613 
12614    /* normally this is done in the channel driver */
12615    test_channel1->nativeformats = AST_FORMAT_GSM;
12616    test_channel1->writeformat = AST_FORMAT_GSM;
12617    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12618    test_channel1->readformat = AST_FORMAT_GSM;
12619    test_channel1->rawreadformat = AST_FORMAT_GSM;
12620    test_channel1->tech = &fake_tech;
12621 
12622    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12623    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12624    if (!(res = vmsayname_exec(test_channel1, dir))) {
12625       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12626       if (ast_fileexists(dir, NULL, NULL)) {
12627          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12628          res = -1;
12629          goto exit_vmsayname_test;
12630       } else {
12631          /* no greeting already exists as expected, let's create one to fully test sayname */
12632          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12633             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12634             goto exit_vmsayname_test;
12635          }
12636          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12637          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12638          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12639          if ((res = symlink(dir, dir2))) {
12640             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12641             goto exit_vmsayname_test;
12642          }
12643          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12644          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12645          res = vmsayname_exec(test_channel1, dir);
12646 
12647          /* TODO: there may be a better way to do this */
12648          unlink(dir2);
12649          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12650          rmdir(dir2);
12651          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12652          rmdir(dir2);
12653       }
12654    }
12655 
12656 exit_vmsayname_test:
12657 
12658    if (test_channel1) {
12659       ast_hangup(test_channel1);
12660    }
12661 
12662    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12663 }
12664 
12665 AST_TEST_DEFINE(test_voicemail_msgcount)
12666 {
12667    int i, j, res = AST_TEST_PASS, syserr;
12668    struct ast_vm_user *vmu;
12669    struct vm_state vms;
12670 #ifdef IMAP_STORAGE
12671    struct ast_channel *chan = NULL;
12672 #endif
12673    struct {
12674       char dir[256];
12675       char file[256];
12676       char txtfile[256];
12677    } tmp[3];
12678    char syscmd[256];
12679    const char origweasels[] = "tt-weasels";
12680    const char testcontext[] = "test";
12681    const char testmailbox[] = "00000000";
12682    const char testspec[] = "00000000@test";
12683    FILE *txt;
12684    int new, old, urgent;
12685    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12686    const int folder2mbox[3] = { 1, 11, 0 };
12687    const int expected_results[3][12] = {
12688       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12689       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12690       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12691       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12692    };
12693 
12694    switch (cmd) {
12695    case TEST_INIT:
12696       info->name = "test_voicemail_msgcount";
12697       info->category = "/apps/app_voicemail/";
12698       info->summary = "Test Voicemail status checks";
12699       info->description =
12700          "Verify that message counts are correct when retrieved through the public API";
12701       return AST_TEST_NOT_RUN;
12702    case TEST_EXECUTE:
12703       break;
12704    }
12705 
12706    /* Make sure the original path was completely empty */
12707    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12708    if ((syserr = ast_safe_system(syscmd))) {
12709       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12710          syserr > 0 ? strerror(syserr) : "unable to fork()");
12711       return AST_TEST_FAIL;
12712    }
12713 
12714 #ifdef IMAP_STORAGE
12715    if (!(chan = ast_dummy_channel_alloc())) {
12716       ast_test_status_update(test, "Unable to create dummy channel\n");
12717       return AST_TEST_FAIL;
12718    }
12719 #endif
12720 
12721    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12722       !(vmu = find_or_create(testcontext, testmailbox))) {
12723       ast_test_status_update(test, "Cannot create vmu structure\n");
12724       ast_unreplace_sigchld();
12725 #ifdef IMAP_STORAGE
12726       chan = ast_channel_unref(chan);
12727 #endif
12728       return AST_TEST_FAIL;
12729    }
12730 
12731    populate_defaults(vmu);
12732    memset(&vms, 0, sizeof(vms));
12733 
12734    /* Create temporary voicemail */
12735    for (i = 0; i < 3; i++) {
12736       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12737       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12738       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12739 
12740       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12741          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12742             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12743          if ((syserr = ast_safe_system(syscmd))) {
12744             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12745                syserr > 0 ? strerror(syserr) : "unable to fork()");
12746             ast_unreplace_sigchld();
12747 #ifdef IMAP_STORAGE
12748             chan = ast_channel_unref(chan);
12749 #endif
12750             return AST_TEST_FAIL;
12751          }
12752       }
12753 
12754       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12755          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12756          fclose(txt);
12757       } else {
12758          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12759          res = AST_TEST_FAIL;
12760          break;
12761       }
12762       open_mailbox(&vms, vmu, folder2mbox[i]);
12763       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12764 
12765       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12766       for (j = 0; j < 3; j++) {
12767          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12768          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12769             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12770                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12771             res = AST_TEST_FAIL;
12772          }
12773       }
12774 
12775       new = old = urgent = 0;
12776       if (ast_app_inboxcount(testspec, &new, &old)) {
12777          ast_test_status_update(test, "inboxcount returned failure\n");
12778          res = AST_TEST_FAIL;
12779       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12780          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12781             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12782          res = AST_TEST_FAIL;
12783       }
12784 
12785       new = old = urgent = 0;
12786       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12787          ast_test_status_update(test, "inboxcount2 returned failure\n");
12788          res = AST_TEST_FAIL;
12789       } else if (old != expected_results[i][6 + 0] ||
12790             urgent != expected_results[i][6 + 1] ||
12791                new != expected_results[i][6 + 2]    ) {
12792          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12793             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12794          res = AST_TEST_FAIL;
12795       }
12796 
12797       new = old = urgent = 0;
12798       for (j = 0; j < 3; j++) {
12799          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12800             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12801                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12802             res = AST_TEST_FAIL;
12803          }
12804       }
12805    }
12806 
12807    for (i = 0; i < 3; i++) {
12808       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12809        * server, in which case, the rm below will not affect the
12810        * voicemails. */
12811       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12812       DISPOSE(tmp[i].dir, 0);
12813    }
12814 
12815    if (vms.deleted) {
12816       ast_free(vms.deleted);
12817    }
12818    if (vms.heard) {
12819       ast_free(vms.heard);
12820    }
12821 
12822 #ifdef IMAP_STORAGE
12823    chan = ast_channel_unref(chan);
12824 #endif
12825 
12826    /* And remove test directory */
12827    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12828    if ((syserr = ast_safe_system(syscmd))) {
12829       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12830          syserr > 0 ? strerror(syserr) : "unable to fork()");
12831    }
12832 
12833    return res;
12834 }
12835 
12836 AST_TEST_DEFINE(test_voicemail_notify_endl)
12837 {
12838    int res = AST_TEST_PASS;
12839    char testcontext[] = "test";
12840    char testmailbox[] = "00000000";
12841    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12842    char attach[256], attach2[256];
12843    char buf[256] = ""; /* No line should actually be longer than 80 */
12844    struct ast_channel *chan = NULL;
12845    struct ast_vm_user *vmu, vmus = {
12846       .flags = 0,
12847    };
12848    FILE *file;
12849    struct {
12850       char *name;
12851       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12852       void *location;
12853       union {
12854          int intval;
12855          char *strval;
12856       } u;
12857    } test_items[] = {
12858       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12859       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12860       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12861       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12862       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12863       { "attach2", STRPTR, attach2, .u.strval = "" },
12864       { "attach", STRPTR, attach, .u.strval = "" },
12865    };
12866    int which;
12867 
12868    switch (cmd) {
12869    case TEST_INIT:
12870       info->name = "test_voicemail_notify_endl";
12871       info->category = "/apps/app_voicemail/";
12872       info->summary = "Test Voicemail notification end-of-line";
12873       info->description =
12874          "Verify that notification emails use a consistent end-of-line character";
12875       return AST_TEST_NOT_RUN;
12876    case TEST_EXECUTE:
12877       break;
12878    }
12879 
12880    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12881    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12882 
12883    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12884       !(vmu = find_or_create(testcontext, testmailbox))) {
12885       ast_test_status_update(test, "Cannot create vmu structure\n");
12886       return AST_TEST_NOT_RUN;
12887    }
12888 
12889    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12890       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12891       return AST_TEST_NOT_RUN;
12892    }
12893 
12894    populate_defaults(vmu);
12895    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12896 #ifdef IMAP_STORAGE
12897    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12898 #endif
12899 
12900    file = tmpfile();
12901    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12902       /* Kill previous test, if any */
12903       rewind(file);
12904       if (ftruncate(fileno(file), 0)) {
12905          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12906          res = AST_TEST_FAIL;
12907          break;
12908       }
12909 
12910       /* Make each change, in order, to the test mailbox */
12911       if (test_items[which].type == INT) {
12912          *((int *) test_items[which].location) = test_items[which].u.intval;
12913       } else if (test_items[which].type == FLAGVAL) {
12914          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12915             ast_clear_flag(vmu, test_items[which].u.intval);
12916          } else {
12917             ast_set_flag(vmu, test_items[which].u.intval);
12918          }
12919       } else if (test_items[which].type == STATIC) {
12920          strcpy(test_items[which].location, test_items[which].u.strval);
12921       } else if (test_items[which].type == STRPTR) {
12922          test_items[which].location = test_items[which].u.strval;
12923       }
12924 
12925       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12926       rewind(file);
12927       while (fgets(buf, sizeof(buf), file)) {
12928          if (
12929 #ifdef IMAP_STORAGE
12930          buf[strlen(buf) - 2] != '\r'
12931 #else
12932          buf[strlen(buf) - 2] == '\r'
12933 #endif
12934          || buf[strlen(buf) - 1] != '\n') {
12935             res = AST_TEST_FAIL;
12936          }
12937       }
12938    }
12939    fclose(file);
12940    return res;
12941 }
12942 
12943 AST_TEST_DEFINE(test_voicemail_load_config)
12944 {
12945    int res = AST_TEST_PASS;
12946    struct ast_vm_user *vmu;
12947    struct ast_config *cfg;
12948    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
12949    int fd;
12950    FILE *file;
12951    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
12952 
12953    switch (cmd) {
12954    case TEST_INIT:
12955       info->name = "test_voicemail_load_config";
12956       info->category = "/apps/app_voicemail/";
12957       info->summary = "Test loading Voicemail config";
12958       info->description =
12959          "Verify that configuration is loaded consistently. "
12960          "This is to test regressions of ASTERISK-18838 where it was noticed that "
12961          "some options were loaded after the mailboxes were instantiated, causing "
12962          "those options not to be set correctly.";
12963       return AST_TEST_NOT_RUN;
12964    case TEST_EXECUTE:
12965       break;
12966    }
12967 
12968    /* build a config file by hand... */
12969    if ((fd = mkstemp(config_filename)) < 0) {
12970       return AST_TEST_FAIL;
12971    }
12972    if (!(file = fdopen(fd, "w"))) {
12973       close(fd);
12974       unlink(config_filename);
12975       return AST_TEST_FAIL;
12976    }
12977    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
12978    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
12979    fputs("00000002 => 9999,Mrs. Test\n", file);
12980    fclose(file);
12981 
12982    if (!(cfg = ast_config_load(config_filename, config_flags))) {
12983       res = AST_TEST_FAIL;
12984       goto cleanup;
12985    }
12986 
12987    load_config_from_memory(1, cfg, NULL);
12988    ast_config_destroy(cfg);
12989 
12990 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
12991    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
12992    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
12993 
12994    AST_LIST_LOCK(&users);
12995    AST_LIST_TRAVERSE(&users, vmu, list) {
12996       if (!strcmp(vmu->mailbox, "00000001")) {
12997          if (0); /* trick to get CHECK to work */
12998          CHECK(vmu, callback, "othercontext")
12999          CHECK(vmu, locale, "nl_NL.UTF-8")
13000          CHECK(vmu, zonetag, "central")
13001       } else if (!strcmp(vmu->mailbox, "00000002")) {
13002          if (0); /* trick to get CHECK to work */
13003          CHECK(vmu, callback, "somecontext")
13004          CHECK(vmu, locale, "de_DE.UTF-8")
13005          CHECK(vmu, zonetag, "european")
13006       }
13007    }
13008    AST_LIST_UNLOCK(&users);
13009 
13010 #undef CHECK
13011 
13012    /* restore config */
13013    load_config(1); /* this might say "Failed to load configuration file." */
13014 
13015 cleanup:
13016    unlink(config_filename);
13017    return res;
13018 }
13019 
13020 #endif /* defined(TEST_FRAMEWORK) */
13021 
13022 static int reload(void)
13023 {
13024    return load_config(1);
13025 }
13026 
13027 static int unload_module(void)
13028 {
13029    int res;
13030 
13031    res = ast_unregister_application(app);
13032    res |= ast_unregister_application(app2);
13033    res |= ast_unregister_application(app3);
13034    res |= ast_unregister_application(app4);
13035    res |= ast_unregister_application(sayname_app);
13036    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13037    res |= ast_manager_unregister("VoicemailUsersList");
13038    res |= ast_data_unregister(NULL);
13039 #ifdef TEST_FRAMEWORK
13040    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13041    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13042    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13043    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13044    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13045 #endif
13046    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13047    ast_uninstall_vm_functions();
13048    ao2_ref(inprocess_container, -1);
13049 
13050    if (poll_thread != AST_PTHREADT_NULL)
13051       stop_poll_thread();
13052 
13053    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13054    ast_unload_realtime("voicemail");
13055    ast_unload_realtime("voicemail_data");
13056 
13057    free_vm_users();
13058    free_vm_zones();
13059    return res;
13060 }
13061 
13062 static int load_module(void)
13063 {
13064    int res;
13065    my_umask = umask(0);
13066    umask(my_umask);
13067 
13068    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13069       return AST_MODULE_LOAD_DECLINE;
13070    }
13071 
13072    /* compute the location of the voicemail spool directory */
13073    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13074    
13075    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13076       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13077    }
13078 
13079    if ((res = load_config(0)))
13080       return res;
13081 
13082    res = ast_register_application_xml(app, vm_exec);
13083    res |= ast_register_application_xml(app2, vm_execmain);
13084    res |= ast_register_application_xml(app3, vm_box_exists);
13085    res |= ast_register_application_xml(app4, vmauthenticate);
13086    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13087    res |= ast_custom_function_register(&mailbox_exists_acf);
13088    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13089 #ifdef TEST_FRAMEWORK
13090    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13091    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13092    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13093    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13094    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13095 #endif
13096 
13097    if (res)
13098       return res;
13099 
13100    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13101    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13102 
13103    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13104    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13105    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13106 
13107    return res;
13108 }
13109 
13110 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13111 {
13112    int cmd = 0;
13113    char destination[80] = "";
13114    int retries = 0;
13115 
13116    if (!num) {
13117       ast_verb(3, "Destination number will be entered manually\n");
13118       while (retries < 3 && cmd != 't') {
13119          destination[1] = '\0';
13120          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13121          if (!cmd)
13122             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13123          if (!cmd)
13124             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13125          if (!cmd) {
13126             cmd = ast_waitfordigit(chan, 6000);
13127             if (cmd)
13128                destination[0] = cmd;
13129          }
13130          if (!cmd) {
13131             retries++;
13132          } else {
13133 
13134             if (cmd < 0)
13135                return 0;
13136             if (cmd == '*') {
13137                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13138                return 0;
13139             }
13140             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13141                retries++;
13142             else
13143                cmd = 't';
13144          }
13145          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13146       }
13147       if (retries >= 3) {
13148          return 0;
13149       }
13150       
13151    } else {
13152       if (option_verbose > 2)
13153          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13154       ast_copy_string(destination, num, sizeof(destination));
13155    }
13156 
13157    if (!ast_strlen_zero(destination)) {
13158       if (destination[strlen(destination) -1 ] == '*')
13159          return 0; 
13160       if (option_verbose > 2)
13161          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13162       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13163       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13164       chan->priority = 0;
13165       return 9;
13166    }
13167    return 0;
13168 }
13169 
13170 /*!
13171  * \brief The advanced options within a message.
13172  * \param chan
13173  * \param vmu 
13174  * \param vms
13175  * \param msg
13176  * \param option
13177  * \param record_gain
13178  *
13179  * Provides handling for the play message envelope, call the person back, or reply to message. 
13180  *
13181  * \return zero on success, -1 on error.
13182  */
13183 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)
13184 {
13185    int res = 0;
13186    char filename[PATH_MAX];
13187    struct ast_config *msg_cfg = NULL;
13188    const char *origtime, *context;
13189    char *name, *num;
13190    int retries = 0;
13191    char *cid;
13192    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13193 
13194    vms->starting = 0; 
13195 
13196    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13197 
13198    /* Retrieve info from VM attribute file */
13199    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13200    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13201    msg_cfg = ast_config_load(filename, config_flags);
13202    DISPOSE(vms->curdir, vms->curmsg);
13203    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13204       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13205       return 0;
13206    }
13207 
13208    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13209       ast_config_destroy(msg_cfg);
13210       return 0;
13211    }
13212 
13213    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13214 
13215    context = ast_variable_retrieve(msg_cfg, "message", "context");
13216    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13217       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13218    switch (option) {
13219    case 3: /* Play message envelope */
13220       if (!res)
13221          res = play_message_datetime(chan, vmu, origtime, filename);
13222       if (!res)
13223          res = play_message_callerid(chan, vms, cid, context, 0);
13224 
13225       res = 't';
13226       break;
13227 
13228    case 2:  /* Call back */
13229 
13230       if (ast_strlen_zero(cid))
13231          break;
13232 
13233       ast_callerid_parse(cid, &name, &num);
13234       while ((res > -1) && (res != 't')) {
13235          switch (res) {
13236          case '1':
13237             if (num) {
13238                /* Dial the CID number */
13239                res = dialout(chan, vmu, num, vmu->callback);
13240                if (res) {
13241                   ast_config_destroy(msg_cfg);
13242                   return 9;
13243                }
13244             } else {
13245                res = '2';
13246             }
13247             break;
13248 
13249          case '2':
13250             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13251             if (!ast_strlen_zero(vmu->dialout)) {
13252                res = dialout(chan, vmu, NULL, vmu->dialout);
13253                if (res) {
13254                   ast_config_destroy(msg_cfg);
13255                   return 9;
13256                }
13257             } else {
13258                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13259                res = ast_play_and_wait(chan, "vm-sorry");
13260             }
13261             ast_config_destroy(msg_cfg);
13262             return res;
13263          case '*':
13264             res = 't';
13265             break;
13266          case '3':
13267          case '4':
13268          case '5':
13269          case '6':
13270          case '7':
13271          case '8':
13272          case '9':
13273          case '0':
13274 
13275             res = ast_play_and_wait(chan, "vm-sorry");
13276             retries++;
13277             break;
13278          default:
13279             if (num) {
13280                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13281                res = ast_play_and_wait(chan, "vm-num-i-have");
13282                if (!res)
13283                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13284                if (!res)
13285                   res = ast_play_and_wait(chan, "vm-tocallnum");
13286                /* Only prompt for a caller-specified number if there is a dialout context specified */
13287                if (!ast_strlen_zero(vmu->dialout)) {
13288                   if (!res)
13289                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13290                }
13291             } else {
13292                res = ast_play_and_wait(chan, "vm-nonumber");
13293                if (!ast_strlen_zero(vmu->dialout)) {
13294                   if (!res)
13295                      res = ast_play_and_wait(chan, "vm-toenternumber");
13296                }
13297             }
13298             if (!res) {
13299                res = ast_play_and_wait(chan, "vm-star-cancel");
13300             }
13301             if (!res) {
13302                res = ast_waitfordigit(chan, 6000);
13303             }
13304             if (!res) {
13305                retries++;
13306                if (retries > 3) {
13307                   res = 't';
13308                }
13309             }
13310             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13311             break; 
13312             
13313          }
13314          if (res == 't')
13315             res = 0;
13316          else if (res == '*')
13317             res = -1;
13318       }
13319       break;
13320       
13321    case 1:  /* Reply */
13322       /* Send reply directly to sender */
13323       if (ast_strlen_zero(cid))
13324          break;
13325 
13326       ast_callerid_parse(cid, &name, &num);
13327       if (!num) {
13328          ast_verb(3, "No CID number available, no reply sent\n");
13329          if (!res)
13330             res = ast_play_and_wait(chan, "vm-nonumber");
13331          ast_config_destroy(msg_cfg);
13332          return res;
13333       } else {
13334          struct ast_vm_user vmu2;
13335          if (find_user(&vmu2, vmu->context, num)) {
13336             struct leave_vm_options leave_options;
13337             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13338             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13339 
13340             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13341             
13342             memset(&leave_options, 0, sizeof(leave_options));
13343             leave_options.record_gain = record_gain;
13344             res = leave_voicemail(chan, mailbox, &leave_options);
13345             if (!res)
13346                res = 't';
13347             ast_config_destroy(msg_cfg);
13348             return res;
13349          } else {
13350             /* Sender has no mailbox, can't reply */
13351             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13352             ast_play_and_wait(chan, "vm-nobox");
13353             res = 't';
13354             ast_config_destroy(msg_cfg);
13355             return res;
13356          }
13357       } 
13358       res = 0;
13359 
13360       break;
13361    }
13362 
13363 #ifndef IMAP_STORAGE
13364    ast_config_destroy(msg_cfg);
13365 
13366    if (!res) {
13367       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13368       vms->heard[msg] = 1;
13369       res = wait_file(chan, vms, vms->fn);
13370    }
13371 #endif
13372    return res;
13373 }
13374 
13375 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13376          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13377          signed char record_gain, struct vm_state *vms, char *flag)
13378 {
13379    /* Record message & let caller review or re-record it, or set options if applicable */
13380    int res = 0;
13381    int cmd = 0;
13382    int max_attempts = 3;
13383    int attempts = 0;
13384    int recorded = 0;
13385    int msg_exists = 0;
13386    signed char zero_gain = 0;
13387    char tempfile[PATH_MAX];
13388    char *acceptdtmf = "#";
13389    char *canceldtmf = "";
13390    int canceleddtmf = 0;
13391 
13392    /* Note that urgent and private are for flagging messages as such in the future */
13393 
13394    /* barf if no pointer passed to store duration in */
13395    if (duration == NULL) {
13396       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13397       return -1;
13398    }
13399 
13400    if (!outsidecaller)
13401       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13402    else
13403       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13404 
13405    cmd = '3';  /* Want to start by recording */
13406 
13407    while ((cmd >= 0) && (cmd != 't')) {
13408       switch (cmd) {
13409       case '1':
13410          if (!msg_exists) {
13411             /* In this case, 1 is to record a message */
13412             cmd = '3';
13413             break;
13414          } else {
13415             /* Otherwise 1 is to save the existing message */
13416             ast_verb(3, "Saving message as is\n");
13417             if (!outsidecaller) 
13418                ast_filerename(tempfile, recordfile, NULL);
13419             ast_stream_and_wait(chan, "vm-msgsaved", "");
13420             if (!outsidecaller) {
13421                /* Saves to IMAP server only if imapgreeting=yes */
13422                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13423                DISPOSE(recordfile, -1);
13424             }
13425             cmd = 't';
13426             return res;
13427          }
13428       case '2':
13429          /* Review */
13430          ast_verb(3, "Reviewing the message\n");
13431          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13432          break;
13433       case '3':
13434          msg_exists = 0;
13435          /* Record */
13436          if (recorded == 1) 
13437             ast_verb(3, "Re-recording the message\n");
13438          else  
13439             ast_verb(3, "Recording the message\n");
13440          
13441          if (recorded && outsidecaller) {
13442             cmd = ast_play_and_wait(chan, INTRO);
13443             cmd = ast_play_and_wait(chan, "beep");
13444          }
13445          recorded = 1;
13446          /* 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 */
13447          if (record_gain)
13448             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13449          if (ast_test_flag(vmu, VM_OPERATOR))
13450             canceldtmf = "0";
13451          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13452          if (strchr(canceldtmf, cmd)) {
13453          /* need this flag here to distinguish between pressing '0' during message recording or after */
13454             canceleddtmf = 1;
13455          }
13456          if (record_gain)
13457             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13458          if (cmd == -1) {
13459             /* User has hung up, no options to give */
13460             if (!outsidecaller) {
13461                /* user was recording a greeting and they hung up, so let's delete the recording. */
13462                ast_filedelete(tempfile, NULL);
13463             }     
13464             return cmd;
13465          }
13466          if (cmd == '0') {
13467             break;
13468          } else if (cmd == '*') {
13469             break;
13470 #if 0
13471          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13472             /* Message is too short */
13473             ast_verb(3, "Message too short\n");
13474             cmd = ast_play_and_wait(chan, "vm-tooshort");
13475             cmd = ast_filedelete(tempfile, NULL);
13476             break;
13477          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13478             /* Message is all silence */
13479             ast_verb(3, "Nothing recorded\n");
13480             cmd = ast_filedelete(tempfile, NULL);
13481             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13482             if (!cmd)
13483                cmd = ast_play_and_wait(chan, "vm-speakup");
13484             break;
13485 #endif
13486          } else {
13487             /* If all is well, a message exists */
13488             msg_exists = 1;
13489             cmd = 0;
13490          }
13491          break;
13492       case '4':
13493          if (outsidecaller) {  /* only mark vm messages */
13494             /* Mark Urgent */
13495             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13496                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13497                res = ast_play_and_wait(chan, "vm-marked-urgent");
13498                strcpy(flag, "Urgent");
13499             } else if (flag) {
13500                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13501                res = ast_play_and_wait(chan, "vm-urgent-removed");
13502                strcpy(flag, "");
13503             } else {
13504                ast_play_and_wait(chan, "vm-sorry");
13505             }
13506             cmd = 0;
13507          } else {
13508             cmd = ast_play_and_wait(chan, "vm-sorry");
13509          }
13510          break;
13511       case '5':
13512       case '6':
13513       case '7':
13514       case '8':
13515       case '9':
13516       case '*':
13517       case '#':
13518          cmd = ast_play_and_wait(chan, "vm-sorry");
13519          break;
13520 #if 0 
13521 /*  XXX Commented out for the moment because of the dangers of deleting
13522     a message while recording (can put the message numbers out of sync) */
13523       case '*':
13524          /* Cancel recording, delete message, offer to take another message*/
13525          cmd = ast_play_and_wait(chan, "vm-deleted");
13526          cmd = ast_filedelete(tempfile, NULL);
13527          if (outsidecaller) {
13528             res = vm_exec(chan, NULL);
13529             return res;
13530          }
13531          else
13532             return 1;
13533 #endif
13534       case '0':
13535          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13536             cmd = ast_play_and_wait(chan, "vm-sorry");
13537             break;
13538          }
13539          if (msg_exists || recorded) {
13540             cmd = ast_play_and_wait(chan, "vm-saveoper");
13541             if (!cmd)
13542                cmd = ast_waitfordigit(chan, 3000);
13543             if (cmd == '1') {
13544                ast_filerename(tempfile, recordfile, NULL);
13545                ast_play_and_wait(chan, "vm-msgsaved");
13546                cmd = '0';
13547             } else if (cmd == '4') {
13548                if (flag) {
13549                   ast_play_and_wait(chan, "vm-marked-urgent");
13550                   strcpy(flag, "Urgent");
13551                }
13552                ast_play_and_wait(chan, "vm-msgsaved");
13553                cmd = '0';
13554             } else {
13555                ast_play_and_wait(chan, "vm-deleted");
13556                DELETE(tempfile, -1, tempfile, vmu);
13557                cmd = '0';
13558             }
13559          }
13560          return cmd;
13561       default:
13562          /* If the caller is an ouside caller, and the review option is enabled,
13563             allow them to review the message, but let the owner of the box review
13564             their OGM's */
13565          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13566             return cmd;
13567          if (msg_exists) {
13568             cmd = ast_play_and_wait(chan, "vm-review");
13569             if (!cmd && outsidecaller) {
13570                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13571                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13572                } else if (flag) {
13573                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13574                }
13575             }
13576          } else {
13577             cmd = ast_play_and_wait(chan, "vm-torerecord");
13578             if (!cmd)
13579                cmd = ast_waitfordigit(chan, 600);
13580          }
13581          
13582          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13583             cmd = ast_play_and_wait(chan, "vm-reachoper");
13584             if (!cmd)
13585                cmd = ast_waitfordigit(chan, 600);
13586          }
13587 #if 0
13588          if (!cmd)
13589             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13590 #endif
13591          if (!cmd)
13592             cmd = ast_waitfordigit(chan, 6000);
13593          if (!cmd) {
13594             attempts++;
13595          }
13596          if (attempts > max_attempts) {
13597             cmd = 't';
13598          }
13599       }
13600    }
13601    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13602       /* Hang up or timeout, so delete the recording. */
13603       ast_filedelete(tempfile, NULL);
13604    }
13605 
13606    if (cmd != 't' && outsidecaller)
13607       ast_play_and_wait(chan, "vm-goodbye");
13608 
13609    return cmd;
13610 }
13611 
13612 /* This is a workaround so that menuselect displays a proper description
13613  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13614  */
13615 
13616 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13617       .load = load_module,
13618       .unload = unload_module,
13619       .reload = reload,
13620       .nonoptreq = "res_adsi,res_smdi",
13621       );

Generated on Sat Mar 10 01:54:01 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7