Tue Aug 20 16:34:23 2013

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: 376262 $")
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 language[MAX_LANGUAGE];     /*!< Config: Language setting */
00644    char zonetag[80];                /*!< Time zone */
00645    char locale[20];                 /*!< The locale (for presentation of date/time) */
00646    char callback[80];
00647    char dialout[80];
00648    char uniqueid[80];               /*!< Unique integer identifier */
00649    char exit[80];
00650    char attachfmt[20];              /*!< Attachment format */
00651    unsigned int flags;              /*!< VM_ flags */ 
00652    int saydurationm;
00653    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00654    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00655    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00656    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00657    int passwordlocation;            /*!< Storage location of the password */
00658 #ifdef IMAP_STORAGE
00659    char imapuser[80];               /*!< IMAP server login */
00660    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00661    char imapfolder[64];             /*!< IMAP voicemail folder */
00662    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00663    int imapversion;                 /*!< If configuration changes, use the new values */
00664 #endif
00665    double volgain;                  /*!< Volume gain for voicemails sent via email */
00666    AST_LIST_ENTRY(ast_vm_user) list;
00667 };
00668 
00669 /*! Voicemail time zones */
00670 struct vm_zone {
00671    AST_LIST_ENTRY(vm_zone) list;
00672    char name[80];
00673    char timezone[80];
00674    char msg_format[512];
00675 };
00676 
00677 #define VMSTATE_MAX_MSG_ARRAY 256
00678 
00679 /*! Voicemail mailbox state */
00680 struct vm_state {
00681    char curbox[80];
00682    char username[80];
00683    char context[80];
00684    char curdir[PATH_MAX];
00685    char vmbox[PATH_MAX];
00686    char fn[PATH_MAX];
00687    char intro[PATH_MAX];
00688    int *deleted;
00689    int *heard;
00690    int dh_arraysize; /* used for deleted / heard allocation */
00691    int curmsg;
00692    int lastmsg;
00693    int newmessages;
00694    int oldmessages;
00695    int urgentmessages;
00696    int starting;
00697    int repeats;
00698 #ifdef IMAP_STORAGE
00699    ast_mutex_t lock;
00700    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00701    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00702    MAILSTREAM *mailstream;
00703    int vmArrayIndex;
00704    char imapuser[80];                   /*!< IMAP server login */
00705    char imapfolder[64];                 /*!< IMAP voicemail folder */
00706    int imapversion;
00707    int interactive;
00708    char introfn[PATH_MAX];              /*!< Name of prepended file */
00709    unsigned int quota_limit;
00710    unsigned int quota_usage;
00711    struct vm_state *persist_vms;
00712 #endif
00713 };
00714 
00715 #ifdef ODBC_STORAGE
00716 static char odbc_database[80];
00717 static char odbc_table[80];
00718 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00719 #define DISPOSE(a,b) remove_file(a,b)
00720 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00721 #define EXISTS(a,b,c,d) (message_exists(a,b))
00722 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00723 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00724 #define DELETE(a,b,c,d) (delete_file(a,b))
00725 #else
00726 #ifdef IMAP_STORAGE
00727 #define DISPOSE(a,b) (imap_remove_file(a,b))
00728 #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))
00729 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00730 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00731 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00732 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00733 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00734 #else
00735 #define RETRIEVE(a,b,c,d)
00736 #define DISPOSE(a,b)
00737 #define STORE(a,b,c,d,e,f,g,h,i,j)
00738 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00739 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00740 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00741 #define DELETE(a,b,c,d) (vm_delete(c))
00742 #endif
00743 #endif
00744 
00745 static char VM_SPOOL_DIR[PATH_MAX];
00746 
00747 static char ext_pass_cmd[128];
00748 static char ext_pass_check_cmd[128];
00749 
00750 static int my_umask;
00751 
00752 #define PWDCHANGE_INTERNAL (1 << 1)
00753 #define PWDCHANGE_EXTERNAL (1 << 2)
00754 static int pwdchange = PWDCHANGE_INTERNAL;
00755 
00756 #ifdef ODBC_STORAGE
00757 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00758 #else
00759 # ifdef IMAP_STORAGE
00760 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00761 # else
00762 # define tdesc "Comedian Mail (Voicemail System)"
00763 # endif
00764 #endif
00765 
00766 static char userscontext[AST_MAX_EXTENSION] = "default";
00767 
00768 static char *addesc = "Comedian Mail";
00769 
00770 /* Leave a message */
00771 static char *app = "VoiceMail";
00772 
00773 /* Check mail, control, etc */
00774 static char *app2 = "VoiceMailMain";
00775 
00776 static char *app3 = "MailboxExists";
00777 static char *app4 = "VMAuthenticate";
00778 
00779 static char *sayname_app = "VMSayName";
00780 
00781 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00782 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00783 static char zonetag[80];
00784 static char locale[20];
00785 static int maxsilence;
00786 static int maxmsg;
00787 static int maxdeletedmsg;
00788 static int silencethreshold = 128;
00789 static char serveremail[80];
00790 static char mailcmd[160];  /* Configurable mail cmd */
00791 static char externnotify[160]; 
00792 static struct ast_smdi_interface *smdi_iface = NULL;
00793 static char vmfmts[80];
00794 static double volgain;
00795 static int vmminsecs;
00796 static int vmmaxsecs;
00797 static int maxgreet;
00798 static int skipms;
00799 static int maxlogins;
00800 static int minpassword;
00801 static int passwordlocation;
00802 
00803 /*! Poll mailboxes for changes since there is something external to
00804  *  app_voicemail that may change them. */
00805 static unsigned int poll_mailboxes;
00806 
00807 /*! Polling frequency */
00808 static unsigned int poll_freq;
00809 /*! By default, poll every 30 seconds */
00810 #define DEFAULT_POLL_FREQ 30
00811 
00812 AST_MUTEX_DEFINE_STATIC(poll_lock);
00813 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00814 static pthread_t poll_thread = AST_PTHREADT_NULL;
00815 static unsigned char poll_thread_run;
00816 
00817 /*! Subscription to ... MWI event subscriptions */
00818 static struct ast_event_sub *mwi_sub_sub;
00819 /*! Subscription to ... MWI event un-subscriptions */
00820 static struct ast_event_sub *mwi_unsub_sub;
00821 
00822 /*!
00823  * \brief An MWI subscription
00824  *
00825  * This is so we can keep track of which mailboxes are subscribed to.
00826  * This way, we know which mailboxes to poll when the pollmailboxes
00827  * option is being used.
00828  */
00829 struct mwi_sub {
00830    AST_RWLIST_ENTRY(mwi_sub) entry;
00831    int old_urgent;
00832    int old_new;
00833    int old_old;
00834    uint32_t uniqueid;
00835    char mailbox[1];
00836 };
00837 
00838 struct mwi_sub_task {
00839    const char *mailbox;
00840    const char *context;
00841    uint32_t uniqueid;
00842 };
00843 
00844 static struct ast_taskprocessor *mwi_subscription_tps;
00845 
00846 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00847 
00848 /* custom audio control prompts for voicemail playback */
00849 static char listen_control_forward_key[12];
00850 static char listen_control_reverse_key[12];
00851 static char listen_control_pause_key[12];
00852 static char listen_control_restart_key[12];
00853 static char listen_control_stop_key[12];
00854 
00855 /* custom password sounds */
00856 static char vm_password[80] = "vm-password";
00857 static char vm_newpassword[80] = "vm-newpassword";
00858 static char vm_passchanged[80] = "vm-passchanged";
00859 static char vm_reenterpassword[80] = "vm-reenterpassword";
00860 static char vm_mismatch[80] = "vm-mismatch";
00861 static char vm_invalid_password[80] = "vm-invalid-password";
00862 static char vm_pls_try_again[80] = "vm-pls-try-again";
00863 
00864 /*
00865  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00866  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00867  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00868  * app.c's __ast_play_and_record function
00869  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00870  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00871  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00872  * more effort than either of the other two.
00873  */
00874 static char vm_prepend_timeout[80] = "vm-then-pound";
00875 
00876 static struct ast_flags globalflags = {0};
00877 
00878 static int saydurationminfo;
00879 
00880 static char dialcontext[AST_MAX_CONTEXT] = "";
00881 static char callcontext[AST_MAX_CONTEXT] = "";
00882 static char exitcontext[AST_MAX_CONTEXT] = "";
00883 
00884 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00885 
00886 
00887 static char *emailbody = NULL;
00888 static char *emailsubject = NULL;
00889 static char *pagerbody = NULL;
00890 static char *pagersubject = NULL;
00891 static char fromstring[100];
00892 static char pagerfromstring[100];
00893 static char charset[32] = "ISO-8859-1";
00894 
00895 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00896 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00897 static int adsiver = 1;
00898 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00899 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00900 
00901 /* Forward declarations - generic */
00902 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00903 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);
00904 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00905 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00906          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00907          signed char record_gain, struct vm_state *vms, char *flag);
00908 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00909 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00910 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);
00911 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);
00912 static void apply_options(struct ast_vm_user *vmu, const char *options);
00913 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);
00914 static int is_valid_dtmf(const char *key);
00915 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00916 static int write_password_to_file(const char *secretfn, const char *password);
00917 static const char *substitute_escapes(const char *value);
00918 static void free_user(struct ast_vm_user *vmu);
00919 
00920 struct ao2_container *inprocess_container;
00921 
00922 struct inprocess {
00923    int count;
00924    char *context;
00925    char mailbox[0];
00926 };
00927 
00928 static int inprocess_hash_fn(const void *obj, const int flags)
00929 {
00930    const struct inprocess *i = obj;
00931    return atoi(i->mailbox);
00932 }
00933 
00934 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00935 {
00936    struct inprocess *i = obj, *j = arg;
00937    if (strcmp(i->mailbox, j->mailbox)) {
00938       return 0;
00939    }
00940    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00941 }
00942 
00943 static int inprocess_count(const char *context, const char *mailbox, int delta)
00944 {
00945    struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00946    arg->context = arg->mailbox + strlen(mailbox) + 1;
00947    strcpy(arg->mailbox, mailbox); /* SAFE */
00948    strcpy(arg->context, context); /* SAFE */
00949    ao2_lock(inprocess_container);
00950    if ((i = ao2_find(inprocess_container, arg, 0))) {
00951       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00952       ao2_unlock(inprocess_container);
00953       ao2_ref(i, -1);
00954       return ret;
00955    }
00956    if (delta < 0) {
00957       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00958    }
00959    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00960       ao2_unlock(inprocess_container);
00961       return 0;
00962    }
00963    i->context = i->mailbox + strlen(mailbox) + 1;
00964    strcpy(i->mailbox, mailbox); /* SAFE */
00965    strcpy(i->context, context); /* SAFE */
00966    i->count = delta;
00967    ao2_link(inprocess_container, i);
00968    ao2_unlock(inprocess_container);
00969    ao2_ref(i, -1);
00970    return 0;
00971 }
00972 
00973 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00974 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00975 #endif
00976 
00977 /*!
00978  * \brief Strips control and non 7-bit clean characters from input string.
00979  *
00980  * \note To map control and none 7-bit characters to a 7-bit clean characters
00981  *  please use ast_str_encode_mine().
00982  */
00983 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00984 {
00985    char *bufptr = buf;
00986    for (; *input; input++) {
00987       if (*input < 32) {
00988          continue;
00989       }
00990       *bufptr++ = *input;
00991       if (bufptr == buf + buflen - 1) {
00992          break;
00993       }
00994    }
00995    *bufptr = '\0';
00996    return buf;
00997 }
00998 
00999 
01000 /*!
01001  * \brief Sets default voicemail system options to a voicemail user.
01002  *
01003  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01004  * - all the globalflags
01005  * - the saydurationminfo
01006  * - the callcontext
01007  * - the dialcontext
01008  * - the exitcontext
01009  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01010  * - volume gain
01011  * - emailsubject, emailbody set to NULL
01012  */
01013 static void populate_defaults(struct ast_vm_user *vmu)
01014 {
01015    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01016    vmu->passwordlocation = passwordlocation;
01017    if (saydurationminfo) {
01018       vmu->saydurationm = saydurationminfo;
01019    }
01020    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01021    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01022    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01023    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01024    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01025    if (vmminsecs) {
01026       vmu->minsecs = vmminsecs;
01027    }
01028    if (vmmaxsecs) {
01029       vmu->maxsecs = vmmaxsecs;
01030    }
01031    if (maxmsg) {
01032       vmu->maxmsg = maxmsg;
01033    }
01034    if (maxdeletedmsg) {
01035       vmu->maxdeletedmsg = maxdeletedmsg;
01036    }
01037    vmu->volgain = volgain;
01038    ast_free(vmu->emailsubject);
01039    vmu->emailsubject = NULL;
01040    ast_free(vmu->emailbody);
01041    vmu->emailbody = NULL;
01042 #ifdef IMAP_STORAGE
01043    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01044 #endif
01045 }
01046 
01047 /*!
01048  * \brief Sets a a specific property value.
01049  * \param vmu The voicemail user object to work with.
01050  * \param var The name of the property to be set.
01051  * \param value The value to be set to the property.
01052  * 
01053  * The property name must be one of the understood properties. See the source for details.
01054  */
01055 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01056 {
01057    int x;
01058    if (!strcasecmp(var, "attach")) {
01059       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01060    } else if (!strcasecmp(var, "attachfmt")) {
01061       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01062    } else if (!strcasecmp(var, "serveremail")) {
01063       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01064    } else if (!strcasecmp(var, "emailbody")) {
01065       vmu->emailbody = ast_strdup(substitute_escapes(value));
01066    } else if (!strcasecmp(var, "emailsubject")) {
01067       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01068    } else if (!strcasecmp(var, "language")) {
01069       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01070    } else if (!strcasecmp(var, "tz")) {
01071       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01072    } else if (!strcasecmp(var, "locale")) {
01073       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01074 #ifdef IMAP_STORAGE
01075    } else if (!strcasecmp(var, "imapuser")) {
01076       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01077       vmu->imapversion = imapversion;
01078    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01079       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01080       vmu->imapversion = imapversion;
01081    } else if (!strcasecmp(var, "imapfolder")) {
01082       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01083    } else if (!strcasecmp(var, "imapvmshareid")) {
01084       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01085       vmu->imapversion = imapversion;
01086 #endif
01087    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01088       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01089    } else if (!strcasecmp(var, "saycid")){
01090       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01091    } else if (!strcasecmp(var, "sendvoicemail")){
01092       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01093    } else if (!strcasecmp(var, "review")){
01094       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01095    } else if (!strcasecmp(var, "tempgreetwarn")){
01096       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01097    } else if (!strcasecmp(var, "messagewrap")){
01098       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01099    } else if (!strcasecmp(var, "operator")) {
01100       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01101    } else if (!strcasecmp(var, "envelope")){
01102       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01103    } else if (!strcasecmp(var, "moveheard")){
01104       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01105    } else if (!strcasecmp(var, "sayduration")){
01106       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01107    } else if (!strcasecmp(var, "saydurationm")){
01108       if (sscanf(value, "%30d", &x) == 1) {
01109          vmu->saydurationm = x;
01110       } else {
01111          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01112       }
01113    } else if (!strcasecmp(var, "forcename")){
01114       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01115    } else if (!strcasecmp(var, "forcegreetings")){
01116       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01117    } else if (!strcasecmp(var, "callback")) {
01118       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01119    } else if (!strcasecmp(var, "dialout")) {
01120       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01121    } else if (!strcasecmp(var, "exitcontext")) {
01122       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01123    } else if (!strcasecmp(var, "minsecs")) {
01124       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01125          vmu->minsecs = x;
01126       } else {
01127          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01128          vmu->minsecs = vmminsecs;
01129       }
01130    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01131       vmu->maxsecs = atoi(value);
01132       if (vmu->maxsecs <= 0) {
01133          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01134          vmu->maxsecs = vmmaxsecs;
01135       } else {
01136          vmu->maxsecs = atoi(value);
01137       }
01138       if (!strcasecmp(var, "maxmessage"))
01139          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01140    } else if (!strcasecmp(var, "maxmsg")) {
01141       vmu->maxmsg = atoi(value);
01142       /* Accept maxmsg=0 (Greetings only voicemail) */
01143       if (vmu->maxmsg < 0) {
01144          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01145          vmu->maxmsg = MAXMSG;
01146       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01147          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01148          vmu->maxmsg = MAXMSGLIMIT;
01149       }
01150    } else if (!strcasecmp(var, "nextaftercmd")) {
01151       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01152    } else if (!strcasecmp(var, "backupdeleted")) {
01153       if (sscanf(value, "%30d", &x) == 1)
01154          vmu->maxdeletedmsg = x;
01155       else if (ast_true(value))
01156          vmu->maxdeletedmsg = MAXMSG;
01157       else
01158          vmu->maxdeletedmsg = 0;
01159 
01160       if (vmu->maxdeletedmsg < 0) {
01161          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01162          vmu->maxdeletedmsg = MAXMSG;
01163       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01164          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01165          vmu->maxdeletedmsg = MAXMSGLIMIT;
01166       }
01167    } else if (!strcasecmp(var, "volgain")) {
01168       sscanf(value, "%30lf", &vmu->volgain);
01169    } else if (!strcasecmp(var, "passwordlocation")) {
01170       if (!strcasecmp(value, "spooldir")) {
01171          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01172       } else {
01173          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01174       }
01175    } else if (!strcasecmp(var, "options")) {
01176       apply_options(vmu, value);
01177    }
01178 }
01179 
01180 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01181 {
01182    int fds[2], pid = 0;
01183 
01184    memset(buf, 0, len);
01185 
01186    if (pipe(fds)) {
01187       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01188    } else {
01189       /* good to go*/
01190       pid = ast_safe_fork(0);
01191 
01192       if (pid < 0) {
01193          /* ok maybe not */
01194          close(fds[0]);
01195          close(fds[1]);
01196          snprintf(buf, len, "FAILURE: Fork failed");
01197       } else if (pid) {
01198          /* parent */
01199          close(fds[1]);
01200          if (read(fds[0], buf, len) < 0) {
01201             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01202          }
01203          close(fds[0]);
01204       } else {
01205          /*  child */
01206          AST_DECLARE_APP_ARGS(arg,
01207             AST_APP_ARG(v)[20];
01208          );
01209          char *mycmd = ast_strdupa(command);
01210 
01211          close(fds[0]);
01212          dup2(fds[1], STDOUT_FILENO);
01213          close(fds[1]);
01214          ast_close_fds_above_n(STDOUT_FILENO);
01215 
01216          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01217 
01218          execv(arg.v[0], arg.v); 
01219          printf("FAILURE: %s", strerror(errno));
01220          _exit(0);
01221       }
01222    }
01223    return buf;
01224 }
01225 
01226 /*!
01227  * \brief Check that password meets minimum required length
01228  * \param vmu The voicemail user to change the password for.
01229  * \param password The password string to check
01230  *
01231  * \return zero on ok, 1 on not ok.
01232  */
01233 static int check_password(struct ast_vm_user *vmu, char *password)
01234 {
01235    /* check minimum length */
01236    if (strlen(password) < minpassword)
01237       return 1;
01238    /* check that password does not contain '*' character */
01239    if (!ast_strlen_zero(password) && password[0] == '*')
01240       return 1;
01241    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01242       char cmd[255], buf[255];
01243 
01244       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01245 
01246       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01247       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01248          ast_debug(5, "Result: %s\n", buf);
01249          if (!strncasecmp(buf, "VALID", 5)) {
01250             ast_debug(3, "Passed password check: '%s'\n", buf);
01251             return 0;
01252          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01253             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01254             return 0;
01255          } else {
01256             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01257             return 1;
01258          }
01259       }
01260    }
01261    return 0;
01262 }
01263 
01264 /*! 
01265  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01266  * \param vmu The voicemail user to change the password for.
01267  * \param password The new value to be set to the password for this user.
01268  * 
01269  * This only works if there is a realtime engine configured.
01270  * This is called from the (top level) vm_change_password.
01271  *
01272  * \return zero on success, -1 on error.
01273  */
01274 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01275 {
01276    int res = -1;
01277    if (!strcmp(vmu->password, password)) {
01278       /* No change (but an update would return 0 rows updated, so we opt out here) */
01279       return 0;
01280    }
01281 
01282    if (strlen(password) > 10) {
01283       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01284    }
01285    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01286       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01287       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01288       res = 0;
01289    }
01290    return res;
01291 }
01292 
01293 /*!
01294  * \brief Destructively Parse options and apply.
01295  */
01296 static void apply_options(struct ast_vm_user *vmu, const char *options)
01297 {  
01298    char *stringp;
01299    char *s;
01300    char *var, *value;
01301    stringp = ast_strdupa(options);
01302    while ((s = strsep(&stringp, "|"))) {
01303       value = s;
01304       if ((var = strsep(&value, "=")) && value) {
01305          apply_option(vmu, var, value);
01306       }
01307    }  
01308 }
01309 
01310 /*!
01311  * \brief Loads the options specific to a voicemail user.
01312  * 
01313  * This is called when a vm_user structure is being set up, such as from load_options.
01314  */
01315 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01316 {
01317    for (; var; var = var->next) {
01318       if (!strcasecmp(var->name, "vmsecret")) {
01319          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01320       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01321          if (ast_strlen_zero(retval->password)) {
01322             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01323                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01324                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01325             } else {
01326                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01327             }
01328          }
01329       } else if (!strcasecmp(var->name, "uniqueid")) {
01330          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01331       } else if (!strcasecmp(var->name, "pager")) {
01332          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01333       } else if (!strcasecmp(var->name, "email")) {
01334          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01335       } else if (!strcasecmp(var->name, "fullname")) {
01336          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01337       } else if (!strcasecmp(var->name, "context")) {
01338          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01339       } else if (!strcasecmp(var->name, "emailsubject")) {
01340          ast_free(retval->emailsubject);
01341          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01342       } else if (!strcasecmp(var->name, "emailbody")) {
01343          ast_free(retval->emailbody);
01344          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01345 #ifdef IMAP_STORAGE
01346       } else if (!strcasecmp(var->name, "imapuser")) {
01347          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01348          retval->imapversion = imapversion;
01349       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01350          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01351          retval->imapversion = imapversion;
01352       } else if (!strcasecmp(var->name, "imapfolder")) {
01353          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01354       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01355          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01356          retval->imapversion = imapversion;
01357 #endif
01358       } else
01359          apply_option(retval, var->name, var->value);
01360    }
01361 }
01362 
01363 /*!
01364  * \brief Determines if a DTMF key entered is valid.
01365  * \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.
01366  *
01367  * Tests the character entered against the set of valid DTMF characters. 
01368  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01369  */
01370 static int is_valid_dtmf(const char *key)
01371 {
01372    int i;
01373    char *local_key = ast_strdupa(key);
01374 
01375    for (i = 0; i < strlen(key); ++i) {
01376       if (!strchr(VALID_DTMF, *local_key)) {
01377          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01378          return 0;
01379       }
01380       local_key++;
01381    }
01382    return 1;
01383 }
01384 
01385 /*!
01386  * \brief Finds a voicemail user from the realtime engine.
01387  * \param ivm
01388  * \param context
01389  * \param mailbox
01390  *
01391  * 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.
01392  *
01393  * \return The ast_vm_user structure for the user that was found.
01394  */
01395 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01396 {
01397    struct ast_variable *var;
01398    struct ast_vm_user *retval;
01399 
01400    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01401       if (ivm) {
01402          memset(retval, 0, sizeof(*retval));
01403       }
01404       populate_defaults(retval);
01405       if (!ivm) {
01406          ast_set_flag(retval, VM_ALLOCED);
01407       }
01408       if (mailbox) {
01409          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01410       }
01411       if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
01412          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01413       } else {
01414          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01415       }
01416       if (var) {
01417          apply_options_full(retval, var);
01418          ast_variables_destroy(var);
01419       } else { 
01420          if (!ivm) 
01421             free_user(retval);
01422          retval = NULL;
01423       }  
01424    } 
01425    return retval;
01426 }
01427 
01428 /*!
01429  * \brief Finds a voicemail user from the users file or the realtime engine.
01430  * \param ivm
01431  * \param context
01432  * \param mailbox
01433  * 
01434  * \return The ast_vm_user structure for the user that was found.
01435  */
01436 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01437 {
01438    /* This function could be made to generate one from a database, too */
01439    struct ast_vm_user *vmu = NULL, *cur;
01440    AST_LIST_LOCK(&users);
01441 
01442    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01443       context = "default";
01444 
01445    AST_LIST_TRAVERSE(&users, cur, list) {
01446 #ifdef IMAP_STORAGE
01447       if (cur->imapversion != imapversion) {
01448          continue;
01449       }
01450 #endif
01451       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01452          break;
01453       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01454          break;
01455    }
01456    if (cur) {
01457       /* Make a copy, so that on a reload, we have no race */
01458       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01459          *vmu = *cur;
01460          if (!ivm) {
01461             vmu->emailbody = ast_strdup(cur->emailbody);
01462             vmu->emailsubject = ast_strdup(cur->emailsubject);
01463          }
01464          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01465          AST_LIST_NEXT(vmu, list) = NULL;
01466       }
01467    } else
01468       vmu = find_user_realtime(ivm, context, mailbox);
01469    AST_LIST_UNLOCK(&users);
01470    return vmu;
01471 }
01472 
01473 /*!
01474  * \brief Resets a user password to a specified password.
01475  * \param context
01476  * \param mailbox
01477  * \param newpass
01478  *
01479  * This does the actual change password work, called by the vm_change_password() function.
01480  *
01481  * \return zero on success, -1 on error.
01482  */
01483 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01484 {
01485    /* This function could be made to generate one from a database, too */
01486    struct ast_vm_user *cur;
01487    int res = -1;
01488    AST_LIST_LOCK(&users);
01489    AST_LIST_TRAVERSE(&users, cur, list) {
01490       if ((!context || !strcasecmp(context, cur->context)) &&
01491          (!strcasecmp(mailbox, cur->mailbox)))
01492             break;
01493    }
01494    if (cur) {
01495       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01496       res = 0;
01497    }
01498    AST_LIST_UNLOCK(&users);
01499    return res;
01500 }
01501 
01502 /*! 
01503  * \brief The handler for the change password option.
01504  * \param vmu The voicemail user to work with.
01505  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01506  * 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.
01507  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01508  */
01509 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01510 {
01511    struct ast_config   *cfg = NULL;
01512    struct ast_variable *var = NULL;
01513    struct ast_category *cat = NULL;
01514    char *category = NULL, *value = NULL, *new = NULL;
01515    const char *tmp = NULL;
01516    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01517    char secretfn[PATH_MAX] = "";
01518    int found = 0;
01519 
01520    if (!change_password_realtime(vmu, newpassword))
01521       return;
01522 
01523    /* check if we should store the secret in the spool directory next to the messages */
01524    switch (vmu->passwordlocation) {
01525    case OPT_PWLOC_SPOOLDIR:
01526       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01527       if (write_password_to_file(secretfn, newpassword) == 0) {
01528          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01529          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01530          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01531          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01532          break;
01533       } else {
01534          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01535       }
01536       /* Fall-through */
01537    case OPT_PWLOC_VOICEMAILCONF:
01538       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01539          while ((category = ast_category_browse(cfg, category))) {
01540             if (!strcasecmp(category, vmu->context)) {
01541                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01542                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01543                   break;
01544                }
01545                value = strstr(tmp, ",");
01546                if (!value) {
01547                   new = ast_alloca(strlen(newpassword)+1);
01548                   sprintf(new, "%s", newpassword);
01549                } else {
01550                   new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
01551                   sprintf(new, "%s%s", newpassword, value);
01552                }
01553                if (!(cat = ast_category_get(cfg, category))) {
01554                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01555                   break;
01556                }
01557                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01558                found = 1;
01559             }
01560          }
01561          /* save the results */
01562          if (found) {
01563             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01564             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01565             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01566             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01567             break;
01568          }
01569       }
01570       /* Fall-through */
01571    case OPT_PWLOC_USERSCONF:
01572       /* check users.conf and update the password stored for the mailbox */
01573       /* if no vmsecret entry exists create one. */
01574       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01575          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01576          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01577             ast_debug(4, "users.conf: %s\n", category);
01578             if (!strcasecmp(category, vmu->mailbox)) {
01579                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01580                   ast_debug(3, "looks like we need to make vmsecret!\n");
01581                   var = ast_variable_new("vmsecret", newpassword, "");
01582                } else {
01583                   var = NULL;
01584                }
01585                new = ast_alloca(strlen(newpassword) + 1);
01586                sprintf(new, "%s", newpassword);
01587                if (!(cat = ast_category_get(cfg, category))) {
01588                   ast_debug(4, "failed to get category!\n");
01589                   ast_free(var);
01590                   break;
01591                }
01592                if (!var) {
01593                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01594                } else {
01595                   ast_variable_append(cat, var);
01596                }
01597                found = 1;
01598                break;
01599             }
01600          }
01601          /* save the results and clean things up */
01602          if (found) {
01603             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01604             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01605             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01606             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01607          }
01608       }
01609    }
01610 }
01611 
01612 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01613 {
01614    char buf[255];
01615    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01616    ast_debug(1, "External password: %s\n",buf);
01617    if (!ast_safe_system(buf)) {
01618       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01619       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01620       /* Reset the password in memory, too */
01621       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01622    }
01623 }
01624 
01625 /*! 
01626  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01627  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01628  * \param len The length of the path string that was written out.
01629  * \param context
01630  * \param ext 
01631  * \param folder 
01632  * 
01633  * The path is constructed as 
01634  *    VM_SPOOL_DIRcontext/ext/folder
01635  *
01636  * \return zero on success, -1 on error.
01637  */
01638 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01639 {
01640    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01641 }
01642 
01643 /*! 
01644  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01645  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01646  * \param len The length of the path string that was written out.
01647  * \param dir 
01648  * \param num 
01649  * 
01650  * The path is constructed as 
01651  *    VM_SPOOL_DIRcontext/ext/folder
01652  *
01653  * \return zero on success, -1 on error.
01654  */
01655 static int make_file(char *dest, const int len, const char *dir, const int num)
01656 {
01657    return snprintf(dest, len, "%s/msg%04d", dir, num);
01658 }
01659 
01660 /* same as mkstemp, but return a FILE * */
01661 static FILE *vm_mkftemp(char *template)
01662 {
01663    FILE *p = NULL;
01664    int pfd = mkstemp(template);
01665    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01666    if (pfd > -1) {
01667       p = fdopen(pfd, "w+");
01668       if (!p) {
01669          close(pfd);
01670          pfd = -1;
01671       }
01672    }
01673    return p;
01674 }
01675 
01676 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01677  * \param dest    String. base directory.
01678  * \param len     Length of dest.
01679  * \param context String. Ignored if is null or empty string.
01680  * \param ext     String. Ignored if is null or empty string.
01681  * \param folder  String. Ignored if is null or empty string. 
01682  * \return -1 on failure, 0 on success.
01683  */
01684 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01685 {
01686    mode_t   mode = VOICEMAIL_DIR_MODE;
01687    int res;
01688 
01689    make_dir(dest, len, context, ext, folder);
01690    if ((res = ast_mkdir(dest, mode))) {
01691       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01692       return -1;
01693    }
01694    return 0;
01695 }
01696 
01697 static const char * const mailbox_folders[] = {
01698 #ifdef IMAP_STORAGE
01699    imapfolder,
01700 #else
01701    "INBOX",
01702 #endif
01703    "Old",
01704    "Work",
01705    "Family",
01706    "Friends",
01707    "Cust1",
01708    "Cust2",
01709    "Cust3",
01710    "Cust4",
01711    "Cust5",
01712    "Deleted",
01713    "Urgent",
01714 };
01715 
01716 static const char *mbox(struct ast_vm_user *vmu, int id)
01717 {
01718 #ifdef IMAP_STORAGE
01719    if (vmu && id == 0) {
01720       return vmu->imapfolder;
01721    }
01722 #endif
01723    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01724 }
01725 
01726 static int get_folder_by_name(const char *name)
01727 {
01728    size_t i;
01729 
01730    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01731       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01732          return i;
01733       }
01734    }
01735 
01736    return -1;
01737 }
01738 
01739 static void free_user(struct ast_vm_user *vmu)
01740 {
01741    if (ast_test_flag(vmu, VM_ALLOCED)) {
01742 
01743       ast_free(vmu->emailbody);
01744       vmu->emailbody = NULL;
01745 
01746       ast_free(vmu->emailsubject);
01747       vmu->emailsubject = NULL;
01748 
01749       ast_free(vmu);
01750    }
01751 }
01752 
01753 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01754 
01755    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01756 
01757    /* remove old allocation */
01758    if (vms->deleted) {
01759       ast_free(vms->deleted);
01760       vms->deleted = NULL;
01761    }
01762    if (vms->heard) {
01763       ast_free(vms->heard);
01764       vms->heard = NULL;
01765    }
01766    vms->dh_arraysize = 0;
01767 
01768    if (arraysize > 0) {
01769       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01770          return -1;
01771       }
01772       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01773          ast_free(vms->deleted);
01774          vms->deleted = NULL;
01775          return -1;
01776       }
01777       vms->dh_arraysize = arraysize;
01778    }
01779 
01780    return 0;
01781 }
01782 
01783 /* All IMAP-specific functions should go in this block. This
01784  * keeps them from being spread out all over the code */
01785 #ifdef IMAP_STORAGE
01786 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01787 {
01788    char arg[10];
01789    struct vm_state *vms;
01790    unsigned long messageNum;
01791 
01792    /* If greetings aren't stored in IMAP, just delete the file */
01793    if (msgnum < 0 && !imapgreetings) {
01794       ast_filedelete(file, NULL);
01795       return;
01796    }
01797 
01798    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01799       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);
01800       return;
01801    }
01802 
01803    if (msgnum < 0) {
01804       imap_delete_old_greeting(file, vms);
01805       return;
01806    }
01807 
01808    /* find real message number based on msgnum */
01809    /* this may be an index into vms->msgArray based on the msgnum. */
01810    messageNum = vms->msgArray[msgnum];
01811    if (messageNum == 0) {
01812       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01813       return;
01814    }
01815    if (option_debug > 2)
01816       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01817    /* delete message */
01818    snprintf (arg, sizeof(arg), "%lu", messageNum);
01819    ast_mutex_lock(&vms->lock);
01820    mail_setflag (vms->mailstream, arg, "\\DELETED");
01821    mail_expunge(vms->mailstream);
01822    ast_mutex_unlock(&vms->lock);
01823 }
01824 
01825 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01826 {
01827    struct vm_state *vms_p;
01828    char *file, *filename;
01829    char *attachment;
01830    int i;
01831    BODY *body;
01832 
01833    /* This function is only used for retrieval of IMAP greetings
01834     * regular messages are not retrieved this way, nor are greetings
01835     * if they are stored locally*/
01836    if (msgnum > -1 || !imapgreetings) {
01837       return 0;
01838    } else {
01839       file = strrchr(ast_strdupa(dir), '/');
01840       if (file)
01841          *file++ = '\0';
01842       else {
01843          ast_debug (1, "Failed to procure file name from directory passed.\n");
01844          return -1;
01845       }
01846    }
01847 
01848    /* check if someone is accessing this box right now... */
01849    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01850       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01851       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01852       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01853       * that's all we need to do.
01854       */
01855       if (!(vms_p = create_vm_state_from_user(vmu))) {
01856          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01857          return -1;
01858       }
01859    }
01860 
01861    /* Greetings will never have a prepended message */
01862    *vms_p->introfn = '\0';
01863 
01864    ast_mutex_lock(&vms_p->lock);
01865    init_mailstream(vms_p, GREETINGS_FOLDER);
01866    if (!vms_p->mailstream) {
01867       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01868       ast_mutex_unlock(&vms_p->lock);
01869       return -1;
01870    }
01871 
01872    /*XXX Yuck, this could probably be done a lot better */
01873    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01874       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01875       /* We have the body, now we extract the file name of the first attachment. */
01876       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01877          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01878       } else {
01879          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01880          ast_mutex_unlock(&vms_p->lock);
01881          return -1;
01882       }
01883       filename = strsep(&attachment, ".");
01884       if (!strcmp(filename, file)) {
01885          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01886          vms_p->msgArray[vms_p->curmsg] = i + 1;
01887          save_body(body, vms_p, "2", attachment, 0);
01888          ast_mutex_unlock(&vms_p->lock);
01889          return 0;
01890       }
01891    }
01892    ast_mutex_unlock(&vms_p->lock);
01893 
01894    return -1;
01895 }
01896 
01897 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01898 {
01899    BODY *body;
01900    char *header_content;
01901    char *attachedfilefmt;
01902    char buf[80];
01903    struct vm_state *vms;
01904    char text_file[PATH_MAX];
01905    FILE *text_file_ptr;
01906    int res = 0;
01907    struct ast_vm_user *vmu;
01908 
01909    if (!(vmu = find_user(NULL, context, mailbox))) {
01910       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01911       return -1;
01912    }
01913    
01914    if (msgnum < 0) {
01915       if (imapgreetings) {
01916          res = imap_retrieve_greeting(dir, msgnum, vmu);
01917          goto exit;
01918       } else {
01919          res = 0;
01920          goto exit;
01921       }
01922    }
01923 
01924    /* Before anything can happen, we need a vm_state so that we can
01925     * actually access the imap server through the vms->mailstream
01926     */
01927    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01928       /* This should not happen. If it does, then I guess we'd
01929        * need to create the vm_state, extract which mailbox to
01930        * open, and then set up the msgArray so that the correct
01931        * IMAP message could be accessed. If I have seen correctly
01932        * though, the vms should be obtainable from the vmstates list
01933        * and should have its msgArray properly set up.
01934        */
01935       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01936       res = -1;
01937       goto exit;
01938    }
01939    
01940    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01941    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01942 
01943    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01944    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01945       res = 0;
01946       goto exit;
01947    }
01948 
01949    if (option_debug > 2)
01950       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01951    if (vms->msgArray[msgnum] == 0) {
01952       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01953       res = -1;
01954       goto exit;
01955    }
01956 
01957    /* This will only work for new messages... */
01958    ast_mutex_lock(&vms->lock);
01959    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01960    ast_mutex_unlock(&vms->lock);
01961    /* empty string means no valid header */
01962    if (ast_strlen_zero(header_content)) {
01963       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01964       res = -1;
01965       goto exit;
01966    }
01967 
01968    ast_mutex_lock(&vms->lock);
01969    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01970    ast_mutex_unlock(&vms->lock);
01971 
01972    /* We have the body, now we extract the file name of the first attachment. */
01973    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01974       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01975    } else {
01976       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01977       res = -1;
01978       goto exit;
01979    }
01980    
01981    /* Find the format of the attached file */
01982 
01983    strsep(&attachedfilefmt, ".");
01984    if (!attachedfilefmt) {
01985       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01986       res = -1;
01987       goto exit;
01988    }
01989    
01990    save_body(body, vms, "2", attachedfilefmt, 0);
01991    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01992       *vms->introfn = '\0';
01993    }
01994 
01995    /* Get info from headers!! */
01996    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01997 
01998    if (!(text_file_ptr = fopen(text_file, "w"))) {
01999       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
02000    }
02001 
02002    fprintf(text_file_ptr, "%s\n", "[message]");
02003 
02004    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
02005    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
02006    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
02007    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02008    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
02009    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02010    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02011    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02012    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02013    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02014    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02015    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02016    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02017    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02018    fclose(text_file_ptr);
02019 
02020 exit:
02021    free_user(vmu);
02022    return res;
02023 }
02024 
02025 static int folder_int(const char *folder)
02026 {
02027    /*assume a NULL folder means INBOX*/
02028    if (!folder) {
02029       return 0;
02030    }
02031    if (!strcasecmp(folder, imapfolder)) {
02032       return 0;
02033    } else if (!strcasecmp(folder, "Old")) {
02034       return 1;
02035    } else if (!strcasecmp(folder, "Work")) {
02036       return 2;
02037    } else if (!strcasecmp(folder, "Family")) {
02038       return 3;
02039    } else if (!strcasecmp(folder, "Friends")) {
02040       return 4;
02041    } else if (!strcasecmp(folder, "Cust1")) {
02042       return 5;
02043    } else if (!strcasecmp(folder, "Cust2")) {
02044       return 6;
02045    } else if (!strcasecmp(folder, "Cust3")) {
02046       return 7;
02047    } else if (!strcasecmp(folder, "Cust4")) {
02048       return 8;
02049    } else if (!strcasecmp(folder, "Cust5")) {
02050       return 9;
02051    } else if (!strcasecmp(folder, "Urgent")) {
02052       return 11;
02053    } else { /*assume they meant INBOX if folder is not found otherwise*/
02054       return 0;
02055    }
02056 }
02057 
02058 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02059 {
02060    SEARCHPGM *pgm;
02061    SEARCHHEADER *hdr;
02062 
02063    struct ast_vm_user *vmu, vmus;
02064    struct vm_state *vms_p;
02065    int ret = 0;
02066    int fold = folder_int(folder);
02067    int urgent = 0;
02068    
02069    /* If URGENT, then look at INBOX */
02070    if (fold == 11) {
02071       fold = NEW_FOLDER;
02072       urgent = 1;
02073    }
02074 
02075    if (ast_strlen_zero(mailbox))
02076       return 0;
02077 
02078    /* We have to get the user before we can open the stream! */
02079    vmu = find_user(&vmus, context, mailbox);
02080    if (!vmu) {
02081       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02082       return -1;
02083    } else {
02084       /* No IMAP account available */
02085       if (vmu->imapuser[0] == '\0') {
02086          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02087          return -1;
02088       }
02089    }
02090    
02091    /* No IMAP account available */
02092    if (vmu->imapuser[0] == '\0') {
02093       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02094       free_user(vmu);
02095       return -1;
02096    }
02097 
02098    /* check if someone is accessing this box right now... */
02099    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02100    if (!vms_p) {
02101       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02102    }
02103    if (vms_p) {
02104       ast_debug(3, "Returning before search - user is logged in\n");
02105       if (fold == 0) { /* INBOX */
02106          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02107       }
02108       if (fold == 1) { /* Old messages */
02109          return vms_p->oldmessages;
02110       }
02111    }
02112 
02113    /* add one if not there... */
02114    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02115    if (!vms_p) {
02116       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02117    }
02118 
02119    if (!vms_p) {
02120       vms_p = create_vm_state_from_user(vmu);
02121    }
02122    ret = init_mailstream(vms_p, fold);
02123    if (!vms_p->mailstream) {
02124       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02125       return -1;
02126    }
02127    if (ret == 0) {
02128       ast_mutex_lock(&vms_p->lock);
02129       pgm = mail_newsearchpgm ();
02130       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02131       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02132       pgm->header = hdr;
02133       if (fold != OLD_FOLDER) {
02134          pgm->unseen = 1;
02135          pgm->seen = 0;
02136       }
02137       /* In the special case where fold is 1 (old messages) we have to do things a bit
02138        * differently. Old messages are stored in the INBOX but are marked as "seen"
02139        */
02140       else {
02141          pgm->unseen = 0;
02142          pgm->seen = 1;
02143       }
02144       /* look for urgent messages */
02145       if (fold == NEW_FOLDER) {
02146          if (urgent) {
02147             pgm->flagged = 1;
02148             pgm->unflagged = 0;
02149          } else {
02150             pgm->flagged = 0;
02151             pgm->unflagged = 1;
02152          }
02153       }
02154       pgm->undeleted = 1;
02155       pgm->deleted = 0;
02156 
02157       vms_p->vmArrayIndex = 0;
02158       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02159       if (fold == 0 && urgent == 0)
02160          vms_p->newmessages = vms_p->vmArrayIndex;
02161       if (fold == 1)
02162          vms_p->oldmessages = vms_p->vmArrayIndex;
02163       if (fold == 0 && urgent == 1)
02164          vms_p->urgentmessages = vms_p->vmArrayIndex;
02165       /*Freeing the searchpgm also frees the searchhdr*/
02166       mail_free_searchpgm(&pgm);
02167       ast_mutex_unlock(&vms_p->lock);
02168       vms_p->updated = 0;
02169       return vms_p->vmArrayIndex;
02170    } else {
02171       ast_mutex_lock(&vms_p->lock);
02172       mail_ping(vms_p->mailstream);
02173       ast_mutex_unlock(&vms_p->lock);
02174    }
02175    return 0;
02176 }
02177 
02178 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02179 {
02180    /* Check if mailbox is full */
02181    check_quota(vms, vmu->imapfolder);
02182    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02183       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02184       ast_play_and_wait(chan, "vm-mailboxfull");
02185       return -1;
02186    }
02187    
02188    /* Check if we have exceeded maxmsg */
02189    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));
02190    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02191       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02192       ast_play_and_wait(chan, "vm-mailboxfull");
02193       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02194       return -1;
02195    }
02196 
02197    return 0;
02198 }
02199 
02200 /*!
02201  * \brief Gets the number of messages that exist in a mailbox folder.
02202  * \param context
02203  * \param mailbox
02204  * \param folder
02205  * 
02206  * This method is used when IMAP backend is used.
02207  * \return The number of messages in this mailbox folder (zero or more).
02208  */
02209 static int messagecount(const char *context, const char *mailbox, const char *folder)
02210 {
02211    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02212       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02213    } else {
02214       return __messagecount(context, mailbox, folder);
02215    }
02216 }
02217 
02218 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)
02219 {
02220    char *myserveremail = serveremail;
02221    char fn[PATH_MAX];
02222    char introfn[PATH_MAX];
02223    char mailbox[256];
02224    char *stringp;
02225    FILE *p = NULL;
02226    char tmp[80] = "/tmp/astmail-XXXXXX";
02227    long len;
02228    void *buf;
02229    int tempcopy = 0;
02230    STRING str;
02231    int ret; /* for better error checking */
02232    char *imap_flags = NIL;
02233    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02234    int box = NEW_FOLDER;
02235 
02236    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02237    if (msgnum < 0) {
02238       if(!imapgreetings) {
02239          return 0;
02240       } else {
02241          box = GREETINGS_FOLDER;
02242       }
02243    }
02244    
02245    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02246       return -1;
02247    }
02248 
02249    /* Set urgent flag for IMAP message */
02250    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02251       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02252       imap_flags = "\\FLAGGED";
02253    }
02254    
02255    /* Attach only the first format */
02256    fmt = ast_strdupa(fmt);
02257    stringp = fmt;
02258    strsep(&stringp, "|");
02259 
02260    if (!ast_strlen_zero(vmu->serveremail))
02261       myserveremail = vmu->serveremail;
02262 
02263    if (msgnum > -1)
02264       make_file(fn, sizeof(fn), dir, msgnum);
02265    else
02266       ast_copy_string (fn, dir, sizeof(fn));
02267 
02268    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02269    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02270       *introfn = '\0';
02271    }
02272    
02273    if (ast_strlen_zero(vmu->email)) {
02274       /* We need the vmu->email to be set when we call make_email_file, but
02275        * if we keep it set, a duplicate e-mail will be created. So at the end
02276        * of this function, we will revert back to an empty string if tempcopy
02277        * is 1.
02278        */
02279       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02280       tempcopy = 1;
02281    }
02282 
02283    if (!strcmp(fmt, "wav49"))
02284       fmt = "WAV";
02285    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02286 
02287    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02288       command hangs. */
02289    if (!(p = vm_mkftemp(tmp))) {
02290       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02291       if (tempcopy)
02292          *(vmu->email) = '\0';
02293       return -1;
02294    }
02295 
02296    if (msgnum < 0 && imapgreetings) {
02297       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02298          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02299          return -1;
02300       }
02301       imap_delete_old_greeting(fn, vms);
02302    }
02303 
02304    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02305       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02306       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02307       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02308    /* read mail file to memory */
02309    len = ftell(p);
02310    rewind(p);
02311    if (!(buf = ast_malloc(len + 1))) {
02312       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02313       fclose(p);
02314       if (tempcopy)
02315          *(vmu->email) = '\0';
02316       return -1;
02317    }
02318    if (fread(buf, len, 1, p) < len) {
02319       if (ferror(p)) {
02320          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02321          return -1;
02322       }
02323    }
02324    ((char *) buf)[len] = '\0';
02325    INIT(&str, mail_string, buf, len);
02326    ret = init_mailstream(vms, box);
02327    if (ret == 0) {
02328       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02329       ast_mutex_lock(&vms->lock);
02330       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02331          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02332       ast_mutex_unlock(&vms->lock);
02333       fclose(p);
02334       unlink(tmp);
02335       ast_free(buf);
02336    } else {
02337       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02338       fclose(p);
02339       unlink(tmp);
02340       ast_free(buf);
02341       return -1;
02342    }
02343    ast_debug(3, "%s stored\n", fn);
02344    
02345    if (tempcopy)
02346       *(vmu->email) = '\0';
02347    inprocess_count(vmu->mailbox, vmu->context, -1);
02348    return 0;
02349 
02350 }
02351 
02352 /*!
02353  * \brief Gets the number of messages that exist in the inbox folder.
02354  * \param mailbox_context
02355  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02356  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02357  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02358  * 
02359  * This method is used when IMAP backend is used.
02360  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02361  *
02362  * \return zero on success, -1 on error.
02363  */
02364 
02365 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02366 {
02367    char tmp[PATH_MAX] = "";
02368    char *mailboxnc;
02369    char *context;
02370    char *mb;
02371    char *cur;
02372    if (newmsgs)
02373       *newmsgs = 0;
02374    if (oldmsgs)
02375       *oldmsgs = 0;
02376    if (urgentmsgs)
02377       *urgentmsgs = 0;
02378 
02379    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02380    /* If no mailbox, return immediately */
02381    if (ast_strlen_zero(mailbox_context))
02382       return 0;
02383    
02384    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02385    context = strchr(tmp, '@');
02386    if (strchr(mailbox_context, ',')) {
02387       int tmpnew, tmpold, tmpurgent;
02388       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02389       mb = tmp;
02390       while ((cur = strsep(&mb, ", "))) {
02391          if (!ast_strlen_zero(cur)) {
02392             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02393                return -1;
02394             else {
02395                if (newmsgs)
02396                   *newmsgs += tmpnew; 
02397                if (oldmsgs)
02398                   *oldmsgs += tmpold;
02399                if (urgentmsgs)
02400                   *urgentmsgs += tmpurgent;
02401             }
02402          }
02403       }
02404       return 0;
02405    }
02406    if (context) {
02407       *context = '\0';
02408       mailboxnc = tmp;
02409       context++;
02410    } else {
02411       context = "default";
02412       mailboxnc = (char *) mailbox_context;
02413    }
02414 
02415    if (newmsgs) {
02416       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02417       if (!vmu) {
02418          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02419          return -1;
02420       }
02421       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02422          free_user(vmu);
02423          return -1;
02424       }
02425       free_user(vmu);
02426    }
02427    if (oldmsgs) {
02428       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02429          return -1;
02430       }
02431    }
02432    if (urgentmsgs) {
02433       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02434          return -1;
02435       }
02436    }
02437    return 0;
02438 }
02439 
02440 /** 
02441  * \brief Determines if the given folder has messages.
02442  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02443  * \param folder the folder to look in
02444  *
02445  * This function is used when the mailbox is stored in an IMAP back end.
02446  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02447  * \return 1 if the folder has one or more messages. zero otherwise.
02448  */
02449 
02450 static int has_voicemail(const char *mailbox, const char *folder)
02451 {
02452    char tmp[256], *tmp2, *box, *context;
02453    ast_copy_string(tmp, mailbox, sizeof(tmp));
02454    tmp2 = tmp;
02455    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02456       while ((box = strsep(&tmp2, ",&"))) {
02457          if (!ast_strlen_zero(box)) {
02458             if (has_voicemail(box, folder)) {
02459                return 1;
02460             }
02461          }
02462       }
02463    }
02464    if ((context = strchr(tmp, '@'))) {
02465       *context++ = '\0';
02466    } else {
02467       context = "default";
02468    }
02469    return __messagecount(context, tmp, folder) ? 1 : 0;
02470 }
02471 
02472 /*!
02473  * \brief Copies a message from one mailbox to another.
02474  * \param chan
02475  * \param vmu
02476  * \param imbox
02477  * \param msgnum
02478  * \param duration
02479  * \param recip
02480  * \param fmt
02481  * \param dir
02482  *
02483  * This works with IMAP storage based mailboxes.
02484  *
02485  * \return zero on success, -1 on error.
02486  */
02487 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)
02488 {
02489    struct vm_state *sendvms = NULL, *destvms = NULL;
02490    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02491    if (msgnum >= recip->maxmsg) {
02492       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02493       return -1;
02494    }
02495    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02496       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02497       return -1;
02498    }
02499    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02500       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02501       return -1;
02502    }
02503    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02504    ast_mutex_lock(&sendvms->lock);
02505    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02506       ast_mutex_unlock(&sendvms->lock);
02507       return 0;
02508    }
02509    ast_mutex_unlock(&sendvms->lock);
02510    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02511    return -1;
02512 }
02513 
02514 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02515 {
02516    char tmp[256], *t = tmp;
02517    size_t left = sizeof(tmp);
02518    
02519    if (box == OLD_FOLDER) {
02520       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02521    } else {
02522       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02523    }
02524 
02525    if (box == NEW_FOLDER) {
02526       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02527    } else {
02528       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02529    }
02530 
02531    /* Build up server information */
02532    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02533 
02534    /* Add authentication user if present */
02535    if (!ast_strlen_zero(authuser))
02536       ast_build_string(&t, &left, "/authuser=%s", authuser);
02537 
02538    /* Add flags if present */
02539    if (!ast_strlen_zero(imapflags))
02540       ast_build_string(&t, &left, "/%s", imapflags);
02541 
02542    /* End with username */
02543 #if 1
02544    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02545 #else
02546    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02547 #endif
02548    if (box == NEW_FOLDER || box == OLD_FOLDER)
02549       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02550    else if (box == GREETINGS_FOLDER)
02551       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02552    else {   /* Other folders such as Friends, Family, etc... */
02553       if (!ast_strlen_zero(imapparentfolder)) {
02554          /* imapparentfolder would typically be set to INBOX */
02555          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02556       } else {
02557          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02558       }
02559    }
02560 }
02561 
02562 static int init_mailstream(struct vm_state *vms, int box)
02563 {
02564    MAILSTREAM *stream = NIL;
02565    long debug;
02566    char tmp[256];
02567    
02568    if (!vms) {
02569       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02570       return -1;
02571    }
02572    if (option_debug > 2)
02573       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02574    if (vms->mailstream == NIL || !vms->mailstream) {
02575       if (option_debug)
02576          ast_log(LOG_DEBUG, "mailstream not set.\n");
02577    } else {
02578       stream = vms->mailstream;
02579    }
02580    /* debug = T;  user wants protocol telemetry? */
02581    debug = NIL;  /* NO protocol telemetry? */
02582 
02583    if (delimiter == '\0') {      /* did not probe the server yet */
02584       char *cp;
02585 #ifdef USE_SYSTEM_IMAP
02586 #include <imap/linkage.c>
02587 #elif defined(USE_SYSTEM_CCLIENT)
02588 #include <c-client/linkage.c>
02589 #else
02590 #include "linkage.c"
02591 #endif
02592       /* Connect to INBOX first to get folders delimiter */
02593       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02594       ast_mutex_lock(&vms->lock);
02595       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02596       ast_mutex_unlock(&vms->lock);
02597       if (stream == NIL) {
02598          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02599          return -1;
02600       }
02601       get_mailbox_delimiter(stream);
02602       /* update delimiter in imapfolder */
02603       for (cp = vms->imapfolder; *cp; cp++)
02604          if (*cp == '/')
02605             *cp = delimiter;
02606    }
02607    /* Now connect to the target folder */
02608    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02609    if (option_debug > 2)
02610       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02611    ast_mutex_lock(&vms->lock);
02612    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02613    ast_mutex_unlock(&vms->lock);
02614    if (vms->mailstream == NIL) {
02615       return -1;
02616    } else {
02617       return 0;
02618    }
02619 }
02620 
02621 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02622 {
02623    SEARCHPGM *pgm;
02624    SEARCHHEADER *hdr;
02625    int ret, urgent = 0;
02626 
02627    /* If Urgent, then look at INBOX */
02628    if (box == 11) {
02629       box = NEW_FOLDER;
02630       urgent = 1;
02631    }
02632 
02633    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02634    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02635    vms->imapversion = vmu->imapversion;
02636    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02637 
02638    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02639       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02640       return -1;
02641    }
02642    
02643    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02644    
02645    /* Check Quota */
02646    if  (box == 0)  {
02647       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02648       check_quota(vms, (char *) mbox(vmu, box));
02649    }
02650 
02651    ast_mutex_lock(&vms->lock);
02652    pgm = mail_newsearchpgm();
02653 
02654    /* Check IMAP folder for Asterisk messages only... */
02655    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02656    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02657    pgm->header = hdr;
02658    pgm->deleted = 0;
02659    pgm->undeleted = 1;
02660 
02661    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02662    if (box == NEW_FOLDER && urgent == 1) {
02663       pgm->unseen = 1;
02664       pgm->seen = 0;
02665       pgm->flagged = 1;
02666       pgm->unflagged = 0;
02667    } else if (box == NEW_FOLDER && urgent == 0) {
02668       pgm->unseen = 1;
02669       pgm->seen = 0;
02670       pgm->flagged = 0;
02671       pgm->unflagged = 1;
02672    } else if (box == OLD_FOLDER) {
02673       pgm->seen = 1;
02674       pgm->unseen = 0;
02675    }
02676 
02677    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02678 
02679    vms->vmArrayIndex = 0;
02680    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02681    vms->lastmsg = vms->vmArrayIndex - 1;
02682    mail_free_searchpgm(&pgm);
02683    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02684     * ensure to allocate enough space to account for all of them. Warn if old messages
02685     * have not been checked first as that is required.
02686     */
02687    if (box == 0 && !vms->dh_arraysize) {
02688       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02689    }
02690    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02691       ast_mutex_unlock(&vms->lock);
02692       return -1;
02693    }
02694 
02695    ast_mutex_unlock(&vms->lock);
02696    return 0;
02697 }
02698 
02699 static void write_file(char *filename, char *buffer, unsigned long len)
02700 {
02701    FILE *output;
02702 
02703    output = fopen (filename, "w");
02704    if (fwrite(buffer, len, 1, output) != 1) {
02705       if (ferror(output)) {
02706          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02707       }
02708    }
02709    fclose (output);
02710 }
02711 
02712 static void update_messages_by_imapuser(const char *user, unsigned long number)
02713 {
02714    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02715 
02716    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02717       return;
02718    }
02719 
02720    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02721    vms->msgArray[vms->vmArrayIndex++] = number;
02722 }
02723 
02724 void mm_searched(MAILSTREAM *stream, unsigned long number)
02725 {
02726    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02727 
02728    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02729       return;
02730 
02731    update_messages_by_imapuser(user, number);
02732 }
02733 
02734 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02735 {
02736    struct ast_variable *var;
02737    struct ast_vm_user *vmu;
02738 
02739    vmu = ast_calloc(1, sizeof *vmu);
02740    if (!vmu)
02741       return NULL;
02742 
02743    populate_defaults(vmu);
02744    ast_set_flag(vmu, VM_ALLOCED);
02745 
02746    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02747    if (var) {
02748       apply_options_full(vmu, var);
02749       ast_variables_destroy(var);
02750       return vmu;
02751    } else {
02752       ast_free(vmu);
02753       return NULL;
02754    }
02755 }
02756 
02757 /* Interfaces to C-client */
02758 
02759 void mm_exists(MAILSTREAM * stream, unsigned long number)
02760 {
02761    /* mail_ping will callback here if new mail! */
02762    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02763    if (number == 0) return;
02764    set_update(stream);
02765 }
02766 
02767 
02768 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02769 {
02770    /* mail_ping will callback here if expunged mail! */
02771    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02772    if (number == 0) return;
02773    set_update(stream);
02774 }
02775 
02776 
02777 void mm_flags(MAILSTREAM * stream, unsigned long number)
02778 {
02779    /* mail_ping will callback here if read mail! */
02780    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02781    if (number == 0) return;
02782    set_update(stream);
02783 }
02784 
02785 
02786 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02787 {
02788    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02789    mm_log (string, errflg);
02790 }
02791 
02792 
02793 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02794 {
02795    if (delimiter == '\0') {
02796       delimiter = delim;
02797    }
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_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02812 {
02813    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02814    if (attributes & LATT_NOINFERIORS)
02815       ast_debug(5, "no inferiors\n");
02816    if (attributes & LATT_NOSELECT)
02817       ast_debug(5, "no select\n");
02818    if (attributes & LATT_MARKED)
02819       ast_debug(5, "marked\n");
02820    if (attributes & LATT_UNMARKED)
02821       ast_debug(5, "unmarked\n");
02822 }
02823 
02824 
02825 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02826 {
02827    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02828    if (status->flags & SA_MESSAGES)
02829       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02830    if (status->flags & SA_RECENT)
02831       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02832    if (status->flags & SA_UNSEEN)
02833       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02834    if (status->flags & SA_UIDVALIDITY)
02835       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02836    if (status->flags & SA_UIDNEXT)
02837       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02838    ast_log(AST_LOG_NOTICE, "\n");
02839 }
02840 
02841 
02842 void mm_log(char *string, long errflg)
02843 {
02844    switch ((short) errflg) {
02845       case NIL:
02846          ast_debug(1, "IMAP Info: %s\n", string);
02847          break;
02848       case PARSE:
02849       case WARN:
02850          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02851          break;
02852       case ERROR:
02853          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02854          break;
02855    }
02856 }
02857 
02858 
02859 void mm_dlog(char *string)
02860 {
02861    ast_log(AST_LOG_NOTICE, "%s\n", string);
02862 }
02863 
02864 
02865 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02866 {
02867    struct ast_vm_user *vmu;
02868 
02869    ast_debug(4, "Entering callback mm_login\n");
02870 
02871    ast_copy_string(user, mb->user, MAILTMPLEN);
02872 
02873    /* We should only do this when necessary */
02874    if (!ast_strlen_zero(authpassword)) {
02875       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02876    } else {
02877       AST_LIST_TRAVERSE(&users, vmu, list) {
02878          if (!strcasecmp(mb->user, vmu->imapuser)) {
02879             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02880             break;
02881          }
02882       }
02883       if (!vmu) {
02884          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02885             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02886             free_user(vmu);
02887          }
02888       }
02889    }
02890 }
02891 
02892 
02893 void mm_critical(MAILSTREAM * stream)
02894 {
02895 }
02896 
02897 
02898 void mm_nocritical(MAILSTREAM * stream)
02899 {
02900 }
02901 
02902 
02903 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02904 {
02905    kill (getpid (), SIGSTOP);
02906    return NIL;
02907 }
02908 
02909 
02910 void mm_fatal(char *string)
02911 {
02912    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02913 }
02914 
02915 /* C-client callback to handle quota */
02916 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02917 {
02918    struct vm_state *vms;
02919    char *mailbox = stream->mailbox, *user;
02920    char buf[1024] = "";
02921    unsigned long usage = 0, limit = 0;
02922    
02923    while (pquota) {
02924       usage = pquota->usage;
02925       limit = pquota->limit;
02926       pquota = pquota->next;
02927    }
02928    
02929    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)))) {
02930       ast_log(AST_LOG_ERROR, "No state found.\n");
02931       return;
02932    }
02933 
02934    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02935 
02936    vms->quota_usage = usage;
02937    vms->quota_limit = limit;
02938 }
02939 
02940 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02941 {
02942    char *start, *eol_pnt;
02943    int taglen;
02944 
02945    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02946       return NULL;
02947 
02948    taglen = strlen(tag) + 1;
02949    if (taglen < 1)
02950       return NULL;
02951 
02952    if (!(start = strstr(header, tag)))
02953       return NULL;
02954 
02955    /* Since we can be called multiple times we should clear our buffer */
02956    memset(buf, 0, len);
02957 
02958    ast_copy_string(buf, start+taglen, len);
02959    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02960       *eol_pnt = '\0';
02961    return buf;
02962 }
02963 
02964 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02965 {
02966    char *start, *quote, *eol_pnt;
02967 
02968    if (ast_strlen_zero(mailbox))
02969       return NULL;
02970 
02971    if (!(start = strstr(mailbox, "/user=")))
02972       return NULL;
02973 
02974    ast_copy_string(buf, start+6, len);
02975 
02976    if (!(quote = strchr(buf, '\"'))) {
02977       if (!(eol_pnt = strchr(buf, '/')))
02978          eol_pnt = strchr(buf,'}');
02979       *eol_pnt = '\0';
02980       return buf;
02981    } else {
02982       eol_pnt = strchr(buf+1,'\"');
02983       *eol_pnt = '\0';
02984       return buf+1;
02985    }
02986 }
02987 
02988 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02989 {
02990    struct vm_state *vms_p;
02991 
02992    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02993    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02994       return vms_p;
02995    }
02996    if (option_debug > 4)
02997       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02998    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02999       return NULL;
03000    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
03001    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
03002    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
03003    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
03004    vms_p->mailstream = NIL; /* save for access from interactive entry point */
03005    vms_p->imapversion = vmu->imapversion;
03006    if (option_debug > 4)
03007       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
03008    vms_p->updated = 1;
03009    /* set mailbox to INBOX! */
03010    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03011    init_vm_state(vms_p);
03012    vmstate_insert(vms_p);
03013    return vms_p;
03014 }
03015 
03016 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03017 {
03018    struct vmstate *vlist = NULL;
03019 
03020    if (interactive) {
03021       struct vm_state *vms;
03022       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03023       vms = pthread_getspecific(ts_vmstate.key);
03024       return vms;
03025    }
03026 
03027    AST_LIST_LOCK(&vmstates);
03028    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03029       if (!vlist->vms) {
03030          ast_debug(3, "error: vms is NULL for %s\n", user);
03031          continue;
03032       }
03033       if (vlist->vms->imapversion != imapversion) {
03034          continue;
03035       }
03036       if (!vlist->vms->imapuser) {
03037          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03038          continue;
03039       }
03040 
03041       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03042          AST_LIST_UNLOCK(&vmstates);
03043          return vlist->vms;
03044       }
03045    }
03046    AST_LIST_UNLOCK(&vmstates);
03047 
03048    ast_debug(3, "%s not found in vmstates\n", user);
03049 
03050    return NULL;
03051 }
03052 
03053 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03054 {
03055 
03056    struct vmstate *vlist = NULL;
03057    const char *local_context = S_OR(context, "default");
03058 
03059    if (interactive) {
03060       struct vm_state *vms;
03061       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03062       vms = pthread_getspecific(ts_vmstate.key);
03063       return vms;
03064    }
03065 
03066    AST_LIST_LOCK(&vmstates);
03067    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03068       if (!vlist->vms) {
03069          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03070          continue;
03071       }
03072       if (vlist->vms->imapversion != imapversion) {
03073          continue;
03074       }
03075       if (!vlist->vms->username || !vlist->vms->context) {
03076          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03077          continue;
03078       }
03079 
03080       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);
03081       
03082       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03083          ast_debug(3, "Found it!\n");
03084          AST_LIST_UNLOCK(&vmstates);
03085          return vlist->vms;
03086       }
03087    }
03088    AST_LIST_UNLOCK(&vmstates);
03089 
03090    ast_debug(3, "%s not found in vmstates\n", mailbox);
03091 
03092    return NULL;
03093 }
03094 
03095 static void vmstate_insert(struct vm_state *vms) 
03096 {
03097    struct vmstate *v;
03098    struct vm_state *altvms;
03099 
03100    /* If interactive, it probably already exists, and we should
03101       use the one we already have since it is more up to date.
03102       We can compare the username to find the duplicate */
03103    if (vms->interactive == 1) {
03104       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03105       if (altvms) {  
03106          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03107          vms->newmessages = altvms->newmessages;
03108          vms->oldmessages = altvms->oldmessages;
03109          vms->vmArrayIndex = altvms->vmArrayIndex;
03110          vms->lastmsg = altvms->lastmsg;
03111          vms->curmsg = altvms->curmsg;
03112          /* get a pointer to the persistent store */
03113          vms->persist_vms = altvms;
03114          /* Reuse the mailstream? */
03115 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03116          vms->mailstream = altvms->mailstream;
03117 #else
03118          vms->mailstream = NIL;
03119 #endif
03120       }
03121       return;
03122    }
03123 
03124    if (!(v = ast_calloc(1, sizeof(*v))))
03125       return;
03126    
03127    v->vms = vms;
03128 
03129    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03130 
03131    AST_LIST_LOCK(&vmstates);
03132    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03133    AST_LIST_UNLOCK(&vmstates);
03134 }
03135 
03136 static void vmstate_delete(struct vm_state *vms) 
03137 {
03138    struct vmstate *vc = NULL;
03139    struct vm_state *altvms = NULL;
03140 
03141    /* If interactive, we should copy pertinent info
03142       back to the persistent state (to make update immediate) */
03143    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03144       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03145       altvms->newmessages = vms->newmessages;
03146       altvms->oldmessages = vms->oldmessages;
03147       altvms->updated = 1;
03148       vms->mailstream = mail_close(vms->mailstream);
03149 
03150       /* Interactive states are not stored within the persistent list */
03151       return;
03152    }
03153    
03154    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03155    
03156    AST_LIST_LOCK(&vmstates);
03157    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03158       if (vc->vms == vms) {
03159          AST_LIST_REMOVE_CURRENT(list);
03160          break;
03161       }
03162    }
03163    AST_LIST_TRAVERSE_SAFE_END
03164    AST_LIST_UNLOCK(&vmstates);
03165    
03166    if (vc) {
03167       ast_mutex_destroy(&vc->vms->lock);
03168       ast_free(vc);
03169    }
03170    else
03171       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03172 }
03173 
03174 static void set_update(MAILSTREAM * stream) 
03175 {
03176    struct vm_state *vms;
03177    char *mailbox = stream->mailbox, *user;
03178    char buf[1024] = "";
03179 
03180    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03181       if (user && option_debug > 2)
03182          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03183       return;
03184    }
03185 
03186    ast_debug(3, "User %s mailbox set for update.\n", user);
03187 
03188    vms->updated = 1; /* Set updated flag since mailbox changed */
03189 }
03190 
03191 static void init_vm_state(struct vm_state *vms) 
03192 {
03193    int x;
03194    vms->vmArrayIndex = 0;
03195    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03196       vms->msgArray[x] = 0;
03197    }
03198    ast_mutex_init(&vms->lock);
03199 }
03200 
03201 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03202 {
03203    char *body_content;
03204    char *body_decoded;
03205    char *fn = is_intro ? vms->introfn : vms->fn;
03206    unsigned long len;
03207    unsigned long newlen;
03208    char filename[256];
03209    
03210    if (!body || body == NIL)
03211       return -1;
03212 
03213    ast_mutex_lock(&vms->lock);
03214    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03215    ast_mutex_unlock(&vms->lock);
03216    if (body_content != NIL) {
03217       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03218       /* ast_debug(1,body_content); */
03219       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03220       /* If the body of the file is empty, return an error */
03221       if (!newlen) {
03222          return -1;
03223       }
03224       write_file(filename, (char *) body_decoded, newlen);
03225    } else {
03226       ast_debug(5, "Body of message is NULL.\n");
03227       return -1;
03228    }
03229    return 0;
03230 }
03231 
03232 /*! 
03233  * \brief Get delimiter via mm_list callback 
03234  * \param stream
03235  *
03236  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03237  */
03238 /* MUTEX should already be held */
03239 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03240    char tmp[50];
03241    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03242    mail_list(stream, tmp, "*");
03243 }
03244 
03245 /*! 
03246  * \brief Check Quota for user 
03247  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03248  * \param mailbox the mailbox to check the quota for.
03249  *
03250  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03251  */
03252 static void check_quota(struct vm_state *vms, char *mailbox) {
03253    ast_mutex_lock(&vms->lock);
03254    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03255    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03256    if (vms && vms->mailstream != NULL) {
03257       imap_getquotaroot(vms->mailstream, mailbox);
03258    } else {
03259       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03260    }
03261    ast_mutex_unlock(&vms->lock);
03262 }
03263 
03264 #endif /* IMAP_STORAGE */
03265 
03266 /*! \brief Lock file path
03267  * only return failure if ast_lock_path returns 'timeout',
03268  * not if the path does not exist or any other reason
03269  */
03270 static int vm_lock_path(const char *path)
03271 {
03272    switch (ast_lock_path(path)) {
03273    case AST_LOCK_TIMEOUT:
03274       return -1;
03275    default:
03276       return 0;
03277    }
03278 }
03279 
03280 
03281 #ifdef ODBC_STORAGE
03282 struct generic_prepare_struct {
03283    char *sql;
03284    int argc;
03285    char **argv;
03286 };
03287 
03288 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03289 {
03290    struct generic_prepare_struct *gps = data;
03291    int res, i;
03292    SQLHSTMT stmt;
03293 
03294    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03295    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03296       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03297       return NULL;
03298    }
03299    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03300    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03301       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03302       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03303       return NULL;
03304    }
03305    for (i = 0; i < gps->argc; i++)
03306       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03307 
03308    return stmt;
03309 }
03310 
03311 /*!
03312  * \brief Retrieves a file from an ODBC data store.
03313  * \param dir the path to the file to be retreived.
03314  * \param msgnum the message number, such as within a mailbox folder.
03315  * 
03316  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03317  * 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.
03318  *
03319  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03320  * The output is the message information file with the name msgnum and the extension .txt
03321  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03322  * 
03323  * \return 0 on success, -1 on error.
03324  */
03325 static int retrieve_file(char *dir, int msgnum)
03326 {
03327    int x = 0;
03328    int res;
03329    int fd = -1;
03330    size_t fdlen = 0;
03331    void *fdm = MAP_FAILED;
03332    SQLSMALLINT colcount = 0;
03333    SQLHSTMT stmt;
03334    char sql[PATH_MAX];
03335    char fmt[80]="";
03336    char *c;
03337    char coltitle[256];
03338    SQLSMALLINT collen;
03339    SQLSMALLINT datatype;
03340    SQLSMALLINT decimaldigits;
03341    SQLSMALLINT nullable;
03342    SQLULEN colsize;
03343    SQLLEN colsize2;
03344    FILE *f = NULL;
03345    char rowdata[80];
03346    char fn[PATH_MAX];
03347    char full_fn[PATH_MAX];
03348    char msgnums[80];
03349    char *argv[] = { dir, msgnums };
03350    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03351 
03352    struct odbc_obj *obj;
03353    obj = ast_odbc_request_obj(odbc_database, 0);
03354    if (obj) {
03355       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03356       c = strchr(fmt, '|');
03357       if (c)
03358          *c = '\0';
03359       if (!strcasecmp(fmt, "wav49"))
03360          strcpy(fmt, "WAV");
03361       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03362       if (msgnum > -1)
03363          make_file(fn, sizeof(fn), dir, msgnum);
03364       else
03365          ast_copy_string(fn, dir, sizeof(fn));
03366 
03367       /* Create the information file */
03368       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03369       
03370       if (!(f = fopen(full_fn, "w+"))) {
03371          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03372          goto yuck;
03373       }
03374       
03375       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03376       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03377       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03378       if (!stmt) {
03379          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03380          ast_odbc_release_obj(obj);
03381          goto yuck;
03382       }
03383       res = SQLFetch(stmt);
03384       if (res == SQL_NO_DATA) {
03385          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03386          ast_odbc_release_obj(obj);
03387          goto yuck;
03388       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03389          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03390          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03391          ast_odbc_release_obj(obj);
03392          goto yuck;
03393       }
03394       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03395       if (fd < 0) {
03396          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03397          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03398          ast_odbc_release_obj(obj);
03399          goto yuck;
03400       }
03401       res = SQLNumResultCols(stmt, &colcount);
03402       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03403          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03404          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03405          ast_odbc_release_obj(obj);
03406          goto yuck;
03407       }
03408       if (f) 
03409          fprintf(f, "[message]\n");
03410       for (x = 0; x < colcount; x++) {
03411          rowdata[0] = '\0';
03412          colsize = 0;
03413          collen = sizeof(coltitle);
03414          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03415                   &datatype, &colsize, &decimaldigits, &nullable);
03416          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03417             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03418             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03419             ast_odbc_release_obj(obj);
03420             goto yuck;
03421          }
03422          if (!strcasecmp(coltitle, "recording")) {
03423             off_t offset;
03424             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03425             fdlen = colsize2;
03426             if (fd > -1) {
03427                char tmp[1]="";
03428                lseek(fd, fdlen - 1, SEEK_SET);
03429                if (write(fd, tmp, 1) != 1) {
03430                   close(fd);
03431                   fd = -1;
03432                   continue;
03433                }
03434                /* Read out in small chunks */
03435                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03436                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03437                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03438                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03439                      ast_odbc_release_obj(obj);
03440                      goto yuck;
03441                   } else {
03442                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03443                      munmap(fdm, CHUNKSIZE);
03444                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03445                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03446                         unlink(full_fn);
03447                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03448                         ast_odbc_release_obj(obj);
03449                         goto yuck;
03450                      }
03451                   }
03452                }
03453                if (truncate(full_fn, fdlen) < 0) {
03454                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03455                }
03456             }
03457          } else {
03458             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03459             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03460                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03461                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03462                ast_odbc_release_obj(obj);
03463                goto yuck;
03464             }
03465             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03466                fprintf(f, "%s=%s\n", coltitle, rowdata);
03467          }
03468       }
03469       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03470       ast_odbc_release_obj(obj);
03471    } else
03472       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03473 yuck:
03474    if (f)
03475       fclose(f);
03476    if (fd > -1)
03477       close(fd);
03478    return x - 1;
03479 }
03480 
03481 /*!
03482  * \brief Determines the highest message number in use for a given user and mailbox folder.
03483  * \param vmu 
03484  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03485  *
03486  * This method is used when mailboxes are stored in an ODBC back end.
03487  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03488  *
03489  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03490 
03491  */
03492 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03493 {
03494    int x = 0;
03495    int res;
03496    SQLHSTMT stmt;
03497    char sql[PATH_MAX];
03498    char rowdata[20];
03499    char *argv[] = { dir };
03500    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03501 
03502    struct odbc_obj *obj;
03503    obj = ast_odbc_request_obj(odbc_database, 0);
03504    if (obj) {
03505       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03506 
03507       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03508       if (!stmt) {
03509          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03510          ast_odbc_release_obj(obj);
03511          goto yuck;
03512       }
03513       res = SQLFetch(stmt);
03514       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03515          if (res == SQL_NO_DATA) {
03516             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03517          } else {
03518             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03519          }
03520 
03521          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03522          ast_odbc_release_obj(obj);
03523          goto yuck;
03524       }
03525       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03526       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03527          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03528          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03529          ast_odbc_release_obj(obj);
03530          goto yuck;
03531       }
03532       if (sscanf(rowdata, "%30d", &x) != 1)
03533          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03534       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03535       ast_odbc_release_obj(obj);
03536       return x;
03537    } else
03538       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03539 yuck:
03540    return x - 1;
03541 }
03542 
03543 /*!
03544  * \brief Determines if the specified message exists.
03545  * \param dir the folder the mailbox folder to look for messages. 
03546  * \param msgnum the message index to query for.
03547  *
03548  * This method is used when mailboxes are stored in an ODBC back end.
03549  *
03550  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03551  */
03552 static int message_exists(char *dir, int msgnum)
03553 {
03554    int x = 0;
03555    int res;
03556    SQLHSTMT stmt;
03557    char sql[PATH_MAX];
03558    char rowdata[20];
03559    char msgnums[20];
03560    char *argv[] = { dir, msgnums };
03561    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03562 
03563    struct odbc_obj *obj;
03564    obj = ast_odbc_request_obj(odbc_database, 0);
03565    if (obj) {
03566       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03567       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03568       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03569       if (!stmt) {
03570          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03571          ast_odbc_release_obj(obj);
03572          goto yuck;
03573       }
03574       res = SQLFetch(stmt);
03575       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03576          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03577          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03578          ast_odbc_release_obj(obj);
03579          goto yuck;
03580       }
03581       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03582       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03583          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03584          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03585          ast_odbc_release_obj(obj);
03586          goto yuck;
03587       }
03588       if (sscanf(rowdata, "%30d", &x) != 1)
03589          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03590       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03591       ast_odbc_release_obj(obj);
03592    } else
03593       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03594 yuck:
03595    return x;
03596 }
03597 
03598 /*!
03599  * \brief returns the number of messages found.
03600  * \param vmu
03601  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03602  *
03603  * This method is used when mailboxes are stored in an ODBC back end.
03604  *
03605  * \return The count of messages being zero or more, less than zero on error.
03606  */
03607 static int count_messages(struct ast_vm_user *vmu, char *dir)
03608 {
03609    int x = 0;
03610    int res;
03611    SQLHSTMT stmt;
03612    char sql[PATH_MAX];
03613    char rowdata[20];
03614    char *argv[] = { dir };
03615    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03616 
03617    struct odbc_obj *obj;
03618    obj = ast_odbc_request_obj(odbc_database, 0);
03619    if (obj) {
03620       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03621       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03622       if (!stmt) {
03623          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03624          ast_odbc_release_obj(obj);
03625          goto yuck;
03626       }
03627       res = SQLFetch(stmt);
03628       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03629          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03630          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03631          ast_odbc_release_obj(obj);
03632          goto yuck;
03633       }
03634       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03635       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03636          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03637          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03638          ast_odbc_release_obj(obj);
03639          goto yuck;
03640       }
03641       if (sscanf(rowdata, "%30d", &x) != 1)
03642          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03643       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03644       ast_odbc_release_obj(obj);
03645       return x;
03646    } else
03647       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03648 yuck:
03649    return x - 1;
03650 
03651 }
03652 
03653 /*!
03654  * \brief Deletes a message from the mailbox folder.
03655  * \param sdir The mailbox folder to work in.
03656  * \param smsg The message index to be deleted.
03657  *
03658  * This method is used when mailboxes are stored in an ODBC back end.
03659  * The specified message is directly deleted from the database 'voicemessages' table.
03660  * 
03661  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03662  */
03663 static void delete_file(const char *sdir, int smsg)
03664 {
03665    SQLHSTMT stmt;
03666    char sql[PATH_MAX];
03667    char msgnums[20];
03668    char *argv[] = { NULL, msgnums };
03669    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03670    struct odbc_obj *obj;
03671 
03672    argv[0] = ast_strdupa(sdir);
03673 
03674    obj = ast_odbc_request_obj(odbc_database, 0);
03675    if (obj) {
03676       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03677       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03678       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03679       if (!stmt)
03680          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03681       else
03682          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03683       ast_odbc_release_obj(obj);
03684    } else
03685       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03686    return;  
03687 }
03688 
03689 /*!
03690  * \brief Copies a voicemail from one mailbox to another.
03691  * \param sdir the folder for which to look for the message to be copied.
03692  * \param smsg the index of the message to be copied.
03693  * \param ddir the destination folder to copy the message into.
03694  * \param dmsg the index to be used for the copied message.
03695  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03696  * \param dmailboxcontext The context for the destination user.
03697  *
03698  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03699  */
03700 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03701 {
03702    SQLHSTMT stmt;
03703    char sql[512];
03704    char msgnums[20];
03705    char msgnumd[20];
03706    struct odbc_obj *obj;
03707    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03708    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03709 
03710    delete_file(ddir, dmsg);
03711    obj = ast_odbc_request_obj(odbc_database, 0);
03712    if (obj) {
03713       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03714       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03715       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);
03716       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03717       if (!stmt)
03718          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03719       else
03720          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03721       ast_odbc_release_obj(obj);
03722    } else
03723       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03724    return;  
03725 }
03726 
03727 struct insert_data {
03728    char *sql;
03729    const char *dir;
03730    const char *msgnums;
03731    void *data;
03732    SQLLEN datalen;
03733    SQLLEN indlen;
03734    const char *context;
03735    const char *macrocontext;
03736    const char *callerid;
03737    const char *origtime;
03738    const char *duration;
03739    const char *mailboxuser;
03740    const char *mailboxcontext;
03741    const char *category;
03742    const char *flag;
03743 };
03744 
03745 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03746 {
03747    struct insert_data *data = vdata;
03748    int res;
03749    SQLHSTMT stmt;
03750 
03751    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03752    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03753       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03754       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03755       return NULL;
03756    }
03757 
03758    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03759    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03760    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03761    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03762    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03763    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03764    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03765    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03766    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03767    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03768    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03769    if (!ast_strlen_zero(data->category)) {
03770       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03771    }
03772    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03773    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03774       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03775       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03776       return NULL;
03777    }
03778 
03779    return stmt;
03780 }
03781 
03782 /*!
03783  * \brief Stores a voicemail into the database.
03784  * \param dir the folder the mailbox folder to store the message.
03785  * \param mailboxuser the user owning the mailbox folder.
03786  * \param mailboxcontext
03787  * \param msgnum the message index for the message to be stored.
03788  *
03789  * This method is used when mailboxes are stored in an ODBC back end.
03790  * The message sound file and information file is looked up on the file system. 
03791  * A SQL query is invoked to store the message into the (MySQL) database.
03792  *
03793  * \return the zero on success -1 on error.
03794  */
03795 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03796 {
03797    int res = 0;
03798    int fd = -1;
03799    void *fdm = MAP_FAILED;
03800    off_t fdlen = -1;
03801    SQLHSTMT stmt;
03802    char sql[PATH_MAX];
03803    char msgnums[20];
03804    char fn[PATH_MAX];
03805    char full_fn[PATH_MAX];
03806    char fmt[80]="";
03807    char *c;
03808    struct ast_config *cfg = NULL;
03809    struct odbc_obj *obj;
03810    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03811       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03812    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03813 
03814    delete_file(dir, msgnum);
03815    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03816       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03817       return -1;
03818    }
03819 
03820    do {
03821       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03822       c = strchr(fmt, '|');
03823       if (c)
03824          *c = '\0';
03825       if (!strcasecmp(fmt, "wav49"))
03826          strcpy(fmt, "WAV");
03827       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03828       if (msgnum > -1)
03829          make_file(fn, sizeof(fn), dir, msgnum);
03830       else
03831          ast_copy_string(fn, dir, sizeof(fn));
03832       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03833       cfg = ast_config_load(full_fn, config_flags);
03834       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03835       fd = open(full_fn, O_RDWR);
03836       if (fd < 0) {
03837          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03838          res = -1;
03839          break;
03840       }
03841       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03842          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03843             idata.context = "";
03844          }
03845          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03846             idata.macrocontext = "";
03847          }
03848          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03849             idata.callerid = "";
03850          }
03851          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03852             idata.origtime = "";
03853          }
03854          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03855             idata.duration = "";
03856          }
03857          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03858             idata.category = "";
03859          }
03860          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03861             idata.flag = "";
03862          }
03863       }
03864       fdlen = lseek(fd, 0, SEEK_END);
03865       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
03866          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
03867          res = -1;
03868          break;
03869       }
03870       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03871       if (fdm == MAP_FAILED) {
03872          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
03873          res = -1;
03874          break;
03875       } 
03876       idata.data = fdm;
03877       idata.datalen = idata.indlen = fdlen;
03878 
03879       if (!ast_strlen_zero(idata.category)) 
03880          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03881       else
03882          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03883 
03884       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03885          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03886       } else {
03887          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03888          res = -1;
03889       }
03890    } while (0);
03891    if (obj) {
03892       ast_odbc_release_obj(obj);
03893    }
03894    if (cfg)
03895       ast_config_destroy(cfg);
03896    if (fdm != MAP_FAILED)
03897       munmap(fdm, fdlen);
03898    if (fd > -1)
03899       close(fd);
03900    return res;
03901 }
03902 
03903 /*!
03904  * \brief Renames a message in a mailbox folder.
03905  * \param sdir The folder of the message to be renamed.
03906  * \param smsg The index of the message to be renamed.
03907  * \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.
03908  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03909  * \param ddir The destination folder for the message to be renamed into
03910  * \param dmsg The destination message for the message to be renamed.
03911  *
03912  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03913  * 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.
03914  * 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.
03915  */
03916 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03917 {
03918    SQLHSTMT stmt;
03919    char sql[PATH_MAX];
03920    char msgnums[20];
03921    char msgnumd[20];
03922    struct odbc_obj *obj;
03923    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03924    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03925 
03926    delete_file(ddir, dmsg);
03927    obj = ast_odbc_request_obj(odbc_database, 0);
03928    if (obj) {
03929       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03930       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03931       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03932       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03933       if (!stmt)
03934          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03935       else
03936          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03937       ast_odbc_release_obj(obj);
03938    } else
03939       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03940    return;  
03941 }
03942 
03943 /*!
03944  * \brief Removes a voicemail message file.
03945  * \param dir the path to the message file.
03946  * \param msgnum the unique number for the message within the mailbox.
03947  *
03948  * Removes the message content file and the information file.
03949  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03950  * Typical use is to clean up after a RETRIEVE operation. 
03951  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03952  * \return zero on success, -1 on error.
03953  */
03954 static int remove_file(char *dir, int msgnum)
03955 {
03956    char fn[PATH_MAX];
03957    char full_fn[PATH_MAX];
03958    char msgnums[80];
03959    
03960    if (msgnum > -1) {
03961       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03962       make_file(fn, sizeof(fn), dir, msgnum);
03963    } else
03964       ast_copy_string(fn, dir, sizeof(fn));
03965    ast_filedelete(fn, NULL);  
03966    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03967    unlink(full_fn);
03968    return 0;
03969 }
03970 #else
03971 #ifndef IMAP_STORAGE
03972 /*!
03973  * \brief Find all .txt files - even if they are not in sequence from 0000.
03974  * \param vmu
03975  * \param dir
03976  *
03977  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03978  *
03979  * \return the count of messages, zero or more.
03980  */
03981 static int count_messages(struct ast_vm_user *vmu, char *dir)
03982 {
03983 
03984    int vmcount = 0;
03985    DIR *vmdir = NULL;
03986    struct dirent *vment = NULL;
03987 
03988    if (vm_lock_path(dir))
03989       return ERROR_LOCK_PATH;
03990 
03991    if ((vmdir = opendir(dir))) {
03992       while ((vment = readdir(vmdir))) {
03993          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03994             vmcount++;
03995          }
03996       }
03997       closedir(vmdir);
03998    }
03999    ast_unlock_path(dir);
04000    
04001    return vmcount;
04002 }
04003 
04004 /*!
04005  * \brief Renames a message in a mailbox folder.
04006  * \param sfn The path to the mailbox information and data file to be renamed.
04007  * \param dfn The path for where the message data and information files will be renamed to.
04008  *
04009  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04010  */
04011 static void rename_file(char *sfn, char *dfn)
04012 {
04013    char stxt[PATH_MAX];
04014    char dtxt[PATH_MAX];
04015    ast_filerename(sfn, dfn, NULL);
04016    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04017    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04018    if (ast_check_realtime("voicemail_data")) {
04019       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04020    }
04021    rename(stxt, dtxt);
04022 }
04023 
04024 /*! 
04025  * \brief Determines the highest message number in use for a given user and mailbox folder.
04026  * \param vmu 
04027  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04028  *
04029  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04030  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04031  *
04032  * \note Should always be called with a lock already set on dir.
04033  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04034  */
04035 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04036 {
04037    int x;
04038    unsigned char map[MAXMSGLIMIT] = "";
04039    DIR *msgdir;
04040    struct dirent *msgdirent;
04041    int msgdirint;
04042    char extension[4];
04043    int stopcount = 0;
04044 
04045    /* Reading the entire directory into a file map scales better than
04046     * doing a stat repeatedly on a predicted sequence.  I suspect this
04047     * is partially due to stat(2) internally doing a readdir(2) itself to
04048     * find each file. */
04049    if (!(msgdir = opendir(dir))) {
04050       return -1;
04051    }
04052 
04053    while ((msgdirent = readdir(msgdir))) {
04054       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04055          map[msgdirint] = 1;
04056          stopcount++;
04057          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04058       }
04059    }
04060    closedir(msgdir);
04061 
04062    for (x = 0; x < vmu->maxmsg; x++) {
04063       if (map[x] == 1) {
04064          stopcount--;
04065       } else if (map[x] == 0 && !stopcount) {
04066          break;
04067       }
04068    }
04069 
04070    return x - 1;
04071 }
04072 
04073 #endif /* #ifndef IMAP_STORAGE */
04074 #endif /* #else of #ifdef ODBC_STORAGE */
04075 #ifndef IMAP_STORAGE
04076 /*!
04077  * \brief Utility function to copy a file.
04078  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04079  * \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.
04080  *
04081  * 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.
04082  * The copy operation copies up to 4096 bytes at once.
04083  *
04084  * \return zero on success, -1 on error.
04085  */
04086 static int copy(char *infile, char *outfile)
04087 {
04088    int ifd;
04089    int ofd;
04090    int res;
04091    int len;
04092    char buf[4096];
04093 
04094 #ifdef HARDLINK_WHEN_POSSIBLE
04095    /* Hard link if possible; saves disk space & is faster */
04096    if (link(infile, outfile)) {
04097 #endif
04098       if ((ifd = open(infile, O_RDONLY)) < 0) {
04099          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04100          return -1;
04101       }
04102       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04103          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04104          close(ifd);
04105          return -1;
04106       }
04107       do {
04108          len = read(ifd, buf, sizeof(buf));
04109          if (len < 0) {
04110             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04111             close(ifd);
04112             close(ofd);
04113             unlink(outfile);
04114          } else if (len) {
04115             res = write(ofd, buf, len);
04116             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04117                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04118                close(ifd);
04119                close(ofd);
04120                unlink(outfile);
04121             }
04122          }
04123       } while (len);
04124       close(ifd);
04125       close(ofd);
04126       return 0;
04127 #ifdef HARDLINK_WHEN_POSSIBLE
04128    } else {
04129       /* Hard link succeeded */
04130       return 0;
04131    }
04132 #endif
04133 }
04134 
04135 /*!
04136  * \brief Copies a voicemail information (envelope) file.
04137  * \param frompath
04138  * \param topath 
04139  *
04140  * Every voicemail has the data (.wav) file, and the information file.
04141  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04142  * This is used by the COPY macro when not using IMAP storage.
04143  */
04144 static void copy_plain_file(char *frompath, char *topath)
04145 {
04146    char frompath2[PATH_MAX], topath2[PATH_MAX];
04147    struct ast_variable *tmp,*var = NULL;
04148    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04149    ast_filecopy(frompath, topath, NULL);
04150    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04151    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04152    if (ast_check_realtime("voicemail_data")) {
04153       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04154       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04155       for (tmp = var; tmp; tmp = tmp->next) {
04156          if (!strcasecmp(tmp->name, "origmailbox")) {
04157             origmailbox = tmp->value;
04158          } else if (!strcasecmp(tmp->name, "context")) {
04159             context = tmp->value;
04160          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04161             macrocontext = tmp->value;
04162          } else if (!strcasecmp(tmp->name, "exten")) {
04163             exten = tmp->value;
04164          } else if (!strcasecmp(tmp->name, "priority")) {
04165             priority = tmp->value;
04166          } else if (!strcasecmp(tmp->name, "callerchan")) {
04167             callerchan = tmp->value;
04168          } else if (!strcasecmp(tmp->name, "callerid")) {
04169             callerid = tmp->value;
04170          } else if (!strcasecmp(tmp->name, "origdate")) {
04171             origdate = tmp->value;
04172          } else if (!strcasecmp(tmp->name, "origtime")) {
04173             origtime = tmp->value;
04174          } else if (!strcasecmp(tmp->name, "category")) {
04175             category = tmp->value;
04176          } else if (!strcasecmp(tmp->name, "duration")) {
04177             duration = tmp->value;
04178          }
04179       }
04180       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);
04181    }
04182    copy(frompath2, topath2);
04183    ast_variables_destroy(var);
04184 }
04185 #endif
04186 
04187 /*! 
04188  * \brief Removes the voicemail sound and information file.
04189  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04190  *
04191  * This is used by the DELETE macro when voicemails are stored on the file system.
04192  *
04193  * \return zero on success, -1 on error.
04194  */
04195 static int vm_delete(char *file)
04196 {
04197    char *txt;
04198    int txtsize = 0;
04199 
04200    txtsize = (strlen(file) + 5)*sizeof(char);
04201    txt = ast_alloca(txtsize);
04202    /* Sprintf here would safe because we alloca'd exactly the right length,
04203     * but trying to eliminate all sprintf's anyhow
04204     */
04205    if (ast_check_realtime("voicemail_data")) {
04206       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04207    }
04208    snprintf(txt, txtsize, "%s.txt", file);
04209    unlink(txt);
04210    return ast_filedelete(file, NULL);
04211 }
04212 
04213 /*!
04214  * \brief utility used by inchar(), for base_encode()
04215  */
04216 static int inbuf(struct baseio *bio, FILE *fi)
04217 {
04218    int l;
04219 
04220    if (bio->ateof)
04221       return 0;
04222 
04223    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04224       if (ferror(fi))
04225          return -1;
04226 
04227       bio->ateof = 1;
04228       return 0;
04229    }
04230 
04231    bio->iolen = l;
04232    bio->iocp = 0;
04233 
04234    return 1;
04235 }
04236 
04237 /*!
04238  * \brief utility used by base_encode()
04239  */
04240 static int inchar(struct baseio *bio, FILE *fi)
04241 {
04242    if (bio->iocp>=bio->iolen) {
04243       if (!inbuf(bio, fi))
04244          return EOF;
04245    }
04246 
04247    return bio->iobuf[bio->iocp++];
04248 }
04249 
04250 /*!
04251  * \brief utility used by base_encode()
04252  */
04253 static int ochar(struct baseio *bio, int c, FILE *so)
04254 {
04255    if (bio->linelength >= BASELINELEN) {
04256       if (fputs(ENDL, so) == EOF) {
04257          return -1;
04258       }
04259 
04260       bio->linelength = 0;
04261    }
04262 
04263    if (putc(((unsigned char) c), so) == EOF) {
04264       return -1;
04265    }
04266 
04267    bio->linelength++;
04268 
04269    return 1;
04270 }
04271 
04272 /*!
04273  * \brief Performs a base 64 encode algorithm on the contents of a File
04274  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04275  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04276  *
04277  * 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 ?
04278  *
04279  * \return zero on success, -1 on error.
04280  */
04281 static int base_encode(char *filename, FILE *so)
04282 {
04283    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04284       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04285       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04286       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04287    int i, hiteof = 0;
04288    FILE *fi;
04289    struct baseio bio;
04290 
04291    memset(&bio, 0, sizeof(bio));
04292    bio.iocp = BASEMAXINLINE;
04293 
04294    if (!(fi = fopen(filename, "rb"))) {
04295       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04296       return -1;
04297    }
04298 
04299    while (!hiteof){
04300       unsigned char igroup[3], ogroup[4];
04301       int c, n;
04302 
04303       memset(igroup, 0, sizeof(igroup));
04304 
04305       for (n = 0; n < 3; n++) {
04306          if ((c = inchar(&bio, fi)) == EOF) {
04307             hiteof = 1;
04308             break;
04309          }
04310 
04311          igroup[n] = (unsigned char) c;
04312       }
04313 
04314       if (n > 0) {
04315          ogroup[0]= dtable[igroup[0] >> 2];
04316          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04317          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04318          ogroup[3]= dtable[igroup[2] & 0x3F];
04319 
04320          if (n < 3) {
04321             ogroup[3] = '=';
04322 
04323             if (n < 2)
04324                ogroup[2] = '=';
04325          }
04326 
04327          for (i = 0; i < 4; i++)
04328             ochar(&bio, ogroup[i], so);
04329       }
04330    }
04331 
04332    fclose(fi);
04333    
04334    if (fputs(ENDL, so) == EOF) {
04335       return 0;
04336    }
04337 
04338    return 1;
04339 }
04340 
04341 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)
04342 {
04343    char callerid[256];
04344    char num[12];
04345    char fromdir[256], fromfile[256];
04346    struct ast_config *msg_cfg;
04347    const char *origcallerid, *origtime;
04348    char origcidname[80], origcidnum[80], origdate[80];
04349    int inttime;
04350    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04351 
04352    /* Prepare variables for substitution in email body and subject */
04353    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04354    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04355    snprintf(num, sizeof(num), "%d", msgnum);
04356    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04357    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04358    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04359    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04360       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04361    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04362    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04363    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04364    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04365    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04366 
04367    /* Retrieve info from VM attribute file */
04368    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04369    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04370    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04371       strcat(fromfile, ".txt");
04372    }
04373    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04374       if (option_debug > 0) {
04375          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04376       }
04377       return;
04378    }
04379 
04380    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04381       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04382       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04383       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04384       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04385    }
04386 
04387    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04388       struct timeval tv = { inttime, };
04389       struct ast_tm tm;
04390       ast_localtime(&tv, &tm, NULL);
04391       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04392       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04393    }
04394    ast_config_destroy(msg_cfg);
04395 }
04396 
04397 /*!
04398  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04399  * \param from The string to work with.
04400  * \param buf The buffer into which to write the modified quoted string.
04401  * \param maxlen Always zero, but see \see ast_str
04402  * 
04403  * \return The destination string with quotes wrapped on it (the to field).
04404  */
04405 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04406 {
04407    const char *ptr;
04408 
04409    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04410    ast_str_set(buf, maxlen, "\"");
04411    for (ptr = from; *ptr; ptr++) {
04412       if (*ptr == '"' || *ptr == '\\') {
04413          ast_str_append(buf, maxlen, "\\%c", *ptr);
04414       } else {
04415          ast_str_append(buf, maxlen, "%c", *ptr);
04416       }
04417    }
04418    ast_str_append(buf, maxlen, "\"");
04419 
04420    return ast_str_buffer(*buf);
04421 }
04422 
04423 /*! \brief
04424  * fill in *tm for current time according to the proper timezone, if any.
04425  * \return tm so it can be used as a function argument.
04426  */
04427 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04428 {
04429    const struct vm_zone *z = NULL;
04430    struct timeval t = ast_tvnow();
04431 
04432    /* Does this user have a timezone specified? */
04433    if (!ast_strlen_zero(vmu->zonetag)) {
04434       /* Find the zone in the list */
04435       AST_LIST_LOCK(&zones);
04436       AST_LIST_TRAVERSE(&zones, z, list) {
04437          if (!strcmp(z->name, vmu->zonetag))
04438             break;
04439       }
04440       AST_LIST_UNLOCK(&zones);
04441    }
04442    ast_localtime(&t, tm, z ? z->timezone : NULL);
04443    return tm;
04444 }
04445 
04446 /*!\brief Check if the string would need encoding within the MIME standard, to
04447  * avoid confusing certain mail software that expects messages to be 7-bit
04448  * clean.
04449  */
04450 static int check_mime(const char *str)
04451 {
04452    for (; *str; str++) {
04453       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04454          return 1;
04455       }
04456    }
04457    return 0;
04458 }
04459 
04460 /*!\brief Encode a string according to the MIME rules for encoding strings
04461  * that are not 7-bit clean or contain control characters.
04462  *
04463  * Additionally, if the encoded string would exceed the MIME limit of 76
04464  * characters per line, then the encoding will be broken up into multiple
04465  * sections, separated by a space character, in order to facilitate
04466  * breaking up the associated header across multiple lines.
04467  *
04468  * \param end An expandable buffer for holding the result
04469  * \param maxlen Always zero, but see \see ast_str
04470  * \param start A string to be encoded
04471  * \param preamble The length of the first line already used for this string,
04472  * to ensure that each line maintains a maximum length of 76 chars.
04473  * \param postamble the length of any additional characters appended to the
04474  * line, used to ensure proper field wrapping.
04475  * \retval The encoded string.
04476  */
04477 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04478 {
04479    struct ast_str *tmp = ast_str_alloca(80);
04480    int first_section = 1;
04481 
04482    ast_str_reset(*end);
04483    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04484    for (; *start; start++) {
04485       int need_encoding = 0;
04486       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04487          need_encoding = 1;
04488       }
04489       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04490          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04491          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04492          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04493          /* Start new line */
04494          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04495          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04496          first_section = 0;
04497       }
04498       if (need_encoding && *start == ' ') {
04499          ast_str_append(&tmp, -1, "_");
04500       } else if (need_encoding) {
04501          ast_str_append(&tmp, -1, "=%hhX", *start);
04502       } else {
04503          ast_str_append(&tmp, -1, "%c", *start);
04504       }
04505    }
04506    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04507    return ast_str_buffer(*end);
04508 }
04509 
04510 /*!
04511  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04512  * \param p The output file to generate the email contents into.
04513  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04514  * \param vmu The voicemail user who is sending the voicemail.
04515  * \param msgnum The message index in the mailbox folder.
04516  * \param context 
04517  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04518  * \param fromfolder
04519  * \param cidnum The caller ID number.
04520  * \param cidname The caller ID name.
04521  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04522  * \param attach2 
04523  * \param format The message sound file format. i.e. .wav
04524  * \param duration The time of the message content, in seconds.
04525  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04526  * \param chan
04527  * \param category
04528  * \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.
04529  * \param flag
04530  *
04531  * 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.
04532  */
04533 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)
04534 {
04535    char date[256];
04536    char host[MAXHOSTNAMELEN] = "";
04537    char who[256];
04538    char bound[256];
04539    char dur[256];
04540    struct ast_tm tm;
04541    char enc_cidnum[256] = "", enc_cidname[256] = "";
04542    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04543    char *greeting_attachment; 
04544    char filename[256];
04545 
04546    if (!str1 || !str2) {
04547       ast_free(str1);
04548       ast_free(str2);
04549       return;
04550    }
04551 
04552    if (cidnum) {
04553       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04554    }
04555    if (cidname) {
04556       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04557    }
04558    gethostname(host, sizeof(host) - 1);
04559 
04560    if (strchr(srcemail, '@')) {
04561       ast_copy_string(who, srcemail, sizeof(who));
04562    } else {
04563       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04564    }
04565 
04566    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04567    if (greeting_attachment) {
04568       *greeting_attachment++ = '\0';
04569    }
04570 
04571    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04572    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04573    fprintf(p, "Date: %s" ENDL, date);
04574 
04575    /* Set date format for voicemail mail */
04576    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04577 
04578    if (!ast_strlen_zero(fromstring)) {
04579       struct ast_channel *ast;
04580       if ((ast = ast_dummy_channel_alloc())) {
04581          char *ptr;
04582          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04583          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04584 
04585          if (check_mime(ast_str_buffer(str1))) {
04586             int first_line = 1;
04587             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04588             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04589                *ptr = '\0';
04590                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04591                first_line = 0;
04592                /* Substring is smaller, so this will never grow */
04593                ast_str_set(&str2, 0, "%s", ptr + 1);
04594             }
04595             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04596          } else {
04597             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04598          }
04599          ast = ast_channel_unref(ast);
04600       } else {
04601          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04602       }
04603    } else {
04604       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04605    }
04606 
04607    if (check_mime(vmu->fullname)) {
04608       int first_line = 1;
04609       char *ptr;
04610       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04611       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04612          *ptr = '\0';
04613          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04614          first_line = 0;
04615          /* Substring is smaller, so this will never grow */
04616          ast_str_set(&str2, 0, "%s", ptr + 1);
04617       }
04618       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04619    } else {
04620       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04621    }
04622 
04623    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04624       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04625       struct ast_channel *ast;
04626       if ((ast = ast_dummy_channel_alloc())) {
04627          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04628          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04629          if (check_mime(ast_str_buffer(str1))) {
04630             int first_line = 1;
04631             char *ptr;
04632             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04633             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04634                *ptr = '\0';
04635                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04636                first_line = 0;
04637                /* Substring is smaller, so this will never grow */
04638                ast_str_set(&str2, 0, "%s", ptr + 1);
04639             }
04640             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04641          } else {
04642             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04643          }
04644          ast = ast_channel_unref(ast);
04645       } else {
04646          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04647       }
04648    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04649       if (ast_strlen_zero(flag)) {
04650          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04651       } else {
04652          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04653       }
04654    } else {
04655       if (ast_strlen_zero(flag)) {
04656          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04657       } else {
04658          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04659       }
04660    }
04661 
04662    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04663       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04664    if (imap) {
04665       /* additional information needed for IMAP searching */
04666       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04667       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04668       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04669       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04670 #ifdef IMAP_STORAGE
04671       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04672 #else
04673       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04674 #endif
04675       /* flag added for Urgent */
04676       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04677       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04678       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04679       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04680       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04681       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04682       if (!ast_strlen_zero(category)) {
04683          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04684       } else {
04685          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04686       }
04687       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04688       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04689       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04690    }
04691    if (!ast_strlen_zero(cidnum)) {
04692       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04693    }
04694    if (!ast_strlen_zero(cidname)) {
04695       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04696    }
04697    fprintf(p, "MIME-Version: 1.0" ENDL);
04698    if (attach_user_voicemail) {
04699       /* Something unique. */
04700       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04701          (int) getpid(), (unsigned int) ast_random());
04702 
04703       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04704       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04705       fprintf(p, "--%s" ENDL, bound);
04706    }
04707    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04708    if (emailbody || vmu->emailbody) {
04709       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04710       struct ast_channel *ast;
04711       if ((ast = ast_dummy_channel_alloc())) {
04712          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04713          ast_str_substitute_variables(&str1, 0, ast, e_body);
04714 #ifdef IMAP_STORAGE
04715             {
04716                /* Convert body to native line terminators for IMAP backend */
04717                char *line = ast_str_buffer(str1), *next;
04718                do {
04719                   /* Terminate line before outputting it to the file */
04720                   if ((next = strchr(line, '\n'))) {
04721                      *next++ = '\0';
04722                   }
04723                   fprintf(p, "%s" ENDL, line);
04724                   line = next;
04725                } while (!ast_strlen_zero(line));
04726             }
04727 #else
04728          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04729 #endif
04730          ast = ast_channel_unref(ast);
04731       } else {
04732          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04733       }
04734    } else if (msgnum > -1) {
04735       if (strcmp(vmu->mailbox, mailbox)) {
04736          /* Forwarded type */
04737          struct ast_config *msg_cfg;
04738          const char *v;
04739          int inttime;
04740          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04741          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04742          /* Retrieve info from VM attribute file */
04743          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04744          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04745          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04746             strcat(fromfile, ".txt");
04747          }
04748          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04749             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04750                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04751             }
04752 
04753             /* You might be tempted to do origdate, except that a) it's in the wrong
04754              * format, and b) it's missing for IMAP recordings. */
04755             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04756                struct timeval tv = { inttime, };
04757                struct ast_tm tm;
04758                ast_localtime(&tv, &tm, NULL);
04759                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04760             }
04761             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04762                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04763                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04764                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04765                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04766                date, origcallerid, origdate);
04767             ast_config_destroy(msg_cfg);
04768          } else {
04769             goto plain_message;
04770          }
04771       } else {
04772 plain_message:
04773          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04774             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04775             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04776             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04777             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04778       }
04779    } else {
04780       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04781             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04782    }
04783 
04784    if (imap || attach_user_voicemail) {
04785       if (!ast_strlen_zero(attach2)) {
04786          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04787          ast_debug(5, "creating second attachment filename %s\n", filename);
04788          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04789          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04790          ast_debug(5, "creating attachment filename %s\n", filename);
04791          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04792       } else {
04793          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04794          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04795          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04796       }
04797    }
04798    ast_free(str1);
04799    ast_free(str2);
04800 }
04801 
04802 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)
04803 {
04804    char tmpdir[256], newtmp[256];
04805    char fname[256];
04806    char tmpcmd[256];
04807    int tmpfd = -1;
04808    int soxstatus = 0;
04809 
04810    /* Eww. We want formats to tell us their own MIME type */
04811    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04812 
04813    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04814       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04815       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04816       tmpfd = mkstemp(newtmp);
04817       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04818       ast_debug(3, "newtmp: %s\n", newtmp);
04819       if (tmpfd > -1) {
04820          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04821          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04822             attach = newtmp;
04823             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04824          } else {
04825             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04826                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04827             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04828          }
04829       }
04830    }
04831    fprintf(p, "--%s" ENDL, bound);
04832    if (msgnum > -1)
04833       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04834    else
04835       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04836    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04837    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04838    if (msgnum > -1)
04839       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04840    else
04841       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04842    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04843    base_encode(fname, p);
04844    if (last)
04845       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04846    if (tmpfd > -1) {
04847       if (soxstatus == 0) {
04848          unlink(fname);
04849       }
04850       close(tmpfd);
04851       unlink(newtmp);
04852    }
04853    return 0;
04854 }
04855 
04856 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)
04857 {
04858    FILE *p = NULL;
04859    char tmp[80] = "/tmp/astmail-XXXXXX";
04860    char tmp2[256];
04861    char *stringp;
04862 
04863    if (vmu && ast_strlen_zero(vmu->email)) {
04864       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04865       return(0);
04866    }
04867 
04868    /* Mail only the first format */
04869    format = ast_strdupa(format);
04870    stringp = format;
04871    strsep(&stringp, "|");
04872 
04873    if (!strcmp(format, "wav49"))
04874       format = "WAV";
04875    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));
04876    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04877       command hangs */
04878    if ((p = vm_mkftemp(tmp)) == NULL) {
04879       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04880       return -1;
04881    } else {
04882       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04883       fclose(p);
04884       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04885       ast_safe_system(tmp2);
04886       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04887    }
04888    return 0;
04889 }
04890 
04891 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)
04892 {
04893    char enc_cidnum[256], enc_cidname[256];
04894    char date[256];
04895    char host[MAXHOSTNAMELEN] = "";
04896    char who[256];
04897    char dur[PATH_MAX];
04898    char tmp[80] = "/tmp/astmail-XXXXXX";
04899    char tmp2[PATH_MAX];
04900    struct ast_tm tm;
04901    FILE *p;
04902    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04903 
04904    if (!str1 || !str2) {
04905       ast_free(str1);
04906       ast_free(str2);
04907       return -1;
04908    }
04909 
04910    if (cidnum) {
04911       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04912    }
04913    if (cidname) {
04914       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04915    }
04916 
04917    if ((p = vm_mkftemp(tmp)) == NULL) {
04918       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04919       ast_free(str1);
04920       ast_free(str2);
04921       return -1;
04922    }
04923    gethostname(host, sizeof(host)-1);
04924    if (strchr(srcemail, '@')) {
04925       ast_copy_string(who, srcemail, sizeof(who));
04926    } else {
04927       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04928    }
04929    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04930    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04931    fprintf(p, "Date: %s\n", date);
04932 
04933    /* Reformat for custom pager format */
04934    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04935 
04936    if (!ast_strlen_zero(pagerfromstring)) {
04937       struct ast_channel *ast;
04938       if ((ast = ast_dummy_channel_alloc())) {
04939          char *ptr;
04940          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04941          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04942 
04943          if (check_mime(ast_str_buffer(str1))) {
04944             int first_line = 1;
04945             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04946             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04947                *ptr = '\0';
04948                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04949                first_line = 0;
04950                /* Substring is smaller, so this will never grow */
04951                ast_str_set(&str2, 0, "%s", ptr + 1);
04952             }
04953             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04954          } else {
04955             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04956          }
04957          ast = ast_channel_unref(ast);
04958       } else {
04959          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04960       }
04961    } else {
04962       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04963    }
04964 
04965    if (check_mime(vmu->fullname)) {
04966       int first_line = 1;
04967       char *ptr;
04968       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04969       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04970          *ptr = '\0';
04971          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04972          first_line = 0;
04973          /* Substring is smaller, so this will never grow */
04974          ast_str_set(&str2, 0, "%s", ptr + 1);
04975       }
04976       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04977    } else {
04978       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04979    }
04980 
04981    if (!ast_strlen_zero(pagersubject)) {
04982       struct ast_channel *ast;
04983       if ((ast = ast_dummy_channel_alloc())) {
04984          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04985          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04986          if (check_mime(ast_str_buffer(str1))) {
04987             int first_line = 1;
04988             char *ptr;
04989             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04990             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04991                *ptr = '\0';
04992                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04993                first_line = 0;
04994                /* Substring is smaller, so this will never grow */
04995                ast_str_set(&str2, 0, "%s", ptr + 1);
04996             }
04997             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04998          } else {
04999             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
05000          }
05001          ast = ast_channel_unref(ast);
05002       } else {
05003          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05004       }
05005    } else {
05006       if (ast_strlen_zero(flag)) {
05007          fprintf(p, "Subject: New VM\n\n");
05008       } else {
05009          fprintf(p, "Subject: New %s VM\n\n", flag);
05010       }
05011    }
05012 
05013    if (pagerbody) {
05014       struct ast_channel *ast;
05015       if ((ast = ast_dummy_channel_alloc())) {
05016          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05017          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05018          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05019          ast = ast_channel_unref(ast);
05020       } else {
05021          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05022       }
05023    } else {
05024       fprintf(p, "New %s long %s msg in box %s\n"
05025             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05026    }
05027 
05028    fclose(p);
05029    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05030    ast_safe_system(tmp2);
05031    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05032    ast_free(str1);
05033    ast_free(str2);
05034    return 0;
05035 }
05036 
05037 /*!
05038  * \brief Gets the current date and time, as formatted string.
05039  * \param s The buffer to hold the output formatted date.
05040  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05041  * 
05042  * The date format string used is "%a %b %e %r UTC %Y".
05043  * 
05044  * \return zero on success, -1 on error.
05045  */
05046 static int get_date(char *s, int len)
05047 {
05048    struct ast_tm tm;
05049    struct timeval t = ast_tvnow();
05050    
05051    ast_localtime(&t, &tm, "UTC");
05052 
05053    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05054 }
05055 
05056 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05057 {
05058    int res;
05059    char fn[PATH_MAX];
05060    char dest[PATH_MAX];
05061 
05062    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05063 
05064    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05065       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05066       return -1;
05067    }
05068 
05069    RETRIEVE(fn, -1, ext, context);
05070    if (ast_fileexists(fn, NULL, NULL) > 0) {
05071       res = ast_stream_and_wait(chan, fn, ecodes);
05072       if (res) {
05073          DISPOSE(fn, -1);
05074          return res;
05075       }
05076    } else {
05077       /* Dispose just in case */
05078       DISPOSE(fn, -1);
05079       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05080       if (res)
05081          return res;
05082       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05083       if (res)
05084          return res;
05085    }
05086    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05087    return res;
05088 }
05089 
05090 static void free_zone(struct vm_zone *z)
05091 {
05092    ast_free(z);
05093 }
05094 
05095 #ifdef ODBC_STORAGE
05096 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05097 {
05098    int x = -1;
05099    int res;
05100    SQLHSTMT stmt = NULL;
05101    char sql[PATH_MAX];
05102    char rowdata[20];
05103    char tmp[PATH_MAX] = "";
05104    struct odbc_obj *obj = NULL;
05105    char *context;
05106    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05107 
05108    if (newmsgs)
05109       *newmsgs = 0;
05110    if (oldmsgs)
05111       *oldmsgs = 0;
05112    if (urgentmsgs)
05113       *urgentmsgs = 0;
05114 
05115    /* If no mailbox, return immediately */
05116    if (ast_strlen_zero(mailbox))
05117       return 0;
05118 
05119    ast_copy_string(tmp, mailbox, sizeof(tmp));
05120 
05121    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05122       int u, n, o;
05123       char *next, *remaining = tmp;
05124       while ((next = strsep(&remaining, " ,"))) {
05125          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05126             return -1;
05127          }
05128          if (urgentmsgs) {
05129             *urgentmsgs += u;
05130          }
05131          if (newmsgs) {
05132             *newmsgs += n;
05133          }
05134          if (oldmsgs) {
05135             *oldmsgs += o;
05136          }
05137       }
05138       return 0;
05139    }
05140 
05141    context = strchr(tmp, '@');
05142    if (context) {
05143       *context = '\0';
05144       context++;
05145    } else
05146       context = "default";
05147 
05148    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05149       do {
05150          if (newmsgs) {
05151             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05152             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05153                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05154                break;
05155             }
05156             res = SQLFetch(stmt);
05157             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05158                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05159                break;
05160             }
05161             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05162             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05163                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05164                break;
05165             }
05166             *newmsgs = atoi(rowdata);
05167             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05168          }
05169 
05170          if (oldmsgs) {
05171             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05172             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05173                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05174                break;
05175             }
05176             res = SQLFetch(stmt);
05177             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05178                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05179                break;
05180             }
05181             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05182             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05183                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05184                break;
05185             }
05186             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05187             *oldmsgs = atoi(rowdata);
05188          }
05189 
05190          if (urgentmsgs) {
05191             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05192             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05193                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05194                break;
05195             }
05196             res = SQLFetch(stmt);
05197             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05198                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05199                break;
05200             }
05201             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05202             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05203                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05204                break;
05205             }
05206             *urgentmsgs = atoi(rowdata);
05207          }
05208 
05209          x = 0;
05210       } while (0);
05211    } else {
05212       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05213    }
05214 
05215    if (stmt) {
05216       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05217    }
05218    if (obj) {
05219       ast_odbc_release_obj(obj);
05220    }
05221    return x;
05222 }
05223 
05224 /*!
05225  * \brief Gets the number of messages that exist in a mailbox folder.
05226  * \param context
05227  * \param mailbox
05228  * \param folder
05229  * 
05230  * This method is used when ODBC backend is used.
05231  * \return The number of messages in this mailbox folder (zero or more).
05232  */
05233 static int messagecount(const char *context, const char *mailbox, const char *folder)
05234 {
05235    struct odbc_obj *obj = NULL;
05236    int nummsgs = 0;
05237    int res;
05238    SQLHSTMT stmt = NULL;
05239    char sql[PATH_MAX];
05240    char rowdata[20];
05241    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05242    if (!folder)
05243       folder = "INBOX";
05244    /* If no mailbox, return immediately */
05245    if (ast_strlen_zero(mailbox))
05246       return 0;
05247 
05248    obj = ast_odbc_request_obj(odbc_database, 0);
05249    if (obj) {
05250       if (!strcmp(folder, "INBOX")) {
05251          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);
05252       } else {
05253          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05254       }
05255       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05256       if (!stmt) {
05257          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05258          goto yuck;
05259       }
05260       res = SQLFetch(stmt);
05261       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05262          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05263          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05264          goto yuck;
05265       }
05266       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05267       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05268          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05269          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05270          goto yuck;
05271       }
05272       nummsgs = atoi(rowdata);
05273       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05274    } else
05275       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05276 
05277 yuck:
05278    if (obj)
05279       ast_odbc_release_obj(obj);
05280    return nummsgs;
05281 }
05282 
05283 /** 
05284  * \brief Determines if the given folder has messages.
05285  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05286  * 
05287  * This function is used when the mailbox is stored in an ODBC back end.
05288  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05289  * \return 1 if the folder has one or more messages. zero otherwise.
05290  */
05291 static int has_voicemail(const char *mailbox, const char *folder)
05292 {
05293    char tmp[256], *tmp2 = tmp, *box, *context;
05294    ast_copy_string(tmp, mailbox, sizeof(tmp));
05295    while ((context = box = strsep(&tmp2, ",&"))) {
05296       strsep(&context, "@");
05297       if (ast_strlen_zero(context))
05298          context = "default";
05299       if (messagecount(context, box, folder))
05300          return 1;
05301    }
05302    return 0;
05303 }
05304 #endif
05305 #ifndef IMAP_STORAGE
05306 /*! 
05307  * \brief Copies a message from one mailbox to another.
05308  * \param chan
05309  * \param vmu
05310  * \param imbox
05311  * \param msgnum
05312  * \param duration
05313  * \param recip
05314  * \param fmt
05315  * \param dir
05316  * \param flag
05317  *
05318  * This is only used by file storage based mailboxes.
05319  *
05320  * \return zero on success, -1 on error.
05321  */
05322 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)
05323 {
05324    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05325    const char *frombox = mbox(vmu, imbox);
05326    const char *userfolder;
05327    int recipmsgnum;
05328    int res = 0;
05329 
05330    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05331 
05332    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05333       userfolder = "Urgent";
05334    } else {
05335       userfolder = "INBOX";
05336    }
05337 
05338    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05339 
05340    if (!dir)
05341       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05342    else
05343       ast_copy_string(fromdir, dir, sizeof(fromdir));
05344 
05345    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05346    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05347 
05348    if (vm_lock_path(todir))
05349       return ERROR_LOCK_PATH;
05350 
05351    recipmsgnum = last_message_index(recip, todir) + 1;
05352    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05353       make_file(topath, sizeof(topath), todir, recipmsgnum);
05354 #ifndef ODBC_STORAGE
05355       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05356          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05357       } else {
05358 #endif
05359          /* If we are prepending a message for ODBC, then the message already
05360           * exists in the database, but we want to force copying from the
05361           * filesystem (since only the FS contains the prepend). */
05362          copy_plain_file(frompath, topath);
05363          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05364          vm_delete(topath);
05365 #ifndef ODBC_STORAGE
05366       }
05367 #endif
05368    } else {
05369       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05370       res = -1;
05371    }
05372    ast_unlock_path(todir);
05373    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05374       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05375       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05376       flag);
05377    
05378    return res;
05379 }
05380 #endif
05381 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05382 
05383 static int messagecount(const char *context, const char *mailbox, const char *folder)
05384 {
05385    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05386 }
05387 
05388 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05389 {
05390    DIR *dir;
05391    struct dirent *de;
05392    char fn[256];
05393    int ret = 0;
05394 
05395    /* If no mailbox, return immediately */
05396    if (ast_strlen_zero(mailbox))
05397       return 0;
05398 
05399    if (ast_strlen_zero(folder))
05400       folder = "INBOX";
05401    if (ast_strlen_zero(context))
05402       context = "default";
05403 
05404    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05405 
05406    if (!(dir = opendir(fn)))
05407       return 0;
05408 
05409    while ((de = readdir(dir))) {
05410       if (!strncasecmp(de->d_name, "msg", 3)) {
05411          if (shortcircuit) {
05412             ret = 1;
05413             break;
05414          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05415             ret++;
05416          }
05417       }
05418    }
05419 
05420    closedir(dir);
05421 
05422    return ret;
05423 }
05424 
05425 /** 
05426  * \brief Determines if the given folder has messages.
05427  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05428  * \param folder the folder to look in
05429  *
05430  * This function is used when the mailbox is stored in a filesystem back end.
05431  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05432  * \return 1 if the folder has one or more messages. zero otherwise.
05433  */
05434 static int has_voicemail(const char *mailbox, const char *folder)
05435 {
05436    char tmp[256], *tmp2 = tmp, *box, *context;
05437    ast_copy_string(tmp, mailbox, sizeof(tmp));
05438    if (ast_strlen_zero(folder)) {
05439       folder = "INBOX";
05440    }
05441    while ((box = strsep(&tmp2, ",&"))) {
05442       if ((context = strchr(box, '@')))
05443          *context++ = '\0';
05444       else
05445          context = "default";
05446       if (__has_voicemail(context, box, folder, 1))
05447          return 1;
05448       /* If we are checking INBOX, we should check Urgent as well */
05449       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05450          return 1;
05451       }
05452    }
05453    return 0;
05454 }
05455 
05456 
05457 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05458 {
05459    char tmp[256];
05460    char *context;
05461 
05462    /* If no mailbox, return immediately */
05463    if (ast_strlen_zero(mailbox))
05464       return 0;
05465 
05466    if (newmsgs)
05467       *newmsgs = 0;
05468    if (oldmsgs)
05469       *oldmsgs = 0;
05470    if (urgentmsgs)
05471       *urgentmsgs = 0;
05472 
05473    if (strchr(mailbox, ',')) {
05474       int tmpnew, tmpold, tmpurgent;
05475       char *mb, *cur;
05476 
05477       ast_copy_string(tmp, mailbox, sizeof(tmp));
05478       mb = tmp;
05479       while ((cur = strsep(&mb, ", "))) {
05480          if (!ast_strlen_zero(cur)) {
05481             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05482                return -1;
05483             else {
05484                if (newmsgs)
05485                   *newmsgs += tmpnew; 
05486                if (oldmsgs)
05487                   *oldmsgs += tmpold;
05488                if (urgentmsgs)
05489                   *urgentmsgs += tmpurgent;
05490             }
05491          }
05492       }
05493       return 0;
05494    }
05495 
05496    ast_copy_string(tmp, mailbox, sizeof(tmp));
05497    
05498    if ((context = strchr(tmp, '@')))
05499       *context++ = '\0';
05500    else
05501       context = "default";
05502 
05503    if (newmsgs)
05504       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05505    if (oldmsgs)
05506       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05507    if (urgentmsgs)
05508       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05509 
05510    return 0;
05511 }
05512 
05513 #endif
05514 
05515 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05516 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05517 {
05518    int urgentmsgs = 0;
05519    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05520    if (newmsgs) {
05521       *newmsgs += urgentmsgs;
05522    }
05523    return res;
05524 }
05525 
05526 static void run_externnotify(char *context, char *extension, const char *flag)
05527 {
05528    char arguments[255];
05529    char ext_context[256] = "";
05530    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05531    struct ast_smdi_mwi_message *mwi_msg;
05532 
05533    if (!ast_strlen_zero(context))
05534       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05535    else
05536       ast_copy_string(ext_context, extension, sizeof(ext_context));
05537 
05538    if (smdi_iface) {
05539       if (ast_app_has_voicemail(ext_context, NULL)) 
05540          ast_smdi_mwi_set(smdi_iface, extension);
05541       else
05542          ast_smdi_mwi_unset(smdi_iface, extension);
05543 
05544       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05545          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05546          if (!strncmp(mwi_msg->cause, "INV", 3))
05547             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05548          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05549             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05550          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05551          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05552       } else {
05553          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05554       }
05555    }
05556 
05557    if (!ast_strlen_zero(externnotify)) {
05558       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05559          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05560       } else {
05561          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05562          ast_debug(1, "Executing %s\n", arguments);
05563          ast_safe_system(arguments);
05564       }
05565    }
05566 }
05567 
05568 /*!
05569  * \brief Variables used for saving a voicemail.
05570  *
05571  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05572  */
05573 struct leave_vm_options {
05574    unsigned int flags;
05575    signed char record_gain;
05576    char *exitcontext;
05577 };
05578 
05579 /*!
05580  * \brief Prompts the user and records a voicemail to a mailbox.
05581  * \param chan
05582  * \param ext
05583  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05584  * 
05585  * 
05586  * 
05587  * \return zero on success, -1 on error.
05588  */
05589 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05590 {
05591 #ifdef IMAP_STORAGE
05592    int newmsgs, oldmsgs;
05593 #else
05594    char urgdir[PATH_MAX];
05595 #endif
05596    char txtfile[PATH_MAX];
05597    char tmptxtfile[PATH_MAX];
05598    struct vm_state *vms = NULL;
05599    char callerid[256];
05600    FILE *txt;
05601    char date[256];
05602    int txtdes;
05603    int res = 0;
05604    int msgnum;
05605    int duration = 0;
05606    int sound_duration = 0;
05607    int ausemacro = 0;
05608    int ousemacro = 0;
05609    int ouseexten = 0;
05610    char tmpdur[16];
05611    char priority[16];
05612    char origtime[16];
05613    char dir[PATH_MAX];
05614    char tmpdir[PATH_MAX];
05615    char fn[PATH_MAX];
05616    char prefile[PATH_MAX] = "";
05617    char tempfile[PATH_MAX] = "";
05618    char ext_context[256] = "";
05619    char fmt[80];
05620    char *context;
05621    char ecodes[17] = "#";
05622    struct ast_str *tmp = ast_str_create(16);
05623    char *tmpptr;
05624    struct ast_vm_user *vmu;
05625    struct ast_vm_user svm;
05626    const char *category = NULL;
05627    const char *code;
05628    const char *alldtmf = "0123456789ABCD*#";
05629    char flag[80];
05630 
05631    if (!tmp) {
05632       return -1;
05633    }
05634 
05635    ast_str_set(&tmp, 0, "%s", ext);
05636    ext = ast_str_buffer(tmp);
05637    if ((context = strchr(ext, '@'))) {
05638       *context++ = '\0';
05639       tmpptr = strchr(context, '&');
05640    } else {
05641       tmpptr = strchr(ext, '&');
05642    }
05643 
05644    if (tmpptr)
05645       *tmpptr++ = '\0';
05646 
05647    ast_channel_lock(chan);
05648    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05649       category = ast_strdupa(category);
05650    }
05651    ast_channel_unlock(chan);
05652 
05653    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05654       ast_copy_string(flag, "Urgent", sizeof(flag));
05655    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05656       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05657    } else {
05658       flag[0] = '\0';
05659    }
05660 
05661    ast_debug(3, "Before find_user\n");
05662    if (!(vmu = find_user(&svm, context, ext))) {
05663       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05664       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05665       ast_free(tmp);
05666       return res;
05667    }
05668    /* Setup pre-file if appropriate */
05669    if (strcmp(vmu->context, "default"))
05670       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05671    else
05672       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05673 
05674    /* Set the path to the prefile. Will be one of 
05675       VM_SPOOL_DIRcontext/ext/busy
05676       VM_SPOOL_DIRcontext/ext/unavail
05677       Depending on the flag set in options.
05678    */
05679    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05680       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05681    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05682       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05683    }
05684    /* Set the path to the tmpfile as
05685       VM_SPOOL_DIR/context/ext/temp
05686       and attempt to create the folder structure.
05687    */
05688    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05689    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05690       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05691       ast_free(tmp);
05692       return -1;
05693    }
05694    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05695    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05696       ast_copy_string(prefile, tempfile, sizeof(prefile));
05697 
05698    DISPOSE(tempfile, -1);
05699    /* It's easier just to try to make it than to check for its existence */
05700 #ifndef IMAP_STORAGE
05701    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05702 #else
05703    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05704    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05705       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05706    }
05707 #endif
05708 
05709    /* Check current or macro-calling context for special extensions */
05710    if (ast_test_flag(vmu, VM_OPERATOR)) {
05711       if (!ast_strlen_zero(vmu->exit)) {
05712          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05713             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05714             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05715             ouseexten = 1;
05716          }
05717       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05718          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05719          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05720          ouseexten = 1;
05721       } else if (!ast_strlen_zero(chan->macrocontext)
05722          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05723             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05724          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05725          ousemacro = 1;
05726       }
05727    }
05728 
05729    if (!ast_strlen_zero(vmu->exit)) {
05730       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05731          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05732          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05733       }
05734    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05735       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05736       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05737    } else if (!ast_strlen_zero(chan->macrocontext)
05738       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05739          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05740       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05741       ausemacro = 1;
05742    }
05743 
05744    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05745       for (code = alldtmf; *code; code++) {
05746          char e[2] = "";
05747          e[0] = *code;
05748          if (strchr(ecodes, e[0]) == NULL
05749             && ast_canmatch_extension(chan,
05750                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
05751                e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05752             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05753          }
05754       }
05755    }
05756 
05757    /* Play the beginning intro if desired */
05758    if (!ast_strlen_zero(prefile)) {
05759 #ifdef ODBC_STORAGE
05760       int success = 
05761 #endif
05762          RETRIEVE(prefile, -1, ext, context);
05763       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05764          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05765             res = ast_waitstream(chan, ecodes);
05766 #ifdef ODBC_STORAGE
05767          if (success == -1) {
05768             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05769             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05770             store_file(prefile, vmu->mailbox, vmu->context, -1);
05771          }
05772 #endif
05773       } else {
05774          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05775          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05776       }
05777       DISPOSE(prefile, -1);
05778       if (res < 0) {
05779          ast_debug(1, "Hang up during prefile playback\n");
05780          free_user(vmu);
05781          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05782          ast_free(tmp);
05783          return -1;
05784       }
05785    }
05786    if (res == '#') {
05787       /* On a '#' we skip the instructions */
05788       ast_set_flag(options, OPT_SILENT);
05789       res = 0;
05790    }
05791    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05792    if (vmu->maxmsg == 0) {
05793       if (option_debug > 2)
05794          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05795       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05796       goto leave_vm_out;
05797    }
05798    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05799       res = ast_stream_and_wait(chan, INTRO, ecodes);
05800       if (res == '#') {
05801          ast_set_flag(options, OPT_SILENT);
05802          res = 0;
05803       }
05804    }
05805    if (res > 0)
05806       ast_stopstream(chan);
05807    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05808     other than the operator -- an automated attendant or mailbox login for example */
05809    if (res == '*') {
05810       chan->exten[0] = 'a';
05811       chan->exten[1] = '\0';
05812       if (!ast_strlen_zero(vmu->exit)) {
05813          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05814       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05815          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05816       }
05817       chan->priority = 0;
05818       free_user(vmu);
05819       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05820       ast_free(tmp);
05821       return 0;
05822    }
05823 
05824    /* Check for a '0' here */
05825    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05826    transfer:
05827       if (ouseexten || ousemacro) {
05828          chan->exten[0] = 'o';
05829          chan->exten[1] = '\0';
05830          if (!ast_strlen_zero(vmu->exit)) {
05831             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05832          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05833             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05834          }
05835          ast_play_and_wait(chan, "transfer");
05836          chan->priority = 0;
05837          free_user(vmu);
05838          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05839       }
05840       ast_free(tmp);
05841       return OPERATOR_EXIT;
05842    }
05843 
05844    /* Allow all other digits to exit Voicemail and return to the dialplan */
05845    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05846       if (!ast_strlen_zero(options->exitcontext)) {
05847          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05848       }
05849       free_user(vmu);
05850       ast_free(tmp);
05851       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05852       return res;
05853    }
05854 
05855    if (res < 0) {
05856       free_user(vmu);
05857       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05858       ast_free(tmp);
05859       return -1;
05860    }
05861    /* The meat of recording the message...  All the announcements and beeps have been played*/
05862    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05863    if (!ast_strlen_zero(fmt)) {
05864       msgnum = 0;
05865 
05866 #ifdef IMAP_STORAGE
05867       /* Is ext a mailbox? */
05868       /* must open stream for this user to get info! */
05869       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05870       if (res < 0) {
05871          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05872          ast_free(tmp);
05873          return -1;
05874       }
05875       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05876       /* It is possible under certain circumstances that inboxcount did not
05877        * create a vm_state when it was needed. This is a catchall which will
05878        * rarely be used.
05879        */
05880          if (!(vms = create_vm_state_from_user(vmu))) {
05881             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05882             ast_free(tmp);
05883             return -1;
05884          }
05885       }
05886       vms->newmessages++;
05887       
05888       /* here is a big difference! We add one to it later */
05889       msgnum = newmsgs + oldmsgs;
05890       ast_debug(3, "Messagecount set to %d\n", msgnum);
05891       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05892       /* set variable for compatibility */
05893       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05894 
05895       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05896          goto leave_vm_out;
05897       }
05898 #else
05899       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05900          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05901          if (!res)
05902             res = ast_waitstream(chan, "");
05903          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05904          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05905          inprocess_count(vmu->mailbox, vmu->context, -1);
05906          goto leave_vm_out;
05907       }
05908 
05909 #endif
05910       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05911       txtdes = mkstemp(tmptxtfile);
05912       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05913       if (txtdes < 0) {
05914          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05915          if (!res)
05916             res = ast_waitstream(chan, "");
05917          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05918          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05919          inprocess_count(vmu->mailbox, vmu->context, -1);
05920          goto leave_vm_out;
05921       }
05922 
05923       /* Now play the beep once we have the message number for our next message. */
05924       if (res >= 0) {
05925          /* Unless we're *really* silent, try to send the beep */
05926          res = ast_stream_and_wait(chan, "beep", "");
05927       }
05928             
05929       /* Store information in real-time storage */
05930       if (ast_check_realtime("voicemail_data")) {
05931          snprintf(priority, sizeof(priority), "%d", chan->priority);
05932          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05933          get_date(date, sizeof(date));
05934          ast_callerid_merge(callerid, sizeof(callerid),
05935             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05936             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05937             "Unknown");
05938          ast_store_realtime("voicemail_data",
05939             "origmailbox", ext,
05940             "context", chan->context,
05941             "macrocontext", chan->macrocontext,
05942             "exten", chan->exten,
05943             "priority", priority,
05944             "callerchan", chan->name,
05945             "callerid", callerid,
05946             "origdate", date,
05947             "origtime", origtime,
05948             "category", S_OR(category, ""),
05949             "filename", tmptxtfile,
05950             SENTINEL);
05951       }
05952 
05953       /* Store information */
05954       txt = fdopen(txtdes, "w+");
05955       if (txt) {
05956          get_date(date, sizeof(date));
05957          ast_callerid_merge(callerid, sizeof(callerid),
05958             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05959             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05960             "Unknown");
05961          fprintf(txt, 
05962             ";\n"
05963             "; Message Information file\n"
05964             ";\n"
05965             "[message]\n"
05966             "origmailbox=%s\n"
05967             "context=%s\n"
05968             "macrocontext=%s\n"
05969             "exten=%s\n"
05970             "rdnis=%s\n"
05971             "priority=%d\n"
05972             "callerchan=%s\n"
05973             "callerid=%s\n"
05974             "origdate=%s\n"
05975             "origtime=%ld\n"
05976             "category=%s\n",
05977             ext,
05978             chan->context,
05979             chan->macrocontext, 
05980             chan->exten,
05981             S_COR(chan->redirecting.from.number.valid,
05982                chan->redirecting.from.number.str, "unknown"),
05983             chan->priority,
05984             chan->name,
05985             callerid,
05986             date, (long) time(NULL),
05987             category ? category : "");
05988       } else {
05989          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05990          inprocess_count(vmu->mailbox, vmu->context, -1);
05991          if (ast_check_realtime("voicemail_data")) {
05992             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05993          }
05994          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05995          goto leave_vm_out;
05996       }
05997       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
05998 
05999       if (txt) {
06000          fprintf(txt, "flag=%s\n", flag);
06001          if (sound_duration < vmu->minsecs) {
06002             fclose(txt);
06003             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
06004             ast_filedelete(tmptxtfile, NULL);
06005             unlink(tmptxtfile);
06006             if (ast_check_realtime("voicemail_data")) {
06007                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06008             }
06009             inprocess_count(vmu->mailbox, vmu->context, -1);
06010          } else {
06011             fprintf(txt, "duration=%d\n", duration);
06012             fclose(txt);
06013             if (vm_lock_path(dir)) {
06014                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06015                /* Delete files */
06016                ast_filedelete(tmptxtfile, NULL);
06017                unlink(tmptxtfile);
06018                inprocess_count(vmu->mailbox, vmu->context, -1);
06019             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06020                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06021                unlink(tmptxtfile);
06022                ast_unlock_path(dir);
06023                inprocess_count(vmu->mailbox, vmu->context, -1);
06024                if (ast_check_realtime("voicemail_data")) {
06025                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06026                }
06027             } else {
06028 #ifndef IMAP_STORAGE
06029                msgnum = last_message_index(vmu, dir) + 1;
06030 #endif
06031                make_file(fn, sizeof(fn), dir, msgnum);
06032 
06033                /* assign a variable with the name of the voicemail file */ 
06034 #ifndef IMAP_STORAGE
06035                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06036 #else
06037                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06038 #endif
06039 
06040                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06041                ast_filerename(tmptxtfile, fn, NULL);
06042                rename(tmptxtfile, txtfile);
06043                inprocess_count(vmu->mailbox, vmu->context, -1);
06044 
06045                /* Properly set permissions on voicemail text descriptor file.
06046                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06047                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06048                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06049 
06050                ast_unlock_path(dir);
06051                if (ast_check_realtime("voicemail_data")) {
06052                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06053                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06054                }
06055                /* We must store the file first, before copying the message, because
06056                 * ODBC storage does the entire copy with SQL.
06057                 */
06058                if (ast_fileexists(fn, NULL, NULL) > 0) {
06059                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06060                }
06061 
06062                /* Are there to be more recipients of this message? */
06063                while (tmpptr) {
06064                   struct ast_vm_user recipu, *recip;
06065                   char *exten, *cntx;
06066 
06067                   exten = strsep(&tmpptr, "&");
06068                   cntx = strchr(exten, '@');
06069                   if (cntx) {
06070                      *cntx = '\0';
06071                      cntx++;
06072                   }
06073                   if ((recip = find_user(&recipu, cntx, exten))) {
06074                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06075                      free_user(recip);
06076                   }
06077                }
06078 #ifndef IMAP_STORAGE
06079                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06080                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06081                   char sfn[PATH_MAX];
06082                   char dfn[PATH_MAX];
06083                   int x;
06084                   /* It's easier just to try to make it than to check for its existence */
06085                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06086                   x = last_message_index(vmu, urgdir) + 1;
06087                   make_file(sfn, sizeof(sfn), dir, msgnum);
06088                   make_file(dfn, sizeof(dfn), urgdir, x);
06089                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06090                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06091                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06092                   ast_copy_string(fn, dfn, sizeof(fn));
06093                   msgnum = x;
06094                }
06095 #endif
06096                /* Notification needs to happen after the copy, though. */
06097                if (ast_fileexists(fn, NULL, NULL)) {
06098 #ifdef IMAP_STORAGE
06099                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06100                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06101                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06102                      flag);
06103 #else
06104                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06105                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06106                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06107                      flag);
06108 #endif
06109                }
06110 
06111                /* Disposal needs to happen after the optional move and copy */
06112                if (ast_fileexists(fn, NULL, NULL)) {
06113                   DISPOSE(dir, msgnum);
06114                }
06115             }
06116          }
06117       } else {
06118          inprocess_count(vmu->mailbox, vmu->context, -1);
06119       }
06120       if (res == '0') {
06121          goto transfer;
06122       } else if (res > 0 && res != 't')
06123          res = 0;
06124 
06125       if (sound_duration < vmu->minsecs)
06126          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06127          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06128       else
06129          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06130    } else
06131       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06132 leave_vm_out:
06133    free_user(vmu);
06134 
06135 #ifdef IMAP_STORAGE
06136    /* expunge message - use UID Expunge if supported on IMAP server*/
06137    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06138    if (expungeonhangup == 1) {
06139       ast_mutex_lock(&vms->lock);
06140 #ifdef HAVE_IMAP_TK2006
06141       if (LEVELUIDPLUS (vms->mailstream)) {
06142          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06143       } else 
06144 #endif
06145          mail_expunge(vms->mailstream);
06146       ast_mutex_unlock(&vms->lock);
06147    }
06148 #endif
06149 
06150    ast_free(tmp);
06151    return res;
06152 }
06153 
06154 #if !defined(IMAP_STORAGE)
06155 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06156 {
06157    /* we know the actual number of messages, so stop process when number is hit */
06158 
06159    int x, dest;
06160    char sfn[PATH_MAX];
06161    char dfn[PATH_MAX];
06162 
06163    if (vm_lock_path(dir)) {
06164       return ERROR_LOCK_PATH;
06165    }
06166 
06167    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06168       make_file(sfn, sizeof(sfn), dir, x);
06169       if (EXISTS(dir, x, sfn, NULL)) {
06170 
06171          if (x != dest) {
06172             make_file(dfn, sizeof(dfn), dir, dest);
06173             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06174          }
06175 
06176          dest++;
06177       }
06178    }
06179    ast_unlock_path(dir);
06180 
06181    return dest;
06182 }
06183 #endif
06184 
06185 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06186 {
06187    int d;
06188    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06189    return d;
06190 }
06191 
06192 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06193 {
06194 #ifdef IMAP_STORAGE
06195    /* we must use mbox(x) folder names, and copy the message there */
06196    /* simple. huh? */
06197    char sequence[10];
06198    char mailbox[256];
06199    int res;
06200 
06201    /* get the real IMAP message number for this message */
06202    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06203    
06204    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06205    ast_mutex_lock(&vms->lock);
06206    /* if save to Old folder, put in INBOX as read */
06207    if (box == OLD_FOLDER) {
06208       mail_setflag(vms->mailstream, sequence, "\\Seen");
06209       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06210    } else if (box == NEW_FOLDER) {
06211       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06212       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06213    }
06214    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06215       ast_mutex_unlock(&vms->lock);
06216       return 0;
06217    }
06218    /* Create the folder if it don't exist */
06219    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06220    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06221    if (mail_create(vms->mailstream, mailbox) == NIL) 
06222       ast_debug(5, "Folder exists.\n");
06223    else
06224       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06225    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06226    ast_mutex_unlock(&vms->lock);
06227    return res;
06228 #else
06229    char *dir = vms->curdir;
06230    char *username = vms->username;
06231    char *context = vmu->context;
06232    char sfn[PATH_MAX];
06233    char dfn[PATH_MAX];
06234    char ddir[PATH_MAX];
06235    const char *dbox = mbox(vmu, box);
06236    int x, i;
06237    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06238 
06239    if (vm_lock_path(ddir))
06240       return ERROR_LOCK_PATH;
06241 
06242    x = last_message_index(vmu, ddir) + 1;
06243 
06244    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06245       x--;
06246       for (i = 1; i <= x; i++) {
06247          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06248          make_file(sfn, sizeof(sfn), ddir, i);
06249          make_file(dfn, sizeof(dfn), ddir, i - 1);
06250          if (EXISTS(ddir, i, sfn, NULL)) {
06251             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06252          } else
06253             break;
06254       }
06255    } else {
06256       if (x >= vmu->maxmsg) {
06257          ast_unlock_path(ddir);
06258          return -1;
06259       }
06260    }
06261    make_file(sfn, sizeof(sfn), dir, msg);
06262    make_file(dfn, sizeof(dfn), ddir, x);
06263    if (strcmp(sfn, dfn)) {
06264       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06265    }
06266    ast_unlock_path(ddir);
06267 #endif
06268    return 0;
06269 }
06270 
06271 static int adsi_logo(unsigned char *buf)
06272 {
06273    int bytes = 0;
06274    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06275    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06276    return bytes;
06277 }
06278 
06279 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06280 {
06281    unsigned char buf[256];
06282    int bytes = 0;
06283    int x;
06284    char num[5];
06285 
06286    *useadsi = 0;
06287    bytes += ast_adsi_data_mode(buf + bytes);
06288    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06289 
06290    bytes = 0;
06291    bytes += adsi_logo(buf);
06292    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06293 #ifdef DISPLAY
06294    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06295 #endif
06296    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06297    bytes += ast_adsi_data_mode(buf + bytes);
06298    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06299 
06300    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06301       bytes = 0;
06302       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06303       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06304       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06305       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06306       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06307       return 0;
06308    }
06309 
06310 #ifdef DISPLAY
06311    /* Add a dot */
06312    bytes = 0;
06313    bytes += ast_adsi_logo(buf);
06314    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06315    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
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    bytes = 0;
06320    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06321    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06322    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06323    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06324    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06325    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06326    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06327 
06328 #ifdef DISPLAY
06329    /* Add another dot */
06330    bytes = 0;
06331    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06332    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06333 
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    /* These buttons we load but don't use yet */
06340    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06341    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06342    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06343    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06344    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06345    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06346    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06347 
06348 #ifdef DISPLAY
06349    /* Add another dot */
06350    bytes = 0;
06351    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06352    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06353    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06354 #endif
06355 
06356    bytes = 0;
06357    for (x = 0; x < 5; x++) {
06358       snprintf(num, sizeof(num), "%d", x);
06359       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06360    }
06361    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06362    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06363 
06364 #ifdef DISPLAY
06365    /* Add another dot */
06366    bytes = 0;
06367    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06368    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06369    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06370 #endif
06371 
06372    if (ast_adsi_end_download(chan)) {
06373       bytes = 0;
06374       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06375       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06376       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06377       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06378       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06379       return 0;
06380    }
06381    bytes = 0;
06382    bytes += ast_adsi_download_disconnect(buf + bytes);
06383    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06384    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06385 
06386    ast_debug(1, "Done downloading scripts...\n");
06387 
06388 #ifdef DISPLAY
06389    /* Add last dot */
06390    bytes = 0;
06391    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06392    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06393 #endif
06394    ast_debug(1, "Restarting session...\n");
06395 
06396    bytes = 0;
06397    /* Load the session now */
06398    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06399       *useadsi = 1;
06400       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06401    } else
06402       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06403 
06404    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06405    return 0;
06406 }
06407 
06408 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06409 {
06410    int x;
06411    if (!ast_adsi_available(chan))
06412       return;
06413    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06414    if (x < 0)
06415       return;
06416    if (!x) {
06417       if (adsi_load_vmail(chan, useadsi)) {
06418          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06419          return;
06420       }
06421    } else
06422       *useadsi = 1;
06423 }
06424 
06425 static void adsi_login(struct ast_channel *chan)
06426 {
06427    unsigned char buf[256];
06428    int bytes = 0;
06429    unsigned char keys[8];
06430    int x;
06431    if (!ast_adsi_available(chan))
06432       return;
06433 
06434    for (x = 0; x < 8; x++)
06435       keys[x] = 0;
06436    /* Set one key for next */
06437    keys[3] = ADSI_KEY_APPS + 3;
06438 
06439    bytes += adsi_logo(buf + bytes);
06440    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06441    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06442    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06443    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06444    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06445    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06446    bytes += ast_adsi_set_keys(buf + bytes, keys);
06447    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06448    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06449 }
06450 
06451 static void adsi_password(struct ast_channel *chan)
06452 {
06453    unsigned char buf[256];
06454    int bytes = 0;
06455    unsigned char keys[8];
06456    int x;
06457    if (!ast_adsi_available(chan))
06458       return;
06459 
06460    for (x = 0; x < 8; x++)
06461       keys[x] = 0;
06462    /* Set one key for next */
06463    keys[3] = ADSI_KEY_APPS + 3;
06464 
06465    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06466    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06467    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06468    bytes += ast_adsi_set_keys(buf + bytes, keys);
06469    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06470    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06471 }
06472 
06473 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06474 {
06475    unsigned char buf[256];
06476    int bytes = 0;
06477    unsigned char keys[8];
06478    int x, y;
06479 
06480    if (!ast_adsi_available(chan))
06481       return;
06482 
06483    for (x = 0; x < 5; x++) {
06484       y = ADSI_KEY_APPS + 12 + start + x;
06485       if (y > ADSI_KEY_APPS + 12 + 4)
06486          y = 0;
06487       keys[x] = ADSI_KEY_SKT | y;
06488    }
06489    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06490    keys[6] = 0;
06491    keys[7] = 0;
06492 
06493    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06494    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06495    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06496    bytes += ast_adsi_set_keys(buf + bytes, keys);
06497    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06498 
06499    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06500 }
06501 
06502 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06503 {
06504    int bytes = 0;
06505    unsigned char buf[256]; 
06506    char buf1[256], buf2[256];
06507    char fn2[PATH_MAX];
06508 
06509    char cid[256] = "";
06510    char *val;
06511    char *name, *num;
06512    char datetime[21] = "";
06513    FILE *f;
06514 
06515    unsigned char keys[8];
06516 
06517    int x;
06518 
06519    if (!ast_adsi_available(chan))
06520       return;
06521 
06522    /* Retrieve important info */
06523    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06524    f = fopen(fn2, "r");
06525    if (f) {
06526       while (!feof(f)) {   
06527          if (!fgets((char *) buf, sizeof(buf), f)) {
06528             continue;
06529          }
06530          if (!feof(f)) {
06531             char *stringp = NULL;
06532             stringp = (char *) buf;
06533             strsep(&stringp, "=");
06534             val = strsep(&stringp, "=");
06535             if (!ast_strlen_zero(val)) {
06536                if (!strcmp((char *) buf, "callerid"))
06537                   ast_copy_string(cid, val, sizeof(cid));
06538                if (!strcmp((char *) buf, "origdate"))
06539                   ast_copy_string(datetime, val, sizeof(datetime));
06540             }
06541          }
06542       }
06543       fclose(f);
06544    }
06545    /* New meaning for keys */
06546    for (x = 0; x < 5; x++)
06547       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06548    keys[6] = 0x0;
06549    keys[7] = 0x0;
06550 
06551    if (!vms->curmsg) {
06552       /* No prev key, provide "Folder" instead */
06553       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06554    }
06555    if (vms->curmsg >= vms->lastmsg) {
06556       /* If last message ... */
06557       if (vms->curmsg) {
06558          /* but not only message, provide "Folder" instead */
06559          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06560          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06561 
06562       } else {
06563          /* Otherwise if only message, leave blank */
06564          keys[3] = 1;
06565       }
06566    }
06567 
06568    if (!ast_strlen_zero(cid)) {
06569       ast_callerid_parse(cid, &name, &num);
06570       if (!name)
06571          name = num;
06572    } else
06573       name = "Unknown Caller";
06574 
06575    /* If deleted, show "undeleted" */
06576 #ifdef IMAP_STORAGE
06577    ast_mutex_lock(&vms->lock);
06578 #endif
06579    if (vms->deleted[vms->curmsg]) {
06580       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06581    }
06582 #ifdef IMAP_STORAGE
06583    ast_mutex_unlock(&vms->lock);
06584 #endif
06585 
06586    /* Except "Exit" */
06587    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06588    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06589       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06590    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06591 
06592    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06593    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06594    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06595    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06596    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06597    bytes += ast_adsi_set_keys(buf + bytes, keys);
06598    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06599 
06600    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06601 }
06602 
06603 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06604 {
06605    int bytes = 0;
06606    unsigned char buf[256];
06607    unsigned char keys[8];
06608 
06609    int x;
06610 
06611    if (!ast_adsi_available(chan))
06612       return;
06613 
06614    /* New meaning for keys */
06615    for (x = 0; x < 5; x++)
06616       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06617 
06618    keys[6] = 0x0;
06619    keys[7] = 0x0;
06620 
06621    if (!vms->curmsg) {
06622       /* No prev key, provide "Folder" instead */
06623       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06624    }
06625    if (vms->curmsg >= vms->lastmsg) {
06626       /* If last message ... */
06627       if (vms->curmsg) {
06628          /* but not only message, provide "Folder" instead */
06629          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06630       } else {
06631          /* Otherwise if only message, leave blank */
06632          keys[3] = 1;
06633       }
06634    }
06635 
06636    /* If deleted, show "undeleted" */
06637 #ifdef IMAP_STORAGE
06638    ast_mutex_lock(&vms->lock);
06639 #endif
06640    if (vms->deleted[vms->curmsg]) {
06641       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06642    }
06643 #ifdef IMAP_STORAGE
06644    ast_mutex_unlock(&vms->lock);
06645 #endif
06646 
06647    /* Except "Exit" */
06648    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06649    bytes += ast_adsi_set_keys(buf + bytes, keys);
06650    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06651 
06652    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06653 }
06654 
06655 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06656 {
06657    unsigned char buf[256] = "";
06658    char buf1[256] = "", buf2[256] = "";
06659    int bytes = 0;
06660    unsigned char keys[8];
06661    int x;
06662 
06663    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06664    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06665    if (!ast_adsi_available(chan))
06666       return;
06667    if (vms->newmessages) {
06668       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06669       if (vms->oldmessages) {
06670          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06671          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06672       } else {
06673          snprintf(buf2, sizeof(buf2), "%s.", newm);
06674       }
06675    } else if (vms->oldmessages) {
06676       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06677       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06678    } else {
06679       strcpy(buf1, "You have no messages.");
06680       buf2[0] = ' ';
06681       buf2[1] = '\0';
06682    }
06683    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06684    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06685    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06686 
06687    for (x = 0; x < 6; x++)
06688       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06689    keys[6] = 0;
06690    keys[7] = 0;
06691 
06692    /* Don't let them listen if there are none */
06693    if (vms->lastmsg < 0)
06694       keys[0] = 1;
06695    bytes += ast_adsi_set_keys(buf + bytes, keys);
06696 
06697    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06698 
06699    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06700 }
06701 
06702 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06703 {
06704    unsigned char buf[256] = "";
06705    char buf1[256] = "", buf2[256] = "";
06706    int bytes = 0;
06707    unsigned char keys[8];
06708    int x;
06709 
06710    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06711 
06712    if (!ast_adsi_available(chan))
06713       return;
06714 
06715    /* Original command keys */
06716    for (x = 0; x < 6; x++)
06717       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06718 
06719    keys[6] = 0;
06720    keys[7] = 0;
06721 
06722    if ((vms->lastmsg + 1) < 1)
06723       keys[0] = 0;
06724 
06725    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06726       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06727 
06728    if (vms->lastmsg + 1)
06729       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06730    else
06731       strcpy(buf2, "no messages.");
06732    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06733    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06734    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06735    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06736    bytes += ast_adsi_set_keys(buf + bytes, keys);
06737 
06738    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06739 
06740    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06741    
06742 }
06743 
06744 /*
06745 static void adsi_clear(struct ast_channel *chan)
06746 {
06747    char buf[256];
06748    int bytes=0;
06749    if (!ast_adsi_available(chan))
06750       return;
06751    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06752    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06753 
06754    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06755 }
06756 */
06757 
06758 static void adsi_goodbye(struct ast_channel *chan)
06759 {
06760    unsigned char buf[256];
06761    int bytes = 0;
06762 
06763    if (!ast_adsi_available(chan))
06764       return;
06765    bytes += adsi_logo(buf + bytes);
06766    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06767    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06768    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06769    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06770 
06771    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06772 }
06773 
06774 /*!\brief get_folder: Folder menu
06775  * Plays "press 1 for INBOX messages" etc.
06776  * Should possibly be internationalized
06777  */
06778 static int get_folder(struct ast_channel *chan, int start)
06779 {
06780    int x;
06781    int d;
06782    char fn[PATH_MAX];
06783    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06784    if (d)
06785       return d;
06786    for (x = start; x < 5; x++) { /* For all folders */
06787       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06788          return d;
06789       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06790       if (d)
06791          return d;
06792       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06793 
06794       /* The inbox folder can have its name changed under certain conditions
06795        * so this checks if the sound file exists for the inbox folder name and
06796        * if it doesn't, plays the default name instead. */
06797       if (x == 0) {
06798          if (ast_fileexists(fn, NULL, NULL)) {
06799             d = vm_play_folder_name(chan, fn);
06800          } else {
06801             ast_verb(1, "failed to find %s\n", fn);
06802             d = vm_play_folder_name(chan, "vm-INBOX");
06803          }
06804       } else {
06805          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06806          d = vm_play_folder_name(chan, fn);
06807       }
06808 
06809       if (d)
06810          return d;
06811       d = ast_waitfordigit(chan, 500);
06812       if (d)
06813          return d;
06814    }
06815 
06816    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06817    if (d)
06818       return d;
06819    d = ast_waitfordigit(chan, 4000);
06820    return d;
06821 }
06822 
06823 /*!
06824  * \brief plays a prompt and waits for a keypress.
06825  * \param chan
06826  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06827  * \param start Does not appear to be used at this time.
06828  *
06829  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06830  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06831  * prompting for the number inputs that correspond to the available folders.
06832  * 
06833  * \return zero on success, or -1 on error.
06834  */
06835 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06836 {
06837    int res = 0;
06838    int loops = 0;
06839 
06840    res = ast_play_and_wait(chan, fn);  /* Folder name */
06841    while (((res < '0') || (res > '9')) &&
06842          (res != '#') && (res >= 0) &&
06843          loops < 4) {
06844       res = get_folder(chan, 0);
06845       loops++;
06846    }
06847    if (loops == 4) { /* give up */
06848       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06849       return '#';
06850    }
06851    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06852    return res;
06853 }
06854 
06855 /*!
06856  * \brief presents the option to prepend to an existing message when forwarding it.
06857  * \param chan
06858  * \param vmu
06859  * \param curdir
06860  * \param curmsg
06861  * \param vm_fmts
06862  * \param context
06863  * \param record_gain
06864  * \param duration
06865  * \param vms
06866  * \param flag 
06867  *
06868  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06869  *
06870  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06871  * \return zero on success, -1 on error.
06872  */
06873 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06874          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06875 {
06876    int cmd = 0;
06877    int retries = 0, prepend_duration = 0, already_recorded = 0;
06878    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06879    char textfile[PATH_MAX];
06880    struct ast_config *msg_cfg;
06881    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06882 #ifndef IMAP_STORAGE
06883    signed char zero_gain = 0;
06884 #endif
06885    const char *duration_str;
06886 
06887    /* Must always populate duration correctly */
06888    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06889    strcpy(textfile, msgfile);
06890    strcpy(backup, msgfile);
06891    strcpy(backup_textfile, msgfile);
06892    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06893    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06894    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06895 
06896    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06897       *duration = atoi(duration_str);
06898    } else {
06899       *duration = 0;
06900    }
06901 
06902    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06903       if (cmd)
06904          retries = 0;
06905       switch (cmd) {
06906       case '1': 
06907 
06908 #ifdef IMAP_STORAGE
06909          /* Record new intro file */
06910          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06911          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06912          ast_play_and_wait(chan, INTRO);
06913          ast_play_and_wait(chan, "beep");
06914          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06915          if (cmd == -1) {
06916             break;
06917          }
06918          cmd = 't';
06919 #else
06920 
06921          /* prepend a message to the current message, update the metadata and return */
06922 
06923          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06924          strcpy(textfile, msgfile);
06925          strncat(textfile, ".txt", sizeof(textfile) - 1);
06926          *duration = 0;
06927 
06928          /* if we can't read the message metadata, stop now */
06929          if (!msg_cfg) {
06930             cmd = 0;
06931             break;
06932          }
06933 
06934          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06935 #ifndef IMAP_STORAGE
06936          if (already_recorded) {
06937             ast_filecopy(backup, msgfile, NULL);
06938             copy(backup_textfile, textfile);
06939          }
06940          else {
06941             ast_filecopy(msgfile, backup, NULL);
06942             copy(textfile, backup_textfile);
06943          }
06944 #endif
06945          already_recorded = 1;
06946 
06947          if (record_gain)
06948             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06949 
06950          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06951 
06952          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06953             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06954             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06955             ast_filerename(backup, msgfile, NULL);
06956          }
06957 
06958          if (record_gain)
06959             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06960 
06961          
06962          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06963             *duration = atoi(duration_str);
06964 
06965          if (prepend_duration) {
06966             struct ast_category *msg_cat;
06967             /* need enough space for a maximum-length message duration */
06968             char duration_buf[12];
06969 
06970             *duration += prepend_duration;
06971             msg_cat = ast_category_get(msg_cfg, "message");
06972             snprintf(duration_buf, 11, "%ld", *duration);
06973             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06974                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06975             }
06976          }
06977 
06978 #endif
06979          break;
06980       case '2': 
06981          /* NULL out introfile so we know there is no intro! */
06982 #ifdef IMAP_STORAGE
06983          *vms->introfn = '\0';
06984 #endif
06985          cmd = 't';
06986          break;
06987       case '*':
06988          cmd = '*';
06989          break;
06990       default: 
06991          /* If time_out and return to menu, reset already_recorded */
06992          already_recorded = 0;
06993 
06994          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06995             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06996          if (!cmd) {
06997             cmd = ast_play_and_wait(chan, "vm-starmain");
06998             /* "press star to return to the main menu" */
06999          }
07000          if (!cmd) {
07001             cmd = ast_waitfordigit(chan, 6000);
07002          }
07003          if (!cmd) {
07004             retries++;
07005          }
07006          if (retries > 3) {
07007             cmd = '*'; /* Let's cancel this beast */
07008          }
07009          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07010       }
07011    }
07012 
07013    if (msg_cfg)
07014       ast_config_destroy(msg_cfg);
07015    if (prepend_duration)
07016       *duration = prepend_duration;
07017 
07018    if (already_recorded && cmd == -1) {
07019       /* restore original message if prepention cancelled */
07020       ast_filerename(backup, msgfile, NULL);
07021       rename(backup_textfile, textfile);
07022    }
07023 
07024    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07025       cmd = 0;
07026    return cmd;
07027 }
07028 
07029 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07030 {
07031    struct ast_event *event;
07032    char *mailbox, *context;
07033 
07034    /* Strip off @default */
07035    context = mailbox = ast_strdupa(box);
07036    strsep(&context, "@");
07037    if (ast_strlen_zero(context))
07038       context = "default";
07039 
07040    if (!(event = ast_event_new(AST_EVENT_MWI,
07041          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07042          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07043          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07044          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07045          AST_EVENT_IE_END))) {
07046       return;
07047    }
07048 
07049    ast_event_queue_and_cache(event);
07050 }
07051 
07052 /*!
07053  * \brief Sends email notification that a user has a new voicemail waiting for them.
07054  * \param chan
07055  * \param vmu
07056  * \param vms
07057  * \param msgnum
07058  * \param duration
07059  * \param fmt
07060  * \param cidnum The Caller ID phone number value.
07061  * \param cidname The Caller ID name value.
07062  * \param flag
07063  *
07064  * \return zero on success, -1 on error.
07065  */
07066 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)
07067 {
07068    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07069    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07070    const char *category;
07071    char *myserveremail = serveremail;
07072 
07073    ast_channel_lock(chan);
07074    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07075       category = ast_strdupa(category);
07076    }
07077    ast_channel_unlock(chan);
07078 
07079 #ifndef IMAP_STORAGE
07080    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07081 #else
07082    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07083 #endif
07084    make_file(fn, sizeof(fn), todir, msgnum);
07085    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07086 
07087    if (!ast_strlen_zero(vmu->attachfmt)) {
07088       if (strstr(fmt, vmu->attachfmt))
07089          fmt = vmu->attachfmt;
07090       else
07091          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);
07092    }
07093 
07094    /* Attach only the first format */
07095    fmt = ast_strdupa(fmt);
07096    stringp = fmt;
07097    strsep(&stringp, "|");
07098 
07099    if (!ast_strlen_zero(vmu->serveremail))
07100       myserveremail = vmu->serveremail;
07101 
07102    if (!ast_strlen_zero(vmu->email)) {
07103       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07104 
07105       if (attach_user_voicemail)
07106          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07107 
07108       /* XXX possible imap issue, should category be NULL XXX */
07109       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07110 
07111       if (attach_user_voicemail)
07112          DISPOSE(todir, msgnum);
07113    }
07114 
07115    if (!ast_strlen_zero(vmu->pager)) {
07116       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07117    }
07118 
07119    if (ast_test_flag(vmu, VM_DELETE))
07120       DELETE(todir, msgnum, fn, vmu);
07121 
07122    /* Leave voicemail for someone */
07123    if (ast_app_has_voicemail(ext_context, NULL)) 
07124       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07125 
07126    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07127 
07128    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);
07129    run_externnotify(vmu->context, vmu->mailbox, flag);
07130 
07131 #ifdef IMAP_STORAGE
07132    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07133    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07134       vm_imap_delete(NULL, vms->curmsg, vmu);
07135       vms->newmessages--;  /* Fix new message count */
07136    }
07137 #endif
07138 
07139    return 0;
07140 }
07141 
07142 /*!
07143  * \brief Sends a voicemail message to a mailbox recipient.
07144  * \param chan
07145  * \param context
07146  * \param vms
07147  * \param sender
07148  * \param fmt
07149  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07150  *             Will be 0 when called to forward an existing message (option 8)
07151  *             Will be 1 when called to leave a message (option 3->5)
07152  * \param record_gain 
07153  * \param urgent
07154  *
07155  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07156  * 
07157  * When in the leave message mode (is_new_message == 1):
07158  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07159  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07160  *
07161  * When in the forward message mode (is_new_message == 0):
07162  *   - retreives the current message to be forwarded
07163  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07164  *   - determines the target mailbox and folders
07165  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07166  *
07167  * \return zero on success, -1 on error.
07168  */
07169 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)
07170 {
07171 #ifdef IMAP_STORAGE
07172    int todircount = 0;
07173    struct vm_state *dstvms;
07174 #endif
07175    char username[70]="";
07176    char fn[PATH_MAX]; /* for playback of name greeting */
07177    char ecodes[16] = "#";
07178    int res = 0, cmd = 0;
07179    struct ast_vm_user *receiver = NULL, *vmtmp;
07180    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07181    char *stringp;
07182    const char *s;
07183    int saved_messages = 0;
07184    int valid_extensions = 0;
07185    char *dir;
07186    int curmsg;
07187    char urgent_str[7] = "";
07188    int prompt_played = 0;
07189 #ifndef IMAP_STORAGE
07190    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07191 #endif
07192    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07193       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07194    }
07195 
07196    if (vms == NULL) return -1;
07197    dir = vms->curdir;
07198    curmsg = vms->curmsg;
07199 
07200    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07201    while (!res && !valid_extensions) {
07202       int use_directory = 0;
07203       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07204          int done = 0;
07205          int retries = 0;
07206          cmd = 0;
07207          while ((cmd >= 0) && !done ){
07208             if (cmd)
07209                retries = 0;
07210             switch (cmd) {
07211             case '1': 
07212                use_directory = 0;
07213                done = 1;
07214                break;
07215             case '2': 
07216                use_directory = 1;
07217                done = 1;
07218                break;
07219             case '*': 
07220                cmd = 't';
07221                done = 1;
07222                break;
07223             default: 
07224                /* Press 1 to enter an extension press 2 to use the directory */
07225                cmd = ast_play_and_wait(chan, "vm-forward");
07226                if (!cmd) {
07227                   cmd = ast_waitfordigit(chan, 3000);
07228                }
07229                if (!cmd) {
07230                   retries++;
07231                }
07232                if (retries > 3) {
07233                   cmd = 't';
07234                   done = 1;
07235                }
07236                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07237             }
07238          }
07239          if (cmd < 0 || cmd == 't')
07240             break;
07241       }
07242       
07243       if (use_directory) {
07244          /* use app_directory */
07245          
07246          char old_context[sizeof(chan->context)];
07247          char old_exten[sizeof(chan->exten)];
07248          int old_priority;
07249          struct ast_app* directory_app;
07250 
07251          directory_app = pbx_findapp("Directory");
07252          if (directory_app) {
07253             char vmcontext[256];
07254             /* make backup copies */
07255             memcpy(old_context, chan->context, sizeof(chan->context));
07256             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07257             old_priority = chan->priority;
07258             
07259             /* call the the Directory, changes the channel */
07260             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07261             res = pbx_exec(chan, directory_app, vmcontext);
07262             
07263             ast_copy_string(username, chan->exten, sizeof(username));
07264             
07265             /* restore the old context, exten, and priority */
07266             memcpy(chan->context, old_context, sizeof(chan->context));
07267             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07268             chan->priority = old_priority;
07269          } else {
07270             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07271             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07272          }
07273       } else {
07274          /* Ask for an extension */
07275          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07276          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07277          prompt_played++;
07278          if (res || prompt_played > 4)
07279             break;
07280          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07281             break;
07282       }
07283       
07284       /* start all over if no username */
07285       if (ast_strlen_zero(username))
07286          continue;
07287       stringp = username;
07288       s = strsep(&stringp, "*");
07289       /* start optimistic */
07290       valid_extensions = 1;
07291       while (s) {
07292          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07293             int oldmsgs;
07294             int newmsgs;
07295             int capacity;
07296             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07297                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07298                /* Shouldn't happen, but allow trying another extension if it does */
07299                res = ast_play_and_wait(chan, "pbx-invalid");
07300                valid_extensions = 0;
07301                break;
07302             }
07303             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07304             if ((newmsgs + oldmsgs) >= capacity) {
07305                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07306                res = ast_play_and_wait(chan, "vm-mailboxfull");
07307                valid_extensions = 0;
07308                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07309                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07310                   free_user(vmtmp);
07311                }
07312                inprocess_count(receiver->mailbox, receiver->context, -1);
07313                break;
07314             }
07315             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07316          } else {
07317             /* XXX Optimization for the future.  When we encounter a single bad extension,
07318              * bailing out on all of the extensions may not be the way to go.  We should
07319              * probably just bail on that single extension, then allow the user to enter
07320              * several more. XXX
07321              */
07322             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07323                free_user(receiver);
07324             }
07325             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07326             /* "I am sorry, that's not a valid extension.  Please try again." */
07327             res = ast_play_and_wait(chan, "pbx-invalid");
07328             valid_extensions = 0;
07329             break;
07330          }
07331 
07332          /* play name if available, else play extension number */
07333          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07334          RETRIEVE(fn, -1, s, receiver->context);
07335          if (ast_fileexists(fn, NULL, NULL) > 0) {
07336             res = ast_stream_and_wait(chan, fn, ecodes);
07337             if (res) {
07338                DISPOSE(fn, -1);
07339                return res;
07340             }
07341          } else {
07342             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07343          }
07344          DISPOSE(fn, -1);
07345 
07346          s = strsep(&stringp, "*");
07347       }
07348       /* break from the loop of reading the extensions */
07349       if (valid_extensions)
07350          break;
07351    }
07352    /* check if we're clear to proceed */
07353    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07354       return res;
07355    if (is_new_message == 1) {
07356       struct leave_vm_options leave_options;
07357       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07358       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07359 
07360       /* Send VoiceMail */
07361       memset(&leave_options, 0, sizeof(leave_options));
07362       leave_options.record_gain = record_gain;
07363       cmd = leave_voicemail(chan, mailbox, &leave_options);
07364    } else {
07365       /* Forward VoiceMail */
07366       long duration = 0;
07367       struct vm_state vmstmp;
07368       int copy_msg_result = 0;
07369       memcpy(&vmstmp, vms, sizeof(vmstmp));
07370 
07371       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07372 
07373       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07374       if (!cmd) {
07375          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07376 #ifdef IMAP_STORAGE
07377             int attach_user_voicemail;
07378             char *myserveremail = serveremail;
07379             
07380             /* get destination mailbox */
07381             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07382             if (!dstvms) {
07383                dstvms = create_vm_state_from_user(vmtmp);
07384             }
07385             if (dstvms) {
07386                init_mailstream(dstvms, 0);
07387                if (!dstvms->mailstream) {
07388                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07389                } else {
07390                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07391                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07392                }
07393             } else {
07394                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07395             }
07396             if (!ast_strlen_zero(vmtmp->serveremail))
07397                myserveremail = vmtmp->serveremail;
07398             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07399             /* NULL category for IMAP storage */
07400             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07401                dstvms->curbox,
07402                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07403                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07404                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07405                NULL, urgent_str);
07406 #else
07407             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07408 #endif
07409             saved_messages++;
07410             AST_LIST_REMOVE_CURRENT(list);
07411             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07412             free_user(vmtmp);
07413             if (res)
07414                break;
07415          }
07416          AST_LIST_TRAVERSE_SAFE_END;
07417          if (saved_messages > 0 && !copy_msg_result) {
07418             /* give confirmation that the message was saved */
07419             /* commented out since we can't forward batches yet
07420             if (saved_messages == 1)
07421                res = ast_play_and_wait(chan, "vm-message");
07422             else
07423                res = ast_play_and_wait(chan, "vm-messages");
07424             if (!res)
07425                res = ast_play_and_wait(chan, "vm-saved"); */
07426 #ifdef IMAP_STORAGE
07427             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07428             if (ast_strlen_zero(vmstmp.introfn))
07429 #endif
07430             res = ast_play_and_wait(chan, "vm-msgsaved");
07431          }
07432 #ifndef IMAP_STORAGE
07433          else {
07434             /* with IMAP, mailbox full warning played by imap_check_limits */
07435             res = ast_play_and_wait(chan, "vm-mailboxfull");
07436          }
07437          /* Restore original message without prepended message if backup exists */
07438          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07439          strcpy(textfile, msgfile);
07440          strcpy(backup, msgfile);
07441          strcpy(backup_textfile, msgfile);
07442          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07443          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07444          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07445          if (ast_fileexists(backup, NULL, NULL) > 0) {
07446             ast_filerename(backup, msgfile, NULL);
07447             rename(backup_textfile, textfile);
07448          }
07449 #endif
07450       }
07451       DISPOSE(dir, curmsg);
07452 #ifndef IMAP_STORAGE
07453       if (cmd) { /* assuming hangup, cleanup backup file */
07454          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07455          strcpy(textfile, msgfile);
07456          strcpy(backup_textfile, msgfile);
07457          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07458          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07459          rename(backup_textfile, textfile);
07460       }
07461 #endif
07462    }
07463 
07464    /* If anything failed above, we still have this list to free */
07465    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07466       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07467       free_user(vmtmp);
07468    }
07469    return res ? res : cmd;
07470 }
07471 
07472 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07473 {
07474    int res;
07475    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07476       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07477    return res;
07478 }
07479 
07480 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07481 {
07482    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07483    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);
07484 }
07485 
07486 static int play_message_category(struct ast_channel *chan, const char *category)
07487 {
07488    int res = 0;
07489 
07490    if (!ast_strlen_zero(category))
07491       res = ast_play_and_wait(chan, category);
07492 
07493    if (res) {
07494       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07495       res = 0;
07496    }
07497 
07498    return res;
07499 }
07500 
07501 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07502 {
07503    int res = 0;
07504    struct vm_zone *the_zone = NULL;
07505    time_t t;
07506 
07507    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07508       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07509       return 0;
07510    }
07511 
07512    /* Does this user have a timezone specified? */
07513    if (!ast_strlen_zero(vmu->zonetag)) {
07514       /* Find the zone in the list */
07515       struct vm_zone *z;
07516       AST_LIST_LOCK(&zones);
07517       AST_LIST_TRAVERSE(&zones, z, list) {
07518          if (!strcmp(z->name, vmu->zonetag)) {
07519             the_zone = z;
07520             break;
07521          }
07522       }
07523       AST_LIST_UNLOCK(&zones);
07524    }
07525 
07526 /* No internal variable parsing for now, so we'll comment it out for the time being */
07527 #if 0
07528    /* Set the DIFF_* variables */
07529    ast_localtime(&t, &time_now, NULL);
07530    tv_now = ast_tvnow();
07531    ast_localtime(&tv_now, &time_then, NULL);
07532 
07533    /* Day difference */
07534    if (time_now.tm_year == time_then.tm_year)
07535       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07536    else
07537       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07538    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07539 
07540    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07541 #endif
07542    if (the_zone) {
07543       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07544    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07545       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07546    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07547       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07548    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07549       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);
07550    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07551       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07552    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07553       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07554    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07555       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07556    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07557       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);
07558    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07559       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07560    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07561       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07562    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07563       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);
07564    } else {
07565       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07566    }
07567 #if 0
07568    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07569 #endif
07570    return res;
07571 }
07572 
07573 
07574 
07575 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07576 {
07577    int res = 0;
07578    int i;
07579    char *callerid, *name;
07580    char prefile[PATH_MAX] = "";
07581    
07582 
07583    /* If voicemail cid is not enabled, or we didn't get cid or context from
07584     * the attribute file, leave now.
07585     *
07586     * TODO Still need to change this so that if this function is called by the
07587     * message envelope (and someone is explicitly requesting to hear the CID),
07588     * it does not check to see if CID is enabled in the config file.
07589     */
07590    if ((cid == NULL)||(context == NULL))
07591       return res;
07592 
07593    /* Strip off caller ID number from name */
07594    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07595    ast_callerid_parse(cid, &name, &callerid);
07596    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07597       /* Check for internal contexts and only */
07598       /* say extension when the call didn't come from an internal context in the list */
07599       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07600          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07601          if ((strcmp(cidinternalcontexts[i], context) == 0))
07602             break;
07603       }
07604       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07605          if (!res) {
07606             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07607             if (!ast_strlen_zero(prefile)) {
07608             /* See if we can find a recorded name for this person instead of their extension number */
07609                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07610                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07611                   if (!callback)
07612                      res = wait_file2(chan, vms, "vm-from");
07613                   res = ast_stream_and_wait(chan, prefile, "");
07614                } else {
07615                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07616                   /* Say "from extension" as one saying to sound smoother */
07617                   if (!callback)
07618                      res = wait_file2(chan, vms, "vm-from-extension");
07619                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07620                }
07621             }
07622          }
07623       } else if (!res) {
07624          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07625          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07626          if (!callback)
07627             res = wait_file2(chan, vms, "vm-from-phonenumber");
07628          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07629       }
07630    } else {
07631       /* Number unknown */
07632       ast_debug(1, "VM-CID: From an unknown number\n");
07633       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07634       res = wait_file2(chan, vms, "vm-unknown-caller");
07635    }
07636    return res;
07637 }
07638 
07639 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07640 {
07641    int res = 0;
07642    int durationm;
07643    int durations;
07644    /* Verify that we have a duration for the message */
07645    if (duration == NULL)
07646       return res;
07647 
07648    /* Convert from seconds to minutes */
07649    durations = atoi(duration);
07650    durationm = (durations / 60);
07651 
07652    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07653 
07654    if ((!res) && (durationm >= minduration)) {
07655       res = wait_file2(chan, vms, "vm-duration");
07656 
07657       /* POLISH syntax */
07658       if (!strncasecmp(chan->language, "pl", 2)) {
07659          div_t num = div(durationm, 10);
07660 
07661          if (durationm == 1) {
07662             res = ast_play_and_wait(chan, "digits/1z");
07663             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07664          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07665             if (num.rem == 2) {
07666                if (!num.quot) {
07667                   res = ast_play_and_wait(chan, "digits/2-ie");
07668                } else {
07669                   res = say_and_wait(chan, durationm - 2 , chan->language);
07670                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07671                }
07672             } else {
07673                res = say_and_wait(chan, durationm, chan->language);
07674             }
07675             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07676          } else {
07677             res = say_and_wait(chan, durationm, chan->language);
07678             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07679          }
07680       /* DEFAULT syntax */
07681       } else {
07682          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07683          res = wait_file2(chan, vms, "vm-minutes");
07684       }
07685    }
07686    return res;
07687 }
07688 
07689 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07690 {
07691    int res = 0;
07692    char filename[256], *cid;
07693    const char *origtime, *context, *category, *duration, *flag;
07694    struct ast_config *msg_cfg;
07695    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07696 
07697    vms->starting = 0;
07698    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07699    adsi_message(chan, vms);
07700    if (!vms->curmsg) {
07701       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07702    } else if (vms->curmsg == vms->lastmsg) {
07703       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07704    }
07705 
07706    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07707    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07708    msg_cfg = ast_config_load(filename, config_flags);
07709    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07710       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07711       return 0;
07712    }
07713    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07714 
07715    /* Play the word urgent if we are listening to urgent messages */
07716    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07717       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07718    }
07719 
07720    if (!res) {
07721       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07722       /* POLISH syntax */
07723       if (!strncasecmp(chan->language, "pl", 2)) {
07724          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07725             int ten, one;
07726             char nextmsg[256];
07727             ten = (vms->curmsg + 1) / 10;
07728             one = (vms->curmsg + 1) % 10;
07729 
07730             if (vms->curmsg < 20) {
07731                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07732                res = wait_file2(chan, vms, nextmsg);
07733             } else {
07734                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07735                res = wait_file2(chan, vms, nextmsg);
07736                if (one > 0) {
07737                   if (!res) {
07738                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07739                      res = wait_file2(chan, vms, nextmsg);
07740                   }
07741                }
07742             }
07743          }
07744          if (!res)
07745             res = wait_file2(chan, vms, "vm-message");
07746       /* HEBREW syntax */
07747       } else if (!strncasecmp(chan->language, "he", 2)) {
07748          if (!vms->curmsg) {
07749             res = wait_file2(chan, vms, "vm-message");
07750             res = wait_file2(chan, vms, "vm-first");
07751          } else if (vms->curmsg == vms->lastmsg) {
07752             res = wait_file2(chan, vms, "vm-message");
07753             res = wait_file2(chan, vms, "vm-last");
07754          } else {
07755             res = wait_file2(chan, vms, "vm-message");
07756             res = wait_file2(chan, vms, "vm-number");
07757             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07758          }
07759       /* VIETNAMESE syntax */
07760       } else if (!strncasecmp(chan->language, "vi", 2)) {
07761          if (!vms->curmsg) {
07762             res = wait_file2(chan, vms, "vm-message");
07763             res = wait_file2(chan, vms, "vm-first");
07764          } else if (vms->curmsg == vms->lastmsg) {
07765             res = wait_file2(chan, vms, "vm-message");
07766             res = wait_file2(chan, vms, "vm-last");
07767          } else {
07768             res = wait_file2(chan, vms, "vm-message");
07769             res = wait_file2(chan, vms, "vm-number");
07770             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07771          }
07772       } else {
07773          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07774             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07775          } else { /* DEFAULT syntax */
07776             res = wait_file2(chan, vms, "vm-message");
07777          }
07778          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07779             if (!res) {
07780                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07781                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07782             }
07783          }
07784       }
07785    }
07786 
07787    if (!msg_cfg) {
07788       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07789       return 0;
07790    }
07791 
07792    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07793       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07794       DISPOSE(vms->curdir, vms->curmsg);
07795       ast_config_destroy(msg_cfg);
07796       return 0;
07797    }
07798 
07799    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07800    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07801    category = ast_variable_retrieve(msg_cfg, "message", "category");
07802 
07803    context = ast_variable_retrieve(msg_cfg, "message", "context");
07804    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07805       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07806    if (!res) {
07807       res = play_message_category(chan, category);
07808    }
07809    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07810       res = play_message_datetime(chan, vmu, origtime, filename);
07811    }
07812    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07813       res = play_message_callerid(chan, vms, cid, context, 0);
07814    }
07815    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07816       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07817    }
07818    /* Allow pressing '1' to skip envelope / callerid */
07819    if (res == '1') {
07820       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07821       res = 0;
07822    }
07823    ast_config_destroy(msg_cfg);
07824 
07825    if (!res) {
07826       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07827 #ifdef IMAP_STORAGE
07828       ast_mutex_lock(&vms->lock);
07829 #endif
07830       vms->heard[vms->curmsg] = 1;
07831 #ifdef IMAP_STORAGE
07832       ast_mutex_unlock(&vms->lock);
07833       /*IMAP storage stores any prepended message from a forward
07834        * as a separate file from the rest of the message
07835        */
07836       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07837          wait_file(chan, vms, vms->introfn);
07838       }
07839 #endif
07840       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07841          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07842          res = 0;
07843       }
07844       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07845    }
07846    DISPOSE(vms->curdir, vms->curmsg);
07847    return res;
07848 }
07849 
07850 #ifdef IMAP_STORAGE
07851 static int imap_remove_file(char *dir, int msgnum)
07852 {
07853    char fn[PATH_MAX];
07854    char full_fn[PATH_MAX];
07855    char intro[PATH_MAX] = {0,};
07856    
07857    if (msgnum > -1) {
07858       make_file(fn, sizeof(fn), dir, msgnum);
07859       snprintf(intro, sizeof(intro), "%sintro", fn);
07860    } else
07861       ast_copy_string(fn, dir, sizeof(fn));
07862    
07863    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07864       ast_filedelete(fn, NULL);
07865       if (!ast_strlen_zero(intro)) {
07866          ast_filedelete(intro, NULL);
07867       }
07868       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07869       unlink(full_fn);
07870    }
07871    return 0;
07872 }
07873 
07874 
07875 
07876 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07877 {
07878    char *file, *filename;
07879    char *attachment;
07880    char arg[10];
07881    int i;
07882    BODY* body;
07883 
07884    file = strrchr(ast_strdupa(dir), '/');
07885    if (file) {
07886       *file++ = '\0';
07887    } else {
07888       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07889       return -1;
07890    }
07891 
07892    ast_mutex_lock(&vms->lock);
07893    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07894       mail_fetchstructure(vms->mailstream, i + 1, &body);
07895       /* We have the body, now we extract the file name of the first attachment. */
07896       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07897          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07898       } else {
07899          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07900          ast_mutex_unlock(&vms->lock);
07901          return -1;
07902       }
07903       filename = strsep(&attachment, ".");
07904       if (!strcmp(filename, file)) {
07905          sprintf(arg, "%d", i + 1);
07906          mail_setflag(vms->mailstream, arg, "\\DELETED");
07907       }
07908    }
07909    mail_expunge(vms->mailstream);
07910    ast_mutex_unlock(&vms->lock);
07911    return 0;
07912 }
07913 
07914 #elif !defined(IMAP_STORAGE)
07915 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07916 {
07917    int count_msg, last_msg;
07918 
07919    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07920 
07921    /* Rename the member vmbox HERE so that we don't try to return before
07922     * we know what's going on.
07923     */
07924    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07925 
07926    /* Faster to make the directory than to check if it exists. */
07927    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07928 
07929    /* traverses directory using readdir (or select query for ODBC) */
07930    count_msg = count_messages(vmu, vms->curdir);
07931    if (count_msg < 0) {
07932       return count_msg;
07933    } else {
07934       vms->lastmsg = count_msg - 1;
07935    }
07936 
07937    if (vm_allocate_dh(vms, vmu, count_msg)) {
07938       return -1;
07939    }
07940 
07941    /*
07942    The following test is needed in case sequencing gets messed up.
07943    There appears to be more than one way to mess up sequence, so
07944    we will not try to find all of the root causes--just fix it when
07945    detected.
07946    */
07947 
07948    if (vm_lock_path(vms->curdir)) {
07949       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07950       return ERROR_LOCK_PATH;
07951    }
07952 
07953    /* for local storage, checks directory for messages up to maxmsg limit */
07954    last_msg = last_message_index(vmu, vms->curdir);
07955    ast_unlock_path(vms->curdir);
07956 
07957    if (last_msg < -1) {
07958       return last_msg;
07959    } else if (vms->lastmsg != last_msg) {
07960       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);
07961       resequence_mailbox(vmu, vms->curdir, count_msg);
07962    }
07963 
07964    return 0;
07965 }
07966 #endif
07967 
07968 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07969 {
07970    int x = 0;
07971    int last_msg_idx = 0;
07972 
07973 #ifndef IMAP_STORAGE
07974    int res = 0, nummsg;
07975    char fn2[PATH_MAX];
07976 #endif
07977 
07978    if (vms->lastmsg <= -1) {
07979       goto done;
07980    }
07981 
07982    vms->curmsg = -1;
07983 #ifndef IMAP_STORAGE
07984    /* Get the deleted messages fixed */
07985    if (vm_lock_path(vms->curdir)) {
07986       return ERROR_LOCK_PATH;
07987    }
07988 
07989    /* update count as message may have arrived while we've got mailbox open */
07990    last_msg_idx = last_message_index(vmu, vms->curdir);
07991    if (last_msg_idx != vms->lastmsg) {
07992       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07993    }
07994 
07995    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07996    for (x = 0; x < last_msg_idx + 1; x++) {
07997       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07998          /* Save this message.  It's not in INBOX or hasn't been heard */
07999          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08000          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
08001             break;
08002          }
08003          vms->curmsg++;
08004          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
08005          if (strcmp(vms->fn, fn2)) {
08006             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
08007          }
08008       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
08009          /* Move to old folder before deleting */
08010          res = save_to_folder(vmu, vms, x, 1);
08011          if (res == ERROR_LOCK_PATH) {
08012             /* If save failed do not delete the message */
08013             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
08014             vms->deleted[x] = 0;
08015             vms->heard[x] = 0;
08016             --x;
08017          }
08018       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08019          /* Move to deleted folder */
08020          res = save_to_folder(vmu, vms, x, 10);
08021          if (res == ERROR_LOCK_PATH) {
08022             /* If save failed do not delete the message */
08023             vms->deleted[x] = 0;
08024             vms->heard[x] = 0;
08025             --x;
08026          }
08027       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08028          /* If realtime storage enabled - we should explicitly delete this message,
08029          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08030          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08031          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08032             DELETE(vms->curdir, x, vms->fn, vmu);
08033          }
08034       }
08035    }
08036 
08037    /* Delete ALL remaining messages */
08038    nummsg = x - 1;
08039    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08040       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08041       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08042          DELETE(vms->curdir, x, vms->fn, vmu);
08043       }
08044    }
08045    ast_unlock_path(vms->curdir);
08046 #else /* defined(IMAP_STORAGE) */
08047    ast_mutex_lock(&vms->lock);
08048    if (vms->deleted) {
08049       /* Since we now expunge after each delete, deleting in reverse order
08050        * ensures that no reordering occurs between each step. */
08051       last_msg_idx = vms->dh_arraysize;
08052       for (x = last_msg_idx - 1; x >= 0; x--) {
08053          if (vms->deleted[x]) {
08054             ast_debug(3, "IMAP delete of %d\n", x);
08055             DELETE(vms->curdir, x, vms->fn, vmu);
08056          }
08057       }
08058    }
08059 #endif
08060 
08061 done:
08062    if (vms->deleted) {
08063       ast_free(vms->deleted);
08064       vms->deleted = NULL;
08065    }
08066    if (vms->heard) {
08067       ast_free(vms->heard);
08068       vms->heard = NULL;
08069    }
08070    vms->dh_arraysize = 0;
08071 #ifdef IMAP_STORAGE
08072    ast_mutex_unlock(&vms->lock);
08073 #endif
08074 
08075    return 0;
08076 }
08077 
08078 /* In Greek even though we CAN use a syntax like "friends messages"
08079  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08080  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08081  * syntax for the above three categories which is more elegant.
08082  */
08083 
08084 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08085 {
08086    int cmd;
08087    char *buf;
08088 
08089    buf = ast_alloca(strlen(box) + 2);
08090    strcpy(buf, box);
08091    strcat(buf, "s");
08092 
08093    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08094       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08095       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08096    } else {
08097       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08098       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08099    }
08100 }
08101 
08102 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08103 {
08104    int cmd;
08105 
08106    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08107       if (!strcasecmp(box, "vm-INBOX"))
08108          cmd = ast_play_and_wait(chan, "vm-new-e");
08109       else
08110          cmd = ast_play_and_wait(chan, "vm-old-e");
08111       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08112    } else {
08113       cmd = ast_play_and_wait(chan, "vm-messages");
08114       return cmd ? cmd : ast_play_and_wait(chan, box);
08115    }
08116 }
08117 
08118 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08119 {
08120    int cmd;
08121 
08122    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08123       cmd = ast_play_and_wait(chan, "vm-messages");
08124       return cmd ? cmd : ast_play_and_wait(chan, box);
08125    } else {
08126       cmd = ast_play_and_wait(chan, box);
08127       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08128    }
08129 }
08130 
08131 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08132 {
08133    int cmd;
08134 
08135    if (  !strncasecmp(chan->language, "it", 2) ||
08136         !strncasecmp(chan->language, "es", 2) ||
08137         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08138       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08139       return cmd ? cmd : ast_play_and_wait(chan, box);
08140    } else if (!strncasecmp(chan->language, "gr", 2)) {
08141       return vm_play_folder_name_gr(chan, box);
08142    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08143       return ast_play_and_wait(chan, box);
08144    } else if (!strncasecmp(chan->language, "pl", 2)) {
08145       return vm_play_folder_name_pl(chan, box);
08146    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08147       return vm_play_folder_name_ua(chan, box);
08148    } else if (!strncasecmp(chan->language, "vi", 2)) {
08149       return ast_play_and_wait(chan, box);
08150    } else {  /* Default English */
08151       cmd = ast_play_and_wait(chan, box);
08152       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08153    }
08154 }
08155 
08156 /* GREEK SYNTAX
08157    In greek the plural for old/new is
08158    different so we need the following files
08159    We also need vm-denExeteMynhmata because
08160    this syntax is different.
08161 
08162    -> vm-Olds.wav : "Palia"
08163    -> vm-INBOXs.wav : "Nea"
08164    -> vm-denExeteMynhmata : "den exete mynhmata"
08165 */
08166 
08167 
08168 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08169 {
08170    int res = 0;
08171 
08172    if (vms->newmessages) {
08173       res = ast_play_and_wait(chan, "vm-youhave");
08174       if (!res) 
08175          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08176       if (!res) {
08177          if ((vms->newmessages == 1)) {
08178             res = ast_play_and_wait(chan, "vm-INBOX");
08179             if (!res)
08180                res = ast_play_and_wait(chan, "vm-message");
08181          } else {
08182             res = ast_play_and_wait(chan, "vm-INBOXs");
08183             if (!res)
08184                res = ast_play_and_wait(chan, "vm-messages");
08185          }
08186       }
08187    } else if (vms->oldmessages){
08188       res = ast_play_and_wait(chan, "vm-youhave");
08189       if (!res)
08190          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08191       if ((vms->oldmessages == 1)){
08192          res = ast_play_and_wait(chan, "vm-Old");
08193          if (!res)
08194             res = ast_play_and_wait(chan, "vm-message");
08195       } else {
08196          res = ast_play_and_wait(chan, "vm-Olds");
08197          if (!res)
08198             res = ast_play_and_wait(chan, "vm-messages");
08199       }
08200    } else if (!vms->oldmessages && !vms->newmessages) 
08201       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08202    return res;
08203 }
08204 
08205 /* Version of vm_intro() designed to work for many languages.
08206  *
08207  * It is hoped that this function can prevent the proliferation of 
08208  * language-specific vm_intro() functions and in time replace the language-
08209  * specific functions which already exist.  An examination of the language-
08210  * specific functions revealed that they all corrected the same deficiencies
08211  * in vm_intro_en() (which was the default function). Namely:
08212  *
08213  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08214  *     wording of the voicemail greeting hides this problem.  For example,
08215  *     vm-INBOX contains only the word "new".  This means that both of these
08216  *     sequences produce valid utterances:
08217  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08218  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08219  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08220  *     in many languages) the first utterance becomes "you have 1 the new message".
08221  *  2) The function contains hardcoded rules for pluralizing the word "message".
08222  *     These rules are correct for English, but not for many other languages.
08223  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08224  *     required in many languages.
08225  *  4) The gender of the word for "message" is not specified. This is a problem
08226  *     because in many languages the gender of the number in phrases such
08227  *     as "you have one new message" must match the gender of the word
08228  *     meaning "message".
08229  *
08230  * Fixing these problems for each new language has meant duplication of effort.
08231  * This new function solves the problems in the following general ways:
08232  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08233  *     and vm-Old respectively for those languages where it makes sense.
08234  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08235  *     on vm-message.
08236  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08237  *     prefix on vm-new and vm-old (none for English).
08238  *  4) Pass the gender of the language's word for "message" as an agument to
08239  *     this function which is can in turn pass on to the functions which 
08240  *     say numbers and put endings on nounds and adjectives.
08241  *
08242  * All languages require these messages:
08243  *  vm-youhave    "You have..."
08244  *  vm-and     "and"
08245  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08246  *
08247  * To use it for English, you will need these additional sound files:
08248  *  vm-new     "new"
08249  *  vm-message    "message", singular
08250  *  vm-messages      "messages", plural
08251  *
08252  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08253  *
08254  *  vm-newn    "novoye" (singular, neuter)
08255  *  vm-newx    "novikh" (counting plural form, genative plural)
08256  *  vm-message    "sobsheniye" (singular form)
08257  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08258  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08259  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08260  *  digits/2n     "dva" (neuter singular)
08261  */
08262 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08263 {
08264    int res;
08265    int lastnum = 0;
08266 
08267    res = ast_play_and_wait(chan, "vm-youhave");
08268 
08269    if (!res && vms->newmessages) {
08270       lastnum = vms->newmessages;
08271 
08272       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08273          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08274       }
08275 
08276       if (!res && vms->oldmessages) {
08277          res = ast_play_and_wait(chan, "vm-and");
08278       }
08279    }
08280 
08281    if (!res && vms->oldmessages) {
08282       lastnum = vms->oldmessages;
08283 
08284       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08285          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08286       }
08287    }
08288 
08289    if (!res) {
08290       if (lastnum == 0) {
08291          res = ast_play_and_wait(chan, "vm-no");
08292       }
08293       if (!res) {
08294          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08295       }
08296    }
08297 
08298    return res;
08299 }
08300 
08301 /* Default Hebrew syntax */
08302 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08303 {
08304    int res = 0;
08305 
08306    /* Introduce messages they have */
08307    if (!res) {
08308       if ((vms->newmessages) || (vms->oldmessages)) {
08309          res = ast_play_and_wait(chan, "vm-youhave");
08310       }
08311       /*
08312        * The word "shtei" refers to the number 2 in hebrew when performing a count
08313        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08314        * an element, this is one of them.
08315        */
08316       if (vms->newmessages) {
08317          if (!res) {
08318             if (vms->newmessages == 1) {
08319                res = ast_play_and_wait(chan, "vm-INBOX1");
08320             } else {
08321                if (vms->newmessages == 2) {
08322                   res = ast_play_and_wait(chan, "vm-shtei");
08323                } else {
08324                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08325                }
08326                res = ast_play_and_wait(chan, "vm-INBOX");
08327             }
08328          }
08329          if (vms->oldmessages && !res) {
08330             res = ast_play_and_wait(chan, "vm-and");
08331             if (vms->oldmessages == 1) {
08332                res = ast_play_and_wait(chan, "vm-Old1");
08333             } else {
08334                if (vms->oldmessages == 2) {
08335                   res = ast_play_and_wait(chan, "vm-shtei");
08336                } else {
08337                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08338                }
08339                res = ast_play_and_wait(chan, "vm-Old");
08340             }
08341          }
08342       }
08343       if (!res && vms->oldmessages && !vms->newmessages) {
08344          if (!res) {
08345             if (vms->oldmessages == 1) {
08346                res = ast_play_and_wait(chan, "vm-Old1");
08347             } else {
08348                if (vms->oldmessages == 2) {
08349                   res = ast_play_and_wait(chan, "vm-shtei");
08350                } else {
08351                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08352                }
08353                res = ast_play_and_wait(chan, "vm-Old");
08354             }
08355          }
08356       }
08357       if (!res) {
08358          if (!vms->oldmessages && !vms->newmessages) {
08359             if (!res) {
08360                res = ast_play_and_wait(chan, "vm-nomessages");
08361             }
08362          }
08363       }
08364    }
08365    return res;
08366 }
08367    
08368 /* Default English syntax */
08369 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08370 {
08371    int res;
08372 
08373    /* Introduce messages they have */
08374    res = ast_play_and_wait(chan, "vm-youhave");
08375    if (!res) {
08376       if (vms->urgentmessages) {
08377          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08378          if (!res)
08379             res = ast_play_and_wait(chan, "vm-Urgent");
08380          if ((vms->oldmessages || vms->newmessages) && !res) {
08381             res = ast_play_and_wait(chan, "vm-and");
08382          } else if (!res) {
08383             if ((vms->urgentmessages == 1))
08384                res = ast_play_and_wait(chan, "vm-message");
08385             else
08386                res = ast_play_and_wait(chan, "vm-messages");
08387          }
08388       }
08389       if (vms->newmessages) {
08390          res = say_and_wait(chan, vms->newmessages, chan->language);
08391          if (!res)
08392             res = ast_play_and_wait(chan, "vm-INBOX");
08393          if (vms->oldmessages && !res)
08394             res = ast_play_and_wait(chan, "vm-and");
08395          else if (!res) {
08396             if ((vms->newmessages == 1))
08397                res = ast_play_and_wait(chan, "vm-message");
08398             else
08399                res = ast_play_and_wait(chan, "vm-messages");
08400          }
08401             
08402       }
08403       if (!res && vms->oldmessages) {
08404          res = say_and_wait(chan, vms->oldmessages, chan->language);
08405          if (!res)
08406             res = ast_play_and_wait(chan, "vm-Old");
08407          if (!res) {
08408             if (vms->oldmessages == 1)
08409                res = ast_play_and_wait(chan, "vm-message");
08410             else
08411                res = ast_play_and_wait(chan, "vm-messages");
08412          }
08413       }
08414       if (!res) {
08415          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08416             res = ast_play_and_wait(chan, "vm-no");
08417             if (!res)
08418                res = ast_play_and_wait(chan, "vm-messages");
08419          }
08420       }
08421    }
08422    return res;
08423 }
08424 
08425 /* ITALIAN syntax */
08426 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08427 {
08428    /* Introduce messages they have */
08429    int res;
08430    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08431       res = ast_play_and_wait(chan, "vm-no") ||
08432          ast_play_and_wait(chan, "vm-message");
08433    else
08434       res = ast_play_and_wait(chan, "vm-youhave");
08435    if (!res && vms->newmessages) {
08436       res = (vms->newmessages == 1) ?
08437          ast_play_and_wait(chan, "digits/un") ||
08438          ast_play_and_wait(chan, "vm-nuovo") ||
08439          ast_play_and_wait(chan, "vm-message") :
08440          /* 2 or more new messages */
08441          say_and_wait(chan, vms->newmessages, chan->language) ||
08442          ast_play_and_wait(chan, "vm-nuovi") ||
08443          ast_play_and_wait(chan, "vm-messages");
08444       if (!res && vms->oldmessages)
08445          res = ast_play_and_wait(chan, "vm-and");
08446    }
08447    if (!res && vms->oldmessages) {
08448       res = (vms->oldmessages == 1) ?
08449          ast_play_and_wait(chan, "digits/un") ||
08450          ast_play_and_wait(chan, "vm-vecchio") ||
08451          ast_play_and_wait(chan, "vm-message") :
08452          /* 2 or more old messages */
08453          say_and_wait(chan, vms->oldmessages, chan->language) ||
08454          ast_play_and_wait(chan, "vm-vecchi") ||
08455          ast_play_and_wait(chan, "vm-messages");
08456    }
08457    return res;
08458 }
08459 
08460 /* POLISH syntax */
08461 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08462 {
08463    /* Introduce messages they have */
08464    int res;
08465    div_t num;
08466 
08467    if (!vms->oldmessages && !vms->newmessages) {
08468       res = ast_play_and_wait(chan, "vm-no");
08469       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08470       return res;
08471    } else {
08472       res = ast_play_and_wait(chan, "vm-youhave");
08473    }
08474 
08475    if (vms->newmessages) {
08476       num = div(vms->newmessages, 10);
08477       if (vms->newmessages == 1) {
08478          res = ast_play_and_wait(chan, "digits/1-a");
08479          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08480          res = res ? res : ast_play_and_wait(chan, "vm-message");
08481       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08482          if (num.rem == 2) {
08483             if (!num.quot) {
08484                res = ast_play_and_wait(chan, "digits/2-ie");
08485             } else {
08486                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08487                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08488             }
08489          } else {
08490             res = say_and_wait(chan, vms->newmessages, chan->language);
08491          }
08492          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08493          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08494       } else {
08495          res = say_and_wait(chan, vms->newmessages, chan->language);
08496          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08497          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08498       }
08499       if (!res && vms->oldmessages)
08500          res = ast_play_and_wait(chan, "vm-and");
08501    }
08502    if (!res && vms->oldmessages) {
08503       num = div(vms->oldmessages, 10);
08504       if (vms->oldmessages == 1) {
08505          res = ast_play_and_wait(chan, "digits/1-a");
08506          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08507          res = res ? res : ast_play_and_wait(chan, "vm-message");
08508       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08509          if (num.rem == 2) {
08510             if (!num.quot) {
08511                res = ast_play_and_wait(chan, "digits/2-ie");
08512             } else {
08513                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08514                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08515             }
08516          } else {
08517             res = say_and_wait(chan, vms->oldmessages, chan->language);
08518          }
08519          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08520          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08521       } else {
08522          res = say_and_wait(chan, vms->oldmessages, chan->language);
08523          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08524          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08525       }
08526    }
08527 
08528    return res;
08529 }
08530 
08531 /* SWEDISH syntax */
08532 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08533 {
08534    /* Introduce messages they have */
08535    int res;
08536 
08537    res = ast_play_and_wait(chan, "vm-youhave");
08538    if (res)
08539       return res;
08540 
08541    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08542       res = ast_play_and_wait(chan, "vm-no");
08543       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08544       return res;
08545    }
08546 
08547    if (vms->newmessages) {
08548       if ((vms->newmessages == 1)) {
08549          res = ast_play_and_wait(chan, "digits/ett");
08550          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08551          res = res ? res : ast_play_and_wait(chan, "vm-message");
08552       } else {
08553          res = say_and_wait(chan, vms->newmessages, chan->language);
08554          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08555          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08556       }
08557       if (!res && vms->oldmessages)
08558          res = ast_play_and_wait(chan, "vm-and");
08559    }
08560    if (!res && vms->oldmessages) {
08561       if (vms->oldmessages == 1) {
08562          res = ast_play_and_wait(chan, "digits/ett");
08563          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08564          res = res ? res : ast_play_and_wait(chan, "vm-message");
08565       } else {
08566          res = say_and_wait(chan, vms->oldmessages, chan->language);
08567          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08568          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08569       }
08570    }
08571 
08572    return res;
08573 }
08574 
08575 /* NORWEGIAN syntax */
08576 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08577 {
08578    /* Introduce messages they have */
08579    int res;
08580 
08581    res = ast_play_and_wait(chan, "vm-youhave");
08582    if (res)
08583       return res;
08584 
08585    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08586       res = ast_play_and_wait(chan, "vm-no");
08587       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08588       return res;
08589    }
08590 
08591    if (vms->newmessages) {
08592       if ((vms->newmessages == 1)) {
08593          res = ast_play_and_wait(chan, "digits/1");
08594          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08595          res = res ? res : ast_play_and_wait(chan, "vm-message");
08596       } else {
08597          res = say_and_wait(chan, vms->newmessages, chan->language);
08598          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08599          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08600       }
08601       if (!res && vms->oldmessages)
08602          res = ast_play_and_wait(chan, "vm-and");
08603    }
08604    if (!res && vms->oldmessages) {
08605       if (vms->oldmessages == 1) {
08606          res = ast_play_and_wait(chan, "digits/1");
08607          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08608          res = res ? res : ast_play_and_wait(chan, "vm-message");
08609       } else {
08610          res = say_and_wait(chan, vms->oldmessages, chan->language);
08611          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08612          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08613       }
08614    }
08615 
08616    return res;
08617 }
08618 
08619 /* GERMAN syntax */
08620 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08621 {
08622    /* Introduce messages they have */
08623    int res;
08624    res = ast_play_and_wait(chan, "vm-youhave");
08625    if (!res) {
08626       if (vms->newmessages) {
08627          if ((vms->newmessages == 1))
08628             res = ast_play_and_wait(chan, "digits/1F");
08629          else
08630             res = say_and_wait(chan, vms->newmessages, chan->language);
08631          if (!res)
08632             res = ast_play_and_wait(chan, "vm-INBOX");
08633          if (vms->oldmessages && !res)
08634             res = ast_play_and_wait(chan, "vm-and");
08635          else if (!res) {
08636             if ((vms->newmessages == 1))
08637                res = ast_play_and_wait(chan, "vm-message");
08638             else
08639                res = ast_play_and_wait(chan, "vm-messages");
08640          }
08641             
08642       }
08643       if (!res && vms->oldmessages) {
08644          if (vms->oldmessages == 1)
08645             res = ast_play_and_wait(chan, "digits/1F");
08646          else
08647             res = say_and_wait(chan, vms->oldmessages, chan->language);
08648          if (!res)
08649             res = ast_play_and_wait(chan, "vm-Old");
08650          if (!res) {
08651             if (vms->oldmessages == 1)
08652                res = ast_play_and_wait(chan, "vm-message");
08653             else
08654                res = ast_play_and_wait(chan, "vm-messages");
08655          }
08656       }
08657       if (!res) {
08658          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08659             res = ast_play_and_wait(chan, "vm-no");
08660             if (!res)
08661                res = ast_play_and_wait(chan, "vm-messages");
08662          }
08663       }
08664    }
08665    return res;
08666 }
08667 
08668 /* SPANISH syntax */
08669 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08670 {
08671    /* Introduce messages they have */
08672    int res;
08673    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08674       res = ast_play_and_wait(chan, "vm-youhaveno");
08675       if (!res)
08676          res = ast_play_and_wait(chan, "vm-messages");
08677    } else {
08678       res = ast_play_and_wait(chan, "vm-youhave");
08679    }
08680    if (!res) {
08681       if (vms->newmessages) {
08682          if (!res) {
08683             if ((vms->newmessages == 1)) {
08684                res = ast_play_and_wait(chan, "digits/1M");
08685                if (!res)
08686                   res = ast_play_and_wait(chan, "vm-message");
08687                if (!res)
08688                   res = ast_play_and_wait(chan, "vm-INBOXs");
08689             } else {
08690                res = say_and_wait(chan, vms->newmessages, chan->language);
08691                if (!res)
08692                   res = ast_play_and_wait(chan, "vm-messages");
08693                if (!res)
08694                   res = ast_play_and_wait(chan, "vm-INBOX");
08695             }
08696          }
08697          if (vms->oldmessages && !res)
08698             res = ast_play_and_wait(chan, "vm-and");
08699       }
08700       if (vms->oldmessages) {
08701          if (!res) {
08702             if (vms->oldmessages == 1) {
08703                res = ast_play_and_wait(chan, "digits/1M");
08704                if (!res)
08705                   res = ast_play_and_wait(chan, "vm-message");
08706                if (!res)
08707                   res = ast_play_and_wait(chan, "vm-Olds");
08708             } else {
08709                res = say_and_wait(chan, vms->oldmessages, chan->language);
08710                if (!res)
08711                   res = ast_play_and_wait(chan, "vm-messages");
08712                if (!res)
08713                   res = ast_play_and_wait(chan, "vm-Old");
08714             }
08715          }
08716       }
08717    }
08718 return res;
08719 }
08720 
08721 /* BRAZILIAN PORTUGUESE syntax */
08722 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08723    /* Introduce messages they have */
08724    int res;
08725    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08726       res = ast_play_and_wait(chan, "vm-nomessages");
08727       return res;
08728    } else {
08729       res = ast_play_and_wait(chan, "vm-youhave");
08730    }
08731    if (vms->newmessages) {
08732       if (!res)
08733          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08734       if ((vms->newmessages == 1)) {
08735          if (!res)
08736             res = ast_play_and_wait(chan, "vm-message");
08737          if (!res)
08738             res = ast_play_and_wait(chan, "vm-INBOXs");
08739       } else {
08740          if (!res)
08741             res = ast_play_and_wait(chan, "vm-messages");
08742          if (!res)
08743             res = ast_play_and_wait(chan, "vm-INBOX");
08744       }
08745       if (vms->oldmessages && !res)
08746          res = ast_play_and_wait(chan, "vm-and");
08747    }
08748    if (vms->oldmessages) {
08749       if (!res)
08750          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08751       if (vms->oldmessages == 1) {
08752          if (!res)
08753             res = ast_play_and_wait(chan, "vm-message");
08754          if (!res)
08755             res = ast_play_and_wait(chan, "vm-Olds");
08756       } else {
08757          if (!res)
08758             res = ast_play_and_wait(chan, "vm-messages");
08759          if (!res)
08760             res = ast_play_and_wait(chan, "vm-Old");
08761       }
08762    }
08763    return res;
08764 }
08765 
08766 /* FRENCH syntax */
08767 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08768 {
08769    /* Introduce messages they have */
08770    int res;
08771    res = ast_play_and_wait(chan, "vm-youhave");
08772    if (!res) {
08773       if (vms->newmessages) {
08774          res = say_and_wait(chan, vms->newmessages, chan->language);
08775          if (!res)
08776             res = ast_play_and_wait(chan, "vm-INBOX");
08777          if (vms->oldmessages && !res)
08778             res = ast_play_and_wait(chan, "vm-and");
08779          else if (!res) {
08780             if ((vms->newmessages == 1))
08781                res = ast_play_and_wait(chan, "vm-message");
08782             else
08783                res = ast_play_and_wait(chan, "vm-messages");
08784          }
08785             
08786       }
08787       if (!res && vms->oldmessages) {
08788          res = say_and_wait(chan, vms->oldmessages, chan->language);
08789          if (!res)
08790             res = ast_play_and_wait(chan, "vm-Old");
08791          if (!res) {
08792             if (vms->oldmessages == 1)
08793                res = ast_play_and_wait(chan, "vm-message");
08794             else
08795                res = ast_play_and_wait(chan, "vm-messages");
08796          }
08797       }
08798       if (!res) {
08799          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08800             res = ast_play_and_wait(chan, "vm-no");
08801             if (!res)
08802                res = ast_play_and_wait(chan, "vm-messages");
08803          }
08804       }
08805    }
08806    return res;
08807 }
08808 
08809 /* DUTCH syntax */
08810 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08811 {
08812    /* Introduce messages they have */
08813    int res;
08814    res = ast_play_and_wait(chan, "vm-youhave");
08815    if (!res) {
08816       if (vms->newmessages) {
08817          res = say_and_wait(chan, vms->newmessages, chan->language);
08818          if (!res) {
08819             if (vms->newmessages == 1)
08820                res = ast_play_and_wait(chan, "vm-INBOXs");
08821             else
08822                res = ast_play_and_wait(chan, "vm-INBOX");
08823          }
08824          if (vms->oldmessages && !res)
08825             res = ast_play_and_wait(chan, "vm-and");
08826          else if (!res) {
08827             if ((vms->newmessages == 1))
08828                res = ast_play_and_wait(chan, "vm-message");
08829             else
08830                res = ast_play_and_wait(chan, "vm-messages");
08831          }
08832             
08833       }
08834       if (!res && vms->oldmessages) {
08835          res = say_and_wait(chan, vms->oldmessages, chan->language);
08836          if (!res) {
08837             if (vms->oldmessages == 1)
08838                res = ast_play_and_wait(chan, "vm-Olds");
08839             else
08840                res = ast_play_and_wait(chan, "vm-Old");
08841          }
08842          if (!res) {
08843             if (vms->oldmessages == 1)
08844                res = ast_play_and_wait(chan, "vm-message");
08845             else
08846                res = ast_play_and_wait(chan, "vm-messages");
08847          }
08848       }
08849       if (!res) {
08850          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08851             res = ast_play_and_wait(chan, "vm-no");
08852             if (!res)
08853                res = ast_play_and_wait(chan, "vm-messages");
08854          }
08855       }
08856    }
08857    return res;
08858 }
08859 
08860 /* PORTUGUESE syntax */
08861 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08862 {
08863    /* Introduce messages they have */
08864    int res;
08865    res = ast_play_and_wait(chan, "vm-youhave");
08866    if (!res) {
08867       if (vms->newmessages) {
08868          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08869          if (!res) {
08870             if ((vms->newmessages == 1)) {
08871                res = ast_play_and_wait(chan, "vm-message");
08872                if (!res)
08873                   res = ast_play_and_wait(chan, "vm-INBOXs");
08874             } else {
08875                res = ast_play_and_wait(chan, "vm-messages");
08876                if (!res)
08877                   res = ast_play_and_wait(chan, "vm-INBOX");
08878             }
08879          }
08880          if (vms->oldmessages && !res)
08881             res = ast_play_and_wait(chan, "vm-and");
08882       }
08883       if (!res && vms->oldmessages) {
08884          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08885          if (!res) {
08886             if (vms->oldmessages == 1) {
08887                res = ast_play_and_wait(chan, "vm-message");
08888                if (!res)
08889                   res = ast_play_and_wait(chan, "vm-Olds");
08890             } else {
08891                res = ast_play_and_wait(chan, "vm-messages");
08892                if (!res)
08893                   res = ast_play_and_wait(chan, "vm-Old");
08894             }
08895          }
08896       }
08897       if (!res) {
08898          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08899             res = ast_play_and_wait(chan, "vm-no");
08900             if (!res)
08901                res = ast_play_and_wait(chan, "vm-messages");
08902          }
08903       }
08904    }
08905    return res;
08906 }
08907 
08908 
08909 /* CZECH syntax */
08910 /* in czech there must be declension of word new and message
08911  * czech        : english        : czech      : english
08912  * --------------------------------------------------------
08913  * vm-youhave   : you have 
08914  * vm-novou     : one new        : vm-zpravu  : message
08915  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08916  * vm-novych    : 5-infinite new : vm-zprav   : messages
08917  * vm-starou   : one old
08918  * vm-stare     : 2-4 old 
08919  * vm-starych   : 5-infinite old
08920  * jednu        : one   - falling 4. 
08921  * vm-no        : no  ( no messages )
08922  */
08923 
08924 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08925 {
08926    int res;
08927    res = ast_play_and_wait(chan, "vm-youhave");
08928    if (!res) {
08929       if (vms->newmessages) {
08930          if (vms->newmessages == 1) {
08931             res = ast_play_and_wait(chan, "digits/jednu");
08932          } else {
08933             res = say_and_wait(chan, vms->newmessages, chan->language);
08934          }
08935          if (!res) {
08936             if ((vms->newmessages == 1))
08937                res = ast_play_and_wait(chan, "vm-novou");
08938             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08939                res = ast_play_and_wait(chan, "vm-nove");
08940             if (vms->newmessages > 4)
08941                res = ast_play_and_wait(chan, "vm-novych");
08942          }
08943          if (vms->oldmessages && !res)
08944             res = ast_play_and_wait(chan, "vm-and");
08945          else if (!res) {
08946             if ((vms->newmessages == 1))
08947                res = ast_play_and_wait(chan, "vm-zpravu");
08948             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08949                res = ast_play_and_wait(chan, "vm-zpravy");
08950             if (vms->newmessages > 4)
08951                res = ast_play_and_wait(chan, "vm-zprav");
08952          }
08953       }
08954       if (!res && vms->oldmessages) {
08955          res = say_and_wait(chan, vms->oldmessages, chan->language);
08956          if (!res) {
08957             if ((vms->oldmessages == 1))
08958                res = ast_play_and_wait(chan, "vm-starou");
08959             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08960                res = ast_play_and_wait(chan, "vm-stare");
08961             if (vms->oldmessages > 4)
08962                res = ast_play_and_wait(chan, "vm-starych");
08963          }
08964          if (!res) {
08965             if ((vms->oldmessages == 1))
08966                res = ast_play_and_wait(chan, "vm-zpravu");
08967             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08968                res = ast_play_and_wait(chan, "vm-zpravy");
08969             if (vms->oldmessages > 4)
08970                res = ast_play_and_wait(chan, "vm-zprav");
08971          }
08972       }
08973       if (!res) {
08974          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08975             res = ast_play_and_wait(chan, "vm-no");
08976             if (!res)
08977                res = ast_play_and_wait(chan, "vm-zpravy");
08978          }
08979       }
08980    }
08981    return res;
08982 }
08983 
08984 /* CHINESE (Taiwan) syntax */
08985 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08986 {
08987    int res;
08988    /* Introduce messages they have */
08989    res = ast_play_and_wait(chan, "vm-you");
08990 
08991    if (!res && vms->newmessages) {
08992       res = ast_play_and_wait(chan, "vm-have");
08993       if (!res)
08994          res = say_and_wait(chan, vms->newmessages, chan->language);
08995       if (!res)
08996          res = ast_play_and_wait(chan, "vm-tong");
08997       if (!res)
08998          res = ast_play_and_wait(chan, "vm-INBOX");
08999       if (vms->oldmessages && !res)
09000          res = ast_play_and_wait(chan, "vm-and");
09001       else if (!res) 
09002          res = ast_play_and_wait(chan, "vm-messages");
09003    }
09004    if (!res && vms->oldmessages) {
09005       res = ast_play_and_wait(chan, "vm-have");
09006       if (!res)
09007          res = say_and_wait(chan, vms->oldmessages, chan->language);
09008       if (!res)
09009          res = ast_play_and_wait(chan, "vm-tong");
09010       if (!res)
09011          res = ast_play_and_wait(chan, "vm-Old");
09012       if (!res)
09013          res = ast_play_and_wait(chan, "vm-messages");
09014    }
09015    if (!res && !vms->oldmessages && !vms->newmessages) {
09016       res = ast_play_and_wait(chan, "vm-haveno");
09017       if (!res)
09018          res = ast_play_and_wait(chan, "vm-messages");
09019    }
09020    return res;
09021 }
09022 
09023 /* Vietnamese syntax */
09024 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09025 {
09026    int res;
09027 
09028    /* Introduce messages they have */
09029    res = ast_play_and_wait(chan, "vm-youhave");
09030    if (!res) {
09031       if (vms->newmessages) {
09032          res = say_and_wait(chan, vms->newmessages, chan->language);
09033          if (!res)
09034             res = ast_play_and_wait(chan, "vm-INBOX");
09035          if (vms->oldmessages && !res)
09036             res = ast_play_and_wait(chan, "vm-and");
09037       }
09038       if (!res && vms->oldmessages) {
09039          res = say_and_wait(chan, vms->oldmessages, chan->language);
09040          if (!res)
09041             res = ast_play_and_wait(chan, "vm-Old");        
09042       }
09043       if (!res) {
09044          if (!vms->oldmessages && !vms->newmessages) {
09045             res = ast_play_and_wait(chan, "vm-no");
09046             if (!res)
09047                res = ast_play_and_wait(chan, "vm-message");
09048          }
09049       }
09050    }
09051    return res;
09052 }
09053 
09054 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09055 {
09056    char prefile[256];
09057    
09058    /* Notify the user that the temp greeting is set and give them the option to remove it */
09059    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09060    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09061       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09062       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09063          ast_play_and_wait(chan, "vm-tempgreetactive");
09064       }
09065       DISPOSE(prefile, -1);
09066    }
09067 
09068    /* Play voicemail intro - syntax is different for different languages */
09069    if (0) {
09070       return 0;
09071    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09072       return vm_intro_cs(chan, vms);
09073    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09074       static int deprecation_warning = 0;
09075       if (deprecation_warning++ % 10 == 0) {
09076          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09077       }
09078       return vm_intro_cs(chan, vms);
09079    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09080       return vm_intro_de(chan, vms);
09081    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09082       return vm_intro_es(chan, vms);
09083    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09084       return vm_intro_fr(chan, vms);
09085    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09086       return vm_intro_gr(chan, vms);
09087    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09088       return vm_intro_he(chan, vms);
09089    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09090       return vm_intro_it(chan, vms);
09091    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09092       return vm_intro_nl(chan, vms);
09093    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09094       return vm_intro_no(chan, vms);
09095    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09096       return vm_intro_pl(chan, vms);
09097    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09098       return vm_intro_pt_BR(chan, vms);
09099    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09100       return vm_intro_pt(chan, vms);
09101    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09102       return vm_intro_multilang(chan, vms, "n");
09103    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09104       return vm_intro_se(chan, vms);
09105    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09106       return vm_intro_multilang(chan, vms, "n");
09107    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09108       return vm_intro_vi(chan, vms);
09109    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09110       return vm_intro_zh(chan, vms);
09111    } else {                                             /* Default to ENGLISH */
09112       return vm_intro_en(chan, vms);
09113    }
09114 }
09115 
09116 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09117 {
09118    int res = 0;
09119    /* Play instructions and wait for new command */
09120    while (!res) {
09121       if (vms->starting) {
09122          if (vms->lastmsg > -1) {
09123             if (skipadvanced)
09124                res = ast_play_and_wait(chan, "vm-onefor-full");
09125             else
09126                res = ast_play_and_wait(chan, "vm-onefor");
09127             if (!res)
09128                res = vm_play_folder_name(chan, vms->vmbox);
09129          }
09130          if (!res) {
09131             if (skipadvanced)
09132                res = ast_play_and_wait(chan, "vm-opts-full");
09133             else
09134                res = ast_play_and_wait(chan, "vm-opts");
09135          }
09136       } else {
09137          /* Added for additional help */
09138          if (skipadvanced) {
09139             res = ast_play_and_wait(chan, "vm-onefor-full");
09140             if (!res)
09141                res = vm_play_folder_name(chan, vms->vmbox);
09142             res = ast_play_and_wait(chan, "vm-opts-full");
09143          }
09144          /* Logic:
09145           * If the current message is not the first OR
09146           * if we're listening to the first new message and there are
09147           * also urgent messages, then prompt for navigation to the
09148           * previous message
09149           */
09150          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09151             res = ast_play_and_wait(chan, "vm-prev");
09152          }
09153          if (!res && !skipadvanced)
09154             res = ast_play_and_wait(chan, "vm-advopts");
09155          if (!res)
09156             res = ast_play_and_wait(chan, "vm-repeat");
09157          /* Logic:
09158           * If we're not listening to the last message OR
09159           * we're listening to the last urgent message and there are
09160           * also new non-urgent messages, then prompt for navigation
09161           * to the next message
09162           */
09163          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09164             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09165             res = ast_play_and_wait(chan, "vm-next");
09166          }
09167          if (!res) {
09168             int curmsg_deleted;
09169 #ifdef IMAP_STORAGE
09170             ast_mutex_lock(&vms->lock);
09171 #endif
09172             curmsg_deleted = vms->deleted[vms->curmsg];
09173 #ifdef IMAP_STORAGE
09174             ast_mutex_unlock(&vms->lock);
09175 #endif
09176             if (!curmsg_deleted) {
09177                res = ast_play_and_wait(chan, "vm-delete");
09178             } else {
09179                res = ast_play_and_wait(chan, "vm-undelete");
09180             }
09181             if (!res) {
09182                res = ast_play_and_wait(chan, "vm-toforward");
09183             }
09184             if (!res) {
09185                res = ast_play_and_wait(chan, "vm-savemessage");
09186             }
09187          }
09188       }
09189       if (!res) {
09190          res = ast_play_and_wait(chan, "vm-helpexit");
09191       }
09192       if (!res)
09193          res = ast_waitfordigit(chan, 6000);
09194       if (!res) {
09195          vms->repeats++;
09196          if (vms->repeats > 2) {
09197             res = 't';
09198          }
09199       }
09200    }
09201    return res;
09202 }
09203 
09204 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09205 {
09206    int res = 0;
09207    /* Play instructions and wait for new command */
09208    while (!res) {
09209       if (vms->lastmsg > -1) {
09210          res = ast_play_and_wait(chan, "vm-listen");
09211          if (!res)
09212             res = vm_play_folder_name(chan, vms->vmbox);
09213          if (!res)
09214             res = ast_play_and_wait(chan, "press");
09215          if (!res)
09216             res = ast_play_and_wait(chan, "digits/1");
09217       }
09218       if (!res)
09219          res = ast_play_and_wait(chan, "vm-opts");
09220       if (!res) {
09221          vms->starting = 0;
09222          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09223       }
09224    }
09225    return res;
09226 }
09227 
09228 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09229 {
09230    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09231       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09232    } else {             /* Default to ENGLISH */
09233       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09234    }
09235 }
09236 
09237 
09238 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09239 {
09240    int cmd = 0;
09241    int duration = 0;
09242    int tries = 0;
09243    char newpassword[80] = "";
09244    char newpassword2[80] = "";
09245    char prefile[PATH_MAX] = "";
09246    unsigned char buf[256];
09247    int bytes = 0;
09248 
09249    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09250    if (ast_adsi_available(chan)) {
09251       bytes += adsi_logo(buf + bytes);
09252       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09253       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09254       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09255       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09256       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09257    }
09258 
09259    /* If forcename is set, have the user record their name */
09260    if (ast_test_flag(vmu, VM_FORCENAME)) {
09261       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09262       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09263          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09264          if (cmd < 0 || cmd == 't' || cmd == '#')
09265             return cmd;
09266       }
09267    }
09268 
09269    /* If forcegreetings is set, have the user record their greetings */
09270    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09271       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09272       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09273          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09274          if (cmd < 0 || cmd == 't' || cmd == '#')
09275             return cmd;
09276       }
09277 
09278       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09279       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09280          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09281          if (cmd < 0 || cmd == 't' || cmd == '#')
09282             return cmd;
09283       }
09284    }
09285 
09286    /*
09287     * Change the password last since new users will be able to skip over any steps this one comes before
09288     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09289     */
09290    for (;;) {
09291       newpassword[1] = '\0';
09292       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09293       if (cmd == '#')
09294          newpassword[0] = '\0';
09295       if (cmd < 0 || cmd == 't' || cmd == '#')
09296          return cmd;
09297       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09298       if (cmd < 0 || cmd == 't' || cmd == '#')
09299          return cmd;
09300       cmd = check_password(vmu, newpassword); /* perform password validation */
09301       if (cmd != 0) {
09302          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09303          cmd = ast_play_and_wait(chan, vm_invalid_password);
09304       } else {
09305          newpassword2[1] = '\0';
09306          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09307          if (cmd == '#')
09308             newpassword2[0] = '\0';
09309          if (cmd < 0 || cmd == 't' || cmd == '#')
09310             return cmd;
09311          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09312          if (cmd < 0 || cmd == 't' || cmd == '#')
09313             return cmd;
09314          if (!strcmp(newpassword, newpassword2))
09315             break;
09316          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09317          cmd = ast_play_and_wait(chan, vm_mismatch);
09318       }
09319       if (++tries == 3)
09320          return -1;
09321       if (cmd != 0) {
09322          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09323       }
09324    }
09325    if (pwdchange & PWDCHANGE_INTERNAL)
09326       vm_change_password(vmu, newpassword);
09327    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09328       vm_change_password_shell(vmu, newpassword);
09329 
09330    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09331    cmd = ast_play_and_wait(chan, vm_passchanged);
09332 
09333    return cmd;
09334 }
09335 
09336 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09337 {
09338    int cmd = 0;
09339    int retries = 0;
09340    int duration = 0;
09341    char newpassword[80] = "";
09342    char newpassword2[80] = "";
09343    char prefile[PATH_MAX] = "";
09344    unsigned char buf[256];
09345    int bytes = 0;
09346 
09347    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09348    if (ast_adsi_available(chan)) {
09349       bytes += adsi_logo(buf + bytes);
09350       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09351       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09352       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09353       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09354       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09355    }
09356    while ((cmd >= 0) && (cmd != 't')) {
09357       if (cmd)
09358          retries = 0;
09359       switch (cmd) {
09360       case '1': /* Record your unavailable message */
09361          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09362          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09363          break;
09364       case '2':  /* Record your busy message */
09365          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09366          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09367          break;
09368       case '3': /* Record greeting */
09369          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09370          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09371          break;
09372       case '4':  /* manage the temporary greeting */
09373          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09374          break;
09375       case '5': /* change password */
09376          if (vmu->password[0] == '-') {
09377             cmd = ast_play_and_wait(chan, "vm-no");
09378             break;
09379          }
09380          newpassword[1] = '\0';
09381          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09382          if (cmd == '#')
09383             newpassword[0] = '\0';
09384          else {
09385             if (cmd < 0)
09386                break;
09387             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09388                break;
09389             }
09390          }
09391          cmd = check_password(vmu, newpassword); /* perform password validation */
09392          if (cmd != 0) {
09393             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09394             cmd = ast_play_and_wait(chan, vm_invalid_password);
09395             if (!cmd) {
09396                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09397             }
09398             break;
09399          }
09400          newpassword2[1] = '\0';
09401          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09402          if (cmd == '#')
09403             newpassword2[0] = '\0';
09404          else {
09405             if (cmd < 0)
09406                break;
09407 
09408             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09409                break;
09410             }
09411          }
09412          if (strcmp(newpassword, newpassword2)) {
09413             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09414             cmd = ast_play_and_wait(chan, vm_mismatch);
09415             if (!cmd) {
09416                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09417             }
09418             break;
09419          }
09420 
09421          if (pwdchange & PWDCHANGE_INTERNAL) {
09422             vm_change_password(vmu, newpassword);
09423          }
09424          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09425             vm_change_password_shell(vmu, newpassword);
09426          }
09427 
09428          ast_debug(1, "User %s set password to %s of length %d\n",
09429             vms->username, newpassword, (int) strlen(newpassword));
09430          cmd = ast_play_and_wait(chan, vm_passchanged);
09431          break;
09432       case '*': 
09433          cmd = 't';
09434          break;
09435       default: 
09436          cmd = 0;
09437          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09438          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09439          if (ast_fileexists(prefile, NULL, NULL)) {
09440             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09441          }
09442          DISPOSE(prefile, -1);
09443          if (!cmd) {
09444             cmd = ast_play_and_wait(chan, "vm-options");
09445          }
09446          if (!cmd) {
09447             cmd = ast_waitfordigit(chan, 6000);
09448          }
09449          if (!cmd) {
09450             retries++;
09451          }
09452          if (retries > 3) {
09453             cmd = 't';
09454          }
09455          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09456       }
09457    }
09458    if (cmd == 't')
09459       cmd = 0;
09460    return cmd;
09461 }
09462 
09463 /*!
09464  * \brief The handler for 'record a temporary greeting'. 
09465  * \param chan
09466  * \param vmu
09467  * \param vms
09468  * \param fmtc
09469  * \param record_gain
09470  *
09471  * This is option 4 from the mailbox options menu.
09472  * This function manages the following promptings:
09473  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09474  * 2: remove (delete) the temporary greeting.
09475  * *: return to the main menu.
09476  *
09477  * \return zero on success, -1 on error.
09478  */
09479 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09480 {
09481    int cmd = 0;
09482    int retries = 0;
09483    int duration = 0;
09484    char prefile[PATH_MAX] = "";
09485    unsigned char buf[256];
09486    int bytes = 0;
09487 
09488    if (ast_adsi_available(chan)) {
09489       bytes += adsi_logo(buf + bytes);
09490       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09491       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09492       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09493       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09494       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09495    }
09496 
09497    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09498    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09499    while ((cmd >= 0) && (cmd != 't')) {
09500       if (cmd)
09501          retries = 0;
09502       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09503       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09504          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09505          if (cmd == -1) {
09506             break;
09507          }
09508          cmd = 't';  
09509       } else {
09510          switch (cmd) {
09511          case '1':
09512             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09513             break;
09514          case '2':
09515             DELETE(prefile, -1, prefile, vmu);
09516             ast_play_and_wait(chan, "vm-tempremoved");
09517             cmd = 't';  
09518             break;
09519          case '*': 
09520             cmd = 't';
09521             break;
09522          default:
09523             cmd = ast_play_and_wait(chan,
09524                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09525                   "vm-tempgreeting2" : "vm-tempgreeting");
09526             if (!cmd) {
09527                cmd = ast_waitfordigit(chan, 6000);
09528             }
09529             if (!cmd) {
09530                retries++;
09531             }
09532             if (retries > 3) {
09533                cmd = 't';
09534             }
09535             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09536          }
09537       }
09538       DISPOSE(prefile, -1);
09539    }
09540    if (cmd == 't')
09541       cmd = 0;
09542    return cmd;
09543 }
09544 
09545 /*!
09546  * \brief Greek syntax for 'You have N messages' greeting.
09547  * \param chan
09548  * \param vms
09549  * \param vmu
09550  *
09551  * \return zero on success, -1 on error.
09552  */   
09553 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09554 {
09555    int cmd = 0;
09556 
09557    if (vms->lastmsg > -1) {
09558       cmd = play_message(chan, vmu, vms);
09559    } else {
09560       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09561       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09562          if (!cmd) {
09563             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09564             cmd = ast_play_and_wait(chan, vms->fn);
09565          }
09566          if (!cmd)
09567             cmd = ast_play_and_wait(chan, "vm-messages");
09568       } else {
09569          if (!cmd)
09570             cmd = ast_play_and_wait(chan, "vm-messages");
09571          if (!cmd) {
09572             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09573             cmd = ast_play_and_wait(chan, vms->fn);
09574          }
09575       }
09576    } 
09577    return cmd;
09578 }
09579 
09580 /* Hebrew Syntax */
09581 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09582 {
09583    int cmd = 0;
09584 
09585    if (vms->lastmsg > -1) {
09586       cmd = play_message(chan, vmu, vms);
09587    } else {
09588       if (!strcasecmp(vms->fn, "INBOX")) {
09589          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09590       } else {
09591          cmd = ast_play_and_wait(chan, "vm-nomessages");
09592       }
09593    }
09594    return cmd;
09595 }
09596 
09597 /*! 
09598  * \brief Default English syntax for 'You have N messages' greeting.
09599  * \param chan
09600  * \param vms
09601  * \param vmu
09602  *
09603  * \return zero on success, -1 on error.
09604  */
09605 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09606 {
09607    int cmd = 0;
09608 
09609    if (vms->lastmsg > -1) {
09610       cmd = play_message(chan, vmu, vms);
09611    } else {
09612       cmd = ast_play_and_wait(chan, "vm-youhave");
09613       if (!cmd) 
09614          cmd = ast_play_and_wait(chan, "vm-no");
09615       if (!cmd) {
09616          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09617          cmd = ast_play_and_wait(chan, vms->fn);
09618       }
09619       if (!cmd)
09620          cmd = ast_play_and_wait(chan, "vm-messages");
09621    }
09622    return cmd;
09623 }
09624 
09625 /*! 
09626  *\brief Italian syntax for 'You have N messages' greeting.
09627  * \param chan
09628  * \param vms
09629  * \param vmu
09630  *
09631  * \return zero on success, -1 on error.
09632  */
09633 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09634 {
09635    int cmd;
09636 
09637    if (vms->lastmsg > -1) {
09638       cmd = play_message(chan, vmu, vms);
09639    } else {
09640       cmd = ast_play_and_wait(chan, "vm-no");
09641       if (!cmd)
09642          cmd = ast_play_and_wait(chan, "vm-message");
09643       if (!cmd) {
09644          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09645          cmd = ast_play_and_wait(chan, vms->fn);
09646       }
09647    }
09648    return cmd;
09649 }
09650 
09651 /*! 
09652  * \brief Spanish syntax for 'You have N messages' greeting.
09653  * \param chan
09654  * \param vms
09655  * \param vmu
09656  *
09657  * \return zero on success, -1 on error.
09658  */
09659 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09660 {
09661    int cmd;
09662 
09663    if (vms->lastmsg > -1) {
09664       cmd = play_message(chan, vmu, vms);
09665    } else {
09666       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09667       if (!cmd)
09668          cmd = ast_play_and_wait(chan, "vm-messages");
09669       if (!cmd) {
09670          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09671          cmd = ast_play_and_wait(chan, vms->fn);
09672       }
09673    }
09674    return cmd;
09675 }
09676 
09677 /*! 
09678  * \brief Portuguese syntax for 'You have N messages' greeting.
09679  * \param chan
09680  * \param vms
09681  * \param vmu
09682  *
09683  * \return zero on success, -1 on error.
09684  */
09685 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09686 {
09687    int cmd;
09688 
09689    if (vms->lastmsg > -1) {
09690       cmd = play_message(chan, vmu, vms);
09691    } else {
09692       cmd = ast_play_and_wait(chan, "vm-no");
09693       if (!cmd) {
09694          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09695          cmd = ast_play_and_wait(chan, vms->fn);
09696       }
09697       if (!cmd)
09698          cmd = ast_play_and_wait(chan, "vm-messages");
09699    }
09700    return cmd;
09701 }
09702 
09703 /*! 
09704  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09705  * \param chan
09706  * \param vms
09707  * \param vmu
09708  *
09709  * \return zero on success, -1 on error.
09710  */
09711 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09712 {
09713    int cmd;
09714 
09715    if (vms->lastmsg > -1) {
09716       cmd = play_message(chan, vmu, vms);
09717    } else {
09718       cmd = ast_play_and_wait(chan, "vm-you");
09719       if (!cmd) 
09720          cmd = ast_play_and_wait(chan, "vm-haveno");
09721       if (!cmd)
09722          cmd = ast_play_and_wait(chan, "vm-messages");
09723       if (!cmd) {
09724          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09725          cmd = ast_play_and_wait(chan, vms->fn);
09726       }
09727    }
09728    return cmd;
09729 }
09730 
09731 /*! 
09732  * \brief Vietnamese syntax for 'You have N messages' greeting.
09733  * \param chan
09734  * \param vms
09735  * \param vmu
09736  *
09737  * \return zero on success, -1 on error.
09738  */
09739 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09740 {
09741    int cmd = 0;
09742 
09743    if (vms->lastmsg > -1) {
09744       cmd = play_message(chan, vmu, vms);
09745    } else {
09746       cmd = ast_play_and_wait(chan, "vm-no");
09747       if (!cmd) {
09748          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09749          cmd = ast_play_and_wait(chan, vms->fn);
09750       }
09751    }
09752    return cmd;
09753 }
09754 
09755 /*!
09756  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09757  * \param chan The channel for the current user. We read the language property from this.
09758  * \param vms passed into the language-specific vm_browse_messages function.
09759  * \param vmu passed into the language-specific vm_browse_messages function.
09760  * 
09761  * The method to be invoked is determined by the value of language code property in the user's channel.
09762  * The default (when unable to match) is to use english.
09763  *
09764  * \return zero on success, -1 on error.
09765  */
09766 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09767 {
09768    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09769       return vm_browse_messages_es(chan, vms, vmu);
09770    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09771       return vm_browse_messages_gr(chan, vms, vmu);
09772    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09773       return vm_browse_messages_he(chan, vms, vmu);
09774    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09775       return vm_browse_messages_it(chan, vms, vmu);
09776    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09777       return vm_browse_messages_pt(chan, vms, vmu);
09778    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09779       return vm_browse_messages_vi(chan, vms, vmu);
09780    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09781       return vm_browse_messages_zh(chan, vms, vmu);
09782    } else {                                             /* Default to English syntax */
09783       return vm_browse_messages_en(chan, vms, vmu);
09784    }
09785 }
09786 
09787 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09788          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09789          int skipuser, int max_logins, int silent)
09790 {
09791    int useadsi = 0, valid = 0, logretries = 0;
09792    char password[AST_MAX_EXTENSION]="", *passptr;
09793    struct ast_vm_user vmus, *vmu = NULL;
09794 
09795    /* If ADSI is supported, setup login screen */
09796    adsi_begin(chan, &useadsi);
09797    if (!skipuser && useadsi)
09798       adsi_login(chan);
09799    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09800    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09801       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09802       return -1;
09803    }
09804 
09805    /* Authenticate them and get their mailbox/password */
09806 
09807    while (!valid && (logretries < max_logins)) {
09808       /* Prompt for, and read in the username */
09809       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09810          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09811          return -1;
09812       }
09813       if (ast_strlen_zero(mailbox)) {
09814          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09815             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09816          } else {
09817             ast_verb(3, "Username not entered\n"); 
09818             return -1;
09819          }
09820       } else if (mailbox[0] == '*') {
09821          /* user entered '*' */
09822          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09823          if (ast_exists_extension(chan, chan->context, "a", 1,
09824             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09825             return -1;
09826          }
09827          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09828          mailbox[0] = '\0';
09829       }
09830 
09831       if (useadsi)
09832          adsi_password(chan);
09833 
09834       if (!ast_strlen_zero(prefix)) {
09835          char fullusername[80] = "";
09836          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09837          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09838          ast_copy_string(mailbox, fullusername, mailbox_size);
09839       }
09840 
09841       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09842       vmu = find_user(&vmus, context, mailbox);
09843       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09844          /* saved password is blank, so don't bother asking */
09845          password[0] = '\0';
09846       } else {
09847          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09848          if (ast_streamfile(chan, vm_password, chan->language)) {
09849             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09850             return -1;
09851          }
09852          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09853             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09854             return -1;
09855          } else if (password[0] == '*') {
09856             /* user entered '*' */
09857             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09858             if (ast_exists_extension(chan, chan->context, "a", 1,
09859                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09860                mailbox[0] = '*';
09861                return -1;
09862             }
09863             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09864             mailbox[0] = '\0';
09865             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09866             vmu = NULL;
09867          }
09868       }
09869 
09870       if (vmu) {
09871          passptr = vmu->password;
09872          if (passptr[0] == '-') passptr++;
09873       }
09874       if (vmu && !strcmp(passptr, password))
09875          valid++;
09876       else {
09877          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09878          if (!ast_strlen_zero(prefix))
09879             mailbox[0] = '\0';
09880       }
09881       logretries++;
09882       if (!valid) {
09883          if (skipuser || logretries >= max_logins) {
09884             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09885             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09886                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09887                return -1;
09888             }
09889          } else {
09890             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09891             if (useadsi)
09892                adsi_login(chan);
09893             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09894                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09895                return -1;
09896             }
09897          }
09898          if (ast_waitstream(chan, "")) /* Channel is hung up */
09899             return -1;
09900       }
09901    }
09902    if (!valid && (logretries >= max_logins)) {
09903       ast_stopstream(chan);
09904       ast_play_and_wait(chan, "vm-goodbye");
09905       return -1;
09906    }
09907    if (vmu && !skipuser) {
09908       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09909    }
09910    return 0;
09911 }
09912 
09913 static int vm_execmain(struct ast_channel *chan, const char *data)
09914 {
09915    /* XXX This is, admittedly, some pretty horrendous code.  For some
09916       reason it just seemed a lot easier to do with GOTO's.  I feel
09917       like I'm back in my GWBASIC days. XXX */
09918    int res = -1;
09919    int cmd = 0;
09920    int valid = 0;
09921    char prefixstr[80] ="";
09922    char ext_context[256]="";
09923    int box;
09924    int useadsi = 0;
09925    int skipuser = 0;
09926    struct vm_state vms;
09927    struct ast_vm_user *vmu = NULL, vmus;
09928    char *context = NULL;
09929    int silentexit = 0;
09930    struct ast_flags flags = { 0 };
09931    signed char record_gain = 0;
09932    int play_auto = 0;
09933    int play_folder = 0;
09934    int in_urgent = 0;
09935 #ifdef IMAP_STORAGE
09936    int deleted = 0;
09937 #endif
09938 
09939    /* Add the vm_state to the active list and keep it active */
09940    memset(&vms, 0, sizeof(vms));
09941 
09942    vms.lastmsg = -1;
09943 
09944    memset(&vmus, 0, sizeof(vmus));
09945 
09946    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09947    if (chan->_state != AST_STATE_UP) {
09948       ast_debug(1, "Before ast_answer\n");
09949       ast_answer(chan);
09950    }
09951 
09952    if (!ast_strlen_zero(data)) {
09953       char *opts[OPT_ARG_ARRAY_SIZE];
09954       char *parse;
09955       AST_DECLARE_APP_ARGS(args,
09956          AST_APP_ARG(argv0);
09957          AST_APP_ARG(argv1);
09958       );
09959 
09960       parse = ast_strdupa(data);
09961 
09962       AST_STANDARD_APP_ARGS(args, parse);
09963 
09964       if (args.argc == 2) {
09965          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09966             return -1;
09967          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09968             int gain;
09969             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09970                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09971                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09972                   return -1;
09973                } else {
09974                   record_gain = (signed char) gain;
09975                }
09976             } else {
09977                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09978             }
09979          }
09980          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09981             play_auto = 1;
09982             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09983                /* See if it is a folder name first */
09984                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09985                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09986                      play_folder = -1;
09987                   }
09988                } else {
09989                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09990                }
09991             } else {
09992                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09993             }
09994             if (play_folder > 9 || play_folder < 0) {
09995                ast_log(AST_LOG_WARNING,
09996                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09997                   opts[OPT_ARG_PLAYFOLDER]);
09998                play_folder = 0;
09999             }
10000          }
10001       } else {
10002          /* old style options parsing */
10003          while (*(args.argv0)) {
10004             if (*(args.argv0) == 's')
10005                ast_set_flag(&flags, OPT_SILENT);
10006             else if (*(args.argv0) == 'p')
10007                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
10008             else 
10009                break;
10010             (args.argv0)++;
10011          }
10012 
10013       }
10014 
10015       valid = ast_test_flag(&flags, OPT_SILENT);
10016 
10017       if ((context = strchr(args.argv0, '@')))
10018          *context++ = '\0';
10019 
10020       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10021          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10022       else
10023          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10024 
10025       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10026          skipuser++;
10027       else
10028          valid = 0;
10029    }
10030 
10031    if (!valid)
10032       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10033 
10034    ast_debug(1, "After vm_authenticate\n");
10035 
10036    if (vms.username[0] == '*') {
10037       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10038 
10039       /* user entered '*' */
10040       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10041          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10042          res = 0; /* prevent hangup */
10043          goto out;
10044       }
10045    }
10046 
10047    if (!res) {
10048       valid = 1;
10049       if (!skipuser)
10050          vmu = &vmus;
10051    } else {
10052       res = 0;
10053    }
10054 
10055    /* If ADSI is supported, setup login screen */
10056    adsi_begin(chan, &useadsi);
10057 
10058    ast_test_suite_assert(valid);
10059    if (!valid) {
10060       goto out;
10061    }
10062    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10063 
10064 #ifdef IMAP_STORAGE
10065    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10066    pthread_setspecific(ts_vmstate.key, &vms);
10067 
10068    vms.interactive = 1;
10069    vms.updated = 1;
10070    if (vmu)
10071       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10072    vmstate_insert(&vms);
10073    init_vm_state(&vms);
10074 #endif
10075    
10076    /* Set language from config to override channel language */
10077    if (!ast_strlen_zero(vmu->language))
10078       ast_string_field_set(chan, language, vmu->language);
10079 
10080    /* Retrieve urgent, old and new message counts */
10081    ast_debug(1, "Before open_mailbox\n");
10082    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10083    if (res < 0)
10084       goto out;
10085    vms.oldmessages = vms.lastmsg + 1;
10086    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10087    /* check INBOX */
10088    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10089    if (res < 0)
10090       goto out;
10091    vms.newmessages = vms.lastmsg + 1;
10092    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10093    /* Start in Urgent */
10094    in_urgent = 1;
10095    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10096    if (res < 0)
10097       goto out;
10098    vms.urgentmessages = vms.lastmsg + 1;
10099    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10100 
10101    /* Select proper mailbox FIRST!! */
10102    if (play_auto) {
10103       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10104       if (vms.urgentmessages) {
10105          in_urgent = 1;
10106          res = open_mailbox(&vms, vmu, 11);
10107       } else {
10108          in_urgent = 0;
10109          res = open_mailbox(&vms, vmu, play_folder);
10110       }
10111       if (res < 0)
10112          goto out;
10113 
10114       /* If there are no new messages, inform the user and hangup */
10115       if (vms.lastmsg == -1) {
10116          in_urgent = 0;
10117          cmd = vm_browse_messages(chan, &vms, vmu);
10118          res = 0;
10119          goto out;
10120       }
10121    } else {
10122       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10123          /* If we only have old messages start here */
10124          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10125          in_urgent = 0;
10126          play_folder = 1;
10127          if (res < 0)
10128             goto out;
10129       } else if (!vms.urgentmessages && vms.newmessages) {
10130          /* If we have new messages but none are urgent */
10131          in_urgent = 0;
10132          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10133          if (res < 0)
10134             goto out;
10135       }
10136    }
10137 
10138    if (useadsi)
10139       adsi_status(chan, &vms);
10140    res = 0;
10141 
10142    /* Check to see if this is a new user */
10143    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10144       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10145       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10146          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10147       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10148       if ((cmd == 't') || (cmd == '#')) {
10149          /* Timeout */
10150          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10151          res = 0;
10152          goto out;
10153       } else if (cmd < 0) {
10154          /* Hangup */
10155          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10156          res = -1;
10157          goto out;
10158       }
10159    }
10160 #ifdef IMAP_STORAGE
10161       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10162       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10163          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10164          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10165       }
10166       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10167       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10168          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10169          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10170       }
10171 #endif
10172 
10173    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10174    if (play_auto) {
10175       cmd = '1';
10176    } else {
10177       cmd = vm_intro(chan, vmu, &vms);
10178    }
10179    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10180 
10181    vms.repeats = 0;
10182    vms.starting = 1;
10183    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10184       /* Run main menu */
10185       switch (cmd) {
10186       case '1': /* First message */
10187          vms.curmsg = 0;
10188          /* Fall through */
10189       case '5': /* Play current message */
10190          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10191          cmd = vm_browse_messages(chan, &vms, vmu);
10192          break;
10193       case '2': /* Change folders */
10194          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10195          if (useadsi)
10196             adsi_folders(chan, 0, "Change to folder...");
10197 
10198          cmd = get_folder2(chan, "vm-changeto", 0);
10199          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10200          if (cmd == '#') {
10201             cmd = 0;
10202          } else if (cmd > 0) {
10203             cmd = cmd - '0';
10204             res = close_mailbox(&vms, vmu);
10205             if (res == ERROR_LOCK_PATH)
10206                goto out;
10207             /* If folder is not urgent, set in_urgent to zero! */
10208             if (cmd != 11) in_urgent = 0;
10209             res = open_mailbox(&vms, vmu, cmd);
10210             if (res < 0)
10211                goto out;
10212             play_folder = cmd;
10213             cmd = 0;
10214          }
10215          if (useadsi)
10216             adsi_status2(chan, &vms);
10217 
10218          if (!cmd) {
10219             cmd = vm_play_folder_name(chan, vms.vmbox);
10220          }
10221 
10222          vms.starting = 1;
10223          vms.curmsg = 0;
10224          break;
10225       case '3': /* Advanced options */
10226          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10227          cmd = 0;
10228          vms.repeats = 0;
10229          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10230             switch (cmd) {
10231             case '1': /* Reply */
10232                if (vms.lastmsg > -1 && !vms.starting) {
10233                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10234                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10235                      res = cmd;
10236                      goto out;
10237                   }
10238                } else {
10239                   cmd = ast_play_and_wait(chan, "vm-sorry");
10240                }
10241                cmd = 't';
10242                break;
10243             case '2': /* Callback */
10244                if (!vms.starting)
10245                   ast_verb(3, "Callback Requested\n");
10246                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10247                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10248                   if (cmd == 9) {
10249                      silentexit = 1;
10250                      goto out;
10251                   } else if (cmd == ERROR_LOCK_PATH) {
10252                      res = cmd;
10253                      goto out;
10254                   }
10255                } else {
10256                   cmd = ast_play_and_wait(chan, "vm-sorry");
10257                }
10258                cmd = 't';
10259                break;
10260             case '3': /* Envelope */
10261                if (vms.lastmsg > -1 && !vms.starting) {
10262                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10263                   if (cmd == ERROR_LOCK_PATH) {
10264                      res = cmd;
10265                      goto out;
10266                   }
10267                } else {
10268                   cmd = ast_play_and_wait(chan, "vm-sorry");
10269                }
10270                cmd = 't';
10271                break;
10272             case '4': /* Dialout */
10273                if (!ast_strlen_zero(vmu->dialout)) {
10274                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10275                   if (cmd == 9) {
10276                      silentexit = 1;
10277                      goto out;
10278                   }
10279                } else {
10280                   cmd = ast_play_and_wait(chan, "vm-sorry");
10281                }
10282                cmd = 't';
10283                break;
10284 
10285             case '5': /* Leave VoiceMail */
10286                if (ast_test_flag(vmu, VM_SVMAIL)) {
10287                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10288                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10289                      res = cmd;
10290                      goto out;
10291                   }
10292                } else {
10293                   cmd = ast_play_and_wait(chan, "vm-sorry");
10294                }
10295                cmd = 't';
10296                break;
10297 
10298             case '*': /* Return to main menu */
10299                cmd = 't';
10300                break;
10301 
10302             default:
10303                cmd = 0;
10304                if (!vms.starting) {
10305                   cmd = ast_play_and_wait(chan, "vm-toreply");
10306                }
10307                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10308                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10309                }
10310                if (!cmd && !vms.starting) {
10311                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10312                }
10313                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10314                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10315                }
10316                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10317                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10318                }
10319                if (!cmd) {
10320                   cmd = ast_play_and_wait(chan, "vm-starmain");
10321                }
10322                if (!cmd) {
10323                   cmd = ast_waitfordigit(chan, 6000);
10324                }
10325                if (!cmd) {
10326                   vms.repeats++;
10327                }
10328                if (vms.repeats > 3) {
10329                   cmd = 't';
10330                }
10331                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10332             }
10333          }
10334          if (cmd == 't') {
10335             cmd = 0;
10336             vms.repeats = 0;
10337          }
10338          break;
10339       case '4': /* Go to the previous message */
10340          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10341          if (vms.curmsg > 0) {
10342             vms.curmsg--;
10343             cmd = play_message(chan, vmu, &vms);
10344          } else {
10345             /* Check if we were listening to new
10346                messages.  If so, go to Urgent messages
10347                instead of saying "no more messages"
10348             */
10349             if (in_urgent == 0 && vms.urgentmessages > 0) {
10350                /* Check for Urgent messages */
10351                in_urgent = 1;
10352                res = close_mailbox(&vms, vmu);
10353                if (res == ERROR_LOCK_PATH)
10354                   goto out;
10355                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10356                if (res < 0)
10357                   goto out;
10358                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10359                vms.curmsg = vms.lastmsg;
10360                if (vms.lastmsg < 0) {
10361                   cmd = ast_play_and_wait(chan, "vm-nomore");
10362                }
10363             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10364                vms.curmsg = vms.lastmsg;
10365                cmd = play_message(chan, vmu, &vms);
10366             } else {
10367                cmd = ast_play_and_wait(chan, "vm-nomore");
10368             }
10369          }
10370          break;
10371       case '6': /* Go to the next message */
10372          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10373          if (vms.curmsg < vms.lastmsg) {
10374             vms.curmsg++;
10375             cmd = play_message(chan, vmu, &vms);
10376          } else {
10377             if (in_urgent && vms.newmessages > 0) {
10378                /* Check if we were listening to urgent
10379                 * messages.  If so, go to regular new messages
10380                 * instead of saying "no more messages"
10381                 */
10382                in_urgent = 0;
10383                res = close_mailbox(&vms, vmu);
10384                if (res == ERROR_LOCK_PATH)
10385                   goto out;
10386                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10387                if (res < 0)
10388                   goto out;
10389                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10390                vms.curmsg = -1;
10391                if (vms.lastmsg < 0) {
10392                   cmd = ast_play_and_wait(chan, "vm-nomore");
10393                }
10394             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10395                vms.curmsg = 0;
10396                cmd = play_message(chan, vmu, &vms);
10397             } else {
10398                cmd = ast_play_and_wait(chan, "vm-nomore");
10399             }
10400          }
10401          break;
10402       case '7': /* Delete the current message */
10403          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10404             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10405             if (useadsi)
10406                adsi_delete(chan, &vms);
10407             if (vms.deleted[vms.curmsg]) {
10408                if (play_folder == 0) {
10409                   if (in_urgent) {
10410                      vms.urgentmessages--;
10411                   } else {
10412                      vms.newmessages--;
10413                   }
10414                }
10415                else if (play_folder == 1)
10416                   vms.oldmessages--;
10417                cmd = ast_play_and_wait(chan, "vm-deleted");
10418             } else {
10419                if (play_folder == 0) {
10420                   if (in_urgent) {
10421                      vms.urgentmessages++;
10422                   } else {
10423                      vms.newmessages++;
10424                   }
10425                }
10426                else if (play_folder == 1)
10427                   vms.oldmessages++;
10428                cmd = ast_play_and_wait(chan, "vm-undeleted");
10429             }
10430             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10431                if (vms.curmsg < vms.lastmsg) {
10432                   vms.curmsg++;
10433                   cmd = play_message(chan, vmu, &vms);
10434                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10435                   vms.curmsg = 0;
10436                   cmd = play_message(chan, vmu, &vms);
10437                } else {
10438                   /* Check if we were listening to urgent
10439                      messages.  If so, go to regular new messages
10440                      instead of saying "no more messages"
10441                   */
10442                   if (in_urgent == 1) {
10443                      /* Check for new messages */
10444                      in_urgent = 0;
10445                      res = close_mailbox(&vms, vmu);
10446                      if (res == ERROR_LOCK_PATH)
10447                         goto out;
10448                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10449                      if (res < 0)
10450                         goto out;
10451                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10452                      vms.curmsg = -1;
10453                      if (vms.lastmsg < 0) {
10454                         cmd = ast_play_and_wait(chan, "vm-nomore");
10455                      }
10456                   } else {
10457                      cmd = ast_play_and_wait(chan, "vm-nomore");
10458                   }
10459                }
10460             }
10461          } else /* Delete not valid if we haven't selected a message */
10462             cmd = 0;
10463 #ifdef IMAP_STORAGE
10464          deleted = 1;
10465 #endif
10466          break;
10467    
10468       case '8': /* Forward the current message */
10469          if (vms.lastmsg > -1) {
10470             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10471             if (cmd == ERROR_LOCK_PATH) {
10472                res = cmd;
10473                goto out;
10474             }
10475          } else {
10476             /* Check if we were listening to urgent
10477                messages.  If so, go to regular new messages
10478                instead of saying "no more messages"
10479             */
10480             if (in_urgent == 1 && vms.newmessages > 0) {
10481                /* Check for new messages */
10482                in_urgent = 0;
10483                res = close_mailbox(&vms, vmu);
10484                if (res == ERROR_LOCK_PATH)
10485                   goto out;
10486                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10487                if (res < 0)
10488                   goto out;
10489                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10490                vms.curmsg = -1;
10491                if (vms.lastmsg < 0) {
10492                   cmd = ast_play_and_wait(chan, "vm-nomore");
10493                }
10494             } else {
10495                cmd = ast_play_and_wait(chan, "vm-nomore");
10496             }
10497          }
10498          break;
10499       case '9': /* Save message to folder */
10500          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10501          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10502             /* No message selected */
10503             cmd = 0;
10504             break;
10505          }
10506          if (useadsi)
10507             adsi_folders(chan, 1, "Save to folder...");
10508          cmd = get_folder2(chan, "vm-savefolder", 1);
10509          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10510          box = 0; /* Shut up compiler */
10511          if (cmd == '#') {
10512             cmd = 0;
10513             break;
10514          } else if (cmd > 0) {
10515             box = cmd = cmd - '0';
10516             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10517             if (cmd == ERROR_LOCK_PATH) {
10518                res = cmd;
10519                goto out;
10520 #ifndef IMAP_STORAGE
10521             } else if (!cmd) {
10522                vms.deleted[vms.curmsg] = 1;
10523 #endif
10524             } else {
10525                vms.deleted[vms.curmsg] = 0;
10526                vms.heard[vms.curmsg] = 0;
10527             }
10528          }
10529          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10530          if (useadsi)
10531             adsi_message(chan, &vms);
10532          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10533          if (!cmd) {
10534             cmd = ast_play_and_wait(chan, "vm-message");
10535             if (!cmd) 
10536                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10537             if (!cmd)
10538                cmd = ast_play_and_wait(chan, "vm-savedto");
10539             if (!cmd)
10540                cmd = vm_play_folder_name(chan, vms.fn);
10541          } else {
10542             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10543          }
10544          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10545             if (vms.curmsg < vms.lastmsg) {
10546                vms.curmsg++;
10547                cmd = play_message(chan, vmu, &vms);
10548             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10549                vms.curmsg = 0;
10550                cmd = play_message(chan, vmu, &vms);
10551             } else {
10552                /* Check if we were listening to urgent
10553                   messages.  If so, go to regular new messages
10554                   instead of saying "no more messages"
10555                */
10556                if (in_urgent == 1 && vms.newmessages > 0) {
10557                   /* Check for new messages */
10558                   in_urgent = 0;
10559                   res = close_mailbox(&vms, vmu);
10560                   if (res == ERROR_LOCK_PATH)
10561                      goto out;
10562                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10563                   if (res < 0)
10564                      goto out;
10565                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10566                   vms.curmsg = -1;
10567                   if (vms.lastmsg < 0) {
10568                      cmd = ast_play_and_wait(chan, "vm-nomore");
10569                   }
10570                } else {
10571                   cmd = ast_play_and_wait(chan, "vm-nomore");
10572                }
10573             }
10574          }
10575          break;
10576       case '*': /* Help */
10577          if (!vms.starting) {
10578             cmd = ast_play_and_wait(chan, "vm-onefor");
10579             if (!strncasecmp(chan->language, "he", 2)) {
10580                cmd = ast_play_and_wait(chan, "vm-for");
10581             }
10582             if (!cmd)
10583                cmd = vm_play_folder_name(chan, vms.vmbox);
10584             if (!cmd)
10585                cmd = ast_play_and_wait(chan, "vm-opts");
10586             if (!cmd)
10587                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10588          } else
10589             cmd = 0;
10590          break;
10591       case '0': /* Mailbox options */
10592          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10593          if (useadsi)
10594             adsi_status(chan, &vms);
10595          break;
10596       default: /* Nothing */
10597          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10598          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10599          break;
10600       }
10601    }
10602    if ((cmd == 't') || (cmd == '#')) {
10603       /* Timeout */
10604       res = 0;
10605    } else {
10606       /* Hangup */
10607       res = -1;
10608    }
10609 
10610 out:
10611    if (res > -1) {
10612       ast_stopstream(chan);
10613       adsi_goodbye(chan);
10614       if (valid && res != OPERATOR_EXIT) {
10615          if (silentexit)
10616             res = ast_play_and_wait(chan, "vm-dialout");
10617          else 
10618             res = ast_play_and_wait(chan, "vm-goodbye");
10619       }
10620       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10621          res = 0;
10622       }
10623       if (useadsi)
10624          ast_adsi_unload_session(chan);
10625    }
10626    if (vmu)
10627       close_mailbox(&vms, vmu);
10628    if (valid) {
10629       int new = 0, old = 0, urgent = 0;
10630       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10631       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10632       /* Urgent flag not passwd to externnotify here */
10633       run_externnotify(vmu->context, vmu->mailbox, NULL);
10634       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10635       queue_mwi_event(ext_context, urgent, new, old);
10636    }
10637 #ifdef IMAP_STORAGE
10638    /* expunge message - use UID Expunge if supported on IMAP server*/
10639    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10640    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10641       ast_mutex_lock(&vms.lock);
10642 #ifdef HAVE_IMAP_TK2006
10643       if (LEVELUIDPLUS (vms.mailstream)) {
10644          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10645       } else 
10646 #endif
10647          mail_expunge(vms.mailstream);
10648       ast_mutex_unlock(&vms.lock);
10649    }
10650    /*  before we delete the state, we should copy pertinent info
10651     *  back to the persistent model */
10652    if (vmu) {
10653       vmstate_delete(&vms);
10654    }
10655 #endif
10656    if (vmu)
10657       free_user(vmu);
10658 
10659 #ifdef IMAP_STORAGE
10660    pthread_setspecific(ts_vmstate.key, NULL);
10661 #endif
10662    return res;
10663 }
10664 
10665 static int vm_exec(struct ast_channel *chan, const char *data)
10666 {
10667    int res = 0;
10668    char *tmp;
10669    struct leave_vm_options leave_options;
10670    struct ast_flags flags = { 0 };
10671    char *opts[OPT_ARG_ARRAY_SIZE];
10672    AST_DECLARE_APP_ARGS(args,
10673       AST_APP_ARG(argv0);
10674       AST_APP_ARG(argv1);
10675    );
10676    
10677    memset(&leave_options, 0, sizeof(leave_options));
10678 
10679    if (chan->_state != AST_STATE_UP)
10680       ast_answer(chan);
10681 
10682    if (!ast_strlen_zero(data)) {
10683       tmp = ast_strdupa(data);
10684       AST_STANDARD_APP_ARGS(args, tmp);
10685       if (args.argc == 2) {
10686          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10687             return -1;
10688          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10689          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10690             int gain;
10691 
10692             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10693                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10694                return -1;
10695             } else {
10696                leave_options.record_gain = (signed char) gain;
10697             }
10698          }
10699          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10700             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10701                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10702          }
10703       }
10704    } else {
10705       char temp[256];
10706       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10707       if (res < 0)
10708          return res;
10709       if (ast_strlen_zero(temp))
10710          return 0;
10711       args.argv0 = ast_strdupa(temp);
10712    }
10713 
10714    res = leave_voicemail(chan, args.argv0, &leave_options);
10715    if (res == 't') {
10716       ast_play_and_wait(chan, "vm-goodbye");
10717       res = 0;
10718    }
10719 
10720    if (res == OPERATOR_EXIT) {
10721       res = 0;
10722    }
10723 
10724    if (res == ERROR_LOCK_PATH) {
10725       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10726       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10727       res = 0;
10728    }
10729 
10730    return res;
10731 }
10732 
10733 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10734 {
10735    struct ast_vm_user *vmu;
10736 
10737    if (!ast_strlen_zero(box) && box[0] == '*') {
10738       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10739             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10740             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10741             "\n\tand will be ignored.\n", box, context);
10742       return NULL;
10743    }
10744 
10745    AST_LIST_TRAVERSE(&users, vmu, list) {
10746       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10747          if (strcasecmp(vmu->context, context)) {
10748             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10749                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10750                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10751                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10752          }
10753          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10754          return NULL;
10755       }
10756       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10757          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10758          return NULL;
10759       }
10760    }
10761    
10762    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10763       return NULL;
10764    
10765    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10766    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10767 
10768    AST_LIST_INSERT_TAIL(&users, vmu, list);
10769    
10770    return vmu;
10771 }
10772 
10773 static int append_mailbox(const char *context, const char *box, const char *data)
10774 {
10775    /* Assumes lock is already held */
10776    char *tmp;
10777    char *stringp;
10778    char *s;
10779    struct ast_vm_user *vmu;
10780    char *mailbox_full;
10781    int new = 0, old = 0, urgent = 0;
10782    char secretfn[PATH_MAX] = "";
10783 
10784    tmp = ast_strdupa(data);
10785 
10786    if (!(vmu = find_or_create(context, box)))
10787       return -1;
10788 
10789    populate_defaults(vmu);
10790 
10791    stringp = tmp;
10792    if ((s = strsep(&stringp, ","))) {
10793       if (!ast_strlen_zero(s) && s[0] == '*') {
10794          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10795             "\n\tmust be reset in voicemail.conf.\n", box);
10796       }
10797       /* assign password regardless of validity to prevent NULL password from being assigned */
10798       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10799    }
10800    if (stringp && (s = strsep(&stringp, ","))) {
10801       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10802    }
10803    if (stringp && (s = strsep(&stringp, ","))) {
10804       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10805    }
10806    if (stringp && (s = strsep(&stringp, ","))) {
10807       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10808    }
10809    if (stringp && (s = strsep(&stringp, ","))) {
10810       apply_options(vmu, s);
10811    }
10812 
10813    switch (vmu->passwordlocation) {
10814    case OPT_PWLOC_SPOOLDIR:
10815       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10816       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10817    }
10818 
10819    mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10820    strcpy(mailbox_full, box);
10821    strcat(mailbox_full, "@");
10822    strcat(mailbox_full, context);
10823 
10824    inboxcount2(mailbox_full, &urgent, &new, &old);
10825    queue_mwi_event(mailbox_full, urgent, new, old);
10826 
10827    return 0;
10828 }
10829 
10830 AST_TEST_DEFINE(test_voicemail_vmuser)
10831 {
10832    int res = 0;
10833    struct ast_vm_user *vmu;
10834    /* language parameter seems to only be used for display in manager action */
10835    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10836       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10837       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10838       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10839       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10840       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10841       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10842       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10843       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10844 #ifdef IMAP_STORAGE
10845    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10846       "imapfolder=INBOX|imapvmshareid=6000";
10847 #endif
10848 
10849    switch (cmd) {
10850    case TEST_INIT:
10851       info->name = "vmuser";
10852       info->category = "/apps/app_voicemail/";
10853       info->summary = "Vmuser unit test";
10854       info->description =
10855          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10856       return AST_TEST_NOT_RUN;
10857    case TEST_EXECUTE:
10858       break;
10859    }
10860 
10861    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10862       return AST_TEST_NOT_RUN;
10863    }
10864    populate_defaults(vmu);
10865    ast_set_flag(vmu, VM_ALLOCED);
10866 
10867    apply_options(vmu, options_string);
10868 
10869    if (!ast_test_flag(vmu, VM_ATTACH)) {
10870       ast_test_status_update(test, "Parse failure for attach option\n");
10871       res = 1;
10872    }
10873    if (strcasecmp(vmu->attachfmt, "wav49")) {
10874       ast_test_status_update(test, "Parse failure for attachftm option\n");
10875       res = 1;
10876    }
10877    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10878       ast_test_status_update(test, "Parse failure for serveremail option\n");
10879       res = 1;
10880    }
10881    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10882       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10883       res = 1;
10884    }
10885    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10886       ast_test_status_update(test, "Parse failure for emailbody option\n");
10887       res = 1;
10888    }
10889    if (strcasecmp(vmu->zonetag, "central")) {
10890       ast_test_status_update(test, "Parse failure for tz option\n");
10891       res = 1;
10892    }
10893    if (!ast_test_flag(vmu, VM_DELETE)) {
10894       ast_test_status_update(test, "Parse failure for delete option\n");
10895       res = 1;
10896    }
10897    if (!ast_test_flag(vmu, VM_SAYCID)) {
10898       ast_test_status_update(test, "Parse failure for saycid option\n");
10899       res = 1;
10900    }
10901    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10902       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10903       res = 1;
10904    }
10905    if (!ast_test_flag(vmu, VM_REVIEW)) {
10906       ast_test_status_update(test, "Parse failure for review option\n");
10907       res = 1;
10908    }
10909    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10910       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10911       res = 1;
10912    }
10913    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10914       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10915       res = 1;
10916    }
10917    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10918       ast_test_status_update(test, "Parse failure for operator option\n");
10919       res = 1;
10920    }
10921    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10922       ast_test_status_update(test, "Parse failure for envelope option\n");
10923       res = 1;
10924    }
10925    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10926       ast_test_status_update(test, "Parse failure for moveheard option\n");
10927       res = 1;
10928    }
10929    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10930       ast_test_status_update(test, "Parse failure for sayduration option\n");
10931       res = 1;
10932    }
10933    if (vmu->saydurationm != 5) {
10934       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10935       res = 1;
10936    }
10937    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10938       ast_test_status_update(test, "Parse failure for forcename option\n");
10939       res = 1;
10940    }
10941    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10942       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10943       res = 1;
10944    }
10945    if (strcasecmp(vmu->callback, "somecontext")) {
10946       ast_test_status_update(test, "Parse failure for callbacks option\n");
10947       res = 1;
10948    }
10949    if (strcasecmp(vmu->dialout, "somecontext2")) {
10950       ast_test_status_update(test, "Parse failure for dialout option\n");
10951       res = 1;
10952    }
10953    if (strcasecmp(vmu->exit, "somecontext3")) {
10954       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10955       res = 1;
10956    }
10957    if (vmu->minsecs != 10) {
10958       ast_test_status_update(test, "Parse failure for minsecs option\n");
10959       res = 1;
10960    }
10961    if (vmu->maxsecs != 100) {
10962       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10963       res = 1;
10964    }
10965    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10966       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10967       res = 1;
10968    }
10969    if (vmu->maxdeletedmsg != 50) {
10970       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10971       res = 1;
10972    }
10973    if (vmu->volgain != 1.3) {
10974       ast_test_status_update(test, "Parse failure for volgain option\n");
10975       res = 1;
10976    }
10977    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10978       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10979       res = 1;
10980    }
10981 #ifdef IMAP_STORAGE
10982    apply_options(vmu, option_string2);
10983 
10984    if (strcasecmp(vmu->imapuser, "imapuser")) {
10985       ast_test_status_update(test, "Parse failure for imapuser option\n");
10986       res = 1;
10987    }
10988    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10989       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10990       res = 1;
10991    }
10992    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10993       ast_test_status_update(test, "Parse failure for imapfolder option\n");
10994       res = 1;
10995    }
10996    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10997       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10998       res = 1;
10999    }
11000 #endif
11001 
11002    free_user(vmu);
11003    return res ? AST_TEST_FAIL : AST_TEST_PASS;
11004 }
11005 
11006 static int vm_box_exists(struct ast_channel *chan, const char *data) 
11007 {
11008    struct ast_vm_user svm;
11009    char *context, *box;
11010    AST_DECLARE_APP_ARGS(args,
11011       AST_APP_ARG(mbox);
11012       AST_APP_ARG(options);
11013    );
11014    static int dep_warning = 0;
11015 
11016    if (ast_strlen_zero(data)) {
11017       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
11018       return -1;
11019    }
11020 
11021    if (!dep_warning) {
11022       dep_warning = 1;
11023       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
11024    }
11025 
11026    box = ast_strdupa(data);
11027 
11028    AST_STANDARD_APP_ARGS(args, box);
11029 
11030    if (args.options) {
11031    }
11032 
11033    if ((context = strchr(args.mbox, '@'))) {
11034       *context = '\0';
11035       context++;
11036    }
11037 
11038    if (find_user(&svm, context, args.mbox)) {
11039       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11040    } else
11041       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11042 
11043    return 0;
11044 }
11045 
11046 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11047 {
11048    struct ast_vm_user svm;
11049    AST_DECLARE_APP_ARGS(arg,
11050       AST_APP_ARG(mbox);
11051       AST_APP_ARG(context);
11052    );
11053 
11054    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11055 
11056    if (ast_strlen_zero(arg.mbox)) {
11057       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11058       return -1;
11059    }
11060 
11061    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11062    return 0;
11063 }
11064 
11065 static struct ast_custom_function mailbox_exists_acf = {
11066    .name = "MAILBOX_EXISTS",
11067    .read = acf_mailbox_exists,
11068 };
11069 
11070 static int vmauthenticate(struct ast_channel *chan, const char *data)
11071 {
11072    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11073    struct ast_vm_user vmus;
11074    char *options = NULL;
11075    int silent = 0, skipuser = 0;
11076    int res = -1;
11077    
11078    if (data) {
11079       s = ast_strdupa(data);
11080       user = strsep(&s, ",");
11081       options = strsep(&s, ",");
11082       if (user) {
11083          s = user;
11084          user = strsep(&s, "@");
11085          context = strsep(&s, "");
11086          if (!ast_strlen_zero(user))
11087             skipuser++;
11088          ast_copy_string(mailbox, user, sizeof(mailbox));
11089       }
11090    }
11091 
11092    if (options) {
11093       silent = (strchr(options, 's')) != NULL;
11094    }
11095 
11096    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11097       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11098       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11099       ast_play_and_wait(chan, "auth-thankyou");
11100       res = 0;
11101    } else if (mailbox[0] == '*') {
11102       /* user entered '*' */
11103       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11104          res = 0; /* prevent hangup */
11105       }
11106    }
11107 
11108    return res;
11109 }
11110 
11111 static char *show_users_realtime(int fd, const char *context)
11112 {
11113    struct ast_config *cfg;
11114    const char *cat = NULL;
11115 
11116    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11117       "context", context, SENTINEL))) {
11118       return CLI_FAILURE;
11119    }
11120 
11121    ast_cli(fd,
11122       "\n"
11123       "=============================================================\n"
11124       "=== Configured Voicemail Users ==============================\n"
11125       "=============================================================\n"
11126       "===\n");
11127 
11128    while ((cat = ast_category_browse(cfg, cat))) {
11129       struct ast_variable *var = NULL;
11130       ast_cli(fd,
11131          "=== Mailbox ...\n"
11132          "===\n");
11133       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11134          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11135       ast_cli(fd,
11136          "===\n"
11137          "=== ---------------------------------------------------------\n"
11138          "===\n");
11139    }
11140 
11141    ast_cli(fd,
11142       "=============================================================\n"
11143       "\n");
11144 
11145    ast_config_destroy(cfg);
11146 
11147    return CLI_SUCCESS;
11148 }
11149 
11150 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11151 {
11152    int which = 0;
11153    int wordlen;
11154    struct ast_vm_user *vmu;
11155    const char *context = "";
11156 
11157    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11158    if (pos > 4)
11159       return NULL;
11160    if (pos == 3)
11161       return (state == 0) ? ast_strdup("for") : NULL;
11162    wordlen = strlen(word);
11163    AST_LIST_TRAVERSE(&users, vmu, list) {
11164       if (!strncasecmp(word, vmu->context, wordlen)) {
11165          if (context && strcmp(context, vmu->context) && ++which > state)
11166             return ast_strdup(vmu->context);
11167          /* ignore repeated contexts ? */
11168          context = vmu->context;
11169       }
11170    }
11171    return NULL;
11172 }
11173 
11174 /*! \brief Show a list of voicemail users in the CLI */
11175 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11176 {
11177    struct ast_vm_user *vmu;
11178 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11179    const char *context = NULL;
11180    int users_counter = 0;
11181 
11182    switch (cmd) {
11183    case CLI_INIT:
11184       e->command = "voicemail show users";
11185       e->usage =
11186          "Usage: voicemail show users [for <context>]\n"
11187          "       Lists all mailboxes currently set up\n";
11188       return NULL;
11189    case CLI_GENERATE:
11190       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11191    }  
11192 
11193    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11194       return CLI_SHOWUSAGE;
11195    if (a->argc == 5) {
11196       if (strcmp(a->argv[3],"for"))
11197          return CLI_SHOWUSAGE;
11198       context = a->argv[4];
11199    }
11200 
11201    if (ast_check_realtime("voicemail")) {
11202       if (!context) {
11203          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11204          return CLI_SHOWUSAGE;
11205       }
11206       return show_users_realtime(a->fd, context);
11207    }
11208 
11209    AST_LIST_LOCK(&users);
11210    if (AST_LIST_EMPTY(&users)) {
11211       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11212       AST_LIST_UNLOCK(&users);
11213       return CLI_FAILURE;
11214    }
11215    if (!context) {
11216       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11217    } else {
11218       int count = 0;
11219       AST_LIST_TRAVERSE(&users, vmu, list) {
11220          if (!strcmp(context, vmu->context)) {
11221             count++;
11222             break;
11223          }
11224       }
11225       if (count) {
11226          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11227       } else {
11228          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11229          AST_LIST_UNLOCK(&users);
11230          return CLI_FAILURE;
11231       }
11232    }
11233    AST_LIST_TRAVERSE(&users, vmu, list) {
11234       int newmsgs = 0, oldmsgs = 0;
11235       char count[12], tmp[256] = "";
11236 
11237       if (!context || !strcmp(context, vmu->context)) {
11238          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11239          inboxcount(tmp, &newmsgs, &oldmsgs);
11240          snprintf(count, sizeof(count), "%d", newmsgs);
11241          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11242          users_counter++;
11243       }
11244    }
11245    AST_LIST_UNLOCK(&users);
11246    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11247    return CLI_SUCCESS;
11248 }
11249 
11250 /*! \brief Show a list of voicemail zones in the CLI */
11251 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11252 {
11253    struct vm_zone *zone;
11254 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11255    char *res = CLI_SUCCESS;
11256 
11257    switch (cmd) {
11258    case CLI_INIT:
11259       e->command = "voicemail show zones";
11260       e->usage =
11261          "Usage: voicemail show zones\n"
11262          "       Lists zone message formats\n";
11263       return NULL;
11264    case CLI_GENERATE:
11265       return NULL;
11266    }
11267 
11268    if (a->argc != 3)
11269       return CLI_SHOWUSAGE;
11270 
11271    AST_LIST_LOCK(&zones);
11272    if (!AST_LIST_EMPTY(&zones)) {
11273       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11274       AST_LIST_TRAVERSE(&zones, zone, list) {
11275          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11276       }
11277    } else {
11278       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11279       res = CLI_FAILURE;
11280    }
11281    AST_LIST_UNLOCK(&zones);
11282 
11283    return res;
11284 }
11285 
11286 /*! \brief Reload voicemail configuration from the CLI */
11287 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11288 {
11289    switch (cmd) {
11290    case CLI_INIT:
11291       e->command = "voicemail reload";
11292       e->usage =
11293          "Usage: voicemail reload\n"
11294          "       Reload voicemail configuration\n";
11295       return NULL;
11296    case CLI_GENERATE:
11297       return NULL;
11298    }
11299 
11300    if (a->argc != 2)
11301       return CLI_SHOWUSAGE;
11302 
11303    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11304    load_config(1);
11305    
11306    return CLI_SUCCESS;
11307 }
11308 
11309 static struct ast_cli_entry cli_voicemail[] = {
11310    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11311    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11312    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11313 };
11314 
11315 #ifdef IMAP_STORAGE
11316    #define DATA_EXPORT_VM_USERS(USER)              \
11317       USER(ast_vm_user, context, AST_DATA_STRING)        \
11318       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11319       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11320       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11321       USER(ast_vm_user, email, AST_DATA_STRING)       \
11322       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11323       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11324       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11325       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11326       USER(ast_vm_user, language, AST_DATA_STRING)       \
11327       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11328       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11329       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11330       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11331       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11332       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11333       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11334       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11335       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11336       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11337       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11338       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11339       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11340       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11341       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11342 #else
11343    #define DATA_EXPORT_VM_USERS(USER)              \
11344       USER(ast_vm_user, context, AST_DATA_STRING)        \
11345       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11346       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11347       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11348       USER(ast_vm_user, email, AST_DATA_STRING)       \
11349       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11350       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11351       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11352       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11353       USER(ast_vm_user, language, AST_DATA_STRING)       \
11354       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11355       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11356       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11357       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11358       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11359       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11360       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11361       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11362       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11363       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11364       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11365       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11366 #endif
11367 
11368 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11369 
11370 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11371    ZONE(vm_zone, name, AST_DATA_STRING)      \
11372    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11373    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11374 
11375 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11376 
11377 /*!
11378  * \internal
11379  * \brief Add voicemail user to the data_root.
11380  * \param[in] search The search tree.
11381  * \param[in] data_root The main result node.
11382  * \param[in] user The voicemail user.
11383  */
11384 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11385     struct ast_data *data_root, struct ast_vm_user *user)
11386 {
11387    struct ast_data *data_user, *data_zone;
11388    struct ast_data *data_state;
11389    struct vm_zone *zone = NULL;
11390    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11391    char ext_context[256] = "";
11392 
11393    data_user = ast_data_add_node(data_root, "user");
11394    if (!data_user) {
11395       return -1;
11396    }
11397 
11398    ast_data_add_structure(ast_vm_user, data_user, user);
11399 
11400    AST_LIST_LOCK(&zones);
11401    AST_LIST_TRAVERSE(&zones, zone, list) {
11402       if (!strcmp(zone->name, user->zonetag)) {
11403          break;
11404       }
11405    }
11406    AST_LIST_UNLOCK(&zones);
11407 
11408    /* state */
11409    data_state = ast_data_add_node(data_user, "state");
11410    if (!data_state) {
11411       return -1;
11412    }
11413    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11414    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11415    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11416    ast_data_add_int(data_state, "newmsg", newmsg);
11417    ast_data_add_int(data_state, "oldmsg", oldmsg);
11418 
11419    if (zone) {
11420       data_zone = ast_data_add_node(data_user, "zone");
11421       ast_data_add_structure(vm_zone, data_zone, zone);
11422    }
11423 
11424    if (!ast_data_search_match(search, data_user)) {
11425       ast_data_remove_node(data_root, data_user);
11426    }
11427 
11428    return 0;
11429 }
11430 
11431 static int vm_users_data_provider_get(const struct ast_data_search *search,
11432    struct ast_data *data_root)
11433 {
11434    struct ast_vm_user *user;
11435 
11436    AST_LIST_LOCK(&users);
11437    AST_LIST_TRAVERSE(&users, user, list) {
11438       vm_users_data_provider_get_helper(search, data_root, user);
11439    }
11440    AST_LIST_UNLOCK(&users);
11441 
11442    return 0;
11443 }
11444 
11445 static const struct ast_data_handler vm_users_data_provider = {
11446    .version = AST_DATA_HANDLER_VERSION,
11447    .get = vm_users_data_provider_get
11448 };
11449 
11450 static const struct ast_data_entry vm_data_providers[] = {
11451    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11452 };
11453 
11454 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11455 {
11456    int new = 0, old = 0, urgent = 0;
11457 
11458    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11459 
11460    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11461       mwi_sub->old_urgent = urgent;
11462       mwi_sub->old_new = new;
11463       mwi_sub->old_old = old;
11464       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11465       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11466    }
11467 }
11468 
11469 static void poll_subscribed_mailboxes(void)
11470 {
11471    struct mwi_sub *mwi_sub;
11472 
11473    AST_RWLIST_RDLOCK(&mwi_subs);
11474    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11475       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11476          poll_subscribed_mailbox(mwi_sub);
11477       }
11478    }
11479    AST_RWLIST_UNLOCK(&mwi_subs);
11480 }
11481 
11482 static void *mb_poll_thread(void *data)
11483 {
11484    while (poll_thread_run) {
11485       struct timespec ts = { 0, };
11486       struct timeval wait;
11487 
11488       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11489       ts.tv_sec = wait.tv_sec;
11490       ts.tv_nsec = wait.tv_usec * 1000;
11491 
11492       ast_mutex_lock(&poll_lock);
11493       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11494       ast_mutex_unlock(&poll_lock);
11495 
11496       if (!poll_thread_run)
11497          break;
11498 
11499       poll_subscribed_mailboxes();
11500    }
11501 
11502    return NULL;
11503 }
11504 
11505 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11506 {
11507    ast_free(mwi_sub);
11508 }
11509 
11510 static int handle_unsubscribe(void *datap)
11511 {
11512    struct mwi_sub *mwi_sub;
11513    uint32_t *uniqueid = datap;
11514    
11515    AST_RWLIST_WRLOCK(&mwi_subs);
11516    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11517       if (mwi_sub->uniqueid == *uniqueid) {
11518          AST_LIST_REMOVE_CURRENT(entry);
11519          break;
11520       }
11521    }
11522    AST_RWLIST_TRAVERSE_SAFE_END
11523    AST_RWLIST_UNLOCK(&mwi_subs);
11524 
11525    if (mwi_sub)
11526       mwi_sub_destroy(mwi_sub);
11527 
11528    ast_free(uniqueid);  
11529    return 0;
11530 }
11531 
11532 static int handle_subscribe(void *datap)
11533 {
11534    unsigned int len;
11535    struct mwi_sub *mwi_sub;
11536    struct mwi_sub_task *p = datap;
11537 
11538    len = sizeof(*mwi_sub);
11539    if (!ast_strlen_zero(p->mailbox))
11540       len += strlen(p->mailbox);
11541 
11542    if (!ast_strlen_zero(p->context))
11543       len += strlen(p->context) + 1; /* Allow for seperator */
11544 
11545    if (!(mwi_sub = ast_calloc(1, len)))
11546       return -1;
11547 
11548    mwi_sub->uniqueid = p->uniqueid;
11549    if (!ast_strlen_zero(p->mailbox))
11550       strcpy(mwi_sub->mailbox, p->mailbox);
11551 
11552    if (!ast_strlen_zero(p->context)) {
11553       strcat(mwi_sub->mailbox, "@");
11554       strcat(mwi_sub->mailbox, p->context);
11555    }
11556 
11557    AST_RWLIST_WRLOCK(&mwi_subs);
11558    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11559    AST_RWLIST_UNLOCK(&mwi_subs);
11560    ast_free((void *) p->mailbox);
11561    ast_free((void *) p->context);
11562    ast_free(p);
11563    poll_subscribed_mailbox(mwi_sub);
11564    return 0;
11565 }
11566 
11567 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11568 {
11569    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11570 
11571    if (!uniqueid) {
11572       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11573       return;
11574    }
11575 
11576    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11577       ast_free(uniqueid);
11578       return;
11579    }
11580 
11581    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11582       ast_free(uniqueid);
11583       return;
11584    }
11585 
11586    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11587    *uniqueid = u;
11588    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11589       ast_free(uniqueid);
11590    }
11591 }
11592 
11593 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11594 {
11595    struct mwi_sub_task *mwist;
11596    
11597    if (ast_event_get_type(event) != AST_EVENT_SUB)
11598       return;
11599 
11600    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11601       return;
11602 
11603    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11604       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11605       return;
11606    }
11607    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11608    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11609    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11610    
11611    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11612       ast_free(mwist);
11613    }
11614 }
11615 
11616 static void start_poll_thread(void)
11617 {
11618    int errcode;
11619    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11620       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11621       AST_EVENT_IE_END);
11622 
11623    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11624       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11625       AST_EVENT_IE_END);
11626 
11627    if (mwi_sub_sub)
11628       ast_event_report_subs(mwi_sub_sub);
11629 
11630    poll_thread_run = 1;
11631 
11632    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11633       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11634    }
11635 }
11636 
11637 static void stop_poll_thread(void)
11638 {
11639    poll_thread_run = 0;
11640 
11641    if (mwi_sub_sub) {
11642       ast_event_unsubscribe(mwi_sub_sub);
11643       mwi_sub_sub = NULL;
11644    }
11645 
11646    if (mwi_unsub_sub) {
11647       ast_event_unsubscribe(mwi_unsub_sub);
11648       mwi_unsub_sub = NULL;
11649    }
11650 
11651    ast_mutex_lock(&poll_lock);
11652    ast_cond_signal(&poll_cond);
11653    ast_mutex_unlock(&poll_lock);
11654 
11655    pthread_join(poll_thread, NULL);
11656 
11657    poll_thread = AST_PTHREADT_NULL;
11658 }
11659 
11660 /*! \brief Manager list voicemail users command */
11661 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11662 {
11663    struct ast_vm_user *vmu = NULL;
11664    const char *id = astman_get_header(m, "ActionID");
11665    char actionid[128] = "";
11666 
11667    if (!ast_strlen_zero(id))
11668       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11669 
11670    AST_LIST_LOCK(&users);
11671 
11672    if (AST_LIST_EMPTY(&users)) {
11673       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11674       AST_LIST_UNLOCK(&users);
11675       return RESULT_SUCCESS;
11676    }
11677    
11678    astman_send_ack(s, m, "Voicemail user list will follow");
11679    
11680    AST_LIST_TRAVERSE(&users, vmu, list) {
11681       char dirname[256];
11682 
11683 #ifdef IMAP_STORAGE
11684       int new, old;
11685       inboxcount(vmu->mailbox, &new, &old);
11686 #endif
11687       
11688       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11689       astman_append(s,
11690          "%s"
11691          "Event: VoicemailUserEntry\r\n"
11692          "VMContext: %s\r\n"
11693          "VoiceMailbox: %s\r\n"
11694          "Fullname: %s\r\n"
11695          "Email: %s\r\n"
11696          "Pager: %s\r\n"
11697          "ServerEmail: %s\r\n"
11698          "MailCommand: %s\r\n"
11699          "Language: %s\r\n"
11700          "TimeZone: %s\r\n"
11701          "Callback: %s\r\n"
11702          "Dialout: %s\r\n"
11703          "UniqueID: %s\r\n"
11704          "ExitContext: %s\r\n"
11705          "SayDurationMinimum: %d\r\n"
11706          "SayEnvelope: %s\r\n"
11707          "SayCID: %s\r\n"
11708          "AttachMessage: %s\r\n"
11709          "AttachmentFormat: %s\r\n"
11710          "DeleteMessage: %s\r\n"
11711          "VolumeGain: %.2f\r\n"
11712          "CanReview: %s\r\n"
11713          "CallOperator: %s\r\n"
11714          "MaxMessageCount: %d\r\n"
11715          "MaxMessageLength: %d\r\n"
11716          "NewMessageCount: %d\r\n"
11717 #ifdef IMAP_STORAGE
11718          "OldMessageCount: %d\r\n"
11719          "IMAPUser: %s\r\n"
11720 #endif
11721          "\r\n",
11722          actionid,
11723          vmu->context,
11724          vmu->mailbox,
11725          vmu->fullname,
11726          vmu->email,
11727          vmu->pager,
11728          ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11729          mailcmd,
11730          vmu->language,
11731          vmu->zonetag,
11732          vmu->callback,
11733          vmu->dialout,
11734          vmu->uniqueid,
11735          vmu->exit,
11736          vmu->saydurationm,
11737          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11738          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11739          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11740          vmu->attachfmt,
11741          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11742          vmu->volgain,
11743          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11744          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11745          vmu->maxmsg,
11746          vmu->maxsecs,
11747 #ifdef IMAP_STORAGE
11748          new, old, vmu->imapuser
11749 #else
11750          count_messages(vmu, dirname)
11751 #endif
11752          );
11753    }     
11754    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11755 
11756    AST_LIST_UNLOCK(&users);
11757 
11758    return RESULT_SUCCESS;
11759 }
11760 
11761 /*! \brief Free the users structure. */
11762 static void free_vm_users(void) 
11763 {
11764    struct ast_vm_user *current;
11765    AST_LIST_LOCK(&users);
11766    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11767       ast_set_flag(current, VM_ALLOCED);
11768       free_user(current);
11769    }
11770    AST_LIST_UNLOCK(&users);
11771 }
11772 
11773 /*! \brief Free the zones structure. */
11774 static void free_vm_zones(void)
11775 {
11776    struct vm_zone *zcur;
11777    AST_LIST_LOCK(&zones);
11778    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11779       free_zone(zcur);
11780    AST_LIST_UNLOCK(&zones);
11781 }
11782 
11783 static const char *substitute_escapes(const char *value)
11784 {
11785    char *current;
11786 
11787    /* Add 16 for fudge factor */
11788    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11789 
11790    ast_str_reset(str);
11791    
11792    /* Substitute strings \r, \n, and \t into the appropriate characters */
11793    for (current = (char *) value; *current; current++) {
11794       if (*current == '\\') {
11795          current++;
11796          if (!*current) {
11797             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11798             break;
11799          }
11800          switch (*current) {
11801          case '\\':
11802             ast_str_append(&str, 0, "\\");
11803             break;
11804          case 'r':
11805             ast_str_append(&str, 0, "\r");
11806             break;
11807          case 'n':
11808 #ifdef IMAP_STORAGE
11809             if (!str->used || str->str[str->used - 1] != '\r') {
11810                ast_str_append(&str, 0, "\r");
11811             }
11812 #endif
11813             ast_str_append(&str, 0, "\n");
11814             break;
11815          case 't':
11816             ast_str_append(&str, 0, "\t");
11817             break;
11818          default:
11819             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11820             break;
11821          }
11822       } else {
11823          ast_str_append(&str, 0, "%c", *current);
11824       }
11825    }
11826 
11827    return ast_str_buffer(str);
11828 }
11829 
11830 static int load_config(int reload)
11831 {
11832    struct ast_config *cfg, *ucfg;
11833    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11834    int res;
11835 
11836    ast_unload_realtime("voicemail");
11837    ast_unload_realtime("voicemail_data");
11838 
11839    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11840       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11841          return 0;
11842       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11843          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11844          ucfg = NULL;
11845       }
11846       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11847       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11848          ast_config_destroy(ucfg);
11849          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11850          return 0;
11851       }
11852    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11853       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11854       return 0;
11855    } else {
11856       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11857       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11858          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11859          ucfg = NULL;
11860       }
11861    }
11862 
11863    res = actual_load_config(reload, cfg, ucfg);
11864 
11865    ast_config_destroy(cfg);
11866    ast_config_destroy(ucfg);
11867 
11868    return res;
11869 }
11870 
11871 #ifdef TEST_FRAMEWORK
11872 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11873 {
11874    ast_unload_realtime("voicemail");
11875    ast_unload_realtime("voicemail_data");
11876    return actual_load_config(reload, cfg, ucfg);
11877 }
11878 #endif
11879 
11880 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11881 {
11882    struct ast_vm_user *current;
11883    char *cat;
11884    struct ast_variable *var;
11885    const char *val;
11886    char *q, *stringp, *tmp;
11887    int x;
11888    int tmpadsi[4];
11889    char secretfn[PATH_MAX] = "";
11890 
11891 #ifdef IMAP_STORAGE
11892    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11893 #endif
11894    /* set audio control prompts */
11895    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11896    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11897    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11898    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11899    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11900 
11901    /* Free all the users structure */  
11902    free_vm_users();
11903 
11904    /* Free all the zones structure */
11905    free_vm_zones();
11906 
11907    AST_LIST_LOCK(&users);  
11908 
11909    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11910    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11911 
11912    if (cfg) {
11913       /* General settings */
11914 
11915       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11916          val = "default";
11917       ast_copy_string(userscontext, val, sizeof(userscontext));
11918       /* Attach voice message to mail message ? */
11919       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11920          val = "yes";
11921       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11922 
11923       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11924          val = "no";
11925       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11926 
11927       volgain = 0.0;
11928       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11929          sscanf(val, "%30lf", &volgain);
11930 
11931 #ifdef ODBC_STORAGE
11932       strcpy(odbc_database, "asterisk");
11933       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11934          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11935       }
11936       strcpy(odbc_table, "voicemessages");
11937       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11938          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11939       }
11940 #endif      
11941       /* Mail command */
11942       strcpy(mailcmd, SENDMAIL);
11943       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11944          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11945 
11946       maxsilence = 0;
11947       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11948          maxsilence = atoi(val);
11949          if (maxsilence > 0)
11950             maxsilence *= 1000;
11951       }
11952       
11953       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11954          maxmsg = MAXMSG;
11955       } else {
11956          maxmsg = atoi(val);
11957          if (maxmsg < 0) {
11958             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11959             maxmsg = MAXMSG;
11960          } else if (maxmsg > MAXMSGLIMIT) {
11961             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11962             maxmsg = MAXMSGLIMIT;
11963          }
11964       }
11965 
11966       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11967          maxdeletedmsg = 0;
11968       } else {
11969          if (sscanf(val, "%30d", &x) == 1)
11970             maxdeletedmsg = x;
11971          else if (ast_true(val))
11972             maxdeletedmsg = MAXMSG;
11973          else
11974             maxdeletedmsg = 0;
11975 
11976          if (maxdeletedmsg < 0) {
11977             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11978             maxdeletedmsg = MAXMSG;
11979          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11980             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11981             maxdeletedmsg = MAXMSGLIMIT;
11982          }
11983       }
11984 
11985       /* Load date format config for voicemail mail */
11986       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11987          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11988       }
11989 
11990       /* Load date format config for voicemail pager mail */
11991       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11992          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11993       }
11994 
11995       /* External password changing command */
11996       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11997          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11998          pwdchange = PWDCHANGE_EXTERNAL;
11999       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
12000          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12001          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
12002       }
12003  
12004       /* External password validation command */
12005       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
12006          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
12007          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
12008       }
12009 
12010 #ifdef IMAP_STORAGE
12011       /* IMAP server address */
12012       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
12013          ast_copy_string(imapserver, val, sizeof(imapserver));
12014       } else {
12015          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
12016       }
12017       /* IMAP server port */
12018       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
12019          ast_copy_string(imapport, val, sizeof(imapport));
12020       } else {
12021          ast_copy_string(imapport, "143", sizeof(imapport));
12022       }
12023       /* IMAP server flags */
12024       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
12025          ast_copy_string(imapflags, val, sizeof(imapflags));
12026       }
12027       /* IMAP server master username */
12028       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12029          ast_copy_string(authuser, val, sizeof(authuser));
12030       }
12031       /* IMAP server master password */
12032       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12033          ast_copy_string(authpassword, val, sizeof(authpassword));
12034       }
12035       /* Expunge on exit */
12036       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12037          if (ast_false(val))
12038             expungeonhangup = 0;
12039          else
12040             expungeonhangup = 1;
12041       } else {
12042          expungeonhangup = 1;
12043       }
12044       /* IMAP voicemail folder */
12045       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12046          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12047       } else {
12048          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12049       }
12050       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12051          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12052       }
12053       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12054          imapgreetings = ast_true(val);
12055       } else {
12056          imapgreetings = 0;
12057       }
12058       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12059          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12060       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12061          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12062          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12063       } else {
12064          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12065       }
12066 
12067       /* There is some very unorthodox casting done here. This is due
12068        * to the way c-client handles the argument passed in. It expects a 
12069        * void pointer and casts the pointer directly to a long without
12070        * first dereferencing it. */
12071       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12072          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12073       } else {
12074          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12075       }
12076 
12077       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12078          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12079       } else {
12080          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12081       }
12082 
12083       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12084          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12085       } else {
12086          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12087       }
12088 
12089       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12090          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12091       } else {
12092          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12093       }
12094 
12095       /* Increment configuration version */
12096       imapversion++;
12097 #endif
12098       /* External voicemail notify application */
12099       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12100          ast_copy_string(externnotify, val, sizeof(externnotify));
12101          ast_debug(1, "found externnotify: %s\n", externnotify);
12102       } else {
12103          externnotify[0] = '\0';
12104       }
12105 
12106       /* SMDI voicemail notification */
12107       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12108          ast_debug(1, "Enabled SMDI voicemail notification\n");
12109          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12110             smdi_iface = ast_smdi_interface_find(val);
12111          } else {
12112             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12113             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12114          }
12115          if (!smdi_iface) {
12116             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12117          } 
12118       }
12119 
12120       /* Silence treshold */
12121       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12122       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12123          silencethreshold = atoi(val);
12124       
12125       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12126          val = ASTERISK_USERNAME;
12127       ast_copy_string(serveremail, val, sizeof(serveremail));
12128       
12129       vmmaxsecs = 0;
12130       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12131          if (sscanf(val, "%30d", &x) == 1) {
12132             vmmaxsecs = x;
12133          } else {
12134             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12135          }
12136       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12137          static int maxmessage_deprecate = 0;
12138          if (maxmessage_deprecate == 0) {
12139             maxmessage_deprecate = 1;
12140             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12141          }
12142          if (sscanf(val, "%30d", &x) == 1) {
12143             vmmaxsecs = x;
12144          } else {
12145             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12146          }
12147       }
12148 
12149       vmminsecs = 0;
12150       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12151          if (sscanf(val, "%30d", &x) == 1) {
12152             vmminsecs = x;
12153             if (maxsilence / 1000 >= vmminsecs) {
12154                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12155             }
12156          } else {
12157             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12158          }
12159       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12160          static int maxmessage_deprecate = 0;
12161          if (maxmessage_deprecate == 0) {
12162             maxmessage_deprecate = 1;
12163             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12164          }
12165          if (sscanf(val, "%30d", &x) == 1) {
12166             vmminsecs = x;
12167             if (maxsilence / 1000 >= vmminsecs) {
12168                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12169             }
12170          } else {
12171             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12172          }
12173       }
12174 
12175       val = ast_variable_retrieve(cfg, "general", "format");
12176       if (!val) {
12177          val = "wav";   
12178       } else {
12179          tmp = ast_strdupa(val);
12180          val = ast_format_str_reduce(tmp);
12181          if (!val) {
12182             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12183             val = "wav";
12184          }
12185       }
12186       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12187 
12188       skipms = 3000;
12189       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12190          if (sscanf(val, "%30d", &x) == 1) {
12191             maxgreet = x;
12192          } else {
12193             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12194          }
12195       }
12196 
12197       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12198          if (sscanf(val, "%30d", &x) == 1) {
12199             skipms = x;
12200          } else {
12201             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12202          }
12203       }
12204 
12205       maxlogins = 3;
12206       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12207          if (sscanf(val, "%30d", &x) == 1) {
12208             maxlogins = x;
12209          } else {
12210             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12211          }
12212       }
12213 
12214       minpassword = MINPASSWORD;
12215       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12216          if (sscanf(val, "%30d", &x) == 1) {
12217             minpassword = x;
12218          } else {
12219             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12220          }
12221       }
12222 
12223       /* Force new user to record name ? */
12224       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12225          val = "no";
12226       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12227 
12228       /* Force new user to record greetings ? */
12229       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12230          val = "no";
12231       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12232 
12233       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12234          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12235          stringp = ast_strdupa(val);
12236          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12237             if (!ast_strlen_zero(stringp)) {
12238                q = strsep(&stringp, ",");
12239                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12240                   q++;
12241                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12242                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12243             } else {
12244                cidinternalcontexts[x][0] = '\0';
12245             }
12246          }
12247       }
12248       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12249          ast_debug(1, "VM Review Option disabled globally\n");
12250          val = "no";
12251       }
12252       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12253 
12254       /* Temporary greeting reminder */
12255       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12256          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12257          val = "no";
12258       } else {
12259          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12260       }
12261       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12262       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12263          ast_debug(1, "VM next message wrap disabled globally\n");
12264          val = "no";
12265       }
12266       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12267 
12268       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12269          ast_debug(1, "VM Operator break disabled globally\n");
12270          val = "no";
12271       }
12272       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12273 
12274       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12275          ast_debug(1, "VM CID Info before msg disabled globally\n");
12276          val = "no";
12277       } 
12278       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12279 
12280       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12281          ast_debug(1, "Send Voicemail msg disabled globally\n");
12282          val = "no";
12283       }
12284       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12285    
12286       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12287          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12288          val = "yes";
12289       }
12290       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12291 
12292       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12293          ast_debug(1, "Move Heard enabled globally\n");
12294          val = "yes";
12295       }
12296       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12297 
12298       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12299          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12300          val = "no";
12301       }
12302       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12303 
12304       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12305          ast_debug(1, "Duration info before msg enabled globally\n");
12306          val = "yes";
12307       }
12308       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12309 
12310       saydurationminfo = 2;
12311       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12312          if (sscanf(val, "%30d", &x) == 1) {
12313             saydurationminfo = x;
12314          } else {
12315             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12316          }
12317       }
12318 
12319       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12320          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12321          val = "no";
12322       }
12323       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12324 
12325       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12326          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12327          ast_debug(1, "found dialout context: %s\n", dialcontext);
12328       } else {
12329          dialcontext[0] = '\0';  
12330       }
12331       
12332       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12333          ast_copy_string(callcontext, val, sizeof(callcontext));
12334          ast_debug(1, "found callback context: %s\n", callcontext);
12335       } else {
12336          callcontext[0] = '\0';
12337       }
12338 
12339       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12340          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12341          ast_debug(1, "found operator context: %s\n", exitcontext);
12342       } else {
12343          exitcontext[0] = '\0';
12344       }
12345       
12346       /* load password sounds configuration */
12347       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12348          ast_copy_string(vm_password, val, sizeof(vm_password));
12349       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12350          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12351       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12352          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12353       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12354          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12355       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12356          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12357       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12358          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12359       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12360          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12361       }
12362       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12363          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12364       }
12365       /* load configurable audio prompts */
12366       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12367          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12368       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12369          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12370       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12371          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12372       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12373          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12374       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12375          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12376 
12377       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12378          val = "no";
12379       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12380 
12381       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12382          val = "voicemail.conf";
12383       }
12384       if (!(strcmp(val, "spooldir"))) {
12385          passwordlocation = OPT_PWLOC_SPOOLDIR;
12386       } else {
12387          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12388       }
12389 
12390       poll_freq = DEFAULT_POLL_FREQ;
12391       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12392          if (sscanf(val, "%30u", &poll_freq) != 1) {
12393             poll_freq = DEFAULT_POLL_FREQ;
12394             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12395          }
12396       }
12397 
12398       poll_mailboxes = 0;
12399       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12400          poll_mailboxes = ast_true(val);
12401 
12402       memset(fromstring, 0, sizeof(fromstring));
12403       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12404       strcpy(charset, "ISO-8859-1");
12405       if (emailbody) {
12406          ast_free(emailbody);
12407          emailbody = NULL;
12408       }
12409       if (emailsubject) {
12410          ast_free(emailsubject);
12411          emailsubject = NULL;
12412       }
12413       if (pagerbody) {
12414          ast_free(pagerbody);
12415          pagerbody = NULL;
12416       }
12417       if (pagersubject) {
12418          ast_free(pagersubject);
12419          pagersubject = NULL;
12420       }
12421       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12422          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12423       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12424          ast_copy_string(fromstring, val, sizeof(fromstring));
12425       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12426          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12427       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12428          ast_copy_string(charset, val, sizeof(charset));
12429       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12430          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12431          for (x = 0; x < 4; x++) {
12432             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12433          }
12434       }
12435       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12436          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12437          for (x = 0; x < 4; x++) {
12438             memcpy(&adsisec[x], &tmpadsi[x], 1);
12439          }
12440       }
12441       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12442          if (atoi(val)) {
12443             adsiver = atoi(val);
12444          }
12445       }
12446       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12447          ast_copy_string(zonetag, val, sizeof(zonetag));
12448       }
12449       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12450          ast_copy_string(locale, val, sizeof(locale));
12451       }
12452       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12453          emailsubject = ast_strdup(substitute_escapes(val));
12454       }
12455       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12456          emailbody = ast_strdup(substitute_escapes(val));
12457       }
12458       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12459          pagersubject = ast_strdup(substitute_escapes(val));
12460       }
12461       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12462          pagerbody = ast_strdup(substitute_escapes(val));
12463       }
12464 
12465       /* load mailboxes from users.conf */
12466       if (ucfg) { 
12467          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12468             if (!strcasecmp(cat, "general")) {
12469                continue;
12470             }
12471             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12472                continue;
12473             if ((current = find_or_create(userscontext, cat))) {
12474                populate_defaults(current);
12475                apply_options_full(current, ast_variable_browse(ucfg, cat));
12476                ast_copy_string(current->context, userscontext, sizeof(current->context));
12477                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12478                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12479                }
12480 
12481                switch (current->passwordlocation) {
12482                case OPT_PWLOC_SPOOLDIR:
12483                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12484                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12485                }
12486             }
12487          }
12488       }
12489 
12490       /* load mailboxes from voicemail.conf */
12491       cat = ast_category_browse(cfg, NULL);
12492       while (cat) {
12493          if (strcasecmp(cat, "general")) {
12494             var = ast_variable_browse(cfg, cat);
12495             if (strcasecmp(cat, "zonemessages")) {
12496                /* Process mailboxes in this context */
12497                while (var) {
12498                   append_mailbox(cat, var->name, var->value);
12499                   var = var->next;
12500                }
12501             } else {
12502                /* Timezones in this context */
12503                while (var) {
12504                   struct vm_zone *z;
12505                   if ((z = ast_malloc(sizeof(*z)))) {
12506                      char *msg_format, *tzone;
12507                      msg_format = ast_strdupa(var->value);
12508                      tzone = strsep(&msg_format, "|,");
12509                      if (msg_format) {
12510                         ast_copy_string(z->name, var->name, sizeof(z->name));
12511                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12512                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12513                         AST_LIST_LOCK(&zones);
12514                         AST_LIST_INSERT_HEAD(&zones, z, list);
12515                         AST_LIST_UNLOCK(&zones);
12516                      } else {
12517                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12518                         ast_free(z);
12519                      }
12520                   } else {
12521                      AST_LIST_UNLOCK(&users);
12522                      return -1;
12523                   }
12524                   var = var->next;
12525                }
12526             }
12527          }
12528          cat = ast_category_browse(cfg, cat);
12529       }
12530 
12531       AST_LIST_UNLOCK(&users);
12532 
12533       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12534          start_poll_thread();
12535       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12536          stop_poll_thread();;
12537 
12538       return 0;
12539    } else {
12540       AST_LIST_UNLOCK(&users);
12541       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12542       return 0;
12543    }
12544 }
12545 
12546 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12547 {
12548    int res = -1;
12549    char dir[PATH_MAX];
12550    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12551    ast_debug(2, "About to try retrieving name file %s\n", dir);
12552    RETRIEVE(dir, -1, mailbox, context);
12553    if (ast_fileexists(dir, NULL, NULL)) {
12554       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12555    }
12556    DISPOSE(dir, -1);
12557    return res;
12558 }
12559 
12560 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12561    struct ast_config *pwconf;
12562    struct ast_flags config_flags = { 0 };
12563 
12564    pwconf = ast_config_load(secretfn, config_flags);
12565    if (pwconf) {
12566       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12567       if (val) {
12568          ast_copy_string(password, val, passwordlen);
12569          ast_config_destroy(pwconf);
12570          return;
12571       }
12572       ast_config_destroy(pwconf);
12573    }
12574    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12575 }
12576 
12577 static int write_password_to_file(const char *secretfn, const char *password) {
12578    struct ast_config *conf;
12579    struct ast_category *cat;
12580    struct ast_variable *var;
12581    int res = -1;
12582 
12583    if (!(conf = ast_config_new())) {
12584       ast_log(LOG_ERROR, "Error creating new config structure\n");
12585       return res;
12586    }
12587    if (!(cat = ast_category_new("general", "", 1))) {
12588       ast_log(LOG_ERROR, "Error creating new category structure\n");
12589       ast_config_destroy(conf);
12590       return res;
12591    }
12592    if (!(var = ast_variable_new("password", password, ""))) {
12593       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12594       ast_config_destroy(conf);
12595       ast_category_destroy(cat);
12596       return res;
12597    }
12598    ast_category_append(conf, cat);
12599    ast_variable_append(cat, var);
12600    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12601       res = 0;
12602    } else {
12603       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12604    }
12605 
12606    ast_config_destroy(conf);
12607    return res;
12608 }
12609 
12610 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12611 {
12612    char *context;
12613    char *args_copy;
12614    int res;
12615 
12616    if (ast_strlen_zero(data)) {
12617       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12618       return -1;
12619    }
12620 
12621    args_copy = ast_strdupa(data);
12622    if ((context = strchr(args_copy, '@'))) {
12623       *context++ = '\0';
12624    } else {
12625       context = "default";
12626    }
12627 
12628    if ((res = sayname(chan, args_copy, context) < 0)) {
12629       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12630       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12631       if (!res) {
12632          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12633       }
12634    }
12635 
12636    return res;
12637 }
12638 
12639 #ifdef TEST_FRAMEWORK
12640 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12641 {
12642    return 0;
12643 }
12644 
12645 static struct ast_frame *fake_read(struct ast_channel *ast)
12646 {
12647    return &ast_null_frame;
12648 }
12649 
12650 AST_TEST_DEFINE(test_voicemail_vmsayname)
12651 {
12652    char dir[PATH_MAX];
12653    char dir2[PATH_MAX];
12654    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12655    static const char TEST_EXTENSION[] = "1234";
12656 
12657    struct ast_channel *test_channel1 = NULL;
12658    int res = -1;
12659 
12660    static const struct ast_channel_tech fake_tech = {
12661       .write = fake_write,
12662       .read = fake_read,
12663    };
12664 
12665    switch (cmd) {
12666    case TEST_INIT:
12667       info->name = "vmsayname_exec";
12668       info->category = "/apps/app_voicemail/";
12669       info->summary = "Vmsayname unit test";
12670       info->description =
12671          "This tests passing various parameters to vmsayname";
12672       return AST_TEST_NOT_RUN;
12673    case TEST_EXECUTE:
12674       break;
12675    }
12676 
12677    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12678         NULL, NULL, 0, 0, "TestChannel1"))) {
12679       goto exit_vmsayname_test;
12680    }
12681 
12682    /* normally this is done in the channel driver */
12683    test_channel1->nativeformats = AST_FORMAT_GSM;
12684    test_channel1->writeformat = AST_FORMAT_GSM;
12685    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12686    test_channel1->readformat = AST_FORMAT_GSM;
12687    test_channel1->rawreadformat = AST_FORMAT_GSM;
12688    test_channel1->tech = &fake_tech;
12689 
12690    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12691    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12692    if (!(res = vmsayname_exec(test_channel1, dir))) {
12693       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12694       if (ast_fileexists(dir, NULL, NULL)) {
12695          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12696          res = -1;
12697          goto exit_vmsayname_test;
12698       } else {
12699          /* no greeting already exists as expected, let's create one to fully test sayname */
12700          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12701             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12702             goto exit_vmsayname_test;
12703          }
12704          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12705          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12706          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12707          if ((res = symlink(dir, dir2))) {
12708             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12709             goto exit_vmsayname_test;
12710          }
12711          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12712          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12713          res = vmsayname_exec(test_channel1, dir);
12714 
12715          /* TODO: there may be a better way to do this */
12716          unlink(dir2);
12717          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12718          rmdir(dir2);
12719          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12720          rmdir(dir2);
12721       }
12722    }
12723 
12724 exit_vmsayname_test:
12725 
12726    if (test_channel1) {
12727       ast_hangup(test_channel1);
12728    }
12729 
12730    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12731 }
12732 
12733 AST_TEST_DEFINE(test_voicemail_msgcount)
12734 {
12735    int i, j, res = AST_TEST_PASS, syserr;
12736    struct ast_vm_user *vmu;
12737    struct vm_state vms;
12738 #ifdef IMAP_STORAGE
12739    struct ast_channel *chan = NULL;
12740 #endif
12741    struct {
12742       char dir[256];
12743       char file[256];
12744       char txtfile[256];
12745    } tmp[3];
12746    char syscmd[256];
12747    const char origweasels[] = "tt-weasels";
12748    const char testcontext[] = "test";
12749    const char testmailbox[] = "00000000";
12750    const char testspec[] = "00000000@test";
12751    FILE *txt;
12752    int new, old, urgent;
12753    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12754    const int folder2mbox[3] = { 1, 11, 0 };
12755    const int expected_results[3][12] = {
12756       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12757       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12758       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12759       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12760    };
12761 
12762    switch (cmd) {
12763    case TEST_INIT:
12764       info->name = "test_voicemail_msgcount";
12765       info->category = "/apps/app_voicemail/";
12766       info->summary = "Test Voicemail status checks";
12767       info->description =
12768          "Verify that message counts are correct when retrieved through the public API";
12769       return AST_TEST_NOT_RUN;
12770    case TEST_EXECUTE:
12771       break;
12772    }
12773 
12774    /* Make sure the original path was completely empty */
12775    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12776    if ((syserr = ast_safe_system(syscmd))) {
12777       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12778          syserr > 0 ? strerror(syserr) : "unable to fork()");
12779       return AST_TEST_FAIL;
12780    }
12781 
12782 #ifdef IMAP_STORAGE
12783    if (!(chan = ast_dummy_channel_alloc())) {
12784       ast_test_status_update(test, "Unable to create dummy channel\n");
12785       return AST_TEST_FAIL;
12786    }
12787 #endif
12788 
12789    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12790       !(vmu = find_or_create(testcontext, testmailbox))) {
12791       ast_test_status_update(test, "Cannot create vmu structure\n");
12792       ast_unreplace_sigchld();
12793 #ifdef IMAP_STORAGE
12794       chan = ast_channel_unref(chan);
12795 #endif
12796       return AST_TEST_FAIL;
12797    }
12798 
12799    populate_defaults(vmu);
12800    memset(&vms, 0, sizeof(vms));
12801 
12802    /* Create temporary voicemail */
12803    for (i = 0; i < 3; i++) {
12804       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12805       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12806       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12807 
12808       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12809          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12810             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12811          if ((syserr = ast_safe_system(syscmd))) {
12812             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12813                syserr > 0 ? strerror(syserr) : "unable to fork()");
12814             ast_unreplace_sigchld();
12815 #ifdef IMAP_STORAGE
12816             chan = ast_channel_unref(chan);
12817 #endif
12818             return AST_TEST_FAIL;
12819          }
12820       }
12821 
12822       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12823          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12824          fclose(txt);
12825       } else {
12826          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12827          res = AST_TEST_FAIL;
12828          break;
12829       }
12830       open_mailbox(&vms, vmu, folder2mbox[i]);
12831       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12832 
12833       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12834       for (j = 0; j < 3; j++) {
12835          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12836          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12837             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12838                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12839             res = AST_TEST_FAIL;
12840          }
12841       }
12842 
12843       new = old = urgent = 0;
12844       if (ast_app_inboxcount(testspec, &new, &old)) {
12845          ast_test_status_update(test, "inboxcount returned failure\n");
12846          res = AST_TEST_FAIL;
12847       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12848          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12849             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12850          res = AST_TEST_FAIL;
12851       }
12852 
12853       new = old = urgent = 0;
12854       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12855          ast_test_status_update(test, "inboxcount2 returned failure\n");
12856          res = AST_TEST_FAIL;
12857       } else if (old != expected_results[i][6 + 0] ||
12858             urgent != expected_results[i][6 + 1] ||
12859                new != expected_results[i][6 + 2]    ) {
12860          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12861             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12862          res = AST_TEST_FAIL;
12863       }
12864 
12865       new = old = urgent = 0;
12866       for (j = 0; j < 3; j++) {
12867          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12868             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12869                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12870             res = AST_TEST_FAIL;
12871          }
12872       }
12873    }
12874 
12875    for (i = 0; i < 3; i++) {
12876       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12877        * server, in which case, the rm below will not affect the
12878        * voicemails. */
12879       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12880       DISPOSE(tmp[i].dir, 0);
12881    }
12882 
12883    if (vms.deleted) {
12884       ast_free(vms.deleted);
12885    }
12886    if (vms.heard) {
12887       ast_free(vms.heard);
12888    }
12889 
12890 #ifdef IMAP_STORAGE
12891    chan = ast_channel_unref(chan);
12892 #endif
12893 
12894    /* And remove test directory */
12895    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12896    if ((syserr = ast_safe_system(syscmd))) {
12897       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12898          syserr > 0 ? strerror(syserr) : "unable to fork()");
12899    }
12900 
12901    return res;
12902 }
12903 
12904 AST_TEST_DEFINE(test_voicemail_notify_endl)
12905 {
12906    int res = AST_TEST_PASS;
12907    char testcontext[] = "test";
12908    char testmailbox[] = "00000000";
12909    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12910    char attach[256], attach2[256];
12911    char buf[256] = ""; /* No line should actually be longer than 80 */
12912    struct ast_channel *chan = NULL;
12913    struct ast_vm_user *vmu, vmus = {
12914       .flags = 0,
12915    };
12916    FILE *file;
12917    struct {
12918       char *name;
12919       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12920       void *location;
12921       union {
12922          int intval;
12923          char *strval;
12924       } u;
12925    } test_items[] = {
12926       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12927       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12928       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12929       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12930       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12931       { "attach2", STRPTR, attach2, .u.strval = "" },
12932       { "attach", STRPTR, attach, .u.strval = "" },
12933    };
12934    int which;
12935 
12936    switch (cmd) {
12937    case TEST_INIT:
12938       info->name = "test_voicemail_notify_endl";
12939       info->category = "/apps/app_voicemail/";
12940       info->summary = "Test Voicemail notification end-of-line";
12941       info->description =
12942          "Verify that notification emails use a consistent end-of-line character";
12943       return AST_TEST_NOT_RUN;
12944    case TEST_EXECUTE:
12945       break;
12946    }
12947 
12948    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12949    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12950 
12951    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12952       !(vmu = find_or_create(testcontext, testmailbox))) {
12953       ast_test_status_update(test, "Cannot create vmu structure\n");
12954       return AST_TEST_NOT_RUN;
12955    }
12956 
12957    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12958       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12959       return AST_TEST_NOT_RUN;
12960    }
12961 
12962    populate_defaults(vmu);
12963    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12964 #ifdef IMAP_STORAGE
12965    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12966 #endif
12967 
12968    file = tmpfile();
12969    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12970       /* Kill previous test, if any */
12971       rewind(file);
12972       if (ftruncate(fileno(file), 0)) {
12973          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12974          res = AST_TEST_FAIL;
12975          break;
12976       }
12977 
12978       /* Make each change, in order, to the test mailbox */
12979       if (test_items[which].type == INT) {
12980          *((int *) test_items[which].location) = test_items[which].u.intval;
12981       } else if (test_items[which].type == FLAGVAL) {
12982          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12983             ast_clear_flag(vmu, test_items[which].u.intval);
12984          } else {
12985             ast_set_flag(vmu, test_items[which].u.intval);
12986          }
12987       } else if (test_items[which].type == STATIC) {
12988          strcpy(test_items[which].location, test_items[which].u.strval);
12989       } else if (test_items[which].type == STRPTR) {
12990          test_items[which].location = test_items[which].u.strval;
12991       }
12992 
12993       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12994       rewind(file);
12995       while (fgets(buf, sizeof(buf), file)) {
12996          if (
12997 #ifdef IMAP_STORAGE
12998          buf[strlen(buf) - 2] != '\r'
12999 #else
13000          buf[strlen(buf) - 2] == '\r'
13001 #endif
13002          || buf[strlen(buf) - 1] != '\n') {
13003             res = AST_TEST_FAIL;
13004          }
13005       }
13006    }
13007    fclose(file);
13008    return res;
13009 }
13010 
13011 AST_TEST_DEFINE(test_voicemail_load_config)
13012 {
13013    int res = AST_TEST_PASS;
13014    struct ast_vm_user *vmu;
13015    struct ast_config *cfg;
13016    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
13017    int fd;
13018    FILE *file;
13019    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
13020 
13021    switch (cmd) {
13022    case TEST_INIT:
13023       info->name = "test_voicemail_load_config";
13024       info->category = "/apps/app_voicemail/";
13025       info->summary = "Test loading Voicemail config";
13026       info->description =
13027          "Verify that configuration is loaded consistently. "
13028          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13029          "some options were loaded after the mailboxes were instantiated, causing "
13030          "those options not to be set correctly.";
13031       return AST_TEST_NOT_RUN;
13032    case TEST_EXECUTE:
13033       break;
13034    }
13035 
13036    /* build a config file by hand... */
13037    if ((fd = mkstemp(config_filename)) < 0) {
13038       return AST_TEST_FAIL;
13039    }
13040    if (!(file = fdopen(fd, "w"))) {
13041       close(fd);
13042       unlink(config_filename);
13043       return AST_TEST_FAIL;
13044    }
13045    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13046    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13047    fputs("00000002 => 9999,Mrs. Test\n", file);
13048    fclose(file);
13049 
13050    if (!(cfg = ast_config_load(config_filename, config_flags))) {
13051       res = AST_TEST_FAIL;
13052       goto cleanup;
13053    }
13054 
13055    load_config_from_memory(1, cfg, NULL);
13056    ast_config_destroy(cfg);
13057 
13058 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13059    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13060    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13061 
13062    AST_LIST_LOCK(&users);
13063    AST_LIST_TRAVERSE(&users, vmu, list) {
13064       if (!strcmp(vmu->mailbox, "00000001")) {
13065          if (0); /* trick to get CHECK to work */
13066          CHECK(vmu, callback, "othercontext")
13067          CHECK(vmu, locale, "nl_NL.UTF-8")
13068          CHECK(vmu, zonetag, "central")
13069       } else if (!strcmp(vmu->mailbox, "00000002")) {
13070          if (0); /* trick to get CHECK to work */
13071          CHECK(vmu, callback, "somecontext")
13072          CHECK(vmu, locale, "de_DE.UTF-8")
13073          CHECK(vmu, zonetag, "european")
13074       }
13075    }
13076    AST_LIST_UNLOCK(&users);
13077 
13078 #undef CHECK
13079 
13080    /* restore config */
13081    load_config(1); /* this might say "Failed to load configuration file." */
13082 
13083 cleanup:
13084    unlink(config_filename);
13085    return res;
13086 }
13087 
13088 #endif /* defined(TEST_FRAMEWORK) */
13089 
13090 static int reload(void)
13091 {
13092    return load_config(1);
13093 }
13094 
13095 static int unload_module(void)
13096 {
13097    int res;
13098 
13099    res = ast_unregister_application(app);
13100    res |= ast_unregister_application(app2);
13101    res |= ast_unregister_application(app3);
13102    res |= ast_unregister_application(app4);
13103    res |= ast_unregister_application(sayname_app);
13104    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13105    res |= ast_manager_unregister("VoicemailUsersList");
13106    res |= ast_data_unregister(NULL);
13107 #ifdef TEST_FRAMEWORK
13108    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13109    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13110    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13111    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13112    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13113 #endif
13114    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13115    ast_uninstall_vm_functions();
13116    ao2_ref(inprocess_container, -1);
13117 
13118    if (poll_thread != AST_PTHREADT_NULL)
13119       stop_poll_thread();
13120 
13121    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13122    ast_unload_realtime("voicemail");
13123    ast_unload_realtime("voicemail_data");
13124 
13125    free_vm_users();
13126    free_vm_zones();
13127    return res;
13128 }
13129 
13130 static int load_module(void)
13131 {
13132    int res;
13133    my_umask = umask(0);
13134    umask(my_umask);
13135 
13136    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13137       return AST_MODULE_LOAD_DECLINE;
13138    }
13139 
13140    /* compute the location of the voicemail spool directory */
13141    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13142    
13143    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13144       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13145    }
13146 
13147    if ((res = load_config(0)))
13148       return res;
13149 
13150    res = ast_register_application_xml(app, vm_exec);
13151    res |= ast_register_application_xml(app2, vm_execmain);
13152    res |= ast_register_application_xml(app3, vm_box_exists);
13153    res |= ast_register_application_xml(app4, vmauthenticate);
13154    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13155    res |= ast_custom_function_register(&mailbox_exists_acf);
13156    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13157 #ifdef TEST_FRAMEWORK
13158    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13159    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13160    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13161    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13162    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13163 #endif
13164 
13165    if (res)
13166       return res;
13167 
13168    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13169    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13170 
13171    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13172    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13173    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13174 
13175    return res;
13176 }
13177 
13178 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13179 {
13180    int cmd = 0;
13181    char destination[80] = "";
13182    int retries = 0;
13183 
13184    if (!num) {
13185       ast_verb(3, "Destination number will be entered manually\n");
13186       while (retries < 3 && cmd != 't') {
13187          destination[1] = '\0';
13188          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13189          if (!cmd)
13190             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13191          if (!cmd)
13192             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13193          if (!cmd) {
13194             cmd = ast_waitfordigit(chan, 6000);
13195             if (cmd)
13196                destination[0] = cmd;
13197          }
13198          if (!cmd) {
13199             retries++;
13200          } else {
13201 
13202             if (cmd < 0)
13203                return 0;
13204             if (cmd == '*') {
13205                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13206                return 0;
13207             }
13208             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13209                retries++;
13210             else
13211                cmd = 't';
13212          }
13213          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13214       }
13215       if (retries >= 3) {
13216          return 0;
13217       }
13218       
13219    } else {
13220       if (option_verbose > 2)
13221          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13222       ast_copy_string(destination, num, sizeof(destination));
13223    }
13224 
13225    if (!ast_strlen_zero(destination)) {
13226       if (destination[strlen(destination) -1 ] == '*')
13227          return 0; 
13228       if (option_verbose > 2)
13229          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13230       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13231       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13232       chan->priority = 0;
13233       return 9;
13234    }
13235    return 0;
13236 }
13237 
13238 /*!
13239  * \brief The advanced options within a message.
13240  * \param chan
13241  * \param vmu 
13242  * \param vms
13243  * \param msg
13244  * \param option
13245  * \param record_gain
13246  *
13247  * Provides handling for the play message envelope, call the person back, or reply to message. 
13248  *
13249  * \return zero on success, -1 on error.
13250  */
13251 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)
13252 {
13253    int res = 0;
13254    char filename[PATH_MAX];
13255    struct ast_config *msg_cfg = NULL;
13256    const char *origtime, *context;
13257    char *name, *num;
13258    int retries = 0;
13259    char *cid;
13260    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13261 
13262    vms->starting = 0; 
13263 
13264    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13265 
13266    /* Retrieve info from VM attribute file */
13267    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13268    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13269    msg_cfg = ast_config_load(filename, config_flags);
13270    DISPOSE(vms->curdir, vms->curmsg);
13271    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13272       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13273       return 0;
13274    }
13275 
13276    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13277       ast_config_destroy(msg_cfg);
13278       return 0;
13279    }
13280 
13281    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13282 
13283    context = ast_variable_retrieve(msg_cfg, "message", "context");
13284    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13285       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13286    switch (option) {
13287    case 3: /* Play message envelope */
13288       if (!res)
13289          res = play_message_datetime(chan, vmu, origtime, filename);
13290       if (!res)
13291          res = play_message_callerid(chan, vms, cid, context, 0);
13292 
13293       res = 't';
13294       break;
13295 
13296    case 2:  /* Call back */
13297 
13298       if (ast_strlen_zero(cid))
13299          break;
13300 
13301       ast_callerid_parse(cid, &name, &num);
13302       while ((res > -1) && (res != 't')) {
13303          switch (res) {
13304          case '1':
13305             if (num) {
13306                /* Dial the CID number */
13307                res = dialout(chan, vmu, num, vmu->callback);
13308                if (res) {
13309                   ast_config_destroy(msg_cfg);
13310                   return 9;
13311                }
13312             } else {
13313                res = '2';
13314             }
13315             break;
13316 
13317          case '2':
13318             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13319             if (!ast_strlen_zero(vmu->dialout)) {
13320                res = dialout(chan, vmu, NULL, vmu->dialout);
13321                if (res) {
13322                   ast_config_destroy(msg_cfg);
13323                   return 9;
13324                }
13325             } else {
13326                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13327                res = ast_play_and_wait(chan, "vm-sorry");
13328             }
13329             ast_config_destroy(msg_cfg);
13330             return res;
13331          case '*':
13332             res = 't';
13333             break;
13334          case '3':
13335          case '4':
13336          case '5':
13337          case '6':
13338          case '7':
13339          case '8':
13340          case '9':
13341          case '0':
13342 
13343             res = ast_play_and_wait(chan, "vm-sorry");
13344             retries++;
13345             break;
13346          default:
13347             if (num) {
13348                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13349                res = ast_play_and_wait(chan, "vm-num-i-have");
13350                if (!res)
13351                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13352                if (!res)
13353                   res = ast_play_and_wait(chan, "vm-tocallnum");
13354                /* Only prompt for a caller-specified number if there is a dialout context specified */
13355                if (!ast_strlen_zero(vmu->dialout)) {
13356                   if (!res)
13357                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13358                }
13359             } else {
13360                res = ast_play_and_wait(chan, "vm-nonumber");
13361                if (!ast_strlen_zero(vmu->dialout)) {
13362                   if (!res)
13363                      res = ast_play_and_wait(chan, "vm-toenternumber");
13364                }
13365             }
13366             if (!res) {
13367                res = ast_play_and_wait(chan, "vm-star-cancel");
13368             }
13369             if (!res) {
13370                res = ast_waitfordigit(chan, 6000);
13371             }
13372             if (!res) {
13373                retries++;
13374                if (retries > 3) {
13375                   res = 't';
13376                }
13377             }
13378             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13379             break; 
13380             
13381          }
13382          if (res == 't')
13383             res = 0;
13384          else if (res == '*')
13385             res = -1;
13386       }
13387       break;
13388       
13389    case 1:  /* Reply */
13390       /* Send reply directly to sender */
13391       if (ast_strlen_zero(cid))
13392          break;
13393 
13394       ast_callerid_parse(cid, &name, &num);
13395       if (!num) {
13396          ast_verb(3, "No CID number available, no reply sent\n");
13397          if (!res)
13398             res = ast_play_and_wait(chan, "vm-nonumber");
13399          ast_config_destroy(msg_cfg);
13400          return res;
13401       } else {
13402          struct ast_vm_user vmu2;
13403          if (find_user(&vmu2, vmu->context, num)) {
13404             struct leave_vm_options leave_options;
13405             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13406             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13407 
13408             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13409             
13410             memset(&leave_options, 0, sizeof(leave_options));
13411             leave_options.record_gain = record_gain;
13412             res = leave_voicemail(chan, mailbox, &leave_options);
13413             if (!res)
13414                res = 't';
13415             ast_config_destroy(msg_cfg);
13416             return res;
13417          } else {
13418             /* Sender has no mailbox, can't reply */
13419             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13420             ast_play_and_wait(chan, "vm-nobox");
13421             res = 't';
13422             ast_config_destroy(msg_cfg);
13423             return res;
13424          }
13425       } 
13426       res = 0;
13427 
13428       break;
13429    }
13430 
13431 #ifndef IMAP_STORAGE
13432    ast_config_destroy(msg_cfg);
13433 
13434    if (!res) {
13435       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13436       vms->heard[msg] = 1;
13437       res = wait_file(chan, vms, vms->fn);
13438    }
13439 #endif
13440    return res;
13441 }
13442 
13443 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13444          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13445          signed char record_gain, struct vm_state *vms, char *flag)
13446 {
13447    /* Record message & let caller review or re-record it, or set options if applicable */
13448    int res = 0;
13449    int cmd = 0;
13450    int max_attempts = 3;
13451    int attempts = 0;
13452    int recorded = 0;
13453    int msg_exists = 0;
13454    signed char zero_gain = 0;
13455    char tempfile[PATH_MAX];
13456    char *acceptdtmf = "#";
13457    char *canceldtmf = "";
13458    int canceleddtmf = 0;
13459 
13460    /* Note that urgent and private are for flagging messages as such in the future */
13461 
13462    /* barf if no pointer passed to store duration in */
13463    if (duration == NULL) {
13464       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13465       return -1;
13466    }
13467 
13468    if (!outsidecaller)
13469       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13470    else
13471       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13472 
13473    cmd = '3';  /* Want to start by recording */
13474 
13475    while ((cmd >= 0) && (cmd != 't')) {
13476       switch (cmd) {
13477       case '1':
13478          if (!msg_exists) {
13479             /* In this case, 1 is to record a message */
13480             cmd = '3';
13481             break;
13482          } else {
13483             /* Otherwise 1 is to save the existing message */
13484             ast_verb(3, "Saving message as is\n");
13485             if (!outsidecaller) 
13486                ast_filerename(tempfile, recordfile, NULL);
13487             ast_stream_and_wait(chan, "vm-msgsaved", "");
13488             if (!outsidecaller) {
13489                /* Saves to IMAP server only if imapgreeting=yes */
13490                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13491                DISPOSE(recordfile, -1);
13492             }
13493             cmd = 't';
13494             return res;
13495          }
13496       case '2':
13497          /* Review */
13498          ast_verb(3, "Reviewing the message\n");
13499          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13500          break;
13501       case '3':
13502          msg_exists = 0;
13503          /* Record */
13504          if (recorded == 1) 
13505             ast_verb(3, "Re-recording the message\n");
13506          else  
13507             ast_verb(3, "Recording the message\n");
13508          
13509          if (recorded && outsidecaller) {
13510             cmd = ast_play_and_wait(chan, INTRO);
13511             cmd = ast_play_and_wait(chan, "beep");
13512          }
13513          recorded = 1;
13514          /* 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 */
13515          if (record_gain)
13516             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13517          if (ast_test_flag(vmu, VM_OPERATOR))
13518             canceldtmf = "0";
13519          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13520          if (strchr(canceldtmf, cmd)) {
13521          /* need this flag here to distinguish between pressing '0' during message recording or after */
13522             canceleddtmf = 1;
13523          }
13524          if (record_gain)
13525             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13526          if (cmd == -1) {
13527             /* User has hung up, no options to give */
13528             if (!outsidecaller) {
13529                /* user was recording a greeting and they hung up, so let's delete the recording. */
13530                ast_filedelete(tempfile, NULL);
13531             }     
13532             return cmd;
13533          }
13534          if (cmd == '0') {
13535             break;
13536          } else if (cmd == '*') {
13537             break;
13538 #if 0
13539          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13540             /* Message is too short */
13541             ast_verb(3, "Message too short\n");
13542             cmd = ast_play_and_wait(chan, "vm-tooshort");
13543             cmd = ast_filedelete(tempfile, NULL);
13544             break;
13545          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13546             /* Message is all silence */
13547             ast_verb(3, "Nothing recorded\n");
13548             cmd = ast_filedelete(tempfile, NULL);
13549             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13550             if (!cmd)
13551                cmd = ast_play_and_wait(chan, "vm-speakup");
13552             break;
13553 #endif
13554          } else {
13555             /* If all is well, a message exists */
13556             msg_exists = 1;
13557             cmd = 0;
13558          }
13559          break;
13560       case '4':
13561          if (outsidecaller) {  /* only mark vm messages */
13562             /* Mark Urgent */
13563             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13564                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13565                res = ast_play_and_wait(chan, "vm-marked-urgent");
13566                strcpy(flag, "Urgent");
13567             } else if (flag) {
13568                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13569                res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13570                strcpy(flag, "");
13571             } else {
13572                ast_play_and_wait(chan, "vm-sorry");
13573             }
13574             cmd = 0;
13575          } else {
13576             cmd = ast_play_and_wait(chan, "vm-sorry");
13577          }
13578          break;
13579       case '5':
13580       case '6':
13581       case '7':
13582       case '8':
13583       case '9':
13584       case '*':
13585       case '#':
13586          cmd = ast_play_and_wait(chan, "vm-sorry");
13587          break;
13588 #if 0 
13589 /*  XXX Commented out for the moment because of the dangers of deleting
13590     a message while recording (can put the message numbers out of sync) */
13591       case '*':
13592          /* Cancel recording, delete message, offer to take another message*/
13593          cmd = ast_play_and_wait(chan, "vm-deleted");
13594          cmd = ast_filedelete(tempfile, NULL);
13595          if (outsidecaller) {
13596             res = vm_exec(chan, NULL);
13597             return res;
13598          }
13599          else
13600             return 1;
13601 #endif
13602       case '0':
13603          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13604             cmd = ast_play_and_wait(chan, "vm-sorry");
13605             break;
13606          }
13607          if (msg_exists || recorded) {
13608             cmd = ast_play_and_wait(chan, "vm-saveoper");
13609             if (!cmd)
13610                cmd = ast_waitfordigit(chan, 3000);
13611             if (cmd == '1') {
13612                ast_filerename(tempfile, recordfile, NULL);
13613                ast_play_and_wait(chan, "vm-msgsaved");
13614                cmd = '0';
13615             } else if (cmd == '4') {
13616                if (flag) {
13617                   ast_play_and_wait(chan, "vm-marked-urgent");
13618                   strcpy(flag, "Urgent");
13619                }
13620                ast_play_and_wait(chan, "vm-msgsaved");
13621                cmd = '0';
13622             } else {
13623                ast_play_and_wait(chan, "vm-deleted");
13624                DELETE(tempfile, -1, tempfile, vmu);
13625                cmd = '0';
13626             }
13627          }
13628          return cmd;
13629       default:
13630          /* If the caller is an ouside caller, and the review option is enabled,
13631             allow them to review the message, but let the owner of the box review
13632             their OGM's */
13633          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13634             return cmd;
13635          if (msg_exists) {
13636             cmd = ast_play_and_wait(chan, "vm-review");
13637             if (!cmd && outsidecaller) {
13638                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13639                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13640                } else if (flag) {
13641                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13642                }
13643             }
13644          } else {
13645             cmd = ast_play_and_wait(chan, "vm-torerecord");
13646             if (!cmd)
13647                cmd = ast_waitfordigit(chan, 600);
13648          }
13649          
13650          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13651             cmd = ast_play_and_wait(chan, "vm-reachoper");
13652             if (!cmd)
13653                cmd = ast_waitfordigit(chan, 600);
13654          }
13655 #if 0
13656          if (!cmd)
13657             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13658 #endif
13659          if (!cmd)
13660             cmd = ast_waitfordigit(chan, 6000);
13661          if (!cmd) {
13662             attempts++;
13663          }
13664          if (attempts > max_attempts) {
13665             cmd = 't';
13666          }
13667       }
13668    }
13669    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13670       /* Hang up or timeout, so delete the recording. */
13671       ast_filedelete(tempfile, NULL);
13672    }
13673 
13674    if (cmd != 't' && outsidecaller)
13675       ast_play_and_wait(chan, "vm-goodbye");
13676 
13677    return cmd;
13678 }
13679 
13680 /* This is a workaround so that menuselect displays a proper description
13681  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13682  */
13683 
13684 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13685       .load = load_module,
13686       .unload = unload_module,
13687       .reload = reload,
13688       .nonoptreq = "res_adsi,res_smdi",
13689       );

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1