Mon Apr 28 2014 10:05:35

Asterisk developer's documentation


app_voicemail.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \author Mark Spencer <markster@digium.com>
00022  * \brief Comedian Mail - Voicemail System
00023  *
00024  * \extref unixODBC (http://www.unixodbc.org/)
00025  * \extref A source distribution of University of Washington's IMAP c-client
00026  *         (http://www.washington.edu/imap/)
00027  *
00028  * \par See also
00029  * \arg \ref Config_vm
00030  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
00031  * \ingroup applications
00032  * \note This module requires res_adsi to load. This needs to be optional
00033  * during compilation.
00034  *
00035  * \note This file is now almost impossible to work with, due to all \#ifdefs.
00036  *       Feels like the database code before realtime. Someone - please come up
00037  *       with a plan to clean this up.
00038  */
00039 
00040 /*** MODULEINFO
00041    <use>res_adsi</use>
00042    <use>res_smdi</use>
00043    <support_level>core</support_level>
00044  ***/
00045 
00046 /*** MAKEOPTS
00047 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
00048    <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
00049       <conflict>ODBC_STORAGE</conflict>
00050       <conflict>IMAP_STORAGE</conflict>
00051       <defaultenabled>yes</defaultenabled>
00052       <support_level>core</support_level>
00053    </member>
00054    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00055       <depend>generic_odbc</depend>
00056       <depend>ltdl</depend>
00057       <conflict>IMAP_STORAGE</conflict>
00058       <conflict>FILE_STORAGE</conflict>
00059       <defaultenabled>no</defaultenabled>
00060       <support_level>core</support_level>
00061    </member>
00062    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00063       <depend>imap_tk</depend>
00064       <conflict>ODBC_STORAGE</conflict>
00065       <conflict>FILE_STORAGE</conflict>
00066       <use>openssl</use>
00067       <defaultenabled>no</defaultenabled>
00068       <support_level>core</support_level>
00069    </member>
00070 </category>
00071 ***/
00072 
00073 #include "asterisk.h"
00074 
00075 #ifdef IMAP_STORAGE
00076 #include <ctype.h>
00077 #include <signal.h>
00078 #include <pwd.h>
00079 #ifdef USE_SYSTEM_IMAP
00080 #include <imap/c-client.h>
00081 #include <imap/imap4r1.h>
00082 #include <imap/linkage.h>
00083 #elif defined (USE_SYSTEM_CCLIENT)
00084 #include <c-client/c-client.h>
00085 #include <c-client/imap4r1.h>
00086 #include <c-client/linkage.h>
00087 #else
00088 #include "c-client.h"
00089 #include "imap4r1.h"
00090 #include "linkage.h"
00091 #endif
00092 #endif
00093 
00094 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 401743 $")
00095 
00096 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00097 #include <sys/time.h>
00098 #include <sys/stat.h>
00099 #include <sys/mman.h>
00100 #include <time.h>
00101 #include <dirent.h>
00102 #if defined(__FreeBSD__) || defined(__OpenBSD__)
00103 #include <sys/wait.h>
00104 #endif
00105 
00106 #include "asterisk/logger.h"
00107 #include "asterisk/lock.h"
00108 #include "asterisk/file.h"
00109 #include "asterisk/channel.h"
00110 #include "asterisk/pbx.h"
00111 #include "asterisk/config.h"
00112 #include "asterisk/say.h"
00113 #include "asterisk/module.h"
00114 #include "asterisk/adsi.h"
00115 #include "asterisk/app.h"
00116 #include "asterisk/manager.h"
00117 #include "asterisk/dsp.h"
00118 #include "asterisk/localtime.h"
00119 #include "asterisk/cli.h"
00120 #include "asterisk/utils.h"
00121 #include "asterisk/stringfields.h"
00122 #include "asterisk/smdi.h"
00123 #include "asterisk/astobj2.h"
00124 #include "asterisk/event.h"
00125 #include "asterisk/taskprocessor.h"
00126 #include "asterisk/test.h"
00127 
00128 #ifdef ODBC_STORAGE
00129 #include "asterisk/res_odbc.h"
00130 #endif
00131 
00132 #ifdef IMAP_STORAGE
00133 #include "asterisk/threadstorage.h"
00134 #endif
00135 
00136 /*** DOCUMENTATION
00137    <application name="VoiceMail" language="en_US">
00138       <synopsis>
00139          Leave a Voicemail message.
00140       </synopsis>
00141       <syntax>
00142          <parameter name="mailboxs" argsep="&amp;" required="true">
00143             <argument name="mailbox1" argsep="@" required="true">
00144                <argument name="mailbox" required="true" />
00145                <argument name="context" />
00146             </argument>
00147             <argument name="mailbox2" argsep="@" multiple="true">
00148                <argument name="mailbox" required="true" />
00149                <argument name="context" />
00150             </argument>
00151          </parameter>
00152          <parameter name="options">
00153             <optionlist>
00154                <option name="b">
00155                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00156                </option>
00157                <option name="d">
00158                   <argument name="c" />
00159                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00160                   if played during the greeting. Context defaults to the current context.</para>
00161                </option>
00162                <option name="g">
00163                   <argument name="#" required="true" />
00164                   <para>Use the specified amount of gain when recording the voicemail
00165                   message. The units are whole-number decibels (dB). Only works on supported
00166                   technologies, which is DAHDI only.</para>
00167                </option>
00168                <option name="s">
00169                   <para>Skip the playback of instructions for leaving a message to the
00170                   calling party.</para>
00171                </option>
00172                <option name="u">
00173                   <para>Play the <literal>unavailable</literal> greeting.</para>
00174                </option>
00175                <option name="U">
00176                   <para>Mark message as <literal>URGENT</literal>.</para>
00177                </option>
00178                <option name="P">
00179                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00180                </option>
00181             </optionlist>
00182          </parameter>
00183       </syntax>
00184       <description>
00185          <para>This application allows the calling party to leave a message for the specified
00186          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00187          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00188          exist.</para>
00189          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00190          <enumlist>
00191             <enum name="0">
00192                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00193             </enum>
00194             <enum name="*">
00195                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00196             </enum>
00197          </enumlist>
00198          <para>This application will set the following channel variable upon completion:</para>
00199          <variablelist>
00200             <variable name="VMSTATUS">
00201                <para>This indicates the status of the execution of the VoiceMail application.</para>
00202                <value name="SUCCESS" />
00203                <value name="USEREXIT" />
00204                <value name="FAILED" />
00205             </variable>
00206          </variablelist>
00207       </description>
00208       <see-also>
00209          <ref type="application">VoiceMailMain</ref>
00210       </see-also>
00211    </application>
00212    <application name="VoiceMailMain" language="en_US">
00213       <synopsis>
00214          Check Voicemail messages.
00215       </synopsis>
00216       <syntax>
00217          <parameter name="mailbox" required="true" argsep="@">
00218             <argument name="mailbox" />
00219             <argument name="context" />
00220          </parameter>
00221          <parameter name="options">
00222             <optionlist>
00223                <option name="p">
00224                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00225                   the mailbox that is entered by the caller.</para>
00226                </option>
00227                <option name="g">
00228                   <argument name="#" required="true" />
00229                   <para>Use the specified amount of gain when recording a voicemail message.
00230                   The units are whole-number decibels (dB).</para>
00231                </option>
00232                <option name="s">
00233                   <para>Skip checking the passcode for the mailbox.</para>
00234                </option>
00235                <option name="a">
00236                   <argument name="folder" required="true" />
00237                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00238                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00239                   <enumlist>
00240                      <enum name="0"><para>INBOX</para></enum>
00241                      <enum name="1"><para>Old</para></enum>
00242                      <enum name="2"><para>Work</para></enum>
00243                      <enum name="3"><para>Family</para></enum>
00244                      <enum name="4"><para>Friends</para></enum>
00245                      <enum name="5"><para>Cust1</para></enum>
00246                      <enum name="6"><para>Cust2</para></enum>
00247                      <enum name="7"><para>Cust3</para></enum>
00248                      <enum name="8"><para>Cust4</para></enum>
00249                      <enum name="9"><para>Cust5</para></enum>
00250                   </enumlist>
00251                </option>
00252             </optionlist>
00253          </parameter>
00254       </syntax>
00255       <description>
00256          <para>This application allows the calling party to check voicemail messages. A specific
00257          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00258          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00259          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00260          <literal>default</literal> context will be used.</para>
00261          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00262          or Password, and the extension exists:</para>
00263          <enumlist>
00264             <enum name="*">
00265                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00266             </enum>
00267          </enumlist>
00268       </description>
00269       <see-also>
00270          <ref type="application">VoiceMail</ref>
00271       </see-also>
00272    </application>
00273    <application name="MailboxExists" language="en_US">
00274       <synopsis>
00275          Check to see if Voicemail mailbox exists.
00276       </synopsis>
00277       <syntax>
00278          <parameter name="mailbox" required="true" argsep="@">
00279             <argument name="mailbox" required="true" />
00280             <argument name="context" />
00281          </parameter>
00282          <parameter name="options">
00283             <para>None options.</para>
00284          </parameter>
00285       </syntax>
00286       <description>
00287          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00288          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00289          will be used.</para>
00290          <para>This application will set the following channel variable upon completion:</para>
00291          <variablelist>
00292             <variable name="VMBOXEXISTSSTATUS">
00293                <para>This will contain the status of the execution of the MailboxExists application.
00294                Possible values include:</para>
00295                <value name="SUCCESS" />
00296                <value name="FAILED" />
00297             </variable>
00298          </variablelist>
00299       </description>
00300    </application>
00301    <application name="VMAuthenticate" language="en_US">
00302       <synopsis>
00303          Authenticate with Voicemail passwords.
00304       </synopsis>
00305       <syntax>
00306          <parameter name="mailbox" required="true" argsep="@">
00307             <argument name="mailbox" />
00308             <argument name="context" />
00309          </parameter>
00310          <parameter name="options">
00311             <optionlist>
00312                <option name="s">
00313                   <para>Skip playing the initial prompts.</para>
00314                </option>
00315             </optionlist>
00316          </parameter>
00317       </syntax>
00318       <description>
00319          <para>This application behaves the same way as the Authenticate application, but the passwords
00320          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00321          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00322          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00323          mailbox.</para>
00324          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00325          or Password, and the extension exists:</para>
00326          <enumlist>
00327             <enum name="*">
00328                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00329             </enum>
00330          </enumlist>
00331       </description>
00332    </application>
00333    <application name="VMSayName" language="en_US">
00334       <synopsis>
00335          Play the name of a voicemail user
00336       </synopsis>
00337       <syntax>
00338          <parameter name="mailbox" required="true" argsep="@">
00339             <argument name="mailbox" />
00340             <argument name="context" />
00341          </parameter>
00342       </syntax>
00343       <description>
00344          <para>This application will say the recorded name of the voicemail user specified as the
00345          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00346       </description>
00347    </application>
00348    <function name="MAILBOX_EXISTS" language="en_US">
00349       <synopsis>
00350          Tell if a mailbox is configured.
00351       </synopsis>
00352       <syntax argsep="@">
00353          <parameter name="mailbox" required="true" />
00354          <parameter name="context" />
00355       </syntax>
00356       <description>
00357          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00358          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00359          context.</para>
00360       </description>
00361    </function>
00362    <manager name="VoicemailUsersList" language="en_US">
00363       <synopsis>
00364          List All Voicemail User Information.
00365       </synopsis>
00366       <syntax>
00367          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00368       </syntax>
00369       <description>
00370       </description>
00371    </manager>
00372  ***/
00373 
00374 #ifdef IMAP_STORAGE
00375 static char imapserver[48];
00376 static char imapport[8];
00377 static char imapflags[128];
00378 static char imapfolder[64];
00379 static char imapparentfolder[64] = "\0";
00380 static char greetingfolder[64];
00381 static char authuser[32];
00382 static char authpassword[42];
00383 static int imapversion = 1;
00384 
00385 static int expungeonhangup = 1;
00386 static int imapgreetings = 0;
00387 static char delimiter = '\0';
00388 
00389 struct vm_state;
00390 struct ast_vm_user;
00391 
00392 AST_THREADSTORAGE(ts_vmstate);
00393 
00394 /* Forward declarations for IMAP */
00395 static int init_mailstream(struct vm_state *vms, int box);
00396 static void write_file(char *filename, char *buffer, unsigned long len);
00397 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00398 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00399 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00400 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00401 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00402 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00403 static void vmstate_insert(struct vm_state *vms);
00404 static void vmstate_delete(struct vm_state *vms);
00405 static void set_update(MAILSTREAM * stream);
00406 static void init_vm_state(struct vm_state *vms);
00407 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00408 static void get_mailbox_delimiter(MAILSTREAM *stream);
00409 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00410 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00411 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
00412 static void update_messages_by_imapuser(const char *user, unsigned long number);
00413 static int vm_delete(char *file);
00414 
00415 static int imap_remove_file (char *dir, int msgnum);
00416 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00417 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00418 static void check_quota(struct vm_state *vms, char *mailbox);
00419 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00420 struct vmstate {
00421    struct vm_state *vms;
00422    AST_LIST_ENTRY(vmstate) list;
00423 };
00424 
00425 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00426 
00427 #endif
00428 
00429 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00430 
00431 #define COMMAND_TIMEOUT 5000
00432 /* Don't modify these here; set your umask at runtime instead */
00433 #define  VOICEMAIL_DIR_MODE   0777
00434 #define  VOICEMAIL_FILE_MODE  0666
00435 #define  CHUNKSIZE   65536
00436 
00437 #define VOICEMAIL_CONFIG "voicemail.conf"
00438 #define ASTERISK_USERNAME "asterisk"
00439 
00440 /* Define fast-forward, pause, restart, and reverse keys
00441  * while listening to a voicemail message - these are
00442  * strings, not characters */
00443 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00444 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00445 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00446 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00447 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00448 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00449 
00450 /* Default mail command to mail voicemail. Change it with the
00451  * mailcmd= command in voicemail.conf */
00452 #define SENDMAIL "/usr/sbin/sendmail -t"
00453 
00454 #define INTRO "vm-intro"
00455 
00456 #define MAXMSG 100
00457 #define MAXMSGLIMIT 9999
00458 
00459 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00460 
00461 #define BASELINELEN 72
00462 #define BASEMAXINLINE 256
00463 #ifdef IMAP_STORAGE
00464 #define ENDL "\r\n"
00465 #else
00466 #define ENDL "\n"
00467 #endif
00468 
00469 #define MAX_DATETIME_FORMAT   512
00470 #define MAX_NUM_CID_CONTEXTS 10
00471 
00472 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00473 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00474 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00475 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00476 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00477 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00478 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00479 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00480 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00481 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00482 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00483 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00484 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00485 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00486 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00487 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00488 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00489 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00490 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00491 #define ERROR_LOCK_PATH  -100
00492 #define OPERATOR_EXIT     300
00493 
00494 
00495 enum vm_box {
00496    NEW_FOLDER,
00497    OLD_FOLDER,
00498    WORK_FOLDER,
00499    FAMILY_FOLDER,
00500    FRIENDS_FOLDER,
00501    GREETINGS_FOLDER
00502 };
00503 
00504 enum vm_option_flags {
00505    OPT_SILENT =           (1 << 0),
00506    OPT_BUSY_GREETING =    (1 << 1),
00507    OPT_UNAVAIL_GREETING = (1 << 2),
00508    OPT_RECORDGAIN =       (1 << 3),
00509    OPT_PREPEND_MAILBOX =  (1 << 4),
00510    OPT_AUTOPLAY =         (1 << 6),
00511    OPT_DTMFEXIT =         (1 << 7),
00512    OPT_MESSAGE_Urgent =   (1 << 8),
00513    OPT_MESSAGE_PRIORITY = (1 << 9)
00514 };
00515 
00516 enum vm_option_args {
00517    OPT_ARG_RECORDGAIN = 0,
00518    OPT_ARG_PLAYFOLDER = 1,
00519    OPT_ARG_DTMFEXIT   = 2,
00520    /* This *must* be the last value in this enum! */
00521    OPT_ARG_ARRAY_SIZE = 3,
00522 };
00523 
00524 enum vm_passwordlocation {
00525    OPT_PWLOC_VOICEMAILCONF = 0,
00526    OPT_PWLOC_SPOOLDIR      = 1,
00527    OPT_PWLOC_USERSCONF     = 2,
00528 };
00529 
00530 AST_APP_OPTIONS(vm_app_options, {
00531    AST_APP_OPTION('s', OPT_SILENT),
00532    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00533    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00534    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00535    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00536    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00537    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00538    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00539    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00540 });
00541 
00542 static int load_config(int reload);
00543 #ifdef TEST_FRAMEWORK
00544 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00545 #endif
00546 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00547 
00548 /*! \page vmlang Voicemail Language Syntaxes Supported
00549 
00550    \par Syntaxes supported, not really language codes.
00551    \arg \b en    - English
00552    \arg \b de    - German
00553    \arg \b es    - Spanish
00554    \arg \b fr    - French
00555    \arg \b it    - Italian
00556    \arg \b nl    - Dutch
00557    \arg \b pt    - Portuguese
00558    \arg \b pt_BR - Portuguese (Brazil)
00559    \arg \b gr    - Greek
00560    \arg \b no    - Norwegian
00561    \arg \b se    - Swedish
00562    \arg \b tw    - Chinese (Taiwan)
00563    \arg \b ua - Ukrainian
00564 
00565 German requires the following additional soundfile:
00566 \arg \b 1F  einE (feminine)
00567 
00568 Spanish requires the following additional soundfile:
00569 \arg \b 1M      un (masculine)
00570 
00571 Dutch, Portuguese & Spanish require the following additional soundfiles:
00572 \arg \b vm-INBOXs singular of 'new'
00573 \arg \b vm-Olds      singular of 'old/heard/read'
00574 
00575 NB these are plural:
00576 \arg \b vm-INBOX  nieuwe (nl)
00577 \arg \b vm-Old    oude (nl)
00578 
00579 Polish uses:
00580 \arg \b vm-new-a  'new', feminine singular accusative
00581 \arg \b vm-new-e  'new', feminine plural accusative
00582 \arg \b vm-new-ych   'new', feminine plural genitive
00583 \arg \b vm-old-a  'old', feminine singular accusative
00584 \arg \b vm-old-e  'old', feminine plural accusative
00585 \arg \b vm-old-ych   'old', feminine plural genitive
00586 \arg \b digits/1-a   'one', not always same as 'digits/1'
00587 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00588 
00589 Swedish uses:
00590 \arg \b vm-nytt      singular of 'new'
00591 \arg \b vm-nya    plural of 'new'
00592 \arg \b vm-gammalt   singular of 'old'
00593 \arg \b vm-gamla  plural of 'old'
00594 \arg \b digits/ett   'one', not always same as 'digits/1'
00595 
00596 Norwegian uses:
00597 \arg \b vm-ny     singular of 'new'
00598 \arg \b vm-nye    plural of 'new'
00599 \arg \b vm-gammel singular of 'old'
00600 \arg \b vm-gamle  plural of 'old'
00601 
00602 Dutch also uses:
00603 \arg \b nl-om     'at'?
00604 
00605 Spanish also uses:
00606 \arg \b vm-youhaveno
00607 
00608 Italian requires the following additional soundfile:
00609 
00610 For vm_intro_it:
00611 \arg \b vm-nuovo  new
00612 \arg \b vm-nuovi  new plural
00613 \arg \b vm-vecchio   old
00614 \arg \b vm-vecchi old plural
00615 
00616 Chinese (Taiwan) requires the following additional soundfile:
00617 \arg \b vm-tong      A class-word for call (tong1)
00618 \arg \b vm-ri     A class-word for day (ri4)
00619 \arg \b vm-you    You (ni3)
00620 \arg \b vm-haveno   Have no (mei2 you3)
00621 \arg \b vm-have     Have (you3)
00622 \arg \b vm-listen   To listen (yao4 ting1)
00623 
00624 
00625 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00626 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00627 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00628 
00629 */
00630 
00631 struct baseio {
00632    int iocp;
00633    int iolen;
00634    int linelength;
00635    int ateof;
00636    unsigned char iobuf[BASEMAXINLINE];
00637 };
00638 
00639 /*! Structure for linked list of users 
00640  * Use ast_vm_user_destroy() to free one of these structures. */
00641 struct ast_vm_user {
00642    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00643    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00644    char password[80];               /*!< Secret pin code, numbers only */
00645    char fullname[80];               /*!< Full name, for directory app */
00646    char email[80];                  /*!< E-mail address */
00647    char *emailsubject;              /*!< E-mail subject */
00648    char *emailbody;                 /*!< E-mail body */
00649    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00650    char serveremail[80];            /*!< From: Mail address */
00651    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00652    char zonetag[80];                /*!< Time zone */
00653    char locale[20];                 /*!< The locale (for presentation of date/time) */
00654    char callback[80];
00655    char dialout[80];
00656    char uniqueid[80];               /*!< Unique integer identifier */
00657    char exit[80];
00658    char attachfmt[20];              /*!< Attachment format */
00659    unsigned int flags;              /*!< VM_ flags */ 
00660    int saydurationm;
00661    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00662    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00663    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00664    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00665    int passwordlocation;            /*!< Storage location of the password */
00666 #ifdef IMAP_STORAGE
00667    char imapuser[80];               /*!< IMAP server login */
00668    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00669    char imapfolder[64];             /*!< IMAP voicemail folder */
00670    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00671    int imapversion;                 /*!< If configuration changes, use the new values */
00672 #endif
00673    double volgain;                  /*!< Volume gain for voicemails sent via email */
00674    AST_LIST_ENTRY(ast_vm_user) list;
00675 };
00676 
00677 /*! Voicemail time zones */
00678 struct vm_zone {
00679    AST_LIST_ENTRY(vm_zone) list;
00680    char name[80];
00681    char timezone[80];
00682    char msg_format[512];
00683 };
00684 
00685 #define VMSTATE_MAX_MSG_ARRAY 256
00686 
00687 /*! Voicemail mailbox state */
00688 struct vm_state {
00689    char curbox[80];
00690    char username[80];
00691    char context[80];
00692    char curdir[PATH_MAX];
00693    char vmbox[PATH_MAX];
00694    char fn[PATH_MAX];
00695    char intro[PATH_MAX];
00696    int *deleted;
00697    int *heard;
00698    int dh_arraysize; /* used for deleted / heard allocation */
00699    int curmsg;
00700    int lastmsg;
00701    int newmessages;
00702    int oldmessages;
00703    int urgentmessages;
00704    int starting;
00705    int repeats;
00706 #ifdef IMAP_STORAGE
00707    ast_mutex_t lock;
00708    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00709    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00710    MAILSTREAM *mailstream;
00711    int vmArrayIndex;
00712    char imapuser[80];                   /*!< IMAP server login */
00713    char imapfolder[64];                 /*!< IMAP voicemail folder */
00714    int imapversion;
00715    int interactive;
00716    char introfn[PATH_MAX];              /*!< Name of prepended file */
00717    unsigned int quota_limit;
00718    unsigned int quota_usage;
00719    struct vm_state *persist_vms;
00720 #endif
00721 };
00722 
00723 #ifdef ODBC_STORAGE
00724 static char odbc_database[80];
00725 static char odbc_table[80];
00726 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00727 #define DISPOSE(a,b) remove_file(a,b)
00728 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00729 #define EXISTS(a,b,c,d) (message_exists(a,b))
00730 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00731 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00732 #define DELETE(a,b,c,d) (delete_file(a,b))
00733 #else
00734 #ifdef IMAP_STORAGE
00735 #define DISPOSE(a,b) (imap_remove_file(a,b))
00736 #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))
00737 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
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_file(g,h));
00741 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00742 #else
00743 #define RETRIEVE(a,b,c,d)
00744 #define DISPOSE(a,b)
00745 #define STORE(a,b,c,d,e,f,g,h,i,j)
00746 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00747 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00748 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00749 #define DELETE(a,b,c,d) (vm_delete(c))
00750 #endif
00751 #endif
00752 
00753 static char VM_SPOOL_DIR[PATH_MAX];
00754 
00755 static char ext_pass_cmd[128];
00756 static char ext_pass_check_cmd[128];
00757 
00758 static int my_umask;
00759 
00760 #define PWDCHANGE_INTERNAL (1 << 1)
00761 #define PWDCHANGE_EXTERNAL (1 << 2)
00762 static int pwdchange = PWDCHANGE_INTERNAL;
00763 
00764 #ifdef ODBC_STORAGE
00765 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00766 #else
00767 # ifdef IMAP_STORAGE
00768 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00769 # else
00770 # define tdesc "Comedian Mail (Voicemail System)"
00771 # endif
00772 #endif
00773 
00774 static char userscontext[AST_MAX_EXTENSION] = "default";
00775 
00776 static char *addesc = "Comedian Mail";
00777 
00778 /* Leave a message */
00779 static char *app = "VoiceMail";
00780 
00781 /* Check mail, control, etc */
00782 static char *app2 = "VoiceMailMain";
00783 
00784 static char *app3 = "MailboxExists";
00785 static char *app4 = "VMAuthenticate";
00786 
00787 static char *sayname_app = "VMSayName";
00788 
00789 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00790 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00791 static char zonetag[80];
00792 static char locale[20];
00793 static int maxsilence;
00794 static int maxmsg;
00795 static int maxdeletedmsg;
00796 static int silencethreshold = 128;
00797 static char serveremail[80];
00798 static char mailcmd[160];  /* Configurable mail cmd */
00799 static char externnotify[160]; 
00800 static struct ast_smdi_interface *smdi_iface = NULL;
00801 static char vmfmts[80];
00802 static double volgain;
00803 static int vmminsecs;
00804 static int vmmaxsecs;
00805 static int maxgreet;
00806 static int skipms;
00807 static int maxlogins;
00808 static int minpassword;
00809 static int passwordlocation;
00810 
00811 /*! Poll mailboxes for changes since there is something external to
00812  *  app_voicemail that may change them. */
00813 static unsigned int poll_mailboxes;
00814 
00815 /*! Polling frequency */
00816 static unsigned int poll_freq;
00817 /*! By default, poll every 30 seconds */
00818 #define DEFAULT_POLL_FREQ 30
00819 
00820 AST_MUTEX_DEFINE_STATIC(poll_lock);
00821 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00822 static pthread_t poll_thread = AST_PTHREADT_NULL;
00823 static unsigned char poll_thread_run;
00824 
00825 /*! Subscription to ... MWI event subscriptions */
00826 static struct ast_event_sub *mwi_sub_sub;
00827 /*! Subscription to ... MWI event un-subscriptions */
00828 static struct ast_event_sub *mwi_unsub_sub;
00829 
00830 /*!
00831  * \brief An MWI subscription
00832  *
00833  * This is so we can keep track of which mailboxes are subscribed to.
00834  * This way, we know which mailboxes to poll when the pollmailboxes
00835  * option is being used.
00836  */
00837 struct mwi_sub {
00838    AST_RWLIST_ENTRY(mwi_sub) entry;
00839    int old_urgent;
00840    int old_new;
00841    int old_old;
00842    uint32_t uniqueid;
00843    char mailbox[1];
00844 };
00845 
00846 struct mwi_sub_task {
00847    const char *mailbox;
00848    const char *context;
00849    uint32_t uniqueid;
00850 };
00851 
00852 static struct ast_taskprocessor *mwi_subscription_tps;
00853 
00854 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00855 
00856 /* custom audio control prompts for voicemail playback */
00857 static char listen_control_forward_key[12];
00858 static char listen_control_reverse_key[12];
00859 static char listen_control_pause_key[12];
00860 static char listen_control_restart_key[12];
00861 static char listen_control_stop_key[12];
00862 
00863 /* custom password sounds */
00864 static char vm_password[80] = "vm-password";
00865 static char vm_newpassword[80] = "vm-newpassword";
00866 static char vm_passchanged[80] = "vm-passchanged";
00867 static char vm_reenterpassword[80] = "vm-reenterpassword";
00868 static char vm_mismatch[80] = "vm-mismatch";
00869 static char vm_invalid_password[80] = "vm-invalid-password";
00870 static char vm_pls_try_again[80] = "vm-pls-try-again";
00871 
00872 /*
00873  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00874  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00875  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00876  * app.c's __ast_play_and_record function
00877  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00878  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00879  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00880  * more effort than either of the other two.
00881  */
00882 static char vm_prepend_timeout[80] = "vm-then-pound";
00883 
00884 static struct ast_flags globalflags = {0};
00885 
00886 static int saydurationminfo;
00887 
00888 static char dialcontext[AST_MAX_CONTEXT] = "";
00889 static char callcontext[AST_MAX_CONTEXT] = "";
00890 static char exitcontext[AST_MAX_CONTEXT] = "";
00891 
00892 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00893 
00894 
00895 static char *emailbody = NULL;
00896 static char *emailsubject = NULL;
00897 static char *pagerbody = NULL;
00898 static char *pagersubject = NULL;
00899 static char fromstring[100];
00900 static char pagerfromstring[100];
00901 static char charset[32] = "ISO-8859-1";
00902 
00903 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00904 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00905 static int adsiver = 1;
00906 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00907 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00908 
00909 /* Forward declarations - generic */
00910 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00911 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);
00912 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00913 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00914          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00915          signed char record_gain, struct vm_state *vms, char *flag);
00916 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00917 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00918 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);
00919 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);
00920 static void apply_options(struct ast_vm_user *vmu, const char *options);
00921 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);
00922 static int is_valid_dtmf(const char *key);
00923 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00924 static int write_password_to_file(const char *secretfn, const char *password);
00925 static const char *substitute_escapes(const char *value);
00926 static void free_user(struct ast_vm_user *vmu);
00927 
00928 struct ao2_container *inprocess_container;
00929 
00930 struct inprocess {
00931    int count;
00932    char *context;
00933    char mailbox[0];
00934 };
00935 
00936 static int inprocess_hash_fn(const void *obj, const int flags)
00937 {
00938    const struct inprocess *i = obj;
00939    return atoi(i->mailbox);
00940 }
00941 
00942 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00943 {
00944    struct inprocess *i = obj, *j = arg;
00945    if (strcmp(i->mailbox, j->mailbox)) {
00946       return 0;
00947    }
00948    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00949 }
00950 
00951 static int inprocess_count(const char *context, const char *mailbox, int delta)
00952 {
00953    struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00954    arg->context = arg->mailbox + strlen(mailbox) + 1;
00955    strcpy(arg->mailbox, mailbox); /* SAFE */
00956    strcpy(arg->context, context); /* SAFE */
00957    ao2_lock(inprocess_container);
00958    if ((i = ao2_find(inprocess_container, arg, 0))) {
00959       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00960       ao2_unlock(inprocess_container);
00961       ao2_ref(i, -1);
00962       return ret;
00963    }
00964    if (delta < 0) {
00965       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00966    }
00967    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00968       ao2_unlock(inprocess_container);
00969       return 0;
00970    }
00971    i->context = i->mailbox + strlen(mailbox) + 1;
00972    strcpy(i->mailbox, mailbox); /* SAFE */
00973    strcpy(i->context, context); /* SAFE */
00974    i->count = delta;
00975    ao2_link(inprocess_container, i);
00976    ao2_unlock(inprocess_container);
00977    ao2_ref(i, -1);
00978    return 0;
00979 }
00980 
00981 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00982 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00983 #endif
00984 
00985 /*!
00986  * \brief Strips control and non 7-bit clean characters from input string.
00987  *
00988  * \note To map control and none 7-bit characters to a 7-bit clean characters
00989  *  please use ast_str_encode_mine().
00990  */
00991 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00992 {
00993    char *bufptr = buf;
00994    for (; *input; input++) {
00995       if (*input < 32) {
00996          continue;
00997       }
00998       *bufptr++ = *input;
00999       if (bufptr == buf + buflen - 1) {
01000          break;
01001       }
01002    }
01003    *bufptr = '\0';
01004    return buf;
01005 }
01006 
01007 
01008 /*!
01009  * \brief Sets default voicemail system options to a voicemail user.
01010  *
01011  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01012  * - all the globalflags
01013  * - the saydurationminfo
01014  * - the callcontext
01015  * - the dialcontext
01016  * - the exitcontext
01017  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01018  * - volume gain
01019  * - emailsubject, emailbody set to NULL
01020  */
01021 static void populate_defaults(struct ast_vm_user *vmu)
01022 {
01023    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01024    vmu->passwordlocation = passwordlocation;
01025    if (saydurationminfo) {
01026       vmu->saydurationm = saydurationminfo;
01027    }
01028    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01029    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01030    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01031    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01032    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01033    if (vmminsecs) {
01034       vmu->minsecs = vmminsecs;
01035    }
01036    if (vmmaxsecs) {
01037       vmu->maxsecs = vmmaxsecs;
01038    }
01039    if (maxmsg) {
01040       vmu->maxmsg = maxmsg;
01041    }
01042    if (maxdeletedmsg) {
01043       vmu->maxdeletedmsg = maxdeletedmsg;
01044    }
01045    vmu->volgain = volgain;
01046    ast_free(vmu->emailsubject);
01047    vmu->emailsubject = NULL;
01048    ast_free(vmu->emailbody);
01049    vmu->emailbody = NULL;
01050 #ifdef IMAP_STORAGE
01051    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01052 #endif
01053 }
01054 
01055 /*!
01056  * \brief Sets a a specific property value.
01057  * \param vmu The voicemail user object to work with.
01058  * \param var The name of the property to be set.
01059  * \param value The value to be set to the property.
01060  * 
01061  * The property name must be one of the understood properties. See the source for details.
01062  */
01063 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01064 {
01065    int x;
01066    if (!strcasecmp(var, "attach")) {
01067       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01068    } else if (!strcasecmp(var, "attachfmt")) {
01069       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01070    } else if (!strcasecmp(var, "serveremail")) {
01071       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01072    } else if (!strcasecmp(var, "emailbody")) {
01073       vmu->emailbody = ast_strdup(substitute_escapes(value));
01074    } else if (!strcasecmp(var, "emailsubject")) {
01075       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01076    } else if (!strcasecmp(var, "language")) {
01077       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01078    } else if (!strcasecmp(var, "tz")) {
01079       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01080    } else if (!strcasecmp(var, "locale")) {
01081       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01082 #ifdef IMAP_STORAGE
01083    } else if (!strcasecmp(var, "imapuser")) {
01084       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01085       vmu->imapversion = imapversion;
01086    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01087       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01088       vmu->imapversion = imapversion;
01089    } else if (!strcasecmp(var, "imapfolder")) {
01090       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01091    } else if (!strcasecmp(var, "imapvmshareid")) {
01092       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01093       vmu->imapversion = imapversion;
01094 #endif
01095    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01096       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01097    } else if (!strcasecmp(var, "saycid")){
01098       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01099    } else if (!strcasecmp(var, "sendvoicemail")){
01100       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01101    } else if (!strcasecmp(var, "review")){
01102       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01103    } else if (!strcasecmp(var, "tempgreetwarn")){
01104       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01105    } else if (!strcasecmp(var, "messagewrap")){
01106       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01107    } else if (!strcasecmp(var, "operator")) {
01108       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01109    } else if (!strcasecmp(var, "envelope")){
01110       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01111    } else if (!strcasecmp(var, "moveheard")){
01112       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01113    } else if (!strcasecmp(var, "sayduration")){
01114       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01115    } else if (!strcasecmp(var, "saydurationm")){
01116       if (sscanf(value, "%30d", &x) == 1) {
01117          vmu->saydurationm = x;
01118       } else {
01119          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01120       }
01121    } else if (!strcasecmp(var, "forcename")){
01122       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01123    } else if (!strcasecmp(var, "forcegreetings")){
01124       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01125    } else if (!strcasecmp(var, "callback")) {
01126       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01127    } else if (!strcasecmp(var, "dialout")) {
01128       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01129    } else if (!strcasecmp(var, "exitcontext")) {
01130       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01131    } else if (!strcasecmp(var, "minsecs")) {
01132       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01133          vmu->minsecs = x;
01134       } else {
01135          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01136          vmu->minsecs = vmminsecs;
01137       }
01138    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01139       vmu->maxsecs = atoi(value);
01140       if (vmu->maxsecs <= 0) {
01141          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01142          vmu->maxsecs = vmmaxsecs;
01143       } else {
01144          vmu->maxsecs = atoi(value);
01145       }
01146       if (!strcasecmp(var, "maxmessage"))
01147          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01148    } else if (!strcasecmp(var, "maxmsg")) {
01149       vmu->maxmsg = atoi(value);
01150       /* Accept maxmsg=0 (Greetings only voicemail) */
01151       if (vmu->maxmsg < 0) {
01152          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01153          vmu->maxmsg = MAXMSG;
01154       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01155          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01156          vmu->maxmsg = MAXMSGLIMIT;
01157       }
01158    } else if (!strcasecmp(var, "nextaftercmd")) {
01159       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01160    } else if (!strcasecmp(var, "backupdeleted")) {
01161       if (sscanf(value, "%30d", &x) == 1)
01162          vmu->maxdeletedmsg = x;
01163       else if (ast_true(value))
01164          vmu->maxdeletedmsg = MAXMSG;
01165       else
01166          vmu->maxdeletedmsg = 0;
01167 
01168       if (vmu->maxdeletedmsg < 0) {
01169          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01170          vmu->maxdeletedmsg = MAXMSG;
01171       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01172          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01173          vmu->maxdeletedmsg = MAXMSGLIMIT;
01174       }
01175    } else if (!strcasecmp(var, "volgain")) {
01176       sscanf(value, "%30lf", &vmu->volgain);
01177    } else if (!strcasecmp(var, "passwordlocation")) {
01178       if (!strcasecmp(value, "spooldir")) {
01179          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01180       } else {
01181          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01182       }
01183    } else if (!strcasecmp(var, "options")) {
01184       apply_options(vmu, value);
01185    }
01186 }
01187 
01188 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01189 {
01190    int fds[2], pid = 0;
01191 
01192    memset(buf, 0, len);
01193 
01194    if (pipe(fds)) {
01195       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01196    } else {
01197       /* good to go*/
01198       pid = ast_safe_fork(0);
01199 
01200       if (pid < 0) {
01201          /* ok maybe not */
01202          close(fds[0]);
01203          close(fds[1]);
01204          snprintf(buf, len, "FAILURE: Fork failed");
01205       } else if (pid) {
01206          /* parent */
01207          close(fds[1]);
01208          if (read(fds[0], buf, len) < 0) {
01209             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01210          }
01211          close(fds[0]);
01212       } else {
01213          /*  child */
01214          AST_DECLARE_APP_ARGS(arg,
01215             AST_APP_ARG(v)[20];
01216          );
01217          char *mycmd = ast_strdupa(command);
01218 
01219          close(fds[0]);
01220          dup2(fds[1], STDOUT_FILENO);
01221          close(fds[1]);
01222          ast_close_fds_above_n(STDOUT_FILENO);
01223 
01224          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01225 
01226          execv(arg.v[0], arg.v); 
01227          printf("FAILURE: %s", strerror(errno));
01228          _exit(0);
01229       }
01230    }
01231    return buf;
01232 }
01233 
01234 /*!
01235  * \brief Check that password meets minimum required length
01236  * \param vmu The voicemail user to change the password for.
01237  * \param password The password string to check
01238  *
01239  * \return zero on ok, 1 on not ok.
01240  */
01241 static int check_password(struct ast_vm_user *vmu, char *password)
01242 {
01243    /* check minimum length */
01244    if (strlen(password) < minpassword)
01245       return 1;
01246    /* check that password does not contain '*' character */
01247    if (!ast_strlen_zero(password) && password[0] == '*')
01248       return 1;
01249    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01250       char cmd[255], buf[255];
01251 
01252       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01253 
01254       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01255       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01256          ast_debug(5, "Result: %s\n", buf);
01257          if (!strncasecmp(buf, "VALID", 5)) {
01258             ast_debug(3, "Passed password check: '%s'\n", buf);
01259             return 0;
01260          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01261             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01262             return 0;
01263          } else {
01264             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01265             return 1;
01266          }
01267       }
01268    }
01269    return 0;
01270 }
01271 
01272 /*! 
01273  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01274  * \param vmu The voicemail user to change the password for.
01275  * \param password The new value to be set to the password for this user.
01276  * 
01277  * This only works if there is a realtime engine configured.
01278  * This is called from the (top level) vm_change_password.
01279  *
01280  * \return zero on success, -1 on error.
01281  */
01282 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01283 {
01284    int res = -1;
01285    if (!strcmp(vmu->password, password)) {
01286       /* No change (but an update would return 0 rows updated, so we opt out here) */
01287       return 0;
01288    }
01289 
01290    if (strlen(password) > 10) {
01291       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01292    }
01293    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01294       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01295       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01296       res = 0;
01297    }
01298    return res;
01299 }
01300 
01301 /*!
01302  * \brief Destructively Parse options and apply.
01303  */
01304 static void apply_options(struct ast_vm_user *vmu, const char *options)
01305 {  
01306    char *stringp;
01307    char *s;
01308    char *var, *value;
01309    stringp = ast_strdupa(options);
01310    while ((s = strsep(&stringp, "|"))) {
01311       value = s;
01312       if ((var = strsep(&value, "=")) && value) {
01313          apply_option(vmu, var, value);
01314       }
01315    }  
01316 }
01317 
01318 /*!
01319  * \brief Loads the options specific to a voicemail user.
01320  * 
01321  * This is called when a vm_user structure is being set up, such as from load_options.
01322  */
01323 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01324 {
01325    for (; var; var = var->next) {
01326       if (!strcasecmp(var->name, "vmsecret")) {
01327          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01328       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01329          if (ast_strlen_zero(retval->password)) {
01330             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01331                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01332                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01333             } else {
01334                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01335             }
01336          }
01337       } else if (!strcasecmp(var->name, "uniqueid")) {
01338          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01339       } else if (!strcasecmp(var->name, "pager")) {
01340          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01341       } else if (!strcasecmp(var->name, "email")) {
01342          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01343       } else if (!strcasecmp(var->name, "fullname")) {
01344          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01345       } else if (!strcasecmp(var->name, "context")) {
01346          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01347       } else if (!strcasecmp(var->name, "emailsubject")) {
01348          ast_free(retval->emailsubject);
01349          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01350       } else if (!strcasecmp(var->name, "emailbody")) {
01351          ast_free(retval->emailbody);
01352          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01353 #ifdef IMAP_STORAGE
01354       } else if (!strcasecmp(var->name, "imapuser")) {
01355          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01356          retval->imapversion = imapversion;
01357       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01358          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01359          retval->imapversion = imapversion;
01360       } else if (!strcasecmp(var->name, "imapfolder")) {
01361          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01362       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01363          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01364          retval->imapversion = imapversion;
01365 #endif
01366       } else
01367          apply_option(retval, var->name, var->value);
01368    }
01369 }
01370 
01371 /*!
01372  * \brief Determines if a DTMF key entered is valid.
01373  * \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.
01374  *
01375  * Tests the character entered against the set of valid DTMF characters. 
01376  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01377  */
01378 static int is_valid_dtmf(const char *key)
01379 {
01380    int i;
01381    char *local_key = ast_strdupa(key);
01382 
01383    for (i = 0; i < strlen(key); ++i) {
01384       if (!strchr(VALID_DTMF, *local_key)) {
01385          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01386          return 0;
01387       }
01388       local_key++;
01389    }
01390    return 1;
01391 }
01392 
01393 /*!
01394  * \brief Finds a voicemail user from the realtime engine.
01395  * \param ivm
01396  * \param context
01397  * \param mailbox
01398  *
01399  * 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.
01400  *
01401  * \return The ast_vm_user structure for the user that was found.
01402  */
01403 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01404 {
01405    struct ast_variable *var;
01406    struct ast_vm_user *retval;
01407 
01408    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01409       if (ivm) {
01410          memset(retval, 0, sizeof(*retval));
01411       }
01412       populate_defaults(retval);
01413       if (!ivm) {
01414          ast_set_flag(retval, VM_ALLOCED);
01415       }
01416       if (mailbox) {
01417          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01418       }
01419       if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
01420          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01421       } else {
01422          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01423       }
01424       if (var) {
01425          apply_options_full(retval, var);
01426          ast_variables_destroy(var);
01427       } else { 
01428          if (!ivm) 
01429             free_user(retval);
01430          retval = NULL;
01431       }  
01432    } 
01433    return retval;
01434 }
01435 
01436 /*!
01437  * \brief Finds a voicemail user from the users file or the realtime engine.
01438  * \param ivm
01439  * \param context
01440  * \param mailbox
01441  * 
01442  * \return The ast_vm_user structure for the user that was found.
01443  */
01444 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01445 {
01446    /* This function could be made to generate one from a database, too */
01447    struct ast_vm_user *vmu = NULL, *cur;
01448    AST_LIST_LOCK(&users);
01449 
01450    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01451       context = "default";
01452 
01453    AST_LIST_TRAVERSE(&users, cur, list) {
01454 #ifdef IMAP_STORAGE
01455       if (cur->imapversion != imapversion) {
01456          continue;
01457       }
01458 #endif
01459       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01460          break;
01461       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01462          break;
01463    }
01464    if (cur) {
01465       /* Make a copy, so that on a reload, we have no race */
01466       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01467          *vmu = *cur;
01468          if (!ivm) {
01469             vmu->emailbody = ast_strdup(cur->emailbody);
01470             vmu->emailsubject = ast_strdup(cur->emailsubject);
01471          }
01472          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01473          AST_LIST_NEXT(vmu, list) = NULL;
01474       }
01475    } else
01476       vmu = find_user_realtime(ivm, context, mailbox);
01477    AST_LIST_UNLOCK(&users);
01478    return vmu;
01479 }
01480 
01481 /*!
01482  * \brief Resets a user password to a specified password.
01483  * \param context
01484  * \param mailbox
01485  * \param newpass
01486  *
01487  * This does the actual change password work, called by the vm_change_password() function.
01488  *
01489  * \return zero on success, -1 on error.
01490  */
01491 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01492 {
01493    /* This function could be made to generate one from a database, too */
01494    struct ast_vm_user *cur;
01495    int res = -1;
01496    AST_LIST_LOCK(&users);
01497    AST_LIST_TRAVERSE(&users, cur, list) {
01498       if ((!context || !strcasecmp(context, cur->context)) &&
01499          (!strcasecmp(mailbox, cur->mailbox)))
01500             break;
01501    }
01502    if (cur) {
01503       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01504       res = 0;
01505    }
01506    AST_LIST_UNLOCK(&users);
01507    return res;
01508 }
01509 
01510 /*!
01511  * \brief Check if configuration file is valid
01512  */
01513 static inline int valid_config(const struct ast_config *cfg)
01514 {
01515    return cfg && cfg != CONFIG_STATUS_FILEINVALID;
01516 }
01517 
01518 /*! 
01519  * \brief The handler for the change password option.
01520  * \param vmu The voicemail user to work with.
01521  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01522  * 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.
01523  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01524  */
01525 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01526 {
01527    struct ast_config   *cfg = NULL;
01528    struct ast_variable *var = NULL;
01529    struct ast_category *cat = NULL;
01530    char *category = NULL, *value = NULL, *new = NULL;
01531    const char *tmp = NULL;
01532    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01533    char secretfn[PATH_MAX] = "";
01534    int found = 0;
01535 
01536    if (!change_password_realtime(vmu, newpassword))
01537       return;
01538 
01539    /* check if we should store the secret in the spool directory next to the messages */
01540    switch (vmu->passwordlocation) {
01541    case OPT_PWLOC_SPOOLDIR:
01542       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01543       if (write_password_to_file(secretfn, newpassword) == 0) {
01544          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01545          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01546          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01547          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01548          break;
01549       } else {
01550          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01551       }
01552       /* Fall-through */
01553    case OPT_PWLOC_VOICEMAILCONF:
01554       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
01555          while ((category = ast_category_browse(cfg, category))) {
01556             if (!strcasecmp(category, vmu->context)) {
01557                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01558                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01559                   break;
01560                }
01561                value = strstr(tmp, ",");
01562                if (!value) {
01563                   new = ast_alloca(strlen(newpassword)+1);
01564                   sprintf(new, "%s", newpassword);
01565                } else {
01566                   new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
01567                   sprintf(new, "%s%s", newpassword, value);
01568                }
01569                if (!(cat = ast_category_get(cfg, category))) {
01570                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01571                   break;
01572                }
01573                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01574                found = 1;
01575             }
01576          }
01577          /* save the results */
01578          if (found) {
01579             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01580             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01581             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01582             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01583             ast_config_destroy(cfg);
01584             break;
01585          }
01586 
01587          ast_config_destroy(cfg);
01588       }
01589       /* Fall-through */
01590    case OPT_PWLOC_USERSCONF:
01591       /* check users.conf and update the password stored for the mailbox */
01592       /* if no vmsecret entry exists create one. */
01593       if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
01594          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01595          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01596             ast_debug(4, "users.conf: %s\n", category);
01597             if (!strcasecmp(category, vmu->mailbox)) {
01598                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01599                   ast_debug(3, "looks like we need to make vmsecret!\n");
01600                   var = ast_variable_new("vmsecret", newpassword, "");
01601                } else {
01602                   var = NULL;
01603                }
01604                new = ast_alloca(strlen(newpassword) + 1);
01605                sprintf(new, "%s", newpassword);
01606                if (!(cat = ast_category_get(cfg, category))) {
01607                   ast_debug(4, "failed to get category!\n");
01608                   ast_free(var);
01609                   break;
01610                }
01611                if (!var) {
01612                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01613                } else {
01614                   ast_variable_append(cat, var);
01615                }
01616                found = 1;
01617                break;
01618             }
01619          }
01620          /* save the results and clean things up */
01621          if (found) {
01622             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01623             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01624             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01625             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01626          }
01627 
01628          ast_config_destroy(cfg);
01629       }
01630    }
01631 }
01632 
01633 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01634 {
01635    char buf[255];
01636    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01637    ast_debug(1, "External password: %s\n",buf);
01638    if (!ast_safe_system(buf)) {
01639       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01640       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01641       /* Reset the password in memory, too */
01642       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01643    }
01644 }
01645 
01646 /*! 
01647  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01648  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01649  * \param len The length of the path string that was written out.
01650  * \param context
01651  * \param ext 
01652  * \param folder 
01653  * 
01654  * The path is constructed as 
01655  *    VM_SPOOL_DIRcontext/ext/folder
01656  *
01657  * \return zero on success, -1 on error.
01658  */
01659 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01660 {
01661    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01662 }
01663 
01664 /*! 
01665  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01666  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01667  * \param len The length of the path string that was written out.
01668  * \param dir 
01669  * \param num 
01670  * 
01671  * The path is constructed as 
01672  *    VM_SPOOL_DIRcontext/ext/folder
01673  *
01674  * \return zero on success, -1 on error.
01675  */
01676 static int make_file(char *dest, const int len, const char *dir, const int num)
01677 {
01678    return snprintf(dest, len, "%s/msg%04d", dir, num);
01679 }
01680 
01681 /* same as mkstemp, but return a FILE * */
01682 static FILE *vm_mkftemp(char *template)
01683 {
01684    FILE *p = NULL;
01685    int pfd = mkstemp(template);
01686    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01687    if (pfd > -1) {
01688       p = fdopen(pfd, "w+");
01689       if (!p) {
01690          close(pfd);
01691          pfd = -1;
01692       }
01693    }
01694    return p;
01695 }
01696 
01697 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01698  * \param dest    String. base directory.
01699  * \param len     Length of dest.
01700  * \param context String. Ignored if is null or empty string.
01701  * \param ext     String. Ignored if is null or empty string.
01702  * \param folder  String. Ignored if is null or empty string. 
01703  * \return -1 on failure, 0 on success.
01704  */
01705 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01706 {
01707    mode_t   mode = VOICEMAIL_DIR_MODE;
01708    int res;
01709 
01710    make_dir(dest, len, context, ext, folder);
01711    if ((res = ast_mkdir(dest, mode))) {
01712       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01713       return -1;
01714    }
01715    return 0;
01716 }
01717 
01718 static const char * const mailbox_folders[] = {
01719 #ifdef IMAP_STORAGE
01720    imapfolder,
01721 #else
01722    "INBOX",
01723 #endif
01724    "Old",
01725    "Work",
01726    "Family",
01727    "Friends",
01728    "Cust1",
01729    "Cust2",
01730    "Cust3",
01731    "Cust4",
01732    "Cust5",
01733    "Deleted",
01734    "Urgent",
01735 };
01736 
01737 static const char *mbox(struct ast_vm_user *vmu, int id)
01738 {
01739 #ifdef IMAP_STORAGE
01740    if (vmu && id == 0) {
01741       return vmu->imapfolder;
01742    }
01743 #endif
01744    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01745 }
01746 
01747 static int get_folder_by_name(const char *name)
01748 {
01749    size_t i;
01750 
01751    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01752       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01753          return i;
01754       }
01755    }
01756 
01757    return -1;
01758 }
01759 
01760 static void free_user(struct ast_vm_user *vmu)
01761 {
01762    if (ast_test_flag(vmu, VM_ALLOCED)) {
01763 
01764       ast_free(vmu->emailbody);
01765       vmu->emailbody = NULL;
01766 
01767       ast_free(vmu->emailsubject);
01768       vmu->emailsubject = NULL;
01769 
01770       ast_free(vmu);
01771    }
01772 }
01773 
01774 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01775 
01776    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01777 
01778    /* remove old allocation */
01779    if (vms->deleted) {
01780       ast_free(vms->deleted);
01781       vms->deleted = NULL;
01782    }
01783    if (vms->heard) {
01784       ast_free(vms->heard);
01785       vms->heard = NULL;
01786    }
01787    vms->dh_arraysize = 0;
01788 
01789    if (arraysize > 0) {
01790       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01791          return -1;
01792       }
01793       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01794          ast_free(vms->deleted);
01795          vms->deleted = NULL;
01796          return -1;
01797       }
01798       vms->dh_arraysize = arraysize;
01799    }
01800 
01801    return 0;
01802 }
01803 
01804 /* All IMAP-specific functions should go in this block. This
01805  * keeps them from being spread out all over the code */
01806 #ifdef IMAP_STORAGE
01807 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01808 {
01809    char arg[10];
01810    struct vm_state *vms;
01811    unsigned long messageNum;
01812 
01813    /* If greetings aren't stored in IMAP, just delete the file */
01814    if (msgnum < 0 && !imapgreetings) {
01815       ast_filedelete(file, NULL);
01816       return;
01817    }
01818 
01819    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01820       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);
01821       return;
01822    }
01823 
01824    if (msgnum < 0) {
01825       imap_delete_old_greeting(file, vms);
01826       return;
01827    }
01828 
01829    /* find real message number based on msgnum */
01830    /* this may be an index into vms->msgArray based on the msgnum. */
01831    messageNum = vms->msgArray[msgnum];
01832    if (messageNum == 0) {
01833       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01834       return;
01835    }
01836    if (option_debug > 2)
01837       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01838    /* delete message */
01839    snprintf (arg, sizeof(arg), "%lu", messageNum);
01840    ast_mutex_lock(&vms->lock);
01841    mail_setflag (vms->mailstream, arg, "\\DELETED");
01842    mail_expunge(vms->mailstream);
01843    ast_mutex_unlock(&vms->lock);
01844 }
01845 
01846 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01847 {
01848    struct vm_state *vms_p;
01849    char *file, *filename;
01850    char *attachment;
01851    int i;
01852    BODY *body;
01853 
01854    /* This function is only used for retrieval of IMAP greetings
01855     * regular messages are not retrieved this way, nor are greetings
01856     * if they are stored locally*/
01857    if (msgnum > -1 || !imapgreetings) {
01858       return 0;
01859    } else {
01860       file = strrchr(ast_strdupa(dir), '/');
01861       if (file)
01862          *file++ = '\0';
01863       else {
01864          ast_debug (1, "Failed to procure file name from directory passed.\n");
01865          return -1;
01866       }
01867    }
01868 
01869    /* check if someone is accessing this box right now... */
01870    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01871       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01872       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01873       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01874       * that's all we need to do.
01875       */
01876       if (!(vms_p = create_vm_state_from_user(vmu))) {
01877          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01878          return -1;
01879       }
01880    }
01881 
01882    /* Greetings will never have a prepended message */
01883    *vms_p->introfn = '\0';
01884 
01885    ast_mutex_lock(&vms_p->lock);
01886    init_mailstream(vms_p, GREETINGS_FOLDER);
01887    if (!vms_p->mailstream) {
01888       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01889       ast_mutex_unlock(&vms_p->lock);
01890       return -1;
01891    }
01892 
01893    /*XXX Yuck, this could probably be done a lot better */
01894    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01895       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01896       /* We have the body, now we extract the file name of the first attachment. */
01897       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01898          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01899       } else {
01900          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01901          ast_mutex_unlock(&vms_p->lock);
01902          return -1;
01903       }
01904       filename = strsep(&attachment, ".");
01905       if (!strcmp(filename, file)) {
01906          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01907          vms_p->msgArray[vms_p->curmsg] = i + 1;
01908          save_body(body, vms_p, "2", attachment, 0);
01909          ast_mutex_unlock(&vms_p->lock);
01910          return 0;
01911       }
01912    }
01913    ast_mutex_unlock(&vms_p->lock);
01914 
01915    return -1;
01916 }
01917 
01918 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01919 {
01920    BODY *body;
01921    char *header_content;
01922    char *attachedfilefmt;
01923    char buf[80];
01924    struct vm_state *vms;
01925    char text_file[PATH_MAX];
01926    FILE *text_file_ptr;
01927    int res = 0;
01928    struct ast_vm_user *vmu;
01929 
01930    if (!(vmu = find_user(NULL, context, mailbox))) {
01931       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01932       return -1;
01933    }
01934    
01935    if (msgnum < 0) {
01936       if (imapgreetings) {
01937          res = imap_retrieve_greeting(dir, msgnum, vmu);
01938          goto exit;
01939       } else {
01940          res = 0;
01941          goto exit;
01942       }
01943    }
01944 
01945    /* Before anything can happen, we need a vm_state so that we can
01946     * actually access the imap server through the vms->mailstream
01947     */
01948    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01949       /* This should not happen. If it does, then I guess we'd
01950        * need to create the vm_state, extract which mailbox to
01951        * open, and then set up the msgArray so that the correct
01952        * IMAP message could be accessed. If I have seen correctly
01953        * though, the vms should be obtainable from the vmstates list
01954        * and should have its msgArray properly set up.
01955        */
01956       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01957       res = -1;
01958       goto exit;
01959    }
01960    
01961    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01962    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01963 
01964    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01965    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01966       res = 0;
01967       goto exit;
01968    }
01969 
01970    if (option_debug > 2)
01971       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01972    if (vms->msgArray[msgnum] == 0) {
01973       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01974       res = -1;
01975       goto exit;
01976    }
01977 
01978    /* This will only work for new messages... */
01979    ast_mutex_lock(&vms->lock);
01980    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01981    ast_mutex_unlock(&vms->lock);
01982    /* empty string means no valid header */
01983    if (ast_strlen_zero(header_content)) {
01984       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01985       res = -1;
01986       goto exit;
01987    }
01988 
01989    ast_mutex_lock(&vms->lock);
01990    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01991    ast_mutex_unlock(&vms->lock);
01992 
01993    /* We have the body, now we extract the file name of the first attachment. */
01994    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01995       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01996    } else {
01997       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01998       res = -1;
01999       goto exit;
02000    }
02001    
02002    /* Find the format of the attached file */
02003 
02004    strsep(&attachedfilefmt, ".");
02005    if (!attachedfilefmt) {
02006       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
02007       res = -1;
02008       goto exit;
02009    }
02010    
02011    save_body(body, vms, "2", attachedfilefmt, 0);
02012    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
02013       *vms->introfn = '\0';
02014    }
02015 
02016    /* Get info from headers!! */
02017    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
02018 
02019    if (!(text_file_ptr = fopen(text_file, "w"))) {
02020       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
02021    }
02022 
02023    fprintf(text_file_ptr, "%s\n", "[message]");
02024 
02025    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
02026    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
02027    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
02028    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02029    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
02030    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02031    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02032    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02033    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02034    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02035    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02036    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02037    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02038    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02039    fclose(text_file_ptr);
02040 
02041 exit:
02042    free_user(vmu);
02043    return res;
02044 }
02045 
02046 static int folder_int(const char *folder)
02047 {
02048    /*assume a NULL folder means INBOX*/
02049    if (!folder) {
02050       return 0;
02051    }
02052    if (!strcasecmp(folder, imapfolder)) {
02053       return 0;
02054    } else if (!strcasecmp(folder, "Old")) {
02055       return 1;
02056    } else if (!strcasecmp(folder, "Work")) {
02057       return 2;
02058    } else if (!strcasecmp(folder, "Family")) {
02059       return 3;
02060    } else if (!strcasecmp(folder, "Friends")) {
02061       return 4;
02062    } else if (!strcasecmp(folder, "Cust1")) {
02063       return 5;
02064    } else if (!strcasecmp(folder, "Cust2")) {
02065       return 6;
02066    } else if (!strcasecmp(folder, "Cust3")) {
02067       return 7;
02068    } else if (!strcasecmp(folder, "Cust4")) {
02069       return 8;
02070    } else if (!strcasecmp(folder, "Cust5")) {
02071       return 9;
02072    } else if (!strcasecmp(folder, "Urgent")) {
02073       return 11;
02074    } else { /*assume they meant INBOX if folder is not found otherwise*/
02075       return 0;
02076    }
02077 }
02078 
02079 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02080 {
02081    SEARCHPGM *pgm;
02082    SEARCHHEADER *hdr;
02083 
02084    struct ast_vm_user *vmu, vmus;
02085    struct vm_state *vms_p;
02086    int ret = 0;
02087    int fold = folder_int(folder);
02088    int urgent = 0;
02089    
02090    /* If URGENT, then look at INBOX */
02091    if (fold == 11) {
02092       fold = NEW_FOLDER;
02093       urgent = 1;
02094    }
02095 
02096    if (ast_strlen_zero(mailbox))
02097       return 0;
02098 
02099    /* We have to get the user before we can open the stream! */
02100    vmu = find_user(&vmus, context, mailbox);
02101    if (!vmu) {
02102       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02103       return -1;
02104    } else {
02105       /* No IMAP account available */
02106       if (vmu->imapuser[0] == '\0') {
02107          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02108          return -1;
02109       }
02110    }
02111    
02112    /* No IMAP account available */
02113    if (vmu->imapuser[0] == '\0') {
02114       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02115       free_user(vmu);
02116       return -1;
02117    }
02118 
02119    /* check if someone is accessing this box right now... */
02120    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02121    if (!vms_p) {
02122       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02123    }
02124    if (vms_p) {
02125       ast_debug(3, "Returning before search - user is logged in\n");
02126       if (fold == 0) { /* INBOX */
02127          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02128       }
02129       if (fold == 1) { /* Old messages */
02130          return vms_p->oldmessages;
02131       }
02132    }
02133 
02134    /* add one if not there... */
02135    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02136    if (!vms_p) {
02137       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02138    }
02139 
02140    if (!vms_p) {
02141       vms_p = create_vm_state_from_user(vmu);
02142    }
02143    ret = init_mailstream(vms_p, fold);
02144    if (!vms_p->mailstream) {
02145       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02146       return -1;
02147    }
02148    if (ret == 0) {
02149       ast_mutex_lock(&vms_p->lock);
02150       pgm = mail_newsearchpgm ();
02151       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02152       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02153       pgm->header = hdr;
02154       if (fold != OLD_FOLDER) {
02155          pgm->unseen = 1;
02156          pgm->seen = 0;
02157       }
02158       /* In the special case where fold is 1 (old messages) we have to do things a bit
02159        * differently. Old messages are stored in the INBOX but are marked as "seen"
02160        */
02161       else {
02162          pgm->unseen = 0;
02163          pgm->seen = 1;
02164       }
02165       /* look for urgent messages */
02166       if (fold == NEW_FOLDER) {
02167          if (urgent) {
02168             pgm->flagged = 1;
02169             pgm->unflagged = 0;
02170          } else {
02171             pgm->flagged = 0;
02172             pgm->unflagged = 1;
02173          }
02174       }
02175       pgm->undeleted = 1;
02176       pgm->deleted = 0;
02177 
02178       vms_p->vmArrayIndex = 0;
02179       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02180       if (fold == 0 && urgent == 0)
02181          vms_p->newmessages = vms_p->vmArrayIndex;
02182       if (fold == 1)
02183          vms_p->oldmessages = vms_p->vmArrayIndex;
02184       if (fold == 0 && urgent == 1)
02185          vms_p->urgentmessages = vms_p->vmArrayIndex;
02186       /*Freeing the searchpgm also frees the searchhdr*/
02187       mail_free_searchpgm(&pgm);
02188       ast_mutex_unlock(&vms_p->lock);
02189       vms_p->updated = 0;
02190       return vms_p->vmArrayIndex;
02191    } else {
02192       ast_mutex_lock(&vms_p->lock);
02193       mail_ping(vms_p->mailstream);
02194       ast_mutex_unlock(&vms_p->lock);
02195    }
02196    return 0;
02197 }
02198 
02199 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02200 {
02201    /* Check if mailbox is full */
02202    check_quota(vms, vmu->imapfolder);
02203    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02204       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02205       ast_play_and_wait(chan, "vm-mailboxfull");
02206       return -1;
02207    }
02208    
02209    /* Check if we have exceeded maxmsg */
02210    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));
02211    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02212       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02213       ast_play_and_wait(chan, "vm-mailboxfull");
02214       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02215       return -1;
02216    }
02217 
02218    return 0;
02219 }
02220 
02221 /*!
02222  * \brief Gets the number of messages that exist in a mailbox folder.
02223  * \param context
02224  * \param mailbox
02225  * \param folder
02226  * 
02227  * This method is used when IMAP backend is used.
02228  * \return The number of messages in this mailbox folder (zero or more).
02229  */
02230 static int messagecount(const char *context, const char *mailbox, const char *folder)
02231 {
02232    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02233       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02234    } else {
02235       return __messagecount(context, mailbox, folder);
02236    }
02237 }
02238 
02239 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)
02240 {
02241    char *myserveremail = serveremail;
02242    char fn[PATH_MAX];
02243    char introfn[PATH_MAX];
02244    char mailbox[256];
02245    char *stringp;
02246    FILE *p = NULL;
02247    char tmp[80] = "/tmp/astmail-XXXXXX";
02248    long len;
02249    void *buf;
02250    int tempcopy = 0;
02251    STRING str;
02252    int ret; /* for better error checking */
02253    char *imap_flags = NIL;
02254    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02255    int box = NEW_FOLDER;
02256 
02257    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02258    if (msgnum < 0) {
02259       if(!imapgreetings) {
02260          return 0;
02261       } else {
02262          box = GREETINGS_FOLDER;
02263       }
02264    }
02265    
02266    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02267       return -1;
02268    }
02269 
02270    /* Set urgent flag for IMAP message */
02271    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02272       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02273       imap_flags = "\\FLAGGED";
02274    }
02275    
02276    /* Attach only the first format */
02277    fmt = ast_strdupa(fmt);
02278    stringp = fmt;
02279    strsep(&stringp, "|");
02280 
02281    if (!ast_strlen_zero(vmu->serveremail))
02282       myserveremail = vmu->serveremail;
02283 
02284    if (msgnum > -1)
02285       make_file(fn, sizeof(fn), dir, msgnum);
02286    else
02287       ast_copy_string (fn, dir, sizeof(fn));
02288 
02289    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02290    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02291       *introfn = '\0';
02292    }
02293    
02294    if (ast_strlen_zero(vmu->email)) {
02295       /* We need the vmu->email to be set when we call make_email_file, but
02296        * if we keep it set, a duplicate e-mail will be created. So at the end
02297        * of this function, we will revert back to an empty string if tempcopy
02298        * is 1.
02299        */
02300       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02301       tempcopy = 1;
02302    }
02303 
02304    if (!strcmp(fmt, "wav49"))
02305       fmt = "WAV";
02306    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02307 
02308    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02309       command hangs. */
02310    if (!(p = vm_mkftemp(tmp))) {
02311       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02312       if (tempcopy)
02313          *(vmu->email) = '\0';
02314       return -1;
02315    }
02316 
02317    if (msgnum < 0 && imapgreetings) {
02318       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02319          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02320          return -1;
02321       }
02322       imap_delete_old_greeting(fn, vms);
02323    }
02324 
02325    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02326       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02327       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02328       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02329    /* read mail file to memory */
02330    len = ftell(p);
02331    rewind(p);
02332    if (!(buf = ast_malloc(len + 1))) {
02333       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02334       fclose(p);
02335       if (tempcopy)
02336          *(vmu->email) = '\0';
02337       return -1;
02338    }
02339    if (fread(buf, len, 1, p) < len) {
02340       if (ferror(p)) {
02341          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02342          return -1;
02343       }
02344    }
02345    ((char *) buf)[len] = '\0';
02346    INIT(&str, mail_string, buf, len);
02347    ret = init_mailstream(vms, box);
02348    if (ret == 0) {
02349       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02350       ast_mutex_lock(&vms->lock);
02351       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02352          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02353       ast_mutex_unlock(&vms->lock);
02354       fclose(p);
02355       unlink(tmp);
02356       ast_free(buf);
02357    } else {
02358       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02359       fclose(p);
02360       unlink(tmp);
02361       ast_free(buf);
02362       return -1;
02363    }
02364    ast_debug(3, "%s stored\n", fn);
02365    
02366    if (tempcopy)
02367       *(vmu->email) = '\0';
02368    inprocess_count(vmu->mailbox, vmu->context, -1);
02369    return 0;
02370 
02371 }
02372 
02373 /*!
02374  * \brief Gets the number of messages that exist in the inbox folder.
02375  * \param mailbox_context
02376  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02377  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02378  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02379  * 
02380  * This method is used when IMAP backend is used.
02381  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02382  *
02383  * \return zero on success, -1 on error.
02384  */
02385 
02386 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02387 {
02388    char tmp[PATH_MAX] = "";
02389    char *mailboxnc;
02390    char *context;
02391    char *mb;
02392    char *cur;
02393    if (newmsgs)
02394       *newmsgs = 0;
02395    if (oldmsgs)
02396       *oldmsgs = 0;
02397    if (urgentmsgs)
02398       *urgentmsgs = 0;
02399 
02400    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02401    /* If no mailbox, return immediately */
02402    if (ast_strlen_zero(mailbox_context))
02403       return 0;
02404    
02405    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02406    context = strchr(tmp, '@');
02407    if (strchr(mailbox_context, ',')) {
02408       int tmpnew, tmpold, tmpurgent;
02409       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02410       mb = tmp;
02411       while ((cur = strsep(&mb, ", "))) {
02412          if (!ast_strlen_zero(cur)) {
02413             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02414                return -1;
02415             else {
02416                if (newmsgs)
02417                   *newmsgs += tmpnew; 
02418                if (oldmsgs)
02419                   *oldmsgs += tmpold;
02420                if (urgentmsgs)
02421                   *urgentmsgs += tmpurgent;
02422             }
02423          }
02424       }
02425       return 0;
02426    }
02427    if (context) {
02428       *context = '\0';
02429       mailboxnc = tmp;
02430       context++;
02431    } else {
02432       context = "default";
02433       mailboxnc = (char *) mailbox_context;
02434    }
02435 
02436    if (newmsgs) {
02437       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02438       if (!vmu) {
02439          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02440          return -1;
02441       }
02442       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02443          free_user(vmu);
02444          return -1;
02445       }
02446       free_user(vmu);
02447    }
02448    if (oldmsgs) {
02449       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02450          return -1;
02451       }
02452    }
02453    if (urgentmsgs) {
02454       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02455          return -1;
02456       }
02457    }
02458    return 0;
02459 }
02460 
02461 /** 
02462  * \brief Determines if the given folder has messages.
02463  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02464  * \param folder the folder to look in
02465  *
02466  * This function is used when the mailbox is stored in an IMAP back end.
02467  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02468  * \return 1 if the folder has one or more messages. zero otherwise.
02469  */
02470 
02471 static int has_voicemail(const char *mailbox, const char *folder)
02472 {
02473    char tmp[256], *tmp2, *box, *context;
02474    ast_copy_string(tmp, mailbox, sizeof(tmp));
02475    tmp2 = tmp;
02476    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02477       while ((box = strsep(&tmp2, ",&"))) {
02478          if (!ast_strlen_zero(box)) {
02479             if (has_voicemail(box, folder)) {
02480                return 1;
02481             }
02482          }
02483       }
02484    }
02485    if ((context = strchr(tmp, '@'))) {
02486       *context++ = '\0';
02487    } else {
02488       context = "default";
02489    }
02490    return __messagecount(context, tmp, folder) ? 1 : 0;
02491 }
02492 
02493 /*!
02494  * \brief Copies a message from one mailbox to another.
02495  * \param chan
02496  * \param vmu
02497  * \param imbox
02498  * \param msgnum
02499  * \param duration
02500  * \param recip
02501  * \param fmt
02502  * \param dir
02503  *
02504  * This works with IMAP storage based mailboxes.
02505  *
02506  * \return zero on success, -1 on error.
02507  */
02508 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)
02509 {
02510    struct vm_state *sendvms = NULL, *destvms = NULL;
02511    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02512    if (msgnum >= recip->maxmsg) {
02513       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02514       return -1;
02515    }
02516    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02517       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02518       return -1;
02519    }
02520    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02521       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02522       return -1;
02523    }
02524    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02525    ast_mutex_lock(&sendvms->lock);
02526    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02527       ast_mutex_unlock(&sendvms->lock);
02528       return 0;
02529    }
02530    ast_mutex_unlock(&sendvms->lock);
02531    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02532    return -1;
02533 }
02534 
02535 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02536 {
02537    char tmp[256], *t = tmp;
02538    size_t left = sizeof(tmp);
02539    
02540    if (box == OLD_FOLDER) {
02541       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02542    } else {
02543       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02544    }
02545 
02546    if (box == NEW_FOLDER) {
02547       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02548    } else {
02549       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02550    }
02551 
02552    /* Build up server information */
02553    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02554 
02555    /* Add authentication user if present */
02556    if (!ast_strlen_zero(authuser))
02557       ast_build_string(&t, &left, "/authuser=%s", authuser);
02558 
02559    /* Add flags if present */
02560    if (!ast_strlen_zero(imapflags))
02561       ast_build_string(&t, &left, "/%s", imapflags);
02562 
02563    /* End with username */
02564 #if 1
02565    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02566 #else
02567    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02568 #endif
02569    if (box == NEW_FOLDER || box == OLD_FOLDER)
02570       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02571    else if (box == GREETINGS_FOLDER)
02572       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02573    else {   /* Other folders such as Friends, Family, etc... */
02574       if (!ast_strlen_zero(imapparentfolder)) {
02575          /* imapparentfolder would typically be set to INBOX */
02576          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02577       } else {
02578          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02579       }
02580    }
02581 }
02582 
02583 static int init_mailstream(struct vm_state *vms, int box)
02584 {
02585    MAILSTREAM *stream = NIL;
02586    long debug;
02587    char tmp[256];
02588    
02589    if (!vms) {
02590       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02591       return -1;
02592    }
02593    if (option_debug > 2)
02594       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02595    if (vms->mailstream == NIL || !vms->mailstream) {
02596       if (option_debug)
02597          ast_log(LOG_DEBUG, "mailstream not set.\n");
02598    } else {
02599       stream = vms->mailstream;
02600    }
02601    /* debug = T;  user wants protocol telemetry? */
02602    debug = NIL;  /* NO protocol telemetry? */
02603 
02604    if (delimiter == '\0') {      /* did not probe the server yet */
02605       char *cp;
02606 #ifdef USE_SYSTEM_IMAP
02607 #include <imap/linkage.c>
02608 #elif defined(USE_SYSTEM_CCLIENT)
02609 #include <c-client/linkage.c>
02610 #else
02611 #include "linkage.c"
02612 #endif
02613       /* Connect to INBOX first to get folders delimiter */
02614       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02615       ast_mutex_lock(&vms->lock);
02616       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02617       ast_mutex_unlock(&vms->lock);
02618       if (stream == NIL) {
02619          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02620          return -1;
02621       }
02622       get_mailbox_delimiter(stream);
02623       /* update delimiter in imapfolder */
02624       for (cp = vms->imapfolder; *cp; cp++)
02625          if (*cp == '/')
02626             *cp = delimiter;
02627    }
02628    /* Now connect to the target folder */
02629    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02630    if (option_debug > 2)
02631       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02632    ast_mutex_lock(&vms->lock);
02633    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02634    ast_mutex_unlock(&vms->lock);
02635    if (vms->mailstream == NIL) {
02636       return -1;
02637    } else {
02638       return 0;
02639    }
02640 }
02641 
02642 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02643 {
02644    SEARCHPGM *pgm;
02645    SEARCHHEADER *hdr;
02646    int ret, urgent = 0;
02647 
02648    /* If Urgent, then look at INBOX */
02649    if (box == 11) {
02650       box = NEW_FOLDER;
02651       urgent = 1;
02652    }
02653 
02654    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02655    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02656    vms->imapversion = vmu->imapversion;
02657    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02658 
02659    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02660       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02661       return -1;
02662    }
02663    
02664    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02665    
02666    /* Check Quota */
02667    if  (box == 0)  {
02668       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02669       check_quota(vms, (char *) mbox(vmu, box));
02670    }
02671 
02672    ast_mutex_lock(&vms->lock);
02673    pgm = mail_newsearchpgm();
02674 
02675    /* Check IMAP folder for Asterisk messages only... */
02676    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02677    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02678    pgm->header = hdr;
02679    pgm->deleted = 0;
02680    pgm->undeleted = 1;
02681 
02682    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02683    if (box == NEW_FOLDER && urgent == 1) {
02684       pgm->unseen = 1;
02685       pgm->seen = 0;
02686       pgm->flagged = 1;
02687       pgm->unflagged = 0;
02688    } else if (box == NEW_FOLDER && urgent == 0) {
02689       pgm->unseen = 1;
02690       pgm->seen = 0;
02691       pgm->flagged = 0;
02692       pgm->unflagged = 1;
02693    } else if (box == OLD_FOLDER) {
02694       pgm->seen = 1;
02695       pgm->unseen = 0;
02696    }
02697 
02698    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02699 
02700    vms->vmArrayIndex = 0;
02701    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02702    vms->lastmsg = vms->vmArrayIndex - 1;
02703    mail_free_searchpgm(&pgm);
02704    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02705     * ensure to allocate enough space to account for all of them. Warn if old messages
02706     * have not been checked first as that is required.
02707     */
02708    if (box == 0 && !vms->dh_arraysize) {
02709       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02710    }
02711    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02712       ast_mutex_unlock(&vms->lock);
02713       return -1;
02714    }
02715 
02716    ast_mutex_unlock(&vms->lock);
02717    return 0;
02718 }
02719 
02720 static void write_file(char *filename, char *buffer, unsigned long len)
02721 {
02722    FILE *output;
02723 
02724    output = fopen (filename, "w");
02725    if (fwrite(buffer, len, 1, output) != 1) {
02726       if (ferror(output)) {
02727          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02728       }
02729    }
02730    fclose (output);
02731 }
02732 
02733 static void update_messages_by_imapuser(const char *user, unsigned long number)
02734 {
02735    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02736 
02737    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02738       return;
02739    }
02740 
02741    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02742    vms->msgArray[vms->vmArrayIndex++] = number;
02743 }
02744 
02745 void mm_searched(MAILSTREAM *stream, unsigned long number)
02746 {
02747    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02748 
02749    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02750       return;
02751 
02752    update_messages_by_imapuser(user, number);
02753 }
02754 
02755 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02756 {
02757    struct ast_variable *var;
02758    struct ast_vm_user *vmu;
02759 
02760    vmu = ast_calloc(1, sizeof *vmu);
02761    if (!vmu)
02762       return NULL;
02763 
02764    populate_defaults(vmu);
02765    ast_set_flag(vmu, VM_ALLOCED);
02766 
02767    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02768    if (var) {
02769       apply_options_full(vmu, var);
02770       ast_variables_destroy(var);
02771       return vmu;
02772    } else {
02773       ast_free(vmu);
02774       return NULL;
02775    }
02776 }
02777 
02778 /* Interfaces to C-client */
02779 
02780 void mm_exists(MAILSTREAM * stream, unsigned long number)
02781 {
02782    /* mail_ping will callback here if new mail! */
02783    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02784    if (number == 0) return;
02785    set_update(stream);
02786 }
02787 
02788 
02789 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02790 {
02791    /* mail_ping will callback here if expunged mail! */
02792    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02793    if (number == 0) return;
02794    set_update(stream);
02795 }
02796 
02797 
02798 void mm_flags(MAILSTREAM * stream, unsigned long number)
02799 {
02800    /* mail_ping will callback here if read mail! */
02801    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02802    if (number == 0) return;
02803    set_update(stream);
02804 }
02805 
02806 
02807 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02808 {
02809    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02810    mm_log (string, errflg);
02811 }
02812 
02813 
02814 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02815 {
02816    if (delimiter == '\0') {
02817       delimiter = delim;
02818    }
02819 
02820    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02821    if (attributes & LATT_NOINFERIORS)
02822       ast_debug(5, "no inferiors\n");
02823    if (attributes & LATT_NOSELECT)
02824       ast_debug(5, "no select\n");
02825    if (attributes & LATT_MARKED)
02826       ast_debug(5, "marked\n");
02827    if (attributes & LATT_UNMARKED)
02828       ast_debug(5, "unmarked\n");
02829 }
02830 
02831 
02832 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02833 {
02834    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02835    if (attributes & LATT_NOINFERIORS)
02836       ast_debug(5, "no inferiors\n");
02837    if (attributes & LATT_NOSELECT)
02838       ast_debug(5, "no select\n");
02839    if (attributes & LATT_MARKED)
02840       ast_debug(5, "marked\n");
02841    if (attributes & LATT_UNMARKED)
02842       ast_debug(5, "unmarked\n");
02843 }
02844 
02845 
02846 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02847 {
02848    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02849    if (status->flags & SA_MESSAGES)
02850       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02851    if (status->flags & SA_RECENT)
02852       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02853    if (status->flags & SA_UNSEEN)
02854       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02855    if (status->flags & SA_UIDVALIDITY)
02856       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02857    if (status->flags & SA_UIDNEXT)
02858       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02859    ast_log(AST_LOG_NOTICE, "\n");
02860 }
02861 
02862 
02863 void mm_log(char *string, long errflg)
02864 {
02865    switch ((short) errflg) {
02866       case NIL:
02867          ast_debug(1, "IMAP Info: %s\n", string);
02868          break;
02869       case PARSE:
02870       case WARN:
02871          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02872          break;
02873       case ERROR:
02874          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02875          break;
02876    }
02877 }
02878 
02879 
02880 void mm_dlog(char *string)
02881 {
02882    ast_log(AST_LOG_NOTICE, "%s\n", string);
02883 }
02884 
02885 
02886 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02887 {
02888    struct ast_vm_user *vmu;
02889 
02890    ast_debug(4, "Entering callback mm_login\n");
02891 
02892    ast_copy_string(user, mb->user, MAILTMPLEN);
02893 
02894    /* We should only do this when necessary */
02895    if (!ast_strlen_zero(authpassword)) {
02896       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02897    } else {
02898       AST_LIST_TRAVERSE(&users, vmu, list) {
02899          if (!strcasecmp(mb->user, vmu->imapuser)) {
02900             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02901             break;
02902          }
02903       }
02904       if (!vmu) {
02905          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02906             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02907             free_user(vmu);
02908          }
02909       }
02910    }
02911 }
02912 
02913 
02914 void mm_critical(MAILSTREAM * stream)
02915 {
02916 }
02917 
02918 
02919 void mm_nocritical(MAILSTREAM * stream)
02920 {
02921 }
02922 
02923 
02924 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02925 {
02926    kill (getpid (), SIGSTOP);
02927    return NIL;
02928 }
02929 
02930 
02931 void mm_fatal(char *string)
02932 {
02933    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02934 }
02935 
02936 /* C-client callback to handle quota */
02937 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02938 {
02939    struct vm_state *vms;
02940    char *mailbox = stream->mailbox, *user;
02941    char buf[1024] = "";
02942    unsigned long usage = 0, limit = 0;
02943    
02944    while (pquota) {
02945       usage = pquota->usage;
02946       limit = pquota->limit;
02947       pquota = pquota->next;
02948    }
02949    
02950    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)))) {
02951       ast_log(AST_LOG_ERROR, "No state found.\n");
02952       return;
02953    }
02954 
02955    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02956 
02957    vms->quota_usage = usage;
02958    vms->quota_limit = limit;
02959 }
02960 
02961 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02962 {
02963    char *start, *eol_pnt;
02964    int taglen;
02965 
02966    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02967       return NULL;
02968 
02969    taglen = strlen(tag) + 1;
02970    if (taglen < 1)
02971       return NULL;
02972 
02973    if (!(start = strstr(header, tag)))
02974       return NULL;
02975 
02976    /* Since we can be called multiple times we should clear our buffer */
02977    memset(buf, 0, len);
02978 
02979    ast_copy_string(buf, start+taglen, len);
02980    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02981       *eol_pnt = '\0';
02982    return buf;
02983 }
02984 
02985 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02986 {
02987    char *start, *quote, *eol_pnt;
02988 
02989    if (ast_strlen_zero(mailbox))
02990       return NULL;
02991 
02992    if (!(start = strstr(mailbox, "/user=")))
02993       return NULL;
02994 
02995    ast_copy_string(buf, start+6, len);
02996 
02997    if (!(quote = strchr(buf, '\"'))) {
02998       if (!(eol_pnt = strchr(buf, '/')))
02999          eol_pnt = strchr(buf,'}');
03000       *eol_pnt = '\0';
03001       return buf;
03002    } else {
03003       eol_pnt = strchr(buf+1,'\"');
03004       *eol_pnt = '\0';
03005       return buf+1;
03006    }
03007 }
03008 
03009 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
03010 {
03011    struct vm_state *vms_p;
03012 
03013    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03014    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
03015       return vms_p;
03016    }
03017    if (option_debug > 4)
03018       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
03019    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
03020       return NULL;
03021    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
03022    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
03023    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
03024    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
03025    vms_p->mailstream = NIL; /* save for access from interactive entry point */
03026    vms_p->imapversion = vmu->imapversion;
03027    if (option_debug > 4)
03028       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
03029    vms_p->updated = 1;
03030    /* set mailbox to INBOX! */
03031    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03032    init_vm_state(vms_p);
03033    vmstate_insert(vms_p);
03034    return vms_p;
03035 }
03036 
03037 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03038 {
03039    struct vmstate *vlist = NULL;
03040 
03041    if (interactive) {
03042       struct vm_state *vms;
03043       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03044       vms = pthread_getspecific(ts_vmstate.key);
03045       return vms;
03046    }
03047 
03048    AST_LIST_LOCK(&vmstates);
03049    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03050       if (!vlist->vms) {
03051          ast_debug(3, "error: vms is NULL for %s\n", user);
03052          continue;
03053       }
03054       if (vlist->vms->imapversion != imapversion) {
03055          continue;
03056       }
03057       if (!vlist->vms->imapuser) {
03058          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03059          continue;
03060       }
03061 
03062       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03063          AST_LIST_UNLOCK(&vmstates);
03064          return vlist->vms;
03065       }
03066    }
03067    AST_LIST_UNLOCK(&vmstates);
03068 
03069    ast_debug(3, "%s not found in vmstates\n", user);
03070 
03071    return NULL;
03072 }
03073 
03074 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03075 {
03076 
03077    struct vmstate *vlist = NULL;
03078    const char *local_context = S_OR(context, "default");
03079 
03080    if (interactive) {
03081       struct vm_state *vms;
03082       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03083       vms = pthread_getspecific(ts_vmstate.key);
03084       return vms;
03085    }
03086 
03087    AST_LIST_LOCK(&vmstates);
03088    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03089       if (!vlist->vms) {
03090          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03091          continue;
03092       }
03093       if (vlist->vms->imapversion != imapversion) {
03094          continue;
03095       }
03096       if (!vlist->vms->username || !vlist->vms->context) {
03097          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03098          continue;
03099       }
03100 
03101       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);
03102       
03103       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03104          ast_debug(3, "Found it!\n");
03105          AST_LIST_UNLOCK(&vmstates);
03106          return vlist->vms;
03107       }
03108    }
03109    AST_LIST_UNLOCK(&vmstates);
03110 
03111    ast_debug(3, "%s not found in vmstates\n", mailbox);
03112 
03113    return NULL;
03114 }
03115 
03116 static void vmstate_insert(struct vm_state *vms) 
03117 {
03118    struct vmstate *v;
03119    struct vm_state *altvms;
03120 
03121    /* If interactive, it probably already exists, and we should
03122       use the one we already have since it is more up to date.
03123       We can compare the username to find the duplicate */
03124    if (vms->interactive == 1) {
03125       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03126       if (altvms) {  
03127          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03128          vms->newmessages = altvms->newmessages;
03129          vms->oldmessages = altvms->oldmessages;
03130          vms->vmArrayIndex = altvms->vmArrayIndex;
03131          vms->lastmsg = altvms->lastmsg;
03132          vms->curmsg = altvms->curmsg;
03133          /* get a pointer to the persistent store */
03134          vms->persist_vms = altvms;
03135          /* Reuse the mailstream? */
03136 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03137          vms->mailstream = altvms->mailstream;
03138 #else
03139          vms->mailstream = NIL;
03140 #endif
03141       }
03142       return;
03143    }
03144 
03145    if (!(v = ast_calloc(1, sizeof(*v))))
03146       return;
03147    
03148    v->vms = vms;
03149 
03150    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03151 
03152    AST_LIST_LOCK(&vmstates);
03153    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03154    AST_LIST_UNLOCK(&vmstates);
03155 }
03156 
03157 static void vmstate_delete(struct vm_state *vms) 
03158 {
03159    struct vmstate *vc = NULL;
03160    struct vm_state *altvms = NULL;
03161 
03162    /* If interactive, we should copy pertinent info
03163       back to the persistent state (to make update immediate) */
03164    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03165       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03166       altvms->newmessages = vms->newmessages;
03167       altvms->oldmessages = vms->oldmessages;
03168       altvms->updated = 1;
03169       vms->mailstream = mail_close(vms->mailstream);
03170 
03171       /* Interactive states are not stored within the persistent list */
03172       return;
03173    }
03174    
03175    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03176    
03177    AST_LIST_LOCK(&vmstates);
03178    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03179       if (vc->vms == vms) {
03180          AST_LIST_REMOVE_CURRENT(list);
03181          break;
03182       }
03183    }
03184    AST_LIST_TRAVERSE_SAFE_END
03185    AST_LIST_UNLOCK(&vmstates);
03186    
03187    if (vc) {
03188       ast_mutex_destroy(&vc->vms->lock);
03189       ast_free(vc);
03190    }
03191    else
03192       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03193 }
03194 
03195 static void set_update(MAILSTREAM * stream) 
03196 {
03197    struct vm_state *vms;
03198    char *mailbox = stream->mailbox, *user;
03199    char buf[1024] = "";
03200 
03201    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03202       if (user && option_debug > 2)
03203          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03204       return;
03205    }
03206 
03207    ast_debug(3, "User %s mailbox set for update.\n", user);
03208 
03209    vms->updated = 1; /* Set updated flag since mailbox changed */
03210 }
03211 
03212 static void init_vm_state(struct vm_state *vms) 
03213 {
03214    int x;
03215    vms->vmArrayIndex = 0;
03216    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03217       vms->msgArray[x] = 0;
03218    }
03219    ast_mutex_init(&vms->lock);
03220 }
03221 
03222 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03223 {
03224    char *body_content;
03225    char *body_decoded;
03226    char *fn = is_intro ? vms->introfn : vms->fn;
03227    unsigned long len;
03228    unsigned long newlen;
03229    char filename[256];
03230    
03231    if (!body || body == NIL)
03232       return -1;
03233 
03234    ast_mutex_lock(&vms->lock);
03235    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03236    ast_mutex_unlock(&vms->lock);
03237    if (body_content != NIL) {
03238       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03239       /* ast_debug(1,body_content); */
03240       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03241       /* If the body of the file is empty, return an error */
03242       if (!newlen) {
03243          return -1;
03244       }
03245       write_file(filename, (char *) body_decoded, newlen);
03246    } else {
03247       ast_debug(5, "Body of message is NULL.\n");
03248       return -1;
03249    }
03250    return 0;
03251 }
03252 
03253 /*! 
03254  * \brief Get delimiter via mm_list callback 
03255  * \param stream
03256  *
03257  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03258  */
03259 /* MUTEX should already be held */
03260 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03261    char tmp[50];
03262    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03263    mail_list(stream, tmp, "*");
03264 }
03265 
03266 /*! 
03267  * \brief Check Quota for user 
03268  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03269  * \param mailbox the mailbox to check the quota for.
03270  *
03271  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03272  */
03273 static void check_quota(struct vm_state *vms, char *mailbox) {
03274    ast_mutex_lock(&vms->lock);
03275    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03276    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03277    if (vms && vms->mailstream != NULL) {
03278       imap_getquotaroot(vms->mailstream, mailbox);
03279    } else {
03280       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03281    }
03282    ast_mutex_unlock(&vms->lock);
03283 }
03284 
03285 #endif /* IMAP_STORAGE */
03286 
03287 /*! \brief Lock file path
03288  * only return failure if ast_lock_path returns 'timeout',
03289  * not if the path does not exist or any other reason
03290  */
03291 static int vm_lock_path(const char *path)
03292 {
03293    switch (ast_lock_path(path)) {
03294    case AST_LOCK_TIMEOUT:
03295       return -1;
03296    default:
03297       return 0;
03298    }
03299 }
03300 
03301 
03302 #ifdef ODBC_STORAGE
03303 struct generic_prepare_struct {
03304    char *sql;
03305    int argc;
03306    char **argv;
03307 };
03308 
03309 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03310 {
03311    struct generic_prepare_struct *gps = data;
03312    int res, i;
03313    SQLHSTMT stmt;
03314 
03315    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03316    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03317       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03318       return NULL;
03319    }
03320    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03321    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03322       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03323       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03324       return NULL;
03325    }
03326    for (i = 0; i < gps->argc; i++)
03327       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03328 
03329    return stmt;
03330 }
03331 
03332 /*!
03333  * \brief Retrieves a file from an ODBC data store.
03334  * \param dir the path to the file to be retreived.
03335  * \param msgnum the message number, such as within a mailbox folder.
03336  * 
03337  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03338  * 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.
03339  *
03340  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03341  * The output is the message information file with the name msgnum and the extension .txt
03342  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03343  * 
03344  * \return 0 on success, -1 on error.
03345  */
03346 static int retrieve_file(char *dir, int msgnum)
03347 {
03348    int x = 0;
03349    int res;
03350    int fd = -1;
03351    size_t fdlen = 0;
03352    void *fdm = MAP_FAILED;
03353    SQLSMALLINT colcount = 0;
03354    SQLHSTMT stmt;
03355    char sql[PATH_MAX];
03356    char fmt[80]="";
03357    char *c;
03358    char coltitle[256];
03359    SQLSMALLINT collen;
03360    SQLSMALLINT datatype;
03361    SQLSMALLINT decimaldigits;
03362    SQLSMALLINT nullable;
03363    SQLULEN colsize;
03364    SQLLEN colsize2;
03365    FILE *f = NULL;
03366    char rowdata[80];
03367    char fn[PATH_MAX];
03368    char full_fn[PATH_MAX];
03369    char msgnums[80];
03370    char *argv[] = { dir, msgnums };
03371    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03372 
03373    struct odbc_obj *obj;
03374    obj = ast_odbc_request_obj(odbc_database, 0);
03375    if (obj) {
03376       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03377       c = strchr(fmt, '|');
03378       if (c)
03379          *c = '\0';
03380       if (!strcasecmp(fmt, "wav49"))
03381          strcpy(fmt, "WAV");
03382       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03383       if (msgnum > -1)
03384          make_file(fn, sizeof(fn), dir, msgnum);
03385       else
03386          ast_copy_string(fn, dir, sizeof(fn));
03387 
03388       /* Create the information file */
03389       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03390       
03391       if (!(f = fopen(full_fn, "w+"))) {
03392          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03393          goto yuck;
03394       }
03395       
03396       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03397       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03398       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03399       if (!stmt) {
03400          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03401          ast_odbc_release_obj(obj);
03402          goto yuck;
03403       }
03404       res = SQLFetch(stmt);
03405       if (res == SQL_NO_DATA) {
03406          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03407          ast_odbc_release_obj(obj);
03408          goto yuck;
03409       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03410          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03411          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03412          ast_odbc_release_obj(obj);
03413          goto yuck;
03414       }
03415       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03416       if (fd < 0) {
03417          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03418          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03419          ast_odbc_release_obj(obj);
03420          goto yuck;
03421       }
03422       res = SQLNumResultCols(stmt, &colcount);
03423       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03424          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03425          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03426          ast_odbc_release_obj(obj);
03427          goto yuck;
03428       }
03429       if (f) 
03430          fprintf(f, "[message]\n");
03431       for (x = 0; x < colcount; x++) {
03432          rowdata[0] = '\0';
03433          colsize = 0;
03434          collen = sizeof(coltitle);
03435          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03436                   &datatype, &colsize, &decimaldigits, &nullable);
03437          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03438             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03439             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03440             ast_odbc_release_obj(obj);
03441             goto yuck;
03442          }
03443          if (!strcasecmp(coltitle, "recording")) {
03444             off_t offset;
03445             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03446             fdlen = colsize2;
03447             if (fd > -1) {
03448                char tmp[1]="";
03449                lseek(fd, fdlen - 1, SEEK_SET);
03450                if (write(fd, tmp, 1) != 1) {
03451                   close(fd);
03452                   fd = -1;
03453                   continue;
03454                }
03455                /* Read out in small chunks */
03456                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03457                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03458                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03459                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03460                      ast_odbc_release_obj(obj);
03461                      goto yuck;
03462                   } else {
03463                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03464                      munmap(fdm, CHUNKSIZE);
03465                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03466                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03467                         unlink(full_fn);
03468                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03469                         ast_odbc_release_obj(obj);
03470                         goto yuck;
03471                      }
03472                   }
03473                }
03474                if (truncate(full_fn, fdlen) < 0) {
03475                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03476                }
03477             }
03478          } else {
03479             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03480             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03481                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03482                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03483                ast_odbc_release_obj(obj);
03484                goto yuck;
03485             }
03486             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03487                fprintf(f, "%s=%s\n", coltitle, rowdata);
03488          }
03489       }
03490       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03491       ast_odbc_release_obj(obj);
03492    } else
03493       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03494 yuck:
03495    if (f)
03496       fclose(f);
03497    if (fd > -1)
03498       close(fd);
03499    return x - 1;
03500 }
03501 
03502 /*!
03503  * \brief Determines the highest message number in use for a given user and mailbox folder.
03504  * \param vmu 
03505  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03506  *
03507  * This method is used when mailboxes are stored in an ODBC back end.
03508  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03509  *
03510  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03511 
03512  */
03513 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03514 {
03515    int x = 0;
03516    int res;
03517    SQLHSTMT stmt;
03518    char sql[PATH_MAX];
03519    char rowdata[20];
03520    char *argv[] = { dir };
03521    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03522 
03523    struct odbc_obj *obj;
03524    obj = ast_odbc_request_obj(odbc_database, 0);
03525    if (obj) {
03526       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03527 
03528       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03529       if (!stmt) {
03530          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03531          ast_odbc_release_obj(obj);
03532          goto yuck;
03533       }
03534       res = SQLFetch(stmt);
03535       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03536          if (res == SQL_NO_DATA) {
03537             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03538          } else {
03539             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03540          }
03541 
03542          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03543          ast_odbc_release_obj(obj);
03544          goto yuck;
03545       }
03546       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03547       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03548          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03549          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03550          ast_odbc_release_obj(obj);
03551          goto yuck;
03552       }
03553       if (sscanf(rowdata, "%30d", &x) != 1)
03554          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03555       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03556       ast_odbc_release_obj(obj);
03557       return x;
03558    } else
03559       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03560 yuck:
03561    return x - 1;
03562 }
03563 
03564 /*!
03565  * \brief Determines if the specified message exists.
03566  * \param dir the folder the mailbox folder to look for messages. 
03567  * \param msgnum the message index to query for.
03568  *
03569  * This method is used when mailboxes are stored in an ODBC back end.
03570  *
03571  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03572  */
03573 static int message_exists(char *dir, int msgnum)
03574 {
03575    int x = 0;
03576    int res;
03577    SQLHSTMT stmt;
03578    char sql[PATH_MAX];
03579    char rowdata[20];
03580    char msgnums[20];
03581    char *argv[] = { dir, msgnums };
03582    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03583 
03584    struct odbc_obj *obj;
03585    obj = ast_odbc_request_obj(odbc_database, 0);
03586    if (obj) {
03587       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03588       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03589       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03590       if (!stmt) {
03591          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03592          ast_odbc_release_obj(obj);
03593          goto yuck;
03594       }
03595       res = SQLFetch(stmt);
03596       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03597          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03598          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03599          ast_odbc_release_obj(obj);
03600          goto yuck;
03601       }
03602       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03603       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03604          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03605          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03606          ast_odbc_release_obj(obj);
03607          goto yuck;
03608       }
03609       if (sscanf(rowdata, "%30d", &x) != 1)
03610          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03611       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03612       ast_odbc_release_obj(obj);
03613    } else
03614       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03615 yuck:
03616    return x;
03617 }
03618 
03619 /*!
03620  * \brief returns the number of messages found.
03621  * \param vmu
03622  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03623  *
03624  * This method is used when mailboxes are stored in an ODBC back end.
03625  *
03626  * \return The count of messages being zero or more, less than zero on error.
03627  */
03628 static int count_messages(struct ast_vm_user *vmu, char *dir)
03629 {
03630    int x = 0;
03631    int res;
03632    SQLHSTMT stmt;
03633    char sql[PATH_MAX];
03634    char rowdata[20];
03635    char *argv[] = { dir };
03636    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03637 
03638    struct odbc_obj *obj;
03639    obj = ast_odbc_request_obj(odbc_database, 0);
03640    if (obj) {
03641       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03642       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03643       if (!stmt) {
03644          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03645          ast_odbc_release_obj(obj);
03646          goto yuck;
03647       }
03648       res = SQLFetch(stmt);
03649       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03650          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03651          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03652          ast_odbc_release_obj(obj);
03653          goto yuck;
03654       }
03655       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03656       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03657          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03658          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03659          ast_odbc_release_obj(obj);
03660          goto yuck;
03661       }
03662       if (sscanf(rowdata, "%30d", &x) != 1)
03663          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03664       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03665       ast_odbc_release_obj(obj);
03666       return x;
03667    } else
03668       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03669 yuck:
03670    return x - 1;
03671 
03672 }
03673 
03674 /*!
03675  * \brief Deletes a message from the mailbox folder.
03676  * \param sdir The mailbox folder to work in.
03677  * \param smsg The message index to be deleted.
03678  *
03679  * This method is used when mailboxes are stored in an ODBC back end.
03680  * The specified message is directly deleted from the database 'voicemessages' table.
03681  * 
03682  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03683  */
03684 static void delete_file(const char *sdir, int smsg)
03685 {
03686    SQLHSTMT stmt;
03687    char sql[PATH_MAX];
03688    char msgnums[20];
03689    char *argv[] = { NULL, msgnums };
03690    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03691    struct odbc_obj *obj;
03692 
03693    argv[0] = ast_strdupa(sdir);
03694 
03695    obj = ast_odbc_request_obj(odbc_database, 0);
03696    if (obj) {
03697       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03698       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03699       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03700       if (!stmt)
03701          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03702       else
03703          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03704       ast_odbc_release_obj(obj);
03705    } else
03706       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03707    return;  
03708 }
03709 
03710 /*!
03711  * \brief Copies a voicemail from one mailbox to another.
03712  * \param sdir the folder for which to look for the message to be copied.
03713  * \param smsg the index of the message to be copied.
03714  * \param ddir the destination folder to copy the message into.
03715  * \param dmsg the index to be used for the copied message.
03716  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03717  * \param dmailboxcontext The context for the destination user.
03718  *
03719  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03720  */
03721 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03722 {
03723    SQLHSTMT stmt;
03724    char sql[512];
03725    char msgnums[20];
03726    char msgnumd[20];
03727    struct odbc_obj *obj;
03728    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03729    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03730 
03731    delete_file(ddir, dmsg);
03732    obj = ast_odbc_request_obj(odbc_database, 0);
03733    if (obj) {
03734       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03735       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03736       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);
03737       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03738       if (!stmt)
03739          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03740       else
03741          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03742       ast_odbc_release_obj(obj);
03743    } else
03744       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03745    return;  
03746 }
03747 
03748 struct insert_data {
03749    char *sql;
03750    const char *dir;
03751    const char *msgnums;
03752    void *data;
03753    SQLLEN datalen;
03754    SQLLEN indlen;
03755    const char *context;
03756    const char *macrocontext;
03757    const char *callerid;
03758    const char *origtime;
03759    const char *duration;
03760    const char *mailboxuser;
03761    const char *mailboxcontext;
03762    const char *category;
03763    const char *flag;
03764 };
03765 
03766 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03767 {
03768    struct insert_data *data = vdata;
03769    int res;
03770    SQLHSTMT stmt;
03771 
03772    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03773    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03774       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03775       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03776       return NULL;
03777    }
03778 
03779    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03780    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03781    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03782    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03783    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03784    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03785    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03786    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03787    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03788    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03789    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03790    if (!ast_strlen_zero(data->category)) {
03791       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03792    }
03793    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03794    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03795       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03796       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03797       return NULL;
03798    }
03799 
03800    return stmt;
03801 }
03802 
03803 /*!
03804  * \brief Stores a voicemail into the database.
03805  * \param dir the folder the mailbox folder to store the message.
03806  * \param mailboxuser the user owning the mailbox folder.
03807  * \param mailboxcontext
03808  * \param msgnum the message index for the message to be stored.
03809  *
03810  * This method is used when mailboxes are stored in an ODBC back end.
03811  * The message sound file and information file is looked up on the file system. 
03812  * A SQL query is invoked to store the message into the (MySQL) database.
03813  *
03814  * \return the zero on success -1 on error.
03815  */
03816 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03817 {
03818    int res = 0;
03819    int fd = -1;
03820    void *fdm = MAP_FAILED;
03821    off_t fdlen = -1;
03822    SQLHSTMT stmt;
03823    char sql[PATH_MAX];
03824    char msgnums[20];
03825    char fn[PATH_MAX];
03826    char full_fn[PATH_MAX];
03827    char fmt[80]="";
03828    char *c;
03829    struct ast_config *cfg = NULL;
03830    struct odbc_obj *obj;
03831    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03832       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03833    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03834 
03835    delete_file(dir, msgnum);
03836    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03837       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03838       return -1;
03839    }
03840 
03841    do {
03842       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03843       c = strchr(fmt, '|');
03844       if (c)
03845          *c = '\0';
03846       if (!strcasecmp(fmt, "wav49"))
03847          strcpy(fmt, "WAV");
03848       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03849       if (msgnum > -1)
03850          make_file(fn, sizeof(fn), dir, msgnum);
03851       else
03852          ast_copy_string(fn, dir, sizeof(fn));
03853       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03854       cfg = ast_config_load(full_fn, config_flags);
03855       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03856       fd = open(full_fn, O_RDWR);
03857       if (fd < 0) {
03858          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03859          res = -1;
03860          break;
03861       }
03862       if (valid_config(cfg)) {
03863          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03864             idata.context = "";
03865          }
03866          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03867             idata.macrocontext = "";
03868          }
03869          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03870             idata.callerid = "";
03871          }
03872          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03873             idata.origtime = "";
03874          }
03875          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03876             idata.duration = "";
03877          }
03878          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03879             idata.category = "";
03880          }
03881          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03882             idata.flag = "";
03883          }
03884       }
03885       fdlen = lseek(fd, 0, SEEK_END);
03886       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
03887          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
03888          res = -1;
03889          break;
03890       }
03891       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03892       if (fdm == MAP_FAILED) {
03893          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
03894          res = -1;
03895          break;
03896       } 
03897       idata.data = fdm;
03898       idata.datalen = idata.indlen = fdlen;
03899 
03900       if (!ast_strlen_zero(idata.category)) 
03901          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03902       else
03903          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03904 
03905       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03906          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03907       } else {
03908          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03909          res = -1;
03910       }
03911    } while (0);
03912    if (obj) {
03913       ast_odbc_release_obj(obj);
03914    }
03915    if (valid_config(cfg))
03916       ast_config_destroy(cfg);
03917    if (fdm != MAP_FAILED)
03918       munmap(fdm, fdlen);
03919    if (fd > -1)
03920       close(fd);
03921    return res;
03922 }
03923 
03924 /*!
03925  * \brief Renames a message in a mailbox folder.
03926  * \param sdir The folder of the message to be renamed.
03927  * \param smsg The index of the message to be renamed.
03928  * \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.
03929  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03930  * \param ddir The destination folder for the message to be renamed into
03931  * \param dmsg The destination message for the message to be renamed.
03932  *
03933  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03934  * 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.
03935  * 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.
03936  */
03937 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03938 {
03939    SQLHSTMT stmt;
03940    char sql[PATH_MAX];
03941    char msgnums[20];
03942    char msgnumd[20];
03943    struct odbc_obj *obj;
03944    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03945    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03946 
03947    delete_file(ddir, dmsg);
03948    obj = ast_odbc_request_obj(odbc_database, 0);
03949    if (obj) {
03950       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03951       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03952       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03953       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03954       if (!stmt)
03955          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03956       else
03957          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03958       ast_odbc_release_obj(obj);
03959    } else
03960       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03961    return;  
03962 }
03963 
03964 /*!
03965  * \brief Removes a voicemail message file.
03966  * \param dir the path to the message file.
03967  * \param msgnum the unique number for the message within the mailbox.
03968  *
03969  * Removes the message content file and the information file.
03970  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03971  * Typical use is to clean up after a RETRIEVE operation. 
03972  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03973  * \return zero on success, -1 on error.
03974  */
03975 static int remove_file(char *dir, int msgnum)
03976 {
03977    char fn[PATH_MAX];
03978    char full_fn[PATH_MAX];
03979    char msgnums[80];
03980    
03981    if (msgnum > -1) {
03982       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03983       make_file(fn, sizeof(fn), dir, msgnum);
03984    } else
03985       ast_copy_string(fn, dir, sizeof(fn));
03986    ast_filedelete(fn, NULL);  
03987    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03988    unlink(full_fn);
03989    return 0;
03990 }
03991 #else
03992 #ifndef IMAP_STORAGE
03993 /*!
03994  * \brief Find all .txt files - even if they are not in sequence from 0000.
03995  * \param vmu
03996  * \param dir
03997  *
03998  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03999  *
04000  * \return the count of messages, zero or more.
04001  */
04002 static int count_messages(struct ast_vm_user *vmu, char *dir)
04003 {
04004 
04005    int vmcount = 0;
04006    DIR *vmdir = NULL;
04007    struct dirent *vment = NULL;
04008 
04009    if (vm_lock_path(dir))
04010       return ERROR_LOCK_PATH;
04011 
04012    if ((vmdir = opendir(dir))) {
04013       while ((vment = readdir(vmdir))) {
04014          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
04015             vmcount++;
04016          }
04017       }
04018       closedir(vmdir);
04019    }
04020    ast_unlock_path(dir);
04021    
04022    return vmcount;
04023 }
04024 
04025 /*!
04026  * \brief Renames a message in a mailbox folder.
04027  * \param sfn The path to the mailbox information and data file to be renamed.
04028  * \param dfn The path for where the message data and information files will be renamed to.
04029  *
04030  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04031  */
04032 static void rename_file(char *sfn, char *dfn)
04033 {
04034    char stxt[PATH_MAX];
04035    char dtxt[PATH_MAX];
04036    ast_filerename(sfn, dfn, NULL);
04037    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04038    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04039    if (ast_check_realtime("voicemail_data")) {
04040       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04041    }
04042    rename(stxt, dtxt);
04043 }
04044 
04045 /*! 
04046  * \brief Determines the highest message number in use for a given user and mailbox folder.
04047  * \param vmu 
04048  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04049  *
04050  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04051  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04052  *
04053  * \note Should always be called with a lock already set on dir.
04054  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04055  */
04056 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04057 {
04058    int x;
04059    unsigned char map[MAXMSGLIMIT] = "";
04060    DIR *msgdir;
04061    struct dirent *msgdirent;
04062    int msgdirint;
04063    char extension[4];
04064    int stopcount = 0;
04065 
04066    /* Reading the entire directory into a file map scales better than
04067     * doing a stat repeatedly on a predicted sequence.  I suspect this
04068     * is partially due to stat(2) internally doing a readdir(2) itself to
04069     * find each file. */
04070    if (!(msgdir = opendir(dir))) {
04071       return -1;
04072    }
04073 
04074    while ((msgdirent = readdir(msgdir))) {
04075       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04076          map[msgdirint] = 1;
04077          stopcount++;
04078          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04079       }
04080    }
04081    closedir(msgdir);
04082 
04083    for (x = 0; x < vmu->maxmsg; x++) {
04084       if (map[x] == 1) {
04085          stopcount--;
04086       } else if (map[x] == 0 && !stopcount) {
04087          break;
04088       }
04089    }
04090 
04091    return x - 1;
04092 }
04093 
04094 #endif /* #ifndef IMAP_STORAGE */
04095 #endif /* #else of #ifdef ODBC_STORAGE */
04096 #ifndef IMAP_STORAGE
04097 /*!
04098  * \brief Utility function to copy a file.
04099  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04100  * \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.
04101  *
04102  * 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.
04103  * The copy operation copies up to 4096 bytes at once.
04104  *
04105  * \return zero on success, -1 on error.
04106  */
04107 static int copy(char *infile, char *outfile)
04108 {
04109    int ifd;
04110    int ofd;
04111    int res;
04112    int len;
04113    char buf[4096];
04114 
04115 #ifdef HARDLINK_WHEN_POSSIBLE
04116    /* Hard link if possible; saves disk space & is faster */
04117    if (link(infile, outfile)) {
04118 #endif
04119       if ((ifd = open(infile, O_RDONLY)) < 0) {
04120          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04121          return -1;
04122       }
04123       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04124          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04125          close(ifd);
04126          return -1;
04127       }
04128       do {
04129          len = read(ifd, buf, sizeof(buf));
04130          if (len < 0) {
04131             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04132             close(ifd);
04133             close(ofd);
04134             unlink(outfile);
04135          } else if (len) {
04136             res = write(ofd, buf, len);
04137             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04138                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04139                close(ifd);
04140                close(ofd);
04141                unlink(outfile);
04142             }
04143          }
04144       } while (len);
04145       close(ifd);
04146       close(ofd);
04147       return 0;
04148 #ifdef HARDLINK_WHEN_POSSIBLE
04149    } else {
04150       /* Hard link succeeded */
04151       return 0;
04152    }
04153 #endif
04154 }
04155 
04156 /*!
04157  * \brief Copies a voicemail information (envelope) file.
04158  * \param frompath
04159  * \param topath 
04160  *
04161  * Every voicemail has the data (.wav) file, and the information file.
04162  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04163  * This is used by the COPY macro when not using IMAP storage.
04164  */
04165 static void copy_plain_file(char *frompath, char *topath)
04166 {
04167    char frompath2[PATH_MAX], topath2[PATH_MAX];
04168    struct ast_variable *tmp,*var = NULL;
04169    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04170    ast_filecopy(frompath, topath, NULL);
04171    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04172    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04173    if (ast_check_realtime("voicemail_data")) {
04174       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04175       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04176       for (tmp = var; tmp; tmp = tmp->next) {
04177          if (!strcasecmp(tmp->name, "origmailbox")) {
04178             origmailbox = tmp->value;
04179          } else if (!strcasecmp(tmp->name, "context")) {
04180             context = tmp->value;
04181          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04182             macrocontext = tmp->value;
04183          } else if (!strcasecmp(tmp->name, "exten")) {
04184             exten = tmp->value;
04185          } else if (!strcasecmp(tmp->name, "priority")) {
04186             priority = tmp->value;
04187          } else if (!strcasecmp(tmp->name, "callerchan")) {
04188             callerchan = tmp->value;
04189          } else if (!strcasecmp(tmp->name, "callerid")) {
04190             callerid = tmp->value;
04191          } else if (!strcasecmp(tmp->name, "origdate")) {
04192             origdate = tmp->value;
04193          } else if (!strcasecmp(tmp->name, "origtime")) {
04194             origtime = tmp->value;
04195          } else if (!strcasecmp(tmp->name, "category")) {
04196             category = tmp->value;
04197          } else if (!strcasecmp(tmp->name, "duration")) {
04198             duration = tmp->value;
04199          }
04200       }
04201       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);
04202    }
04203    copy(frompath2, topath2);
04204    ast_variables_destroy(var);
04205 }
04206 #endif
04207 
04208 /*! 
04209  * \brief Removes the voicemail sound and information file.
04210  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04211  *
04212  * This is used by the DELETE macro when voicemails are stored on the file system.
04213  *
04214  * \return zero on success, -1 on error.
04215  */
04216 static int vm_delete(char *file)
04217 {
04218    char *txt;
04219    int txtsize = 0;
04220 
04221    txtsize = (strlen(file) + 5)*sizeof(char);
04222    txt = ast_alloca(txtsize);
04223    /* Sprintf here would safe because we alloca'd exactly the right length,
04224     * but trying to eliminate all sprintf's anyhow
04225     */
04226    if (ast_check_realtime("voicemail_data")) {
04227       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04228    }
04229    snprintf(txt, txtsize, "%s.txt", file);
04230    unlink(txt);
04231    return ast_filedelete(file, NULL);
04232 }
04233 
04234 /*!
04235  * \brief utility used by inchar(), for base_encode()
04236  */
04237 static int inbuf(struct baseio *bio, FILE *fi)
04238 {
04239    int l;
04240 
04241    if (bio->ateof)
04242       return 0;
04243 
04244    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04245       if (ferror(fi))
04246          return -1;
04247 
04248       bio->ateof = 1;
04249       return 0;
04250    }
04251 
04252    bio->iolen = l;
04253    bio->iocp = 0;
04254 
04255    return 1;
04256 }
04257 
04258 /*!
04259  * \brief utility used by base_encode()
04260  */
04261 static int inchar(struct baseio *bio, FILE *fi)
04262 {
04263    if (bio->iocp>=bio->iolen) {
04264       if (!inbuf(bio, fi))
04265          return EOF;
04266    }
04267 
04268    return bio->iobuf[bio->iocp++];
04269 }
04270 
04271 /*!
04272  * \brief utility used by base_encode()
04273  */
04274 static int ochar(struct baseio *bio, int c, FILE *so)
04275 {
04276    if (bio->linelength >= BASELINELEN) {
04277       if (fputs(ENDL, so) == EOF) {
04278          return -1;
04279       }
04280 
04281       bio->linelength = 0;
04282    }
04283 
04284    if (putc(((unsigned char) c), so) == EOF) {
04285       return -1;
04286    }
04287 
04288    bio->linelength++;
04289 
04290    return 1;
04291 }
04292 
04293 /*!
04294  * \brief Performs a base 64 encode algorithm on the contents of a File
04295  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04296  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04297  *
04298  * 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 ?
04299  *
04300  * \return zero on success, -1 on error.
04301  */
04302 static int base_encode(char *filename, FILE *so)
04303 {
04304    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04305       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04306       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04307       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04308    int i, hiteof = 0;
04309    FILE *fi;
04310    struct baseio bio;
04311 
04312    memset(&bio, 0, sizeof(bio));
04313    bio.iocp = BASEMAXINLINE;
04314 
04315    if (!(fi = fopen(filename, "rb"))) {
04316       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04317       return -1;
04318    }
04319 
04320    while (!hiteof){
04321       unsigned char igroup[3], ogroup[4];
04322       int c, n;
04323 
04324       memset(igroup, 0, sizeof(igroup));
04325 
04326       for (n = 0; n < 3; n++) {
04327          if ((c = inchar(&bio, fi)) == EOF) {
04328             hiteof = 1;
04329             break;
04330          }
04331 
04332          igroup[n] = (unsigned char) c;
04333       }
04334 
04335       if (n > 0) {
04336          ogroup[0]= dtable[igroup[0] >> 2];
04337          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04338          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04339          ogroup[3]= dtable[igroup[2] & 0x3F];
04340 
04341          if (n < 3) {
04342             ogroup[3] = '=';
04343 
04344             if (n < 2)
04345                ogroup[2] = '=';
04346          }
04347 
04348          for (i = 0; i < 4; i++)
04349             ochar(&bio, ogroup[i], so);
04350       }
04351    }
04352 
04353    fclose(fi);
04354    
04355    if (fputs(ENDL, so) == EOF) {
04356       return 0;
04357    }
04358 
04359    return 1;
04360 }
04361 
04362 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)
04363 {
04364    char callerid[256];
04365    char num[12];
04366    char fromdir[256], fromfile[256];
04367    struct ast_config *msg_cfg;
04368    const char *origcallerid, *origtime;
04369    char origcidname[80], origcidnum[80], origdate[80];
04370    int inttime;
04371    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04372 
04373    /* Prepare variables for substitution in email body and subject */
04374    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04375    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04376    snprintf(num, sizeof(num), "%d", msgnum);
04377    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04378    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04379    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04380    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04381       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04382    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04383    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04384    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04385    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04386    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04387 
04388    /* Retrieve info from VM attribute file */
04389    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04390    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04391    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04392       strcat(fromfile, ".txt");
04393    }
04394    if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
04395       if (option_debug > 0) {
04396          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04397       }
04398       return;
04399    }
04400 
04401    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04402       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04403       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04404       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04405       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04406    }
04407 
04408    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04409       struct timeval tv = { inttime, };
04410       struct ast_tm tm;
04411       ast_localtime(&tv, &tm, NULL);
04412       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04413       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04414    }
04415    ast_config_destroy(msg_cfg);
04416 }
04417 
04418 /*!
04419  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04420  * \param from The string to work with.
04421  * \param buf The buffer into which to write the modified quoted string.
04422  * \param maxlen Always zero, but see \see ast_str
04423  * 
04424  * \return The destination string with quotes wrapped on it (the to field).
04425  */
04426 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04427 {
04428    const char *ptr;
04429 
04430    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04431    ast_str_set(buf, maxlen, "\"");
04432    for (ptr = from; *ptr; ptr++) {
04433       if (*ptr == '"' || *ptr == '\\') {
04434          ast_str_append(buf, maxlen, "\\%c", *ptr);
04435       } else {
04436          ast_str_append(buf, maxlen, "%c", *ptr);
04437       }
04438    }
04439    ast_str_append(buf, maxlen, "\"");
04440 
04441    return ast_str_buffer(*buf);
04442 }
04443 
04444 /*! \brief
04445  * fill in *tm for current time according to the proper timezone, if any.
04446  * \return tm so it can be used as a function argument.
04447  */
04448 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04449 {
04450    const struct vm_zone *z = NULL;
04451    struct timeval t = ast_tvnow();
04452 
04453    /* Does this user have a timezone specified? */
04454    if (!ast_strlen_zero(vmu->zonetag)) {
04455       /* Find the zone in the list */
04456       AST_LIST_LOCK(&zones);
04457       AST_LIST_TRAVERSE(&zones, z, list) {
04458          if (!strcmp(z->name, vmu->zonetag))
04459             break;
04460       }
04461       AST_LIST_UNLOCK(&zones);
04462    }
04463    ast_localtime(&t, tm, z ? z->timezone : NULL);
04464    return tm;
04465 }
04466 
04467 /*!\brief Check if the string would need encoding within the MIME standard, to
04468  * avoid confusing certain mail software that expects messages to be 7-bit
04469  * clean.
04470  */
04471 static int check_mime(const char *str)
04472 {
04473    for (; *str; str++) {
04474       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04475          return 1;
04476       }
04477    }
04478    return 0;
04479 }
04480 
04481 /*!\brief Encode a string according to the MIME rules for encoding strings
04482  * that are not 7-bit clean or contain control characters.
04483  *
04484  * Additionally, if the encoded string would exceed the MIME limit of 76
04485  * characters per line, then the encoding will be broken up into multiple
04486  * sections, separated by a space character, in order to facilitate
04487  * breaking up the associated header across multiple lines.
04488  *
04489  * \param end An expandable buffer for holding the result
04490  * \param maxlen Always zero, but see \see ast_str
04491  * \param start A string to be encoded
04492  * \param preamble The length of the first line already used for this string,
04493  * to ensure that each line maintains a maximum length of 76 chars.
04494  * \param postamble the length of any additional characters appended to the
04495  * line, used to ensure proper field wrapping.
04496  * \retval The encoded string.
04497  */
04498 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04499 {
04500    struct ast_str *tmp = ast_str_alloca(80);
04501    int first_section = 1;
04502 
04503    ast_str_reset(*end);
04504    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04505    for (; *start; start++) {
04506       int need_encoding = 0;
04507       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04508          need_encoding = 1;
04509       }
04510       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04511          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04512          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04513          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04514          /* Start new line */
04515          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04516          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04517          first_section = 0;
04518       }
04519       if (need_encoding && *start == ' ') {
04520          ast_str_append(&tmp, -1, "_");
04521       } else if (need_encoding) {
04522          ast_str_append(&tmp, -1, "=%hhX", *start);
04523       } else {
04524          ast_str_append(&tmp, -1, "%c", *start);
04525       }
04526    }
04527    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04528    return ast_str_buffer(*end);
04529 }
04530 
04531 /*!
04532  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04533  * \param p The output file to generate the email contents into.
04534  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04535  * \param vmu The voicemail user who is sending the voicemail.
04536  * \param msgnum The message index in the mailbox folder.
04537  * \param context 
04538  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04539  * \param fromfolder
04540  * \param cidnum The caller ID number.
04541  * \param cidname The caller ID name.
04542  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04543  * \param attach2 
04544  * \param format The message sound file format. i.e. .wav
04545  * \param duration The time of the message content, in seconds.
04546  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04547  * \param chan
04548  * \param category
04549  * \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.
04550  * \param flag
04551  *
04552  * 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.
04553  */
04554 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)
04555 {
04556    char date[256];
04557    char host[MAXHOSTNAMELEN] = "";
04558    char who[256];
04559    char bound[256];
04560    char dur[256];
04561    struct ast_tm tm;
04562    char enc_cidnum[256] = "", enc_cidname[256] = "";
04563    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04564    char *greeting_attachment; 
04565    char filename[256];
04566 
04567    if (!str1 || !str2) {
04568       ast_free(str1);
04569       ast_free(str2);
04570       return;
04571    }
04572 
04573    if (cidnum) {
04574       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04575    }
04576    if (cidname) {
04577       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04578    }
04579    gethostname(host, sizeof(host) - 1);
04580 
04581    if (strchr(srcemail, '@')) {
04582       ast_copy_string(who, srcemail, sizeof(who));
04583    } else {
04584       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04585    }
04586 
04587    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04588    if (greeting_attachment) {
04589       *greeting_attachment++ = '\0';
04590    }
04591 
04592    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04593    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04594    fprintf(p, "Date: %s" ENDL, date);
04595 
04596    /* Set date format for voicemail mail */
04597    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04598 
04599    if (!ast_strlen_zero(fromstring)) {
04600       struct ast_channel *ast;
04601       if ((ast = ast_dummy_channel_alloc())) {
04602          char *ptr;
04603          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04604          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04605 
04606          if (check_mime(ast_str_buffer(str1))) {
04607             int first_line = 1;
04608             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04609             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04610                *ptr = '\0';
04611                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04612                first_line = 0;
04613                /* Substring is smaller, so this will never grow */
04614                ast_str_set(&str2, 0, "%s", ptr + 1);
04615             }
04616             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04617          } else {
04618             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04619          }
04620          ast = ast_channel_unref(ast);
04621       } else {
04622          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04623       }
04624    } else {
04625       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04626    }
04627 
04628    if (check_mime(vmu->fullname)) {
04629       int first_line = 1;
04630       char *ptr;
04631       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04632       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04633          *ptr = '\0';
04634          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04635          first_line = 0;
04636          /* Substring is smaller, so this will never grow */
04637          ast_str_set(&str2, 0, "%s", ptr + 1);
04638       }
04639       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04640    } else {
04641       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04642    }
04643 
04644    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04645       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04646       struct ast_channel *ast;
04647       if ((ast = ast_dummy_channel_alloc())) {
04648          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04649          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04650          if (check_mime(ast_str_buffer(str1))) {
04651             int first_line = 1;
04652             char *ptr;
04653             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04654             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04655                *ptr = '\0';
04656                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04657                first_line = 0;
04658                /* Substring is smaller, so this will never grow */
04659                ast_str_set(&str2, 0, "%s", ptr + 1);
04660             }
04661             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04662          } else {
04663             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04664          }
04665          ast = ast_channel_unref(ast);
04666       } else {
04667          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04668       }
04669    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04670       if (ast_strlen_zero(flag)) {
04671          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04672       } else {
04673          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04674       }
04675    } else {
04676       if (ast_strlen_zero(flag)) {
04677          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04678       } else {
04679          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04680       }
04681    }
04682 
04683    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04684       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04685    if (imap) {
04686       /* additional information needed for IMAP searching */
04687       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04688       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04689       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04690       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04691 #ifdef IMAP_STORAGE
04692       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04693 #else
04694       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04695 #endif
04696       /* flag added for Urgent */
04697       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04698       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04699       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04700       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04701       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04702       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04703       if (!ast_strlen_zero(category)) {
04704          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04705       } else {
04706          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04707       }
04708       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04709       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04710       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04711    }
04712    if (!ast_strlen_zero(cidnum)) {
04713       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04714    }
04715    if (!ast_strlen_zero(cidname)) {
04716       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04717    }
04718    fprintf(p, "MIME-Version: 1.0" ENDL);
04719    if (attach_user_voicemail) {
04720       /* Something unique. */
04721       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04722          (int) getpid(), (unsigned int) ast_random());
04723 
04724       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04725       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04726       fprintf(p, "--%s" ENDL, bound);
04727    }
04728    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04729    if (emailbody || vmu->emailbody) {
04730       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04731       struct ast_channel *ast;
04732       if ((ast = ast_dummy_channel_alloc())) {
04733          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04734          ast_str_substitute_variables(&str1, 0, ast, e_body);
04735 #ifdef IMAP_STORAGE
04736             {
04737                /* Convert body to native line terminators for IMAP backend */
04738                char *line = ast_str_buffer(str1), *next;
04739                do {
04740                   /* Terminate line before outputting it to the file */
04741                   if ((next = strchr(line, '\n'))) {
04742                      *next++ = '\0';
04743                   }
04744                   fprintf(p, "%s" ENDL, line);
04745                   line = next;
04746                } while (!ast_strlen_zero(line));
04747             }
04748 #else
04749          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04750 #endif
04751          ast = ast_channel_unref(ast);
04752       } else {
04753          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04754       }
04755    } else if (msgnum > -1) {
04756       if (strcmp(vmu->mailbox, mailbox)) {
04757          /* Forwarded type */
04758          struct ast_config *msg_cfg;
04759          const char *v;
04760          int inttime;
04761          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04762          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04763          /* Retrieve info from VM attribute file */
04764          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04765          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04766          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04767             strcat(fromfile, ".txt");
04768          }
04769          if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
04770             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04771                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04772             }
04773 
04774             /* You might be tempted to do origdate, except that a) it's in the wrong
04775              * format, and b) it's missing for IMAP recordings. */
04776             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04777                struct timeval tv = { inttime, };
04778                struct ast_tm tm;
04779                ast_localtime(&tv, &tm, NULL);
04780                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04781             }
04782             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04783                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04784                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04785                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04786                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04787                date, origcallerid, origdate);
04788             ast_config_destroy(msg_cfg);
04789          } else {
04790             goto plain_message;
04791          }
04792       } else {
04793 plain_message:
04794          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04795             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04796             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04797             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04798             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04799       }
04800    } else {
04801       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04802             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04803    }
04804 
04805    if (imap || attach_user_voicemail) {
04806       if (!ast_strlen_zero(attach2)) {
04807          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04808          ast_debug(5, "creating second attachment filename %s\n", filename);
04809          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04810          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04811          ast_debug(5, "creating attachment filename %s\n", filename);
04812          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04813       } else {
04814          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04815          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04816          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04817       }
04818    }
04819    ast_free(str1);
04820    ast_free(str2);
04821 }
04822 
04823 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)
04824 {
04825    char tmpdir[256], newtmp[256];
04826    char fname[256];
04827    char tmpcmd[256];
04828    int tmpfd = -1;
04829    int soxstatus = 0;
04830 
04831    /* Eww. We want formats to tell us their own MIME type */
04832    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04833 
04834    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04835       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04836       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04837       tmpfd = mkstemp(newtmp);
04838       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04839       ast_debug(3, "newtmp: %s\n", newtmp);
04840       if (tmpfd > -1) {
04841          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04842          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04843             attach = newtmp;
04844             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04845          } else {
04846             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04847                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04848             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04849          }
04850       }
04851    }
04852    fprintf(p, "--%s" ENDL, bound);
04853    if (msgnum > -1)
04854       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04855    else
04856       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04857    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04858    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04859    if (msgnum > -1)
04860       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04861    else
04862       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04863    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04864    base_encode(fname, p);
04865    if (last)
04866       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04867    if (tmpfd > -1) {
04868       if (soxstatus == 0) {
04869          unlink(fname);
04870       }
04871       close(tmpfd);
04872       unlink(newtmp);
04873    }
04874    return 0;
04875 }
04876 
04877 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)
04878 {
04879    FILE *p = NULL;
04880    char tmp[80] = "/tmp/astmail-XXXXXX";
04881    char tmp2[256];
04882    char *stringp;
04883 
04884    if (vmu && ast_strlen_zero(vmu->email)) {
04885       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04886       return(0);
04887    }
04888 
04889    /* Mail only the first format */
04890    format = ast_strdupa(format);
04891    stringp = format;
04892    strsep(&stringp, "|");
04893 
04894    if (!strcmp(format, "wav49"))
04895       format = "WAV";
04896    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));
04897    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04898       command hangs */
04899    if ((p = vm_mkftemp(tmp)) == NULL) {
04900       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04901       return -1;
04902    } else {
04903       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04904       fclose(p);
04905       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04906       ast_safe_system(tmp2);
04907       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04908    }
04909    return 0;
04910 }
04911 
04912 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)
04913 {
04914    char enc_cidnum[256], enc_cidname[256];
04915    char date[256];
04916    char host[MAXHOSTNAMELEN] = "";
04917    char who[256];
04918    char dur[PATH_MAX];
04919    char tmp[80] = "/tmp/astmail-XXXXXX";
04920    char tmp2[PATH_MAX];
04921    struct ast_tm tm;
04922    FILE *p;
04923    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04924 
04925    if (!str1 || !str2) {
04926       ast_free(str1);
04927       ast_free(str2);
04928       return -1;
04929    }
04930 
04931    if (cidnum) {
04932       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04933    }
04934    if (cidname) {
04935       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04936    }
04937 
04938    if ((p = vm_mkftemp(tmp)) == NULL) {
04939       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04940       ast_free(str1);
04941       ast_free(str2);
04942       return -1;
04943    }
04944    gethostname(host, sizeof(host)-1);
04945    if (strchr(srcemail, '@')) {
04946       ast_copy_string(who, srcemail, sizeof(who));
04947    } else {
04948       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04949    }
04950    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04951    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04952    fprintf(p, "Date: %s\n", date);
04953 
04954    /* Reformat for custom pager format */
04955    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04956 
04957    if (!ast_strlen_zero(pagerfromstring)) {
04958       struct ast_channel *ast;
04959       if ((ast = ast_dummy_channel_alloc())) {
04960          char *ptr;
04961          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04962          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04963 
04964          if (check_mime(ast_str_buffer(str1))) {
04965             int first_line = 1;
04966             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04967             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04968                *ptr = '\0';
04969                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04970                first_line = 0;
04971                /* Substring is smaller, so this will never grow */
04972                ast_str_set(&str2, 0, "%s", ptr + 1);
04973             }
04974             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04975          } else {
04976             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04977          }
04978          ast = ast_channel_unref(ast);
04979       } else {
04980          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04981       }
04982    } else {
04983       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04984    }
04985 
04986    if (check_mime(vmu->fullname)) {
04987       int first_line = 1;
04988       char *ptr;
04989       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04990       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04991          *ptr = '\0';
04992          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", 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 <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04998    } else {
04999       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
05000    }
05001 
05002    if (!ast_strlen_zero(pagersubject)) {
05003       struct ast_channel *ast;
05004       if ((ast = ast_dummy_channel_alloc())) {
05005          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05006          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
05007          if (check_mime(ast_str_buffer(str1))) {
05008             int first_line = 1;
05009             char *ptr;
05010             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
05011             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05012                *ptr = '\0';
05013                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05014                first_line = 0;
05015                /* Substring is smaller, so this will never grow */
05016                ast_str_set(&str2, 0, "%s", ptr + 1);
05017             }
05018             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05019          } else {
05020             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
05021          }
05022          ast = ast_channel_unref(ast);
05023       } else {
05024          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05025       }
05026    } else {
05027       if (ast_strlen_zero(flag)) {
05028          fprintf(p, "Subject: New VM\n\n");
05029       } else {
05030          fprintf(p, "Subject: New %s VM\n\n", flag);
05031       }
05032    }
05033 
05034    if (pagerbody) {
05035       struct ast_channel *ast;
05036       if ((ast = ast_dummy_channel_alloc())) {
05037          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05038          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05039          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05040          ast = ast_channel_unref(ast);
05041       } else {
05042          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05043       }
05044    } else {
05045       fprintf(p, "New %s long %s msg in box %s\n"
05046             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05047    }
05048 
05049    fclose(p);
05050    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05051    ast_safe_system(tmp2);
05052    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05053    ast_free(str1);
05054    ast_free(str2);
05055    return 0;
05056 }
05057 
05058 /*!
05059  * \brief Gets the current date and time, as formatted string.
05060  * \param s The buffer to hold the output formatted date.
05061  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05062  * 
05063  * The date format string used is "%a %b %e %r UTC %Y".
05064  * 
05065  * \return zero on success, -1 on error.
05066  */
05067 static int get_date(char *s, int len)
05068 {
05069    struct ast_tm tm;
05070    struct timeval t = ast_tvnow();
05071    
05072    ast_localtime(&t, &tm, "UTC");
05073 
05074    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05075 }
05076 
05077 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05078 {
05079    int res;
05080    char fn[PATH_MAX];
05081    char dest[PATH_MAX];
05082 
05083    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05084 
05085    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05086       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05087       return -1;
05088    }
05089 
05090    RETRIEVE(fn, -1, ext, context);
05091    if (ast_fileexists(fn, NULL, NULL) > 0) {
05092       res = ast_stream_and_wait(chan, fn, ecodes);
05093       if (res) {
05094          DISPOSE(fn, -1);
05095          return res;
05096       }
05097    } else {
05098       /* Dispose just in case */
05099       DISPOSE(fn, -1);
05100       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05101       if (res)
05102          return res;
05103       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05104       if (res)
05105          return res;
05106    }
05107    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05108    return res;
05109 }
05110 
05111 static void free_zone(struct vm_zone *z)
05112 {
05113    ast_free(z);
05114 }
05115 
05116 #ifdef ODBC_STORAGE
05117 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05118 {
05119    int x = -1;
05120    int res;
05121    SQLHSTMT stmt = NULL;
05122    char sql[PATH_MAX];
05123    char rowdata[20];
05124    char tmp[PATH_MAX] = "";
05125    struct odbc_obj *obj = NULL;
05126    char *context;
05127    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05128 
05129    if (newmsgs)
05130       *newmsgs = 0;
05131    if (oldmsgs)
05132       *oldmsgs = 0;
05133    if (urgentmsgs)
05134       *urgentmsgs = 0;
05135 
05136    /* If no mailbox, return immediately */
05137    if (ast_strlen_zero(mailbox))
05138       return 0;
05139 
05140    ast_copy_string(tmp, mailbox, sizeof(tmp));
05141 
05142    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05143       int u, n, o;
05144       char *next, *remaining = tmp;
05145       while ((next = strsep(&remaining, " ,"))) {
05146          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05147             return -1;
05148          }
05149          if (urgentmsgs) {
05150             *urgentmsgs += u;
05151          }
05152          if (newmsgs) {
05153             *newmsgs += n;
05154          }
05155          if (oldmsgs) {
05156             *oldmsgs += o;
05157          }
05158       }
05159       return 0;
05160    }
05161 
05162    context = strchr(tmp, '@');
05163    if (context) {
05164       *context = '\0';
05165       context++;
05166    } else
05167       context = "default";
05168 
05169    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05170       do {
05171          if (newmsgs) {
05172             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05173             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05174                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05175                break;
05176             }
05177             res = SQLFetch(stmt);
05178             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05179                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05180                break;
05181             }
05182             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05183             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05184                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05185                break;
05186             }
05187             *newmsgs = atoi(rowdata);
05188             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05189          }
05190 
05191          if (oldmsgs) {
05192             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05193             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05194                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05195                break;
05196             }
05197             res = SQLFetch(stmt);
05198             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05199                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05200                break;
05201             }
05202             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05203             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05204                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05205                break;
05206             }
05207             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05208             *oldmsgs = atoi(rowdata);
05209          }
05210 
05211          if (urgentmsgs) {
05212             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05213             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05214                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05215                break;
05216             }
05217             res = SQLFetch(stmt);
05218             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05219                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05220                break;
05221             }
05222             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05223             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05224                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05225                break;
05226             }
05227             *urgentmsgs = atoi(rowdata);
05228          }
05229 
05230          x = 0;
05231       } while (0);
05232    } else {
05233       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05234    }
05235 
05236    if (stmt) {
05237       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05238    }
05239    if (obj) {
05240       ast_odbc_release_obj(obj);
05241    }
05242    return x;
05243 }
05244 
05245 /*!
05246  * \brief Gets the number of messages that exist in a mailbox folder.
05247  * \param context
05248  * \param mailbox
05249  * \param folder
05250  * 
05251  * This method is used when ODBC backend is used.
05252  * \return The number of messages in this mailbox folder (zero or more).
05253  */
05254 static int messagecount(const char *context, const char *mailbox, const char *folder)
05255 {
05256    struct odbc_obj *obj = NULL;
05257    int nummsgs = 0;
05258    int res;
05259    SQLHSTMT stmt = NULL;
05260    char sql[PATH_MAX];
05261    char rowdata[20];
05262    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05263    if (!folder)
05264       folder = "INBOX";
05265    /* If no mailbox, return immediately */
05266    if (ast_strlen_zero(mailbox))
05267       return 0;
05268 
05269    obj = ast_odbc_request_obj(odbc_database, 0);
05270    if (obj) {
05271       if (!strcmp(folder, "INBOX")) {
05272          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);
05273       } else {
05274          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05275       }
05276       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05277       if (!stmt) {
05278          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05279          goto yuck;
05280       }
05281       res = SQLFetch(stmt);
05282       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05283          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05284          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05285          goto yuck;
05286       }
05287       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05288       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05289          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05290          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05291          goto yuck;
05292       }
05293       nummsgs = atoi(rowdata);
05294       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05295    } else
05296       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05297 
05298 yuck:
05299    if (obj)
05300       ast_odbc_release_obj(obj);
05301    return nummsgs;
05302 }
05303 
05304 /** 
05305  * \brief Determines if the given folder has messages.
05306  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05307  * 
05308  * This function is used when the mailbox is stored in an ODBC back end.
05309  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05310  * \return 1 if the folder has one or more messages. zero otherwise.
05311  */
05312 static int has_voicemail(const char *mailbox, const char *folder)
05313 {
05314    char tmp[256], *tmp2 = tmp, *box, *context;
05315    ast_copy_string(tmp, mailbox, sizeof(tmp));
05316    while ((context = box = strsep(&tmp2, ",&"))) {
05317       strsep(&context, "@");
05318       if (ast_strlen_zero(context))
05319          context = "default";
05320       if (messagecount(context, box, folder))
05321          return 1;
05322    }
05323    return 0;
05324 }
05325 #endif
05326 #ifndef IMAP_STORAGE
05327 /*! 
05328  * \brief Copies a message from one mailbox to another.
05329  * \param chan
05330  * \param vmu
05331  * \param imbox
05332  * \param msgnum
05333  * \param duration
05334  * \param recip
05335  * \param fmt
05336  * \param dir
05337  * \param flag
05338  *
05339  * This is only used by file storage based mailboxes.
05340  *
05341  * \return zero on success, -1 on error.
05342  */
05343 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)
05344 {
05345    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05346    const char *frombox = mbox(vmu, imbox);
05347    const char *userfolder;
05348    int recipmsgnum;
05349    int res = 0;
05350 
05351    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05352 
05353    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05354       userfolder = "Urgent";
05355    } else {
05356       userfolder = "INBOX";
05357    }
05358 
05359    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05360 
05361    if (!dir)
05362       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05363    else
05364       ast_copy_string(fromdir, dir, sizeof(fromdir));
05365 
05366    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05367    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05368 
05369    if (vm_lock_path(todir))
05370       return ERROR_LOCK_PATH;
05371 
05372    recipmsgnum = last_message_index(recip, todir) + 1;
05373    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05374       make_file(topath, sizeof(topath), todir, recipmsgnum);
05375 #ifndef ODBC_STORAGE
05376       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05377          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05378       } else {
05379 #endif
05380          /* If we are prepending a message for ODBC, then the message already
05381           * exists in the database, but we want to force copying from the
05382           * filesystem (since only the FS contains the prepend). */
05383          copy_plain_file(frompath, topath);
05384          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05385          vm_delete(topath);
05386 #ifndef ODBC_STORAGE
05387       }
05388 #endif
05389    } else {
05390       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05391       res = -1;
05392    }
05393    ast_unlock_path(todir);
05394    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05395       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05396       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05397       flag);
05398    
05399    return res;
05400 }
05401 #endif
05402 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05403 
05404 static int messagecount(const char *context, const char *mailbox, const char *folder)
05405 {
05406    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05407 }
05408 
05409 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05410 {
05411    DIR *dir;
05412    struct dirent *de;
05413    char fn[256];
05414    int ret = 0;
05415 
05416    /* If no mailbox, return immediately */
05417    if (ast_strlen_zero(mailbox))
05418       return 0;
05419 
05420    if (ast_strlen_zero(folder))
05421       folder = "INBOX";
05422    if (ast_strlen_zero(context))
05423       context = "default";
05424 
05425    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05426 
05427    if (!(dir = opendir(fn)))
05428       return 0;
05429 
05430    while ((de = readdir(dir))) {
05431       if (!strncasecmp(de->d_name, "msg", 3)) {
05432          if (shortcircuit) {
05433             ret = 1;
05434             break;
05435          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05436             ret++;
05437          }
05438       }
05439    }
05440 
05441    closedir(dir);
05442 
05443    return ret;
05444 }
05445 
05446 /** 
05447  * \brief Determines if the given folder has messages.
05448  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05449  * \param folder the folder to look in
05450  *
05451  * This function is used when the mailbox is stored in a filesystem back end.
05452  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05453  * \return 1 if the folder has one or more messages. zero otherwise.
05454  */
05455 static int has_voicemail(const char *mailbox, const char *folder)
05456 {
05457    char tmp[256], *tmp2 = tmp, *box, *context;
05458    ast_copy_string(tmp, mailbox, sizeof(tmp));
05459    if (ast_strlen_zero(folder)) {
05460       folder = "INBOX";
05461    }
05462    while ((box = strsep(&tmp2, ",&"))) {
05463       if ((context = strchr(box, '@')))
05464          *context++ = '\0';
05465       else
05466          context = "default";
05467       if (__has_voicemail(context, box, folder, 1))
05468          return 1;
05469       /* If we are checking INBOX, we should check Urgent as well */
05470       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05471          return 1;
05472       }
05473    }
05474    return 0;
05475 }
05476 
05477 
05478 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05479 {
05480    char tmp[256];
05481    char *context;
05482 
05483    /* If no mailbox, return immediately */
05484    if (ast_strlen_zero(mailbox))
05485       return 0;
05486 
05487    if (newmsgs)
05488       *newmsgs = 0;
05489    if (oldmsgs)
05490       *oldmsgs = 0;
05491    if (urgentmsgs)
05492       *urgentmsgs = 0;
05493 
05494    if (strchr(mailbox, ',')) {
05495       int tmpnew, tmpold, tmpurgent;
05496       char *mb, *cur;
05497 
05498       ast_copy_string(tmp, mailbox, sizeof(tmp));
05499       mb = tmp;
05500       while ((cur = strsep(&mb, ", "))) {
05501          if (!ast_strlen_zero(cur)) {
05502             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05503                return -1;
05504             else {
05505                if (newmsgs)
05506                   *newmsgs += tmpnew; 
05507                if (oldmsgs)
05508                   *oldmsgs += tmpold;
05509                if (urgentmsgs)
05510                   *urgentmsgs += tmpurgent;
05511             }
05512          }
05513       }
05514       return 0;
05515    }
05516 
05517    ast_copy_string(tmp, mailbox, sizeof(tmp));
05518    
05519    if ((context = strchr(tmp, '@')))
05520       *context++ = '\0';
05521    else
05522       context = "default";
05523 
05524    if (newmsgs)
05525       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05526    if (oldmsgs)
05527       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05528    if (urgentmsgs)
05529       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05530 
05531    return 0;
05532 }
05533 
05534 #endif
05535 
05536 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05537 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05538 {
05539    int urgentmsgs = 0;
05540    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05541    if (newmsgs) {
05542       *newmsgs += urgentmsgs;
05543    }
05544    return res;
05545 }
05546 
05547 static void run_externnotify(char *context, char *extension, const char *flag)
05548 {
05549    char arguments[255];
05550    char ext_context[256] = "";
05551    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05552    struct ast_smdi_mwi_message *mwi_msg;
05553 
05554    if (!ast_strlen_zero(context))
05555       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05556    else
05557       ast_copy_string(ext_context, extension, sizeof(ext_context));
05558 
05559    if (smdi_iface) {
05560       if (ast_app_has_voicemail(ext_context, NULL)) 
05561          ast_smdi_mwi_set(smdi_iface, extension);
05562       else
05563          ast_smdi_mwi_unset(smdi_iface, extension);
05564 
05565       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05566          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05567          if (!strncmp(mwi_msg->cause, "INV", 3))
05568             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05569          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05570             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05571          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05572          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05573       } else {
05574          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05575       }
05576    }
05577 
05578    if (!ast_strlen_zero(externnotify)) {
05579       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05580          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05581       } else {
05582          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
05583             externnotify, S_OR(context, "\"\""),
05584             extension, newvoicemails,
05585             oldvoicemails, urgentvoicemails);
05586          ast_debug(1, "Executing %s\n", arguments);
05587          ast_safe_system(arguments);
05588       }
05589    }
05590 }
05591 
05592 /*!
05593  * \brief Variables used for saving a voicemail.
05594  *
05595  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05596  */
05597 struct leave_vm_options {
05598    unsigned int flags;
05599    signed char record_gain;
05600    char *exitcontext;
05601 };
05602 
05603 /*!
05604  * \brief Prompts the user and records a voicemail to a mailbox.
05605  * \param chan
05606  * \param ext
05607  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05608  * 
05609  * 
05610  * 
05611  * \return zero on success, -1 on error.
05612  */
05613 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05614 {
05615 #ifdef IMAP_STORAGE
05616    int newmsgs, oldmsgs;
05617 #else
05618    char urgdir[PATH_MAX];
05619 #endif
05620    char txtfile[PATH_MAX];
05621    char tmptxtfile[PATH_MAX];
05622    struct vm_state *vms = NULL;
05623    char callerid[256];
05624    FILE *txt;
05625    char date[256];
05626    int txtdes;
05627    int res = 0;
05628    int msgnum;
05629    int duration = 0;
05630    int sound_duration = 0;
05631    int ausemacro = 0;
05632    int ousemacro = 0;
05633    int ouseexten = 0;
05634    char tmpdur[16];
05635    char priority[16];
05636    char origtime[16];
05637    char dir[PATH_MAX];
05638    char tmpdir[PATH_MAX];
05639    char fn[PATH_MAX];
05640    char prefile[PATH_MAX] = "";
05641    char tempfile[PATH_MAX] = "";
05642    char ext_context[256] = "";
05643    char fmt[80];
05644    char *context;
05645    char ecodes[17] = "#";
05646    struct ast_str *tmp = ast_str_create(16);
05647    char *tmpptr;
05648    struct ast_vm_user *vmu;
05649    struct ast_vm_user svm;
05650    const char *category = NULL;
05651    const char *code;
05652    const char *alldtmf = "0123456789ABCD*#";
05653    char flag[80];
05654 
05655    if (!tmp) {
05656       return -1;
05657    }
05658 
05659    ast_str_set(&tmp, 0, "%s", ext);
05660    ext = ast_str_buffer(tmp);
05661    if ((context = strchr(ext, '@'))) {
05662       *context++ = '\0';
05663       tmpptr = strchr(context, '&');
05664    } else {
05665       tmpptr = strchr(ext, '&');
05666    }
05667 
05668    if (tmpptr)
05669       *tmpptr++ = '\0';
05670 
05671    ast_channel_lock(chan);
05672    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05673       category = ast_strdupa(category);
05674    }
05675    ast_channel_unlock(chan);
05676 
05677    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05678       ast_copy_string(flag, "Urgent", sizeof(flag));
05679    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05680       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05681    } else {
05682       flag[0] = '\0';
05683    }
05684 
05685    ast_debug(3, "Before find_user\n");
05686    if (!(vmu = find_user(&svm, context, ext))) {
05687       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05688       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05689       ast_free(tmp);
05690       return res;
05691    }
05692    /* Setup pre-file if appropriate */
05693    if (strcmp(vmu->context, "default"))
05694       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05695    else
05696       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05697 
05698    /* Set the path to the prefile. Will be one of 
05699       VM_SPOOL_DIRcontext/ext/busy
05700       VM_SPOOL_DIRcontext/ext/unavail
05701       Depending on the flag set in options.
05702    */
05703    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05704       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05705    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05706       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05707    }
05708    /* Set the path to the tmpfile as
05709       VM_SPOOL_DIR/context/ext/temp
05710       and attempt to create the folder structure.
05711    */
05712    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05713    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05714       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05715       ast_free(tmp);
05716       return -1;
05717    }
05718    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05719    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05720       ast_copy_string(prefile, tempfile, sizeof(prefile));
05721 
05722    DISPOSE(tempfile, -1);
05723    /* It's easier just to try to make it than to check for its existence */
05724 #ifndef IMAP_STORAGE
05725    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05726 #else
05727    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05728    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05729       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05730    }
05731 #endif
05732 
05733    /* Check current or macro-calling context for special extensions */
05734    if (ast_test_flag(vmu, VM_OPERATOR)) {
05735       if (!ast_strlen_zero(vmu->exit)) {
05736          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05737             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05738             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05739             ouseexten = 1;
05740          }
05741       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05742          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05743          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05744          ouseexten = 1;
05745       } else if (!ast_strlen_zero(chan->macrocontext)
05746          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05747             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05748          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05749          ousemacro = 1;
05750       }
05751    }
05752 
05753    if (!ast_strlen_zero(vmu->exit)) {
05754       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05755          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05756          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05757       }
05758    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05759       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05760       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05761    } else if (!ast_strlen_zero(chan->macrocontext)
05762       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05763          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05764       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05765       ausemacro = 1;
05766    }
05767 
05768    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05769       for (code = alldtmf; *code; code++) {
05770          char e[2] = "";
05771          e[0] = *code;
05772          if (strchr(ecodes, e[0]) == NULL
05773             && ast_canmatch_extension(chan,
05774                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
05775                e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05776             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05777          }
05778       }
05779    }
05780 
05781    /* Play the beginning intro if desired */
05782    if (!ast_strlen_zero(prefile)) {
05783 #ifdef ODBC_STORAGE
05784       int success = 
05785 #endif
05786          RETRIEVE(prefile, -1, ext, context);
05787       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05788          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05789             res = ast_waitstream(chan, ecodes);
05790 #ifdef ODBC_STORAGE
05791          if (success == -1) {
05792             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05793             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05794             store_file(prefile, vmu->mailbox, vmu->context, -1);
05795          }
05796 #endif
05797       } else {
05798          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05799          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05800       }
05801       DISPOSE(prefile, -1);
05802       if (res < 0) {
05803          ast_debug(1, "Hang up during prefile playback\n");
05804          free_user(vmu);
05805          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05806          ast_free(tmp);
05807          return -1;
05808       }
05809    }
05810    if (res == '#') {
05811       /* On a '#' we skip the instructions */
05812       ast_set_flag(options, OPT_SILENT);
05813       res = 0;
05814    }
05815    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05816    if (vmu->maxmsg == 0) {
05817       if (option_debug > 2)
05818          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05819       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05820       goto leave_vm_out;
05821    }
05822    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05823       res = ast_stream_and_wait(chan, INTRO, ecodes);
05824       if (res == '#') {
05825          ast_set_flag(options, OPT_SILENT);
05826          res = 0;
05827       }
05828    }
05829    if (res > 0)
05830       ast_stopstream(chan);
05831    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05832     other than the operator -- an automated attendant or mailbox login for example */
05833    if (res == '*') {
05834       chan->exten[0] = 'a';
05835       chan->exten[1] = '\0';
05836       if (!ast_strlen_zero(vmu->exit)) {
05837          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05838       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05839          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05840       }
05841       chan->priority = 0;
05842       free_user(vmu);
05843       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05844       ast_free(tmp);
05845       return 0;
05846    }
05847 
05848    /* Check for a '0' here */
05849    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05850    transfer:
05851       if (ouseexten || ousemacro) {
05852          chan->exten[0] = 'o';
05853          chan->exten[1] = '\0';
05854          if (!ast_strlen_zero(vmu->exit)) {
05855             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05856          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05857             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05858          }
05859          ast_play_and_wait(chan, "transfer");
05860          chan->priority = 0;
05861          free_user(vmu);
05862          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05863       }
05864       ast_free(tmp);
05865       return OPERATOR_EXIT;
05866    }
05867 
05868    /* Allow all other digits to exit Voicemail and return to the dialplan */
05869    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05870       if (!ast_strlen_zero(options->exitcontext)) {
05871          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05872       }
05873       free_user(vmu);
05874       ast_free(tmp);
05875       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05876       return res;
05877    }
05878 
05879    if (res < 0) {
05880       free_user(vmu);
05881       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05882       ast_free(tmp);
05883       return -1;
05884    }
05885    /* The meat of recording the message...  All the announcements and beeps have been played*/
05886    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05887    if (!ast_strlen_zero(fmt)) {
05888       msgnum = 0;
05889 
05890 #ifdef IMAP_STORAGE
05891       /* Is ext a mailbox? */
05892       /* must open stream for this user to get info! */
05893       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05894       if (res < 0) {
05895          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05896          ast_free(tmp);
05897          return -1;
05898       }
05899       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05900       /* It is possible under certain circumstances that inboxcount did not
05901        * create a vm_state when it was needed. This is a catchall which will
05902        * rarely be used.
05903        */
05904          if (!(vms = create_vm_state_from_user(vmu))) {
05905             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05906             ast_free(tmp);
05907             return -1;
05908          }
05909       }
05910       vms->newmessages++;
05911       
05912       /* here is a big difference! We add one to it later */
05913       msgnum = newmsgs + oldmsgs;
05914       ast_debug(3, "Messagecount set to %d\n", msgnum);
05915       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05916       /* set variable for compatibility */
05917       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05918 
05919       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05920          goto leave_vm_out;
05921       }
05922 #else
05923       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05924          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05925          if (!res)
05926             res = ast_waitstream(chan, "");
05927          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05928          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05929          inprocess_count(vmu->mailbox, vmu->context, -1);
05930          goto leave_vm_out;
05931       }
05932 
05933 #endif
05934       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05935       txtdes = mkstemp(tmptxtfile);
05936       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05937       if (txtdes < 0) {
05938          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05939          if (!res)
05940             res = ast_waitstream(chan, "");
05941          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05942          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05943          inprocess_count(vmu->mailbox, vmu->context, -1);
05944          goto leave_vm_out;
05945       }
05946 
05947       /* Now play the beep once we have the message number for our next message. */
05948       if (res >= 0) {
05949          /* Unless we're *really* silent, try to send the beep */
05950          res = ast_stream_and_wait(chan, "beep", "");
05951       }
05952             
05953       /* Store information in real-time storage */
05954       if (ast_check_realtime("voicemail_data")) {
05955          snprintf(priority, sizeof(priority), "%d", chan->priority);
05956          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05957          get_date(date, sizeof(date));
05958          ast_callerid_merge(callerid, sizeof(callerid),
05959             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05960             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05961             "Unknown");
05962          ast_store_realtime("voicemail_data",
05963             "origmailbox", ext,
05964             "context", chan->context,
05965             "macrocontext", chan->macrocontext,
05966             "exten", chan->exten,
05967             "priority", priority,
05968             "callerchan", chan->name,
05969             "callerid", callerid,
05970             "origdate", date,
05971             "origtime", origtime,
05972             "category", S_OR(category, ""),
05973             "filename", tmptxtfile,
05974             SENTINEL);
05975       }
05976 
05977       /* Store information */
05978       txt = fdopen(txtdes, "w+");
05979       if (txt) {
05980          get_date(date, sizeof(date));
05981          ast_callerid_merge(callerid, sizeof(callerid),
05982             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05983             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05984             "Unknown");
05985          fprintf(txt, 
05986             ";\n"
05987             "; Message Information file\n"
05988             ";\n"
05989             "[message]\n"
05990             "origmailbox=%s\n"
05991             "context=%s\n"
05992             "macrocontext=%s\n"
05993             "exten=%s\n"
05994             "rdnis=%s\n"
05995             "priority=%d\n"
05996             "callerchan=%s\n"
05997             "callerid=%s\n"
05998             "origdate=%s\n"
05999             "origtime=%ld\n"
06000             "category=%s\n",
06001             ext,
06002             chan->context,
06003             chan->macrocontext, 
06004             chan->exten,
06005             S_COR(chan->redirecting.from.number.valid,
06006                chan->redirecting.from.number.str, "unknown"),
06007             chan->priority,
06008             chan->name,
06009             callerid,
06010             date, (long) time(NULL),
06011             category ? category : "");
06012       } else {
06013          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
06014          inprocess_count(vmu->mailbox, vmu->context, -1);
06015          if (ast_check_realtime("voicemail_data")) {
06016             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06017          }
06018          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
06019          goto leave_vm_out;
06020       }
06021       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
06022 
06023       if (txt) {
06024          fprintf(txt, "flag=%s\n", flag);
06025          if (sound_duration < vmu->minsecs) {
06026             fclose(txt);
06027             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
06028             ast_filedelete(tmptxtfile, NULL);
06029             unlink(tmptxtfile);
06030             if (ast_check_realtime("voicemail_data")) {
06031                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06032             }
06033             inprocess_count(vmu->mailbox, vmu->context, -1);
06034          } else {
06035             fprintf(txt, "duration=%d\n", duration);
06036             fclose(txt);
06037             if (vm_lock_path(dir)) {
06038                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06039                /* Delete files */
06040                ast_filedelete(tmptxtfile, NULL);
06041                unlink(tmptxtfile);
06042                inprocess_count(vmu->mailbox, vmu->context, -1);
06043             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06044                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06045                unlink(tmptxtfile);
06046                ast_unlock_path(dir);
06047                inprocess_count(vmu->mailbox, vmu->context, -1);
06048                if (ast_check_realtime("voicemail_data")) {
06049                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06050                }
06051             } else {
06052 #ifndef IMAP_STORAGE
06053                msgnum = last_message_index(vmu, dir) + 1;
06054 #endif
06055                make_file(fn, sizeof(fn), dir, msgnum);
06056 
06057                /* assign a variable with the name of the voicemail file */ 
06058 #ifndef IMAP_STORAGE
06059                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06060 #else
06061                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06062 #endif
06063 
06064                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06065                ast_filerename(tmptxtfile, fn, NULL);
06066                rename(tmptxtfile, txtfile);
06067                inprocess_count(vmu->mailbox, vmu->context, -1);
06068 
06069                /* Properly set permissions on voicemail text descriptor file.
06070                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06071                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06072                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06073 
06074                ast_unlock_path(dir);
06075                if (ast_check_realtime("voicemail_data")) {
06076                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06077                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06078                }
06079                /* We must store the file first, before copying the message, because
06080                 * ODBC storage does the entire copy with SQL.
06081                 */
06082                if (ast_fileexists(fn, NULL, NULL) > 0) {
06083                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06084                }
06085 
06086                /* Are there to be more recipients of this message? */
06087                while (tmpptr) {
06088                   struct ast_vm_user recipu, *recip;
06089                   char *exten, *cntx;
06090 
06091                   exten = strsep(&tmpptr, "&");
06092                   cntx = strchr(exten, '@');
06093                   if (cntx) {
06094                      *cntx = '\0';
06095                      cntx++;
06096                   }
06097                   if ((recip = find_user(&recipu, cntx, exten))) {
06098                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06099                      free_user(recip);
06100                   }
06101                }
06102 #ifndef IMAP_STORAGE
06103                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06104                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06105                   char sfn[PATH_MAX];
06106                   char dfn[PATH_MAX];
06107                   int x;
06108                   /* It's easier just to try to make it than to check for its existence */
06109                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06110                   x = last_message_index(vmu, urgdir) + 1;
06111                   make_file(sfn, sizeof(sfn), dir, msgnum);
06112                   make_file(dfn, sizeof(dfn), urgdir, x);
06113                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06114                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06115                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06116                   ast_copy_string(fn, dfn, sizeof(fn));
06117                   msgnum = x;
06118                }
06119 #endif
06120                /* Notification needs to happen after the copy, though. */
06121                if (ast_fileexists(fn, NULL, NULL)) {
06122 #ifdef IMAP_STORAGE
06123                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06124                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06125                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06126                      flag);
06127 #else
06128                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06129                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06130                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06131                      flag);
06132 #endif
06133                }
06134 
06135                /* Disposal needs to happen after the optional move and copy */
06136                if (ast_fileexists(fn, NULL, NULL)) {
06137                   DISPOSE(dir, msgnum);
06138                }
06139             }
06140          }
06141       } else {
06142          inprocess_count(vmu->mailbox, vmu->context, -1);
06143       }
06144       if (res == '0') {
06145          goto transfer;
06146       } else if (res > 0 && res != 't')
06147          res = 0;
06148 
06149       if (sound_duration < vmu->minsecs)
06150          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06151          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06152       else
06153          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06154    } else
06155       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06156 leave_vm_out:
06157    free_user(vmu);
06158 
06159 #ifdef IMAP_STORAGE
06160    /* expunge message - use UID Expunge if supported on IMAP server*/
06161    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06162    if (expungeonhangup == 1) {
06163       ast_mutex_lock(&vms->lock);
06164 #ifdef HAVE_IMAP_TK2006
06165       if (LEVELUIDPLUS (vms->mailstream)) {
06166          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06167       } else 
06168 #endif
06169          mail_expunge(vms->mailstream);
06170       ast_mutex_unlock(&vms->lock);
06171    }
06172 #endif
06173 
06174    ast_free(tmp);
06175    return res;
06176 }
06177 
06178 #if !defined(IMAP_STORAGE)
06179 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06180 {
06181    /* we know the actual number of messages, so stop process when number is hit */
06182 
06183    int x, dest;
06184    char sfn[PATH_MAX];
06185    char dfn[PATH_MAX];
06186 
06187    if (vm_lock_path(dir)) {
06188       return ERROR_LOCK_PATH;
06189    }
06190 
06191    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06192       make_file(sfn, sizeof(sfn), dir, x);
06193       if (EXISTS(dir, x, sfn, NULL)) {
06194 
06195          if (x != dest) {
06196             make_file(dfn, sizeof(dfn), dir, dest);
06197             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06198          }
06199 
06200          dest++;
06201       }
06202    }
06203    ast_unlock_path(dir);
06204 
06205    return dest;
06206 }
06207 #endif
06208 
06209 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06210 {
06211    int d;
06212    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06213    return d;
06214 }
06215 
06216 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06217 {
06218 #ifdef IMAP_STORAGE
06219    /* we must use mbox(x) folder names, and copy the message there */
06220    /* simple. huh? */
06221    char sequence[10];
06222    char mailbox[256];
06223    int res;
06224 
06225    /* get the real IMAP message number for this message */
06226    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06227    
06228    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06229    ast_mutex_lock(&vms->lock);
06230    /* if save to Old folder, put in INBOX as read */
06231    if (box == OLD_FOLDER) {
06232       mail_setflag(vms->mailstream, sequence, "\\Seen");
06233       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06234    } else if (box == NEW_FOLDER) {
06235       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06236       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06237    }
06238    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06239       ast_mutex_unlock(&vms->lock);
06240       return 0;
06241    }
06242    /* Create the folder if it don't exist */
06243    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06244    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06245    if (mail_create(vms->mailstream, mailbox) == NIL) 
06246       ast_debug(5, "Folder exists.\n");
06247    else
06248       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06249    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06250    ast_mutex_unlock(&vms->lock);
06251    return res;
06252 #else
06253    char *dir = vms->curdir;
06254    char *username = vms->username;
06255    char *context = vmu->context;
06256    char sfn[PATH_MAX];
06257    char dfn[PATH_MAX];
06258    char ddir[PATH_MAX];
06259    const char *dbox = mbox(vmu, box);
06260    int x, i;
06261    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06262 
06263    if (vm_lock_path(ddir))
06264       return ERROR_LOCK_PATH;
06265 
06266    x = last_message_index(vmu, ddir) + 1;
06267 
06268    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06269       x--;
06270       for (i = 1; i <= x; i++) {
06271          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06272          make_file(sfn, sizeof(sfn), ddir, i);
06273          make_file(dfn, sizeof(dfn), ddir, i - 1);
06274          if (EXISTS(ddir, i, sfn, NULL)) {
06275             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06276          } else
06277             break;
06278       }
06279    } else {
06280       if (x >= vmu->maxmsg) {
06281          ast_unlock_path(ddir);
06282          return -1;
06283       }
06284    }
06285    make_file(sfn, sizeof(sfn), dir, msg);
06286    make_file(dfn, sizeof(dfn), ddir, x);
06287    if (strcmp(sfn, dfn)) {
06288       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06289    }
06290    ast_unlock_path(ddir);
06291 #endif
06292    return 0;
06293 }
06294 
06295 static int adsi_logo(unsigned char *buf)
06296 {
06297    int bytes = 0;
06298    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06299    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06300    return bytes;
06301 }
06302 
06303 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06304 {
06305    unsigned char buf[256];
06306    int bytes = 0;
06307    int x;
06308    char num[5];
06309 
06310    *useadsi = 0;
06311    bytes += ast_adsi_data_mode(buf + bytes);
06312    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06313 
06314    bytes = 0;
06315    bytes += adsi_logo(buf);
06316    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06317 #ifdef DISPLAY
06318    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06319 #endif
06320    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06321    bytes += ast_adsi_data_mode(buf + bytes);
06322    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06323 
06324    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06325       bytes = 0;
06326       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06327       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06328       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06329       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06330       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06331       return 0;
06332    }
06333 
06334 #ifdef DISPLAY
06335    /* Add a dot */
06336    bytes = 0;
06337    bytes += ast_adsi_logo(buf);
06338    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06339    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06340    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06341    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06342 #endif
06343    bytes = 0;
06344    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06345    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06346    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06347    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06348    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06349    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06350    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06351 
06352 #ifdef DISPLAY
06353    /* Add another dot */
06354    bytes = 0;
06355    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06356    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06357 
06358    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06359    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06360 #endif
06361 
06362    bytes = 0;
06363    /* These buttons we load but don't use yet */
06364    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06365    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06366    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06367    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06368    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06369    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06370    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06371 
06372 #ifdef DISPLAY
06373    /* Add another dot */
06374    bytes = 0;
06375    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06376    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06377    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06378 #endif
06379 
06380    bytes = 0;
06381    for (x = 0; x < 5; x++) {
06382       snprintf(num, sizeof(num), "%d", x);
06383       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06384    }
06385    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06386    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06387 
06388 #ifdef DISPLAY
06389    /* Add another dot */
06390    bytes = 0;
06391    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06392    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06393    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06394 #endif
06395 
06396    if (ast_adsi_end_download(chan)) {
06397       bytes = 0;
06398       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06399       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06400       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06401       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06402       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06403       return 0;
06404    }
06405    bytes = 0;
06406    bytes += ast_adsi_download_disconnect(buf + bytes);
06407    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06408    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06409 
06410    ast_debug(1, "Done downloading scripts...\n");
06411 
06412 #ifdef DISPLAY
06413    /* Add last dot */
06414    bytes = 0;
06415    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06416    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06417 #endif
06418    ast_debug(1, "Restarting session...\n");
06419 
06420    bytes = 0;
06421    /* Load the session now */
06422    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06423       *useadsi = 1;
06424       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06425    } else
06426       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06427 
06428    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06429    return 0;
06430 }
06431 
06432 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06433 {
06434    int x;
06435    if (!ast_adsi_available(chan))
06436       return;
06437    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06438    if (x < 0)
06439       return;
06440    if (!x) {
06441       if (adsi_load_vmail(chan, useadsi)) {
06442          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06443          return;
06444       }
06445    } else
06446       *useadsi = 1;
06447 }
06448 
06449 static void adsi_login(struct ast_channel *chan)
06450 {
06451    unsigned char buf[256];
06452    int bytes = 0;
06453    unsigned char keys[8];
06454    int x;
06455    if (!ast_adsi_available(chan))
06456       return;
06457 
06458    for (x = 0; x < 8; x++)
06459       keys[x] = 0;
06460    /* Set one key for next */
06461    keys[3] = ADSI_KEY_APPS + 3;
06462 
06463    bytes += adsi_logo(buf + bytes);
06464    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06465    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06466    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06467    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06468    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06469    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06470    bytes += ast_adsi_set_keys(buf + bytes, keys);
06471    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06472    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06473 }
06474 
06475 static void adsi_password(struct ast_channel *chan)
06476 {
06477    unsigned char buf[256];
06478    int bytes = 0;
06479    unsigned char keys[8];
06480    int x;
06481    if (!ast_adsi_available(chan))
06482       return;
06483 
06484    for (x = 0; x < 8; x++)
06485       keys[x] = 0;
06486    /* Set one key for next */
06487    keys[3] = ADSI_KEY_APPS + 3;
06488 
06489    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06490    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06491    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06492    bytes += ast_adsi_set_keys(buf + bytes, keys);
06493    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06494    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06495 }
06496 
06497 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06498 {
06499    unsigned char buf[256];
06500    int bytes = 0;
06501    unsigned char keys[8];
06502    int x, y;
06503 
06504    if (!ast_adsi_available(chan))
06505       return;
06506 
06507    for (x = 0; x < 5; x++) {
06508       y = ADSI_KEY_APPS + 12 + start + x;
06509       if (y > ADSI_KEY_APPS + 12 + 4)
06510          y = 0;
06511       keys[x] = ADSI_KEY_SKT | y;
06512    }
06513    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06514    keys[6] = 0;
06515    keys[7] = 0;
06516 
06517    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06518    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06519    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06520    bytes += ast_adsi_set_keys(buf + bytes, keys);
06521    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06522 
06523    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06524 }
06525 
06526 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06527 {
06528    int bytes = 0;
06529    unsigned char buf[256]; 
06530    char buf1[256], buf2[256];
06531    char fn2[PATH_MAX];
06532 
06533    char cid[256] = "";
06534    char *val;
06535    char *name, *num;
06536    char datetime[21] = "";
06537    FILE *f;
06538 
06539    unsigned char keys[8];
06540 
06541    int x;
06542 
06543    if (!ast_adsi_available(chan))
06544       return;
06545 
06546    /* Retrieve important info */
06547    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06548    f = fopen(fn2, "r");
06549    if (f) {
06550       while (!feof(f)) {   
06551          if (!fgets((char *) buf, sizeof(buf), f)) {
06552             continue;
06553          }
06554          if (!feof(f)) {
06555             char *stringp = NULL;
06556             stringp = (char *) buf;
06557             strsep(&stringp, "=");
06558             val = strsep(&stringp, "=");
06559             if (!ast_strlen_zero(val)) {
06560                if (!strcmp((char *) buf, "callerid"))
06561                   ast_copy_string(cid, val, sizeof(cid));
06562                if (!strcmp((char *) buf, "origdate"))
06563                   ast_copy_string(datetime, val, sizeof(datetime));
06564             }
06565          }
06566       }
06567       fclose(f);
06568    }
06569    /* New meaning for keys */
06570    for (x = 0; x < 5; x++)
06571       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06572    keys[6] = 0x0;
06573    keys[7] = 0x0;
06574 
06575    if (!vms->curmsg) {
06576       /* No prev key, provide "Folder" instead */
06577       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06578    }
06579    if (vms->curmsg >= vms->lastmsg) {
06580       /* If last message ... */
06581       if (vms->curmsg) {
06582          /* but not only message, provide "Folder" instead */
06583          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06584          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06585 
06586       } else {
06587          /* Otherwise if only message, leave blank */
06588          keys[3] = 1;
06589       }
06590    }
06591 
06592    if (!ast_strlen_zero(cid)) {
06593       ast_callerid_parse(cid, &name, &num);
06594       if (!name)
06595          name = num;
06596    } else
06597       name = "Unknown Caller";
06598 
06599    /* If deleted, show "undeleted" */
06600 #ifdef IMAP_STORAGE
06601    ast_mutex_lock(&vms->lock);
06602 #endif
06603    if (vms->deleted[vms->curmsg]) {
06604       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06605    }
06606 #ifdef IMAP_STORAGE
06607    ast_mutex_unlock(&vms->lock);
06608 #endif
06609 
06610    /* Except "Exit" */
06611    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06612    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06613       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06614    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06615 
06616    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06617    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06618    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06619    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06620    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06621    bytes += ast_adsi_set_keys(buf + bytes, keys);
06622    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06623 
06624    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06625 }
06626 
06627 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06628 {
06629    int bytes = 0;
06630    unsigned char buf[256];
06631    unsigned char keys[8];
06632 
06633    int x;
06634 
06635    if (!ast_adsi_available(chan))
06636       return;
06637 
06638    /* New meaning for keys */
06639    for (x = 0; x < 5; x++)
06640       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06641 
06642    keys[6] = 0x0;
06643    keys[7] = 0x0;
06644 
06645    if (!vms->curmsg) {
06646       /* No prev key, provide "Folder" instead */
06647       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06648    }
06649    if (vms->curmsg >= vms->lastmsg) {
06650       /* If last message ... */
06651       if (vms->curmsg) {
06652          /* but not only message, provide "Folder" instead */
06653          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06654       } else {
06655          /* Otherwise if only message, leave blank */
06656          keys[3] = 1;
06657       }
06658    }
06659 
06660    /* If deleted, show "undeleted" */
06661 #ifdef IMAP_STORAGE
06662    ast_mutex_lock(&vms->lock);
06663 #endif
06664    if (vms->deleted[vms->curmsg]) {
06665       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06666    }
06667 #ifdef IMAP_STORAGE
06668    ast_mutex_unlock(&vms->lock);
06669 #endif
06670 
06671    /* Except "Exit" */
06672    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06673    bytes += ast_adsi_set_keys(buf + bytes, keys);
06674    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06675 
06676    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06677 }
06678 
06679 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06680 {
06681    unsigned char buf[256] = "";
06682    char buf1[256] = "", buf2[256] = "";
06683    int bytes = 0;
06684    unsigned char keys[8];
06685    int x;
06686 
06687    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06688    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06689    if (!ast_adsi_available(chan))
06690       return;
06691    if (vms->newmessages) {
06692       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06693       if (vms->oldmessages) {
06694          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06695          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06696       } else {
06697          snprintf(buf2, sizeof(buf2), "%s.", newm);
06698       }
06699    } else if (vms->oldmessages) {
06700       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06701       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06702    } else {
06703       strcpy(buf1, "You have no messages.");
06704       buf2[0] = ' ';
06705       buf2[1] = '\0';
06706    }
06707    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06708    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06709    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06710 
06711    for (x = 0; x < 6; x++)
06712       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06713    keys[6] = 0;
06714    keys[7] = 0;
06715 
06716    /* Don't let them listen if there are none */
06717    if (vms->lastmsg < 0)
06718       keys[0] = 1;
06719    bytes += ast_adsi_set_keys(buf + bytes, keys);
06720 
06721    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06722 
06723    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06724 }
06725 
06726 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06727 {
06728    unsigned char buf[256] = "";
06729    char buf1[256] = "", buf2[256] = "";
06730    int bytes = 0;
06731    unsigned char keys[8];
06732    int x;
06733 
06734    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06735 
06736    if (!ast_adsi_available(chan))
06737       return;
06738 
06739    /* Original command keys */
06740    for (x = 0; x < 6; x++)
06741       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06742 
06743    keys[6] = 0;
06744    keys[7] = 0;
06745 
06746    if ((vms->lastmsg + 1) < 1)
06747       keys[0] = 0;
06748 
06749    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06750       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06751 
06752    if (vms->lastmsg + 1)
06753       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06754    else
06755       strcpy(buf2, "no messages.");
06756    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06757    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06758    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06759    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06760    bytes += ast_adsi_set_keys(buf + bytes, keys);
06761 
06762    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06763 
06764    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06765    
06766 }
06767 
06768 /*
06769 static void adsi_clear(struct ast_channel *chan)
06770 {
06771    char buf[256];
06772    int bytes=0;
06773    if (!ast_adsi_available(chan))
06774       return;
06775    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06776    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06777 
06778    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06779 }
06780 */
06781 
06782 static void adsi_goodbye(struct ast_channel *chan)
06783 {
06784    unsigned char buf[256];
06785    int bytes = 0;
06786 
06787    if (!ast_adsi_available(chan))
06788       return;
06789    bytes += adsi_logo(buf + bytes);
06790    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06791    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06792    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06793    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06794 
06795    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06796 }
06797 
06798 /*!\brief get_folder: Folder menu
06799  * Plays "press 1 for INBOX messages" etc.
06800  * Should possibly be internationalized
06801  */
06802 static int get_folder(struct ast_channel *chan, int start)
06803 {
06804    int x;
06805    int d;
06806    char fn[PATH_MAX];
06807    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06808    if (d)
06809       return d;
06810    for (x = start; x < 5; x++) { /* For all folders */
06811       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06812          return d;
06813       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06814       if (d)
06815          return d;
06816       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06817 
06818       /* The inbox folder can have its name changed under certain conditions
06819        * so this checks if the sound file exists for the inbox folder name and
06820        * if it doesn't, plays the default name instead. */
06821       if (x == 0) {
06822          if (ast_fileexists(fn, NULL, NULL)) {
06823             d = vm_play_folder_name(chan, fn);
06824          } else {
06825             ast_verb(1, "failed to find %s\n", fn);
06826             d = vm_play_folder_name(chan, "vm-INBOX");
06827          }
06828       } else {
06829          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06830          d = vm_play_folder_name(chan, fn);
06831       }
06832 
06833       if (d)
06834          return d;
06835       d = ast_waitfordigit(chan, 500);
06836       if (d)
06837          return d;
06838    }
06839 
06840    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06841    if (d)
06842       return d;
06843    d = ast_waitfordigit(chan, 4000);
06844    return d;
06845 }
06846 
06847 /*!
06848  * \brief plays a prompt and waits for a keypress.
06849  * \param chan
06850  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06851  * \param start Does not appear to be used at this time.
06852  *
06853  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06854  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06855  * prompting for the number inputs that correspond to the available folders.
06856  * 
06857  * \return zero on success, or -1 on error.
06858  */
06859 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06860 {
06861    int res = 0;
06862    int loops = 0;
06863 
06864    res = ast_play_and_wait(chan, fn);  /* Folder name */
06865    while (((res < '0') || (res > '9')) &&
06866          (res != '#') && (res >= 0) &&
06867          loops < 4) {
06868       res = get_folder(chan, 0);
06869       loops++;
06870    }
06871    if (loops == 4) { /* give up */
06872       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06873       return '#';
06874    }
06875    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06876    return res;
06877 }
06878 
06879 /*!
06880  * \brief presents the option to prepend to an existing message when forwarding it.
06881  * \param chan
06882  * \param vmu
06883  * \param curdir
06884  * \param curmsg
06885  * \param vm_fmts
06886  * \param context
06887  * \param record_gain
06888  * \param duration
06889  * \param vms
06890  * \param flag 
06891  *
06892  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06893  *
06894  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06895  * \return zero on success, -1 on error.
06896  */
06897 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06898          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06899 {
06900    int cmd = 0;
06901    int retries = 0, prepend_duration = 0, already_recorded = 0;
06902    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06903    char textfile[PATH_MAX];
06904    struct ast_config *msg_cfg;
06905    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06906 #ifndef IMAP_STORAGE
06907    signed char zero_gain = 0;
06908 #endif
06909    const char *duration_str;
06910 
06911    /* Must always populate duration correctly */
06912    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06913    strcpy(textfile, msgfile);
06914    strcpy(backup, msgfile);
06915    strcpy(backup_textfile, msgfile);
06916    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06917    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06918    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06919 
06920    if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06921       *duration = atoi(duration_str);
06922    } else {
06923       *duration = 0;
06924    }
06925 
06926    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06927       if (cmd)
06928          retries = 0;
06929       switch (cmd) {
06930       case '1': 
06931 
06932 #ifdef IMAP_STORAGE
06933          /* Record new intro file */
06934          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06935          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06936          ast_play_and_wait(chan, INTRO);
06937          ast_play_and_wait(chan, "beep");
06938          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06939          if (cmd == -1) {
06940             break;
06941          }
06942          cmd = 't';
06943 #else
06944 
06945          /* prepend a message to the current message, update the metadata and return */
06946 
06947          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06948          strcpy(textfile, msgfile);
06949          strncat(textfile, ".txt", sizeof(textfile) - 1);
06950          *duration = 0;
06951 
06952          /* if we can't read the message metadata, stop now */
06953          if (!valid_config(msg_cfg)) {
06954             cmd = 0;
06955             break;
06956          }
06957 
06958          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06959 #ifndef IMAP_STORAGE
06960          if (already_recorded) {
06961             ast_filecopy(backup, msgfile, NULL);
06962             copy(backup_textfile, textfile);
06963          }
06964          else {
06965             ast_filecopy(msgfile, backup, NULL);
06966             copy(textfile, backup_textfile);
06967          }
06968 #endif
06969          already_recorded = 1;
06970 
06971          if (record_gain)
06972             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06973 
06974          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06975 
06976          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06977             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06978             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06979             ast_filerename(backup, msgfile, NULL);
06980          }
06981 
06982          if (record_gain)
06983             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06984 
06985          
06986          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06987             *duration = atoi(duration_str);
06988 
06989          if (prepend_duration) {
06990             struct ast_category *msg_cat;
06991             /* need enough space for a maximum-length message duration */
06992             char duration_buf[12];
06993 
06994             *duration += prepend_duration;
06995             msg_cat = ast_category_get(msg_cfg, "message");
06996             snprintf(duration_buf, 11, "%ld", *duration);
06997             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06998                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06999             }
07000          }
07001 
07002 #endif
07003          break;
07004       case '2': 
07005          /* NULL out introfile so we know there is no intro! */
07006 #ifdef IMAP_STORAGE
07007          *vms->introfn = '\0';
07008 #endif
07009          cmd = 't';
07010          break;
07011       case '*':
07012          cmd = '*';
07013          break;
07014       default: 
07015          /* If time_out and return to menu, reset already_recorded */
07016          already_recorded = 0;
07017 
07018          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
07019             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
07020          if (!cmd) {
07021             cmd = ast_play_and_wait(chan, "vm-starmain");
07022             /* "press star to return to the main menu" */
07023          }
07024          if (!cmd) {
07025             cmd = ast_waitfordigit(chan, 6000);
07026          }
07027          if (!cmd) {
07028             retries++;
07029          }
07030          if (retries > 3) {
07031             cmd = '*'; /* Let's cancel this beast */
07032          }
07033          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07034       }
07035    }
07036 
07037    if (valid_config(msg_cfg))
07038       ast_config_destroy(msg_cfg);
07039    if (prepend_duration)
07040       *duration = prepend_duration;
07041 
07042    if (already_recorded && cmd == -1) {
07043       /* restore original message if prepention cancelled */
07044       ast_filerename(backup, msgfile, NULL);
07045       rename(backup_textfile, textfile);
07046    }
07047 
07048    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07049       cmd = 0;
07050    return cmd;
07051 }
07052 
07053 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07054 {
07055    struct ast_event *event;
07056    char *mailbox, *context;
07057 
07058    /* Strip off @default */
07059    context = mailbox = ast_strdupa(box);
07060    strsep(&context, "@");
07061    if (ast_strlen_zero(context))
07062       context = "default";
07063 
07064    if (!(event = ast_event_new(AST_EVENT_MWI,
07065          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07066          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07067          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07068          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07069          AST_EVENT_IE_END))) {
07070       return;
07071    }
07072 
07073    ast_event_queue_and_cache(event);
07074 }
07075 
07076 /*!
07077  * \brief Sends email notification that a user has a new voicemail waiting for them.
07078  * \param chan
07079  * \param vmu
07080  * \param vms
07081  * \param msgnum
07082  * \param duration
07083  * \param fmt
07084  * \param cidnum The Caller ID phone number value.
07085  * \param cidname The Caller ID name value.
07086  * \param flag
07087  *
07088  * \return zero on success, -1 on error.
07089  */
07090 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)
07091 {
07092    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07093    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07094    const char *category;
07095    char *myserveremail = serveremail;
07096 
07097    ast_channel_lock(chan);
07098    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07099       category = ast_strdupa(category);
07100    }
07101    ast_channel_unlock(chan);
07102 
07103 #ifndef IMAP_STORAGE
07104    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07105 #else
07106    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07107 #endif
07108    make_file(fn, sizeof(fn), todir, msgnum);
07109    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07110 
07111    if (!ast_strlen_zero(vmu->attachfmt)) {
07112       if (strstr(fmt, vmu->attachfmt))
07113          fmt = vmu->attachfmt;
07114       else
07115          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);
07116    }
07117 
07118    /* Attach only the first format */
07119    fmt = ast_strdupa(fmt);
07120    stringp = fmt;
07121    strsep(&stringp, "|");
07122 
07123    if (!ast_strlen_zero(vmu->serveremail))
07124       myserveremail = vmu->serveremail;
07125 
07126    if (!ast_strlen_zero(vmu->email)) {
07127       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07128 
07129       if (attach_user_voicemail)
07130          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07131 
07132       /* XXX possible imap issue, should category be NULL XXX */
07133       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07134 
07135       if (attach_user_voicemail)
07136          DISPOSE(todir, msgnum);
07137    }
07138 
07139    if (!ast_strlen_zero(vmu->pager)) {
07140       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07141    }
07142 
07143    if (ast_test_flag(vmu, VM_DELETE))
07144       DELETE(todir, msgnum, fn, vmu);
07145 
07146    /* Leave voicemail for someone */
07147    if (ast_app_has_voicemail(ext_context, NULL)) 
07148       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07149 
07150    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07151 
07152    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);
07153    run_externnotify(vmu->context, vmu->mailbox, flag);
07154 
07155 #ifdef IMAP_STORAGE
07156    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07157    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07158       vm_imap_delete(NULL, vms->curmsg, vmu);
07159       vms->newmessages--;  /* Fix new message count */
07160    }
07161 #endif
07162 
07163    return 0;
07164 }
07165 
07166 /*!
07167  * \brief Sends a voicemail message to a mailbox recipient.
07168  * \param chan
07169  * \param context
07170  * \param vms
07171  * \param sender
07172  * \param fmt
07173  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07174  *             Will be 0 when called to forward an existing message (option 8)
07175  *             Will be 1 when called to leave a message (option 3->5)
07176  * \param record_gain 
07177  * \param urgent
07178  *
07179  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07180  * 
07181  * When in the leave message mode (is_new_message == 1):
07182  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07183  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07184  *
07185  * When in the forward message mode (is_new_message == 0):
07186  *   - retreives the current message to be forwarded
07187  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07188  *   - determines the target mailbox and folders
07189  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07190  *
07191  * \return zero on success, -1 on error.
07192  */
07193 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)
07194 {
07195 #ifdef IMAP_STORAGE
07196    int todircount = 0;
07197    struct vm_state *dstvms;
07198 #endif
07199    char username[70]="";
07200    char fn[PATH_MAX]; /* for playback of name greeting */
07201    char ecodes[16] = "#";
07202    int res = 0, cmd = 0;
07203    struct ast_vm_user *receiver = NULL, *vmtmp;
07204    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07205    char *stringp;
07206    const char *s;
07207    int saved_messages = 0;
07208    int valid_extensions = 0;
07209    char *dir;
07210    int curmsg;
07211    char urgent_str[7] = "";
07212    int prompt_played = 0;
07213 #ifndef IMAP_STORAGE
07214    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07215 #endif
07216    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07217       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07218    }
07219 
07220    if (vms == NULL) return -1;
07221    dir = vms->curdir;
07222    curmsg = vms->curmsg;
07223 
07224    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07225    while (!res && !valid_extensions) {
07226       int use_directory = 0;
07227       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07228          int done = 0;
07229          int retries = 0;
07230          cmd = 0;
07231          while ((cmd >= 0) && !done ){
07232             if (cmd)
07233                retries = 0;
07234             switch (cmd) {
07235             case '1': 
07236                use_directory = 0;
07237                done = 1;
07238                break;
07239             case '2': 
07240                use_directory = 1;
07241                done = 1;
07242                break;
07243             case '*': 
07244                cmd = 't';
07245                done = 1;
07246                break;
07247             default: 
07248                /* Press 1 to enter an extension press 2 to use the directory */
07249                cmd = ast_play_and_wait(chan, "vm-forward");
07250                if (!cmd) {
07251                   cmd = ast_waitfordigit(chan, 3000);
07252                }
07253                if (!cmd) {
07254                   retries++;
07255                }
07256                if (retries > 3) {
07257                   cmd = 't';
07258                   done = 1;
07259                }
07260                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07261             }
07262          }
07263          if (cmd < 0 || cmd == 't')
07264             break;
07265       }
07266       
07267       if (use_directory) {
07268          /* use app_directory */
07269          
07270          char old_context[sizeof(chan->context)];
07271          char old_exten[sizeof(chan->exten)];
07272          int old_priority;
07273          struct ast_app* directory_app;
07274 
07275          directory_app = pbx_findapp("Directory");
07276          if (directory_app) {
07277             char vmcontext[256];
07278             /* make backup copies */
07279             memcpy(old_context, chan->context, sizeof(chan->context));
07280             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07281             old_priority = chan->priority;
07282             
07283             /* call the the Directory, changes the channel */
07284             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07285             res = pbx_exec(chan, directory_app, vmcontext);
07286             
07287             ast_copy_string(username, chan->exten, sizeof(username));
07288             
07289             /* restore the old context, exten, and priority */
07290             memcpy(chan->context, old_context, sizeof(chan->context));
07291             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07292             chan->priority = old_priority;
07293          } else {
07294             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07295             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07296          }
07297       } else {
07298          /* Ask for an extension */
07299          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07300          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07301          prompt_played++;
07302          if (res || prompt_played > 4)
07303             break;
07304          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07305             break;
07306       }
07307       
07308       /* start all over if no username */
07309       if (ast_strlen_zero(username))
07310          continue;
07311       stringp = username;
07312       s = strsep(&stringp, "*");
07313       /* start optimistic */
07314       valid_extensions = 1;
07315       while (s) {
07316          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07317             int oldmsgs;
07318             int newmsgs;
07319             int capacity;
07320             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07321                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07322                /* Shouldn't happen, but allow trying another extension if it does */
07323                res = ast_play_and_wait(chan, "pbx-invalid");
07324                valid_extensions = 0;
07325                break;
07326             }
07327             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07328             if ((newmsgs + oldmsgs) >= capacity) {
07329                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07330                res = ast_play_and_wait(chan, "vm-mailboxfull");
07331                valid_extensions = 0;
07332                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07333                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07334                   free_user(vmtmp);
07335                }
07336                inprocess_count(receiver->mailbox, receiver->context, -1);
07337                break;
07338             }
07339             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07340          } else {
07341             /* XXX Optimization for the future.  When we encounter a single bad extension,
07342              * bailing out on all of the extensions may not be the way to go.  We should
07343              * probably just bail on that single extension, then allow the user to enter
07344              * several more. XXX
07345              */
07346             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07347                free_user(receiver);
07348             }
07349             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07350             /* "I am sorry, that's not a valid extension.  Please try again." */
07351             res = ast_play_and_wait(chan, "pbx-invalid");
07352             valid_extensions = 0;
07353             break;
07354          }
07355 
07356          /* play name if available, else play extension number */
07357          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07358          RETRIEVE(fn, -1, s, receiver->context);
07359          if (ast_fileexists(fn, NULL, NULL) > 0) {
07360             res = ast_stream_and_wait(chan, fn, ecodes);
07361             if (res) {
07362                DISPOSE(fn, -1);
07363                return res;
07364             }
07365          } else {
07366             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07367          }
07368          DISPOSE(fn, -1);
07369 
07370          s = strsep(&stringp, "*");
07371       }
07372       /* break from the loop of reading the extensions */
07373       if (valid_extensions)
07374          break;
07375    }
07376    /* check if we're clear to proceed */
07377    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07378       return res;
07379    if (is_new_message == 1) {
07380       struct leave_vm_options leave_options;
07381       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07382       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07383 
07384       /* Send VoiceMail */
07385       memset(&leave_options, 0, sizeof(leave_options));
07386       leave_options.record_gain = record_gain;
07387       cmd = leave_voicemail(chan, mailbox, &leave_options);
07388    } else {
07389       /* Forward VoiceMail */
07390       long duration = 0;
07391       struct vm_state vmstmp;
07392       int copy_msg_result = 0;
07393       memcpy(&vmstmp, vms, sizeof(vmstmp));
07394 
07395       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07396 
07397       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07398       if (!cmd) {
07399          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07400 #ifdef IMAP_STORAGE
07401             int attach_user_voicemail;
07402             char *myserveremail = serveremail;
07403             
07404             /* get destination mailbox */
07405             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07406             if (!dstvms) {
07407                dstvms = create_vm_state_from_user(vmtmp);
07408             }
07409             if (dstvms) {
07410                init_mailstream(dstvms, 0);
07411                if (!dstvms->mailstream) {
07412                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07413                } else {
07414                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07415                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07416                }
07417             } else {
07418                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07419             }
07420             if (!ast_strlen_zero(vmtmp->serveremail))
07421                myserveremail = vmtmp->serveremail;
07422             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07423             /* NULL category for IMAP storage */
07424             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07425                dstvms->curbox,
07426                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07427                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07428                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07429                NULL, urgent_str);
07430 #else
07431             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07432 #endif
07433             saved_messages++;
07434             AST_LIST_REMOVE_CURRENT(list);
07435             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07436             free_user(vmtmp);
07437             if (res)
07438                break;
07439          }
07440          AST_LIST_TRAVERSE_SAFE_END;
07441          if (saved_messages > 0 && !copy_msg_result) {
07442             /* give confirmation that the message was saved */
07443             /* commented out since we can't forward batches yet
07444             if (saved_messages == 1)
07445                res = ast_play_and_wait(chan, "vm-message");
07446             else
07447                res = ast_play_and_wait(chan, "vm-messages");
07448             if (!res)
07449                res = ast_play_and_wait(chan, "vm-saved"); */
07450 #ifdef IMAP_STORAGE
07451             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07452             if (ast_strlen_zero(vmstmp.introfn))
07453 #endif
07454             res = ast_play_and_wait(chan, "vm-msgsaved");
07455          }
07456 #ifndef IMAP_STORAGE
07457          else {
07458             /* with IMAP, mailbox full warning played by imap_check_limits */
07459             res = ast_play_and_wait(chan, "vm-mailboxfull");
07460          }
07461          /* Restore original message without prepended message if backup exists */
07462          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07463          strcpy(textfile, msgfile);
07464          strcpy(backup, msgfile);
07465          strcpy(backup_textfile, msgfile);
07466          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07467          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07468          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07469          if (ast_fileexists(backup, NULL, NULL) > 0) {
07470             ast_filerename(backup, msgfile, NULL);
07471             rename(backup_textfile, textfile);
07472          }
07473 #endif
07474       }
07475       DISPOSE(dir, curmsg);
07476 #ifndef IMAP_STORAGE
07477       if (cmd) { /* assuming hangup, cleanup backup file */
07478          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07479          strcpy(textfile, msgfile);
07480          strcpy(backup_textfile, msgfile);
07481          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07482          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07483          rename(backup_textfile, textfile);
07484       }
07485 #endif
07486    }
07487 
07488    /* If anything failed above, we still have this list to free */
07489    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07490       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07491       free_user(vmtmp);
07492    }
07493    return res ? res : cmd;
07494 }
07495 
07496 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07497 {
07498    int res;
07499    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07500       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07501    return res;
07502 }
07503 
07504 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07505 {
07506    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07507    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);
07508 }
07509 
07510 static int play_message_category(struct ast_channel *chan, const char *category)
07511 {
07512    int res = 0;
07513 
07514    if (!ast_strlen_zero(category))
07515       res = ast_play_and_wait(chan, category);
07516 
07517    if (res) {
07518       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07519       res = 0;
07520    }
07521 
07522    return res;
07523 }
07524 
07525 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07526 {
07527    int res = 0;
07528    struct vm_zone *the_zone = NULL;
07529    time_t t;
07530 
07531    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07532       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07533       return 0;
07534    }
07535 
07536    /* Does this user have a timezone specified? */
07537    if (!ast_strlen_zero(vmu->zonetag)) {
07538       /* Find the zone in the list */
07539       struct vm_zone *z;
07540       AST_LIST_LOCK(&zones);
07541       AST_LIST_TRAVERSE(&zones, z, list) {
07542          if (!strcmp(z->name, vmu->zonetag)) {
07543             the_zone = z;
07544             break;
07545          }
07546       }
07547       AST_LIST_UNLOCK(&zones);
07548    }
07549 
07550 /* No internal variable parsing for now, so we'll comment it out for the time being */
07551 #if 0
07552    /* Set the DIFF_* variables */
07553    ast_localtime(&t, &time_now, NULL);
07554    tv_now = ast_tvnow();
07555    ast_localtime(&tv_now, &time_then, NULL);
07556 
07557    /* Day difference */
07558    if (time_now.tm_year == time_then.tm_year)
07559       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07560    else
07561       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07562    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07563 
07564    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07565 #endif
07566    if (the_zone) {
07567       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07568    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07569       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07570    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07571       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07572    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07573       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);
07574    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07575       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07576    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07577       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07578    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07579       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07580    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07581       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);
07582    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07583       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07584    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07585       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07586    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07587       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);
07588    } else {
07589       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07590    }
07591 #if 0
07592    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07593 #endif
07594    return res;
07595 }
07596 
07597 
07598 
07599 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07600 {
07601    int res = 0;
07602    int i;
07603    char *callerid, *name;
07604    char prefile[PATH_MAX] = "";
07605    
07606 
07607    /* If voicemail cid is not enabled, or we didn't get cid or context from
07608     * the attribute file, leave now.
07609     *
07610     * TODO Still need to change this so that if this function is called by the
07611     * message envelope (and someone is explicitly requesting to hear the CID),
07612     * it does not check to see if CID is enabled in the config file.
07613     */
07614    if ((cid == NULL)||(context == NULL))
07615       return res;
07616 
07617    /* Strip off caller ID number from name */
07618    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07619    ast_callerid_parse(cid, &name, &callerid);
07620    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07621       /* Check for internal contexts and only */
07622       /* say extension when the call didn't come from an internal context in the list */
07623       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07624          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07625          if ((strcmp(cidinternalcontexts[i], context) == 0))
07626             break;
07627       }
07628       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07629          if (!res) {
07630             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07631             if (!ast_strlen_zero(prefile)) {
07632             /* See if we can find a recorded name for this person instead of their extension number */
07633                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07634                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07635                   if (!callback)
07636                      res = wait_file2(chan, vms, "vm-from");
07637                   res = ast_stream_and_wait(chan, prefile, "");
07638                } else {
07639                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07640                   /* Say "from extension" as one saying to sound smoother */
07641                   if (!callback)
07642                      res = wait_file2(chan, vms, "vm-from-extension");
07643                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07644                }
07645             }
07646          }
07647       } else if (!res) {
07648          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07649          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07650          if (!callback)
07651             res = wait_file2(chan, vms, "vm-from-phonenumber");
07652          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07653       }
07654    } else {
07655       /* Number unknown */
07656       ast_debug(1, "VM-CID: From an unknown number\n");
07657       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07658       res = wait_file2(chan, vms, "vm-unknown-caller");
07659    }
07660    return res;
07661 }
07662 
07663 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07664 {
07665    int res = 0;
07666    int durationm;
07667    int durations;
07668    /* Verify that we have a duration for the message */
07669    if (duration == NULL)
07670       return res;
07671 
07672    /* Convert from seconds to minutes */
07673    durations = atoi(duration);
07674    durationm = (durations / 60);
07675 
07676    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07677 
07678    if ((!res) && (durationm >= minduration)) {
07679       res = wait_file2(chan, vms, "vm-duration");
07680 
07681       /* POLISH syntax */
07682       if (!strncasecmp(chan->language, "pl", 2)) {
07683          div_t num = div(durationm, 10);
07684 
07685          if (durationm == 1) {
07686             res = ast_play_and_wait(chan, "digits/1z");
07687             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07688          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07689             if (num.rem == 2) {
07690                if (!num.quot) {
07691                   res = ast_play_and_wait(chan, "digits/2-ie");
07692                } else {
07693                   res = say_and_wait(chan, durationm - 2 , chan->language);
07694                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07695                }
07696             } else {
07697                res = say_and_wait(chan, durationm, chan->language);
07698             }
07699             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07700          } else {
07701             res = say_and_wait(chan, durationm, chan->language);
07702             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07703          }
07704       /* DEFAULT syntax */
07705       } else {
07706          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07707          res = wait_file2(chan, vms, "vm-minutes");
07708       }
07709    }
07710    return res;
07711 }
07712 
07713 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07714 {
07715    int res = 0;
07716    char filename[256], *cid;
07717    const char *origtime, *context, *category, *duration, *flag;
07718    struct ast_config *msg_cfg;
07719    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07720 
07721    vms->starting = 0;
07722    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07723    adsi_message(chan, vms);
07724    if (!vms->curmsg) {
07725       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07726    } else if (vms->curmsg == vms->lastmsg) {
07727       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07728    }
07729 
07730    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07731    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07732    msg_cfg = ast_config_load(filename, config_flags);
07733    if (!valid_config(msg_cfg)) {
07734       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07735       return 0;
07736    }
07737    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07738 
07739    /* Play the word urgent if we are listening to urgent messages */
07740    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07741       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07742    }
07743 
07744    if (!res) {
07745       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07746       /* POLISH syntax */
07747       if (!strncasecmp(chan->language, "pl", 2)) {
07748          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07749             int ten, one;
07750             char nextmsg[256];
07751             ten = (vms->curmsg + 1) / 10;
07752             one = (vms->curmsg + 1) % 10;
07753 
07754             if (vms->curmsg < 20) {
07755                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07756                res = wait_file2(chan, vms, nextmsg);
07757             } else {
07758                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07759                res = wait_file2(chan, vms, nextmsg);
07760                if (one > 0) {
07761                   if (!res) {
07762                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07763                      res = wait_file2(chan, vms, nextmsg);
07764                   }
07765                }
07766             }
07767          }
07768          if (!res)
07769             res = wait_file2(chan, vms, "vm-message");
07770       /* HEBREW syntax */
07771       } else if (!strncasecmp(chan->language, "he", 2)) {
07772          if (!vms->curmsg) {
07773             res = wait_file2(chan, vms, "vm-message");
07774             res = wait_file2(chan, vms, "vm-first");
07775          } else if (vms->curmsg == vms->lastmsg) {
07776             res = wait_file2(chan, vms, "vm-message");
07777             res = wait_file2(chan, vms, "vm-last");
07778          } else {
07779             res = wait_file2(chan, vms, "vm-message");
07780             res = wait_file2(chan, vms, "vm-number");
07781             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07782          }
07783       /* VIETNAMESE syntax */
07784       } else if (!strncasecmp(chan->language, "vi", 2)) {
07785          if (!vms->curmsg) {
07786             res = wait_file2(chan, vms, "vm-message");
07787             res = wait_file2(chan, vms, "vm-first");
07788          } else if (vms->curmsg == vms->lastmsg) {
07789             res = wait_file2(chan, vms, "vm-message");
07790             res = wait_file2(chan, vms, "vm-last");
07791          } else {
07792             res = wait_file2(chan, vms, "vm-message");
07793             res = wait_file2(chan, vms, "vm-number");
07794             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07795          }
07796       } else {
07797          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07798             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07799          } else { /* DEFAULT syntax */
07800             res = wait_file2(chan, vms, "vm-message");
07801          }
07802          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07803             if (!res) {
07804                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07805                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07806             }
07807          }
07808       }
07809    }
07810 
07811    if (!valid_config(msg_cfg)) {
07812       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07813       return 0;
07814    }
07815 
07816    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07817       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07818       DISPOSE(vms->curdir, vms->curmsg);
07819       ast_config_destroy(msg_cfg);
07820       return 0;
07821    }
07822 
07823    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07824    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07825    category = ast_variable_retrieve(msg_cfg, "message", "category");
07826 
07827    context = ast_variable_retrieve(msg_cfg, "message", "context");
07828    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07829       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07830    if (!res) {
07831       res = play_message_category(chan, category);
07832    }
07833    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07834       res = play_message_datetime(chan, vmu, origtime, filename);
07835    }
07836    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07837       res = play_message_callerid(chan, vms, cid, context, 0);
07838    }
07839    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07840       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07841    }
07842    /* Allow pressing '1' to skip envelope / callerid */
07843    if (res == '1') {
07844       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07845       res = 0;
07846    }
07847    ast_config_destroy(msg_cfg);
07848 
07849    if (!res) {
07850       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07851 #ifdef IMAP_STORAGE
07852       ast_mutex_lock(&vms->lock);
07853 #endif
07854       vms->heard[vms->curmsg] = 1;
07855 #ifdef IMAP_STORAGE
07856       ast_mutex_unlock(&vms->lock);
07857       /*IMAP storage stores any prepended message from a forward
07858        * as a separate file from the rest of the message
07859        */
07860       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07861          wait_file(chan, vms, vms->introfn);
07862       }
07863 #endif
07864       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07865          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07866          res = 0;
07867       }
07868       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07869    }
07870    DISPOSE(vms->curdir, vms->curmsg);
07871    return res;
07872 }
07873 
07874 #ifdef IMAP_STORAGE
07875 static int imap_remove_file(char *dir, int msgnum)
07876 {
07877    char fn[PATH_MAX];
07878    char full_fn[PATH_MAX];
07879    char intro[PATH_MAX] = {0,};
07880    
07881    if (msgnum > -1) {
07882       make_file(fn, sizeof(fn), dir, msgnum);
07883       snprintf(intro, sizeof(intro), "%sintro", fn);
07884    } else
07885       ast_copy_string(fn, dir, sizeof(fn));
07886    
07887    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07888       ast_filedelete(fn, NULL);
07889       if (!ast_strlen_zero(intro)) {
07890          ast_filedelete(intro, NULL);
07891       }
07892       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07893       unlink(full_fn);
07894    }
07895    return 0;
07896 }
07897 
07898 
07899 
07900 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07901 {
07902    char *file, *filename;
07903    char *attachment;
07904    char arg[10];
07905    int i;
07906    BODY* body;
07907 
07908    file = strrchr(ast_strdupa(dir), '/');
07909    if (file) {
07910       *file++ = '\0';
07911    } else {
07912       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07913       return -1;
07914    }
07915 
07916    ast_mutex_lock(&vms->lock);
07917    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07918       mail_fetchstructure(vms->mailstream, i + 1, &body);
07919       /* We have the body, now we extract the file name of the first attachment. */
07920       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07921          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07922       } else {
07923          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07924          ast_mutex_unlock(&vms->lock);
07925          return -1;
07926       }
07927       filename = strsep(&attachment, ".");
07928       if (!strcmp(filename, file)) {
07929          sprintf(arg, "%d", i + 1);
07930          mail_setflag(vms->mailstream, arg, "\\DELETED");
07931       }
07932    }
07933    mail_expunge(vms->mailstream);
07934    ast_mutex_unlock(&vms->lock);
07935    return 0;
07936 }
07937 
07938 #elif !defined(IMAP_STORAGE)
07939 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07940 {
07941    int count_msg, last_msg;
07942 
07943    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07944 
07945    /* Rename the member vmbox HERE so that we don't try to return before
07946     * we know what's going on.
07947     */
07948    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07949 
07950    /* Faster to make the directory than to check if it exists. */
07951    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07952 
07953    /* traverses directory using readdir (or select query for ODBC) */
07954    count_msg = count_messages(vmu, vms->curdir);
07955    if (count_msg < 0) {
07956       return count_msg;
07957    } else {
07958       vms->lastmsg = count_msg - 1;
07959    }
07960 
07961    if (vm_allocate_dh(vms, vmu, count_msg)) {
07962       return -1;
07963    }
07964 
07965    /*
07966    The following test is needed in case sequencing gets messed up.
07967    There appears to be more than one way to mess up sequence, so
07968    we will not try to find all of the root causes--just fix it when
07969    detected.
07970    */
07971 
07972    if (vm_lock_path(vms->curdir)) {
07973       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07974       return ERROR_LOCK_PATH;
07975    }
07976 
07977    /* for local storage, checks directory for messages up to maxmsg limit */
07978    last_msg = last_message_index(vmu, vms->curdir);
07979    ast_unlock_path(vms->curdir);
07980 
07981    if (last_msg < -1) {
07982       return last_msg;
07983    } else if (vms->lastmsg != last_msg) {
07984       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);
07985       resequence_mailbox(vmu, vms->curdir, count_msg);
07986    }
07987 
07988    return 0;
07989 }
07990 #endif
07991 
07992 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07993 {
07994    int x = 0;
07995    int last_msg_idx = 0;
07996 
07997 #ifndef IMAP_STORAGE
07998    int res = 0, nummsg;
07999    char fn2[PATH_MAX];
08000 #endif
08001 
08002    if (vms->lastmsg <= -1) {
08003       goto done;
08004    }
08005 
08006    vms->curmsg = -1;
08007 #ifndef IMAP_STORAGE
08008    /* Get the deleted messages fixed */
08009    if (vm_lock_path(vms->curdir)) {
08010       return ERROR_LOCK_PATH;
08011    }
08012 
08013    /* update count as message may have arrived while we've got mailbox open */
08014    last_msg_idx = last_message_index(vmu, vms->curdir);
08015    if (last_msg_idx != vms->lastmsg) {
08016       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
08017    }
08018 
08019    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
08020    for (x = 0; x < last_msg_idx + 1; x++) {
08021       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
08022          /* Save this message.  It's not in INBOX or hasn't been heard */
08023          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08024          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
08025             break;
08026          }
08027          vms->curmsg++;
08028          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
08029          if (strcmp(vms->fn, fn2)) {
08030             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
08031          }
08032       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
08033          /* Move to old folder before deleting */
08034          res = save_to_folder(vmu, vms, x, 1);
08035          if (res == ERROR_LOCK_PATH) {
08036             /* If save failed do not delete the message */
08037             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
08038             vms->deleted[x] = 0;
08039             vms->heard[x] = 0;
08040             --x;
08041          }
08042       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08043          /* Move to deleted folder */
08044          res = save_to_folder(vmu, vms, x, 10);
08045          if (res == ERROR_LOCK_PATH) {
08046             /* If save failed do not delete the message */
08047             vms->deleted[x] = 0;
08048             vms->heard[x] = 0;
08049             --x;
08050          }
08051       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08052          /* If realtime storage enabled - we should explicitly delete this message,
08053          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08054          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08055          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08056             DELETE(vms->curdir, x, vms->fn, vmu);
08057          }
08058       }
08059    }
08060 
08061    /* Delete ALL remaining messages */
08062    nummsg = x - 1;
08063    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08064       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08065       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08066          DELETE(vms->curdir, x, vms->fn, vmu);
08067       }
08068    }
08069    ast_unlock_path(vms->curdir);
08070 #else /* defined(IMAP_STORAGE) */
08071    ast_mutex_lock(&vms->lock);
08072    if (vms->deleted) {
08073       /* Since we now expunge after each delete, deleting in reverse order
08074        * ensures that no reordering occurs between each step. */
08075       last_msg_idx = vms->dh_arraysize;
08076       for (x = last_msg_idx - 1; x >= 0; x--) {
08077          if (vms->deleted[x]) {
08078             ast_debug(3, "IMAP delete of %d\n", x);
08079             DELETE(vms->curdir, x, vms->fn, vmu);
08080          }
08081       }
08082    }
08083 #endif
08084 
08085 done:
08086    if (vms->deleted) {
08087       ast_free(vms->deleted);
08088       vms->deleted = NULL;
08089    }
08090    if (vms->heard) {
08091       ast_free(vms->heard);
08092       vms->heard = NULL;
08093    }
08094    vms->dh_arraysize = 0;
08095 #ifdef IMAP_STORAGE
08096    ast_mutex_unlock(&vms->lock);
08097 #endif
08098 
08099    return 0;
08100 }
08101 
08102 /* In Greek even though we CAN use a syntax like "friends messages"
08103  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08104  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08105  * syntax for the above three categories which is more elegant.
08106  */
08107 
08108 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08109 {
08110    int cmd;
08111    char *buf;
08112 
08113    buf = ast_alloca(strlen(box) + 2);
08114    strcpy(buf, box);
08115    strcat(buf, "s");
08116 
08117    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08118       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08119       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08120    } else {
08121       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08122       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08123    }
08124 }
08125 
08126 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08127 {
08128    int cmd;
08129 
08130    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08131       if (!strcasecmp(box, "vm-INBOX"))
08132          cmd = ast_play_and_wait(chan, "vm-new-e");
08133       else
08134          cmd = ast_play_and_wait(chan, "vm-old-e");
08135       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08136    } else {
08137       cmd = ast_play_and_wait(chan, "vm-messages");
08138       return cmd ? cmd : ast_play_and_wait(chan, box);
08139    }
08140 }
08141 
08142 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08143 {
08144    int cmd;
08145 
08146    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08147       cmd = ast_play_and_wait(chan, "vm-messages");
08148       return cmd ? cmd : ast_play_and_wait(chan, box);
08149    } else {
08150       cmd = ast_play_and_wait(chan, box);
08151       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08152    }
08153 }
08154 
08155 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08156 {
08157    int cmd;
08158 
08159    if (  !strncasecmp(chan->language, "it", 2) ||
08160         !strncasecmp(chan->language, "es", 2) ||
08161         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08162       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08163       return cmd ? cmd : ast_play_and_wait(chan, box);
08164    } else if (!strncasecmp(chan->language, "gr", 2)) {
08165       return vm_play_folder_name_gr(chan, box);
08166    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08167       return ast_play_and_wait(chan, box);
08168    } else if (!strncasecmp(chan->language, "pl", 2)) {
08169       return vm_play_folder_name_pl(chan, box);
08170    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08171       return vm_play_folder_name_ua(chan, box);
08172    } else if (!strncasecmp(chan->language, "vi", 2)) {
08173       return ast_play_and_wait(chan, box);
08174    } else {  /* Default English */
08175       cmd = ast_play_and_wait(chan, box);
08176       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08177    }
08178 }
08179 
08180 /* GREEK SYNTAX
08181    In greek the plural for old/new is
08182    different so we need the following files
08183    We also need vm-denExeteMynhmata because
08184    this syntax is different.
08185 
08186    -> vm-Olds.wav : "Palia"
08187    -> vm-INBOXs.wav : "Nea"
08188    -> vm-denExeteMynhmata : "den exete mynhmata"
08189 */
08190 
08191 
08192 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08193 {
08194    int res = 0;
08195 
08196    if (vms->newmessages) {
08197       res = ast_play_and_wait(chan, "vm-youhave");
08198       if (!res) 
08199          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08200       if (!res) {
08201          if ((vms->newmessages == 1)) {
08202             res = ast_play_and_wait(chan, "vm-INBOX");
08203             if (!res)
08204                res = ast_play_and_wait(chan, "vm-message");
08205          } else {
08206             res = ast_play_and_wait(chan, "vm-INBOXs");
08207             if (!res)
08208                res = ast_play_and_wait(chan, "vm-messages");
08209          }
08210       }
08211    } else if (vms->oldmessages){
08212       res = ast_play_and_wait(chan, "vm-youhave");
08213       if (!res)
08214          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08215       if ((vms->oldmessages == 1)){
08216          res = ast_play_and_wait(chan, "vm-Old");
08217          if (!res)
08218             res = ast_play_and_wait(chan, "vm-message");
08219       } else {
08220          res = ast_play_and_wait(chan, "vm-Olds");
08221          if (!res)
08222             res = ast_play_and_wait(chan, "vm-messages");
08223       }
08224    } else if (!vms->oldmessages && !vms->newmessages) 
08225       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08226    return res;
08227 }
08228 
08229 /* Version of vm_intro() designed to work for many languages.
08230  *
08231  * It is hoped that this function can prevent the proliferation of 
08232  * language-specific vm_intro() functions and in time replace the language-
08233  * specific functions which already exist.  An examination of the language-
08234  * specific functions revealed that they all corrected the same deficiencies
08235  * in vm_intro_en() (which was the default function). Namely:
08236  *
08237  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08238  *     wording of the voicemail greeting hides this problem.  For example,
08239  *     vm-INBOX contains only the word "new".  This means that both of these
08240  *     sequences produce valid utterances:
08241  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08242  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08243  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08244  *     in many languages) the first utterance becomes "you have 1 the new message".
08245  *  2) The function contains hardcoded rules for pluralizing the word "message".
08246  *     These rules are correct for English, but not for many other languages.
08247  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08248  *     required in many languages.
08249  *  4) The gender of the word for "message" is not specified. This is a problem
08250  *     because in many languages the gender of the number in phrases such
08251  *     as "you have one new message" must match the gender of the word
08252  *     meaning "message".
08253  *
08254  * Fixing these problems for each new language has meant duplication of effort.
08255  * This new function solves the problems in the following general ways:
08256  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08257  *     and vm-Old respectively for those languages where it makes sense.
08258  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08259  *     on vm-message.
08260  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08261  *     prefix on vm-new and vm-old (none for English).
08262  *  4) Pass the gender of the language's word for "message" as an agument to
08263  *     this function which is can in turn pass on to the functions which 
08264  *     say numbers and put endings on nounds and adjectives.
08265  *
08266  * All languages require these messages:
08267  *  vm-youhave    "You have..."
08268  *  vm-and     "and"
08269  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08270  *
08271  * To use it for English, you will need these additional sound files:
08272  *  vm-new     "new"
08273  *  vm-message    "message", singular
08274  *  vm-messages      "messages", plural
08275  *
08276  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08277  *
08278  *  vm-newn    "novoye" (singular, neuter)
08279  *  vm-newx    "novikh" (counting plural form, genative plural)
08280  *  vm-message    "sobsheniye" (singular form)
08281  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08282  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08283  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08284  *  digits/2n     "dva" (neuter singular)
08285  */
08286 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08287 {
08288    int res;
08289    int lastnum = 0;
08290 
08291    res = ast_play_and_wait(chan, "vm-youhave");
08292 
08293    if (!res && vms->newmessages) {
08294       lastnum = vms->newmessages;
08295 
08296       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08297          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08298       }
08299 
08300       if (!res && vms->oldmessages) {
08301          res = ast_play_and_wait(chan, "vm-and");
08302       }
08303    }
08304 
08305    if (!res && vms->oldmessages) {
08306       lastnum = vms->oldmessages;
08307 
08308       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08309          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08310       }
08311    }
08312 
08313    if (!res) {
08314       if (lastnum == 0) {
08315          res = ast_play_and_wait(chan, "vm-no");
08316       }
08317       if (!res) {
08318          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08319       }
08320    }
08321 
08322    return res;
08323 }
08324 
08325 /* Default Hebrew syntax */
08326 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08327 {
08328    int res = 0;
08329 
08330    /* Introduce messages they have */
08331    if (!res) {
08332       if ((vms->newmessages) || (vms->oldmessages)) {
08333          res = ast_play_and_wait(chan, "vm-youhave");
08334       }
08335       /*
08336        * The word "shtei" refers to the number 2 in hebrew when performing a count
08337        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08338        * an element, this is one of them.
08339        */
08340       if (vms->newmessages) {
08341          if (!res) {
08342             if (vms->newmessages == 1) {
08343                res = ast_play_and_wait(chan, "vm-INBOX1");
08344             } else {
08345                if (vms->newmessages == 2) {
08346                   res = ast_play_and_wait(chan, "vm-shtei");
08347                } else {
08348                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08349                }
08350                res = ast_play_and_wait(chan, "vm-INBOX");
08351             }
08352          }
08353          if (vms->oldmessages && !res) {
08354             res = ast_play_and_wait(chan, "vm-and");
08355             if (vms->oldmessages == 1) {
08356                res = ast_play_and_wait(chan, "vm-Old1");
08357             } else {
08358                if (vms->oldmessages == 2) {
08359                   res = ast_play_and_wait(chan, "vm-shtei");
08360                } else {
08361                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08362                }
08363                res = ast_play_and_wait(chan, "vm-Old");
08364             }
08365          }
08366       }
08367       if (!res && vms->oldmessages && !vms->newmessages) {
08368          if (!res) {
08369             if (vms->oldmessages == 1) {
08370                res = ast_play_and_wait(chan, "vm-Old1");
08371             } else {
08372                if (vms->oldmessages == 2) {
08373                   res = ast_play_and_wait(chan, "vm-shtei");
08374                } else {
08375                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08376                }
08377                res = ast_play_and_wait(chan, "vm-Old");
08378             }
08379          }
08380       }
08381       if (!res) {
08382          if (!vms->oldmessages && !vms->newmessages) {
08383             if (!res) {
08384                res = ast_play_and_wait(chan, "vm-nomessages");
08385             }
08386          }
08387       }
08388    }
08389    return res;
08390 }
08391    
08392 /* Default English syntax */
08393 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08394 {
08395    int res;
08396 
08397    /* Introduce messages they have */
08398    res = ast_play_and_wait(chan, "vm-youhave");
08399    if (!res) {
08400       if (vms->urgentmessages) {
08401          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08402          if (!res)
08403             res = ast_play_and_wait(chan, "vm-Urgent");
08404          if ((vms->oldmessages || vms->newmessages) && !res) {
08405             res = ast_play_and_wait(chan, "vm-and");
08406          } else if (!res) {
08407             if ((vms->urgentmessages == 1))
08408                res = ast_play_and_wait(chan, "vm-message");
08409             else
08410                res = ast_play_and_wait(chan, "vm-messages");
08411          }
08412       }
08413       if (vms->newmessages) {
08414          res = say_and_wait(chan, vms->newmessages, chan->language);
08415          if (!res)
08416             res = ast_play_and_wait(chan, "vm-INBOX");
08417          if (vms->oldmessages && !res)
08418             res = ast_play_and_wait(chan, "vm-and");
08419          else if (!res) {
08420             if ((vms->newmessages == 1))
08421                res = ast_play_and_wait(chan, "vm-message");
08422             else
08423                res = ast_play_and_wait(chan, "vm-messages");
08424          }
08425             
08426       }
08427       if (!res && vms->oldmessages) {
08428          res = say_and_wait(chan, vms->oldmessages, chan->language);
08429          if (!res)
08430             res = ast_play_and_wait(chan, "vm-Old");
08431          if (!res) {
08432             if (vms->oldmessages == 1)
08433                res = ast_play_and_wait(chan, "vm-message");
08434             else
08435                res = ast_play_and_wait(chan, "vm-messages");
08436          }
08437       }
08438       if (!res) {
08439          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08440             res = ast_play_and_wait(chan, "vm-no");
08441             if (!res)
08442                res = ast_play_and_wait(chan, "vm-messages");
08443          }
08444       }
08445    }
08446    return res;
08447 }
08448 
08449 /* ITALIAN syntax */
08450 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08451 {
08452    /* Introduce messages they have */
08453    int res;
08454    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08455       res = ast_play_and_wait(chan, "vm-no") ||
08456          ast_play_and_wait(chan, "vm-message");
08457    else
08458       res = ast_play_and_wait(chan, "vm-youhave");
08459    if (!res && vms->newmessages) {
08460       res = (vms->newmessages == 1) ?
08461          ast_play_and_wait(chan, "digits/un") ||
08462          ast_play_and_wait(chan, "vm-nuovo") ||
08463          ast_play_and_wait(chan, "vm-message") :
08464          /* 2 or more new messages */
08465          say_and_wait(chan, vms->newmessages, chan->language) ||
08466          ast_play_and_wait(chan, "vm-nuovi") ||
08467          ast_play_and_wait(chan, "vm-messages");
08468       if (!res && vms->oldmessages)
08469          res = ast_play_and_wait(chan, "vm-and");
08470    }
08471    if (!res && vms->oldmessages) {
08472       res = (vms->oldmessages == 1) ?
08473          ast_play_and_wait(chan, "digits/un") ||
08474          ast_play_and_wait(chan, "vm-vecchio") ||
08475          ast_play_and_wait(chan, "vm-message") :
08476          /* 2 or more old messages */
08477          say_and_wait(chan, vms->oldmessages, chan->language) ||
08478          ast_play_and_wait(chan, "vm-vecchi") ||
08479          ast_play_and_wait(chan, "vm-messages");
08480    }
08481    return res;
08482 }
08483 
08484 /* POLISH syntax */
08485 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08486 {
08487    /* Introduce messages they have */
08488    int res;
08489    div_t num;
08490 
08491    if (!vms->oldmessages && !vms->newmessages) {
08492       res = ast_play_and_wait(chan, "vm-no");
08493       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08494       return res;
08495    } else {
08496       res = ast_play_and_wait(chan, "vm-youhave");
08497    }
08498 
08499    if (vms->newmessages) {
08500       num = div(vms->newmessages, 10);
08501       if (vms->newmessages == 1) {
08502          res = ast_play_and_wait(chan, "digits/1-a");
08503          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08504          res = res ? res : ast_play_and_wait(chan, "vm-message");
08505       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08506          if (num.rem == 2) {
08507             if (!num.quot) {
08508                res = ast_play_and_wait(chan, "digits/2-ie");
08509             } else {
08510                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08511                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08512             }
08513          } else {
08514             res = say_and_wait(chan, vms->newmessages, chan->language);
08515          }
08516          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08517          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08518       } else {
08519          res = say_and_wait(chan, vms->newmessages, chan->language);
08520          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08521          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08522       }
08523       if (!res && vms->oldmessages)
08524          res = ast_play_and_wait(chan, "vm-and");
08525    }
08526    if (!res && vms->oldmessages) {
08527       num = div(vms->oldmessages, 10);
08528       if (vms->oldmessages == 1) {
08529          res = ast_play_and_wait(chan, "digits/1-a");
08530          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08531          res = res ? res : ast_play_and_wait(chan, "vm-message");
08532       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08533          if (num.rem == 2) {
08534             if (!num.quot) {
08535                res = ast_play_and_wait(chan, "digits/2-ie");
08536             } else {
08537                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08538                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08539             }
08540          } else {
08541             res = say_and_wait(chan, vms->oldmessages, chan->language);
08542          }
08543          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08544          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08545       } else {
08546          res = say_and_wait(chan, vms->oldmessages, chan->language);
08547          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08548          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08549       }
08550    }
08551 
08552    return res;
08553 }
08554 
08555 /* SWEDISH syntax */
08556 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08557 {
08558    /* Introduce messages they have */
08559    int res;
08560 
08561    res = ast_play_and_wait(chan, "vm-youhave");
08562    if (res)
08563       return res;
08564 
08565    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08566       res = ast_play_and_wait(chan, "vm-no");
08567       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08568       return res;
08569    }
08570 
08571    if (vms->newmessages) {
08572       if ((vms->newmessages == 1)) {
08573          res = ast_play_and_wait(chan, "digits/ett");
08574          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08575          res = res ? res : ast_play_and_wait(chan, "vm-message");
08576       } else {
08577          res = say_and_wait(chan, vms->newmessages, chan->language);
08578          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08579          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08580       }
08581       if (!res && vms->oldmessages)
08582          res = ast_play_and_wait(chan, "vm-and");
08583    }
08584    if (!res && vms->oldmessages) {
08585       if (vms->oldmessages == 1) {
08586          res = ast_play_and_wait(chan, "digits/ett");
08587          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08588          res = res ? res : ast_play_and_wait(chan, "vm-message");
08589       } else {
08590          res = say_and_wait(chan, vms->oldmessages, chan->language);
08591          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08592          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08593       }
08594    }
08595 
08596    return res;
08597 }
08598 
08599 /* NORWEGIAN syntax */
08600 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08601 {
08602    /* Introduce messages they have */
08603    int res;
08604 
08605    res = ast_play_and_wait(chan, "vm-youhave");
08606    if (res)
08607       return res;
08608 
08609    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08610       res = ast_play_and_wait(chan, "vm-no");
08611       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08612       return res;
08613    }
08614 
08615    if (vms->newmessages) {
08616       if ((vms->newmessages == 1)) {
08617          res = ast_play_and_wait(chan, "digits/1");
08618          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08619          res = res ? res : ast_play_and_wait(chan, "vm-message");
08620       } else {
08621          res = say_and_wait(chan, vms->newmessages, chan->language);
08622          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08623          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08624       }
08625       if (!res && vms->oldmessages)
08626          res = ast_play_and_wait(chan, "vm-and");
08627    }
08628    if (!res && vms->oldmessages) {
08629       if (vms->oldmessages == 1) {
08630          res = ast_play_and_wait(chan, "digits/1");
08631          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08632          res = res ? res : ast_play_and_wait(chan, "vm-message");
08633       } else {
08634          res = say_and_wait(chan, vms->oldmessages, chan->language);
08635          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08636          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08637       }
08638    }
08639 
08640    return res;
08641 }
08642 
08643 /* GERMAN syntax */
08644 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08645 {
08646    /* Introduce messages they have */
08647    int res;
08648    res = ast_play_and_wait(chan, "vm-youhave");
08649    if (!res) {
08650       if (vms->newmessages) {
08651          if ((vms->newmessages == 1))
08652             res = ast_play_and_wait(chan, "digits/1F");
08653          else
08654             res = say_and_wait(chan, vms->newmessages, chan->language);
08655          if (!res)
08656             res = ast_play_and_wait(chan, "vm-INBOX");
08657          if (vms->oldmessages && !res)
08658             res = ast_play_and_wait(chan, "vm-and");
08659          else if (!res) {
08660             if ((vms->newmessages == 1))
08661                res = ast_play_and_wait(chan, "vm-message");
08662             else
08663                res = ast_play_and_wait(chan, "vm-messages");
08664          }
08665             
08666       }
08667       if (!res && vms->oldmessages) {
08668          if (vms->oldmessages == 1)
08669             res = ast_play_and_wait(chan, "digits/1F");
08670          else
08671             res = say_and_wait(chan, vms->oldmessages, chan->language);
08672          if (!res)
08673             res = ast_play_and_wait(chan, "vm-Old");
08674          if (!res) {
08675             if (vms->oldmessages == 1)
08676                res = ast_play_and_wait(chan, "vm-message");
08677             else
08678                res = ast_play_and_wait(chan, "vm-messages");
08679          }
08680       }
08681       if (!res) {
08682          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08683             res = ast_play_and_wait(chan, "vm-no");
08684             if (!res)
08685                res = ast_play_and_wait(chan, "vm-messages");
08686          }
08687       }
08688    }
08689    return res;
08690 }
08691 
08692 /* SPANISH syntax */
08693 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08694 {
08695    /* Introduce messages they have */
08696    int res;
08697    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08698       res = ast_play_and_wait(chan, "vm-youhaveno");
08699       if (!res)
08700          res = ast_play_and_wait(chan, "vm-messages");
08701    } else {
08702       res = ast_play_and_wait(chan, "vm-youhave");
08703    }
08704    if (!res) {
08705       if (vms->newmessages) {
08706          if (!res) {
08707             if ((vms->newmessages == 1)) {
08708                res = ast_play_and_wait(chan, "digits/1M");
08709                if (!res)
08710                   res = ast_play_and_wait(chan, "vm-message");
08711                if (!res)
08712                   res = ast_play_and_wait(chan, "vm-INBOXs");
08713             } else {
08714                res = say_and_wait(chan, vms->newmessages, chan->language);
08715                if (!res)
08716                   res = ast_play_and_wait(chan, "vm-messages");
08717                if (!res)
08718                   res = ast_play_and_wait(chan, "vm-INBOX");
08719             }
08720          }
08721          if (vms->oldmessages && !res)
08722             res = ast_play_and_wait(chan, "vm-and");
08723       }
08724       if (vms->oldmessages) {
08725          if (!res) {
08726             if (vms->oldmessages == 1) {
08727                res = ast_play_and_wait(chan, "digits/1M");
08728                if (!res)
08729                   res = ast_play_and_wait(chan, "vm-message");
08730                if (!res)
08731                   res = ast_play_and_wait(chan, "vm-Olds");
08732             } else {
08733                res = say_and_wait(chan, vms->oldmessages, chan->language);
08734                if (!res)
08735                   res = ast_play_and_wait(chan, "vm-messages");
08736                if (!res)
08737                   res = ast_play_and_wait(chan, "vm-Old");
08738             }
08739          }
08740       }
08741    }
08742 return res;
08743 }
08744 
08745 /* BRAZILIAN PORTUGUESE syntax */
08746 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08747    /* Introduce messages they have */
08748    int res;
08749    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08750       res = ast_play_and_wait(chan, "vm-nomessages");
08751       return res;
08752    } else {
08753       res = ast_play_and_wait(chan, "vm-youhave");
08754    }
08755    if (vms->newmessages) {
08756       if (!res)
08757          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08758       if ((vms->newmessages == 1)) {
08759          if (!res)
08760             res = ast_play_and_wait(chan, "vm-message");
08761          if (!res)
08762             res = ast_play_and_wait(chan, "vm-INBOXs");
08763       } else {
08764          if (!res)
08765             res = ast_play_and_wait(chan, "vm-messages");
08766          if (!res)
08767             res = ast_play_and_wait(chan, "vm-INBOX");
08768       }
08769       if (vms->oldmessages && !res)
08770          res = ast_play_and_wait(chan, "vm-and");
08771    }
08772    if (vms->oldmessages) {
08773       if (!res)
08774          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08775       if (vms->oldmessages == 1) {
08776          if (!res)
08777             res = ast_play_and_wait(chan, "vm-message");
08778          if (!res)
08779             res = ast_play_and_wait(chan, "vm-Olds");
08780       } else {
08781          if (!res)
08782             res = ast_play_and_wait(chan, "vm-messages");
08783          if (!res)
08784             res = ast_play_and_wait(chan, "vm-Old");
08785       }
08786    }
08787    return res;
08788 }
08789 
08790 /* FRENCH syntax */
08791 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08792 {
08793    /* Introduce messages they have */
08794    int res;
08795    res = ast_play_and_wait(chan, "vm-youhave");
08796    if (!res) {
08797       if (vms->newmessages) {
08798          res = say_and_wait(chan, vms->newmessages, chan->language);
08799          if (!res)
08800             res = ast_play_and_wait(chan, "vm-INBOX");
08801          if (vms->oldmessages && !res)
08802             res = ast_play_and_wait(chan, "vm-and");
08803          else if (!res) {
08804             if ((vms->newmessages == 1))
08805                res = ast_play_and_wait(chan, "vm-message");
08806             else
08807                res = ast_play_and_wait(chan, "vm-messages");
08808          }
08809             
08810       }
08811       if (!res && vms->oldmessages) {
08812          res = say_and_wait(chan, vms->oldmessages, chan->language);
08813          if (!res)
08814             res = ast_play_and_wait(chan, "vm-Old");
08815          if (!res) {
08816             if (vms->oldmessages == 1)
08817                res = ast_play_and_wait(chan, "vm-message");
08818             else
08819                res = ast_play_and_wait(chan, "vm-messages");
08820          }
08821       }
08822       if (!res) {
08823          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08824             res = ast_play_and_wait(chan, "vm-no");
08825             if (!res)
08826                res = ast_play_and_wait(chan, "vm-messages");
08827          }
08828       }
08829    }
08830    return res;
08831 }
08832 
08833 /* DUTCH syntax */
08834 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08835 {
08836    /* Introduce messages they have */
08837    int res;
08838    res = ast_play_and_wait(chan, "vm-youhave");
08839    if (!res) {
08840       if (vms->newmessages) {
08841          res = say_and_wait(chan, vms->newmessages, chan->language);
08842          if (!res) {
08843             if (vms->newmessages == 1)
08844                res = ast_play_and_wait(chan, "vm-INBOXs");
08845             else
08846                res = ast_play_and_wait(chan, "vm-INBOX");
08847          }
08848          if (vms->oldmessages && !res)
08849             res = ast_play_and_wait(chan, "vm-and");
08850          else if (!res) {
08851             if ((vms->newmessages == 1))
08852                res = ast_play_and_wait(chan, "vm-message");
08853             else
08854                res = ast_play_and_wait(chan, "vm-messages");
08855          }
08856             
08857       }
08858       if (!res && vms->oldmessages) {
08859          res = say_and_wait(chan, vms->oldmessages, chan->language);
08860          if (!res) {
08861             if (vms->oldmessages == 1)
08862                res = ast_play_and_wait(chan, "vm-Olds");
08863             else
08864                res = ast_play_and_wait(chan, "vm-Old");
08865          }
08866          if (!res) {
08867             if (vms->oldmessages == 1)
08868                res = ast_play_and_wait(chan, "vm-message");
08869             else
08870                res = ast_play_and_wait(chan, "vm-messages");
08871          }
08872       }
08873       if (!res) {
08874          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08875             res = ast_play_and_wait(chan, "vm-no");
08876             if (!res)
08877                res = ast_play_and_wait(chan, "vm-messages");
08878          }
08879       }
08880    }
08881    return res;
08882 }
08883 
08884 /* PORTUGUESE syntax */
08885 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08886 {
08887    /* Introduce messages they have */
08888    int res;
08889    res = ast_play_and_wait(chan, "vm-youhave");
08890    if (!res) {
08891       if (vms->newmessages) {
08892          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08893          if (!res) {
08894             if ((vms->newmessages == 1)) {
08895                res = ast_play_and_wait(chan, "vm-message");
08896                if (!res)
08897                   res = ast_play_and_wait(chan, "vm-INBOXs");
08898             } else {
08899                res = ast_play_and_wait(chan, "vm-messages");
08900                if (!res)
08901                   res = ast_play_and_wait(chan, "vm-INBOX");
08902             }
08903          }
08904          if (vms->oldmessages && !res)
08905             res = ast_play_and_wait(chan, "vm-and");
08906       }
08907       if (!res && vms->oldmessages) {
08908          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08909          if (!res) {
08910             if (vms->oldmessages == 1) {
08911                res = ast_play_and_wait(chan, "vm-message");
08912                if (!res)
08913                   res = ast_play_and_wait(chan, "vm-Olds");
08914             } else {
08915                res = ast_play_and_wait(chan, "vm-messages");
08916                if (!res)
08917                   res = ast_play_and_wait(chan, "vm-Old");
08918             }
08919          }
08920       }
08921       if (!res) {
08922          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08923             res = ast_play_and_wait(chan, "vm-no");
08924             if (!res)
08925                res = ast_play_and_wait(chan, "vm-messages");
08926          }
08927       }
08928    }
08929    return res;
08930 }
08931 
08932 
08933 /* CZECH syntax */
08934 /* in czech there must be declension of word new and message
08935  * czech        : english        : czech      : english
08936  * --------------------------------------------------------
08937  * vm-youhave   : you have 
08938  * vm-novou     : one new        : vm-zpravu  : message
08939  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08940  * vm-novych    : 5-infinite new : vm-zprav   : messages
08941  * vm-starou   : one old
08942  * vm-stare     : 2-4 old 
08943  * vm-starych   : 5-infinite old
08944  * jednu        : one   - falling 4. 
08945  * vm-no        : no  ( no messages )
08946  */
08947 
08948 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08949 {
08950    int res;
08951    res = ast_play_and_wait(chan, "vm-youhave");
08952    if (!res) {
08953       if (vms->newmessages) {
08954          if (vms->newmessages == 1) {
08955             res = ast_play_and_wait(chan, "digits/jednu");
08956          } else {
08957             res = say_and_wait(chan, vms->newmessages, chan->language);
08958          }
08959          if (!res) {
08960             if ((vms->newmessages == 1))
08961                res = ast_play_and_wait(chan, "vm-novou");
08962             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08963                res = ast_play_and_wait(chan, "vm-nove");
08964             if (vms->newmessages > 4)
08965                res = ast_play_and_wait(chan, "vm-novych");
08966          }
08967          if (vms->oldmessages && !res)
08968             res = ast_play_and_wait(chan, "vm-and");
08969          else if (!res) {
08970             if ((vms->newmessages == 1))
08971                res = ast_play_and_wait(chan, "vm-zpravu");
08972             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08973                res = ast_play_and_wait(chan, "vm-zpravy");
08974             if (vms->newmessages > 4)
08975                res = ast_play_and_wait(chan, "vm-zprav");
08976          }
08977       }
08978       if (!res && vms->oldmessages) {
08979          res = say_and_wait(chan, vms->oldmessages, chan->language);
08980          if (!res) {
08981             if ((vms->oldmessages == 1))
08982                res = ast_play_and_wait(chan, "vm-starou");
08983             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08984                res = ast_play_and_wait(chan, "vm-stare");
08985             if (vms->oldmessages > 4)
08986                res = ast_play_and_wait(chan, "vm-starych");
08987          }
08988          if (!res) {
08989             if ((vms->oldmessages == 1))
08990                res = ast_play_and_wait(chan, "vm-zpravu");
08991             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08992                res = ast_play_and_wait(chan, "vm-zpravy");
08993             if (vms->oldmessages > 4)
08994                res = ast_play_and_wait(chan, "vm-zprav");
08995          }
08996       }
08997       if (!res) {
08998          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08999             res = ast_play_and_wait(chan, "vm-no");
09000             if (!res)
09001                res = ast_play_and_wait(chan, "vm-zpravy");
09002          }
09003       }
09004    }
09005    return res;
09006 }
09007 
09008 /* CHINESE (Taiwan) syntax */
09009 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
09010 {
09011    int res;
09012    /* Introduce messages they have */
09013    res = ast_play_and_wait(chan, "vm-you");
09014 
09015    if (!res && vms->newmessages) {
09016       res = ast_play_and_wait(chan, "vm-have");
09017       if (!res)
09018          res = say_and_wait(chan, vms->newmessages, chan->language);
09019       if (!res)
09020          res = ast_play_and_wait(chan, "vm-tong");
09021       if (!res)
09022          res = ast_play_and_wait(chan, "vm-INBOX");
09023       if (vms->oldmessages && !res)
09024          res = ast_play_and_wait(chan, "vm-and");
09025       else if (!res) 
09026          res = ast_play_and_wait(chan, "vm-messages");
09027    }
09028    if (!res && vms->oldmessages) {
09029       res = ast_play_and_wait(chan, "vm-have");
09030       if (!res)
09031          res = say_and_wait(chan, vms->oldmessages, chan->language);
09032       if (!res)
09033          res = ast_play_and_wait(chan, "vm-tong");
09034       if (!res)
09035          res = ast_play_and_wait(chan, "vm-Old");
09036       if (!res)
09037          res = ast_play_and_wait(chan, "vm-messages");
09038    }
09039    if (!res && !vms->oldmessages && !vms->newmessages) {
09040       res = ast_play_and_wait(chan, "vm-haveno");
09041       if (!res)
09042          res = ast_play_and_wait(chan, "vm-messages");
09043    }
09044    return res;
09045 }
09046 
09047 /* Vietnamese syntax */
09048 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09049 {
09050    int res;
09051 
09052    /* Introduce messages they have */
09053    res = ast_play_and_wait(chan, "vm-youhave");
09054    if (!res) {
09055       if (vms->newmessages) {
09056          res = say_and_wait(chan, vms->newmessages, chan->language);
09057          if (!res)
09058             res = ast_play_and_wait(chan, "vm-INBOX");
09059          if (vms->oldmessages && !res)
09060             res = ast_play_and_wait(chan, "vm-and");
09061       }
09062       if (!res && vms->oldmessages) {
09063          res = say_and_wait(chan, vms->oldmessages, chan->language);
09064          if (!res)
09065             res = ast_play_and_wait(chan, "vm-Old");        
09066       }
09067       if (!res) {
09068          if (!vms->oldmessages && !vms->newmessages) {
09069             res = ast_play_and_wait(chan, "vm-no");
09070             if (!res)
09071                res = ast_play_and_wait(chan, "vm-message");
09072          }
09073       }
09074    }
09075    return res;
09076 }
09077 
09078 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09079 {
09080    char prefile[256];
09081    
09082    /* Notify the user that the temp greeting is set and give them the option to remove it */
09083    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09084    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09085       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09086       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09087          ast_play_and_wait(chan, "vm-tempgreetactive");
09088       }
09089       DISPOSE(prefile, -1);
09090    }
09091 
09092    /* Play voicemail intro - syntax is different for different languages */
09093    if (0) {
09094       return 0;
09095    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09096       return vm_intro_cs(chan, vms);
09097    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09098       static int deprecation_warning = 0;
09099       if (deprecation_warning++ % 10 == 0) {
09100          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09101       }
09102       return vm_intro_cs(chan, vms);
09103    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09104       return vm_intro_de(chan, vms);
09105    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09106       return vm_intro_es(chan, vms);
09107    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09108       return vm_intro_fr(chan, vms);
09109    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09110       return vm_intro_gr(chan, vms);
09111    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09112       return vm_intro_he(chan, vms);
09113    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09114       return vm_intro_it(chan, vms);
09115    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09116       return vm_intro_nl(chan, vms);
09117    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09118       return vm_intro_no(chan, vms);
09119    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09120       return vm_intro_pl(chan, vms);
09121    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09122       return vm_intro_pt_BR(chan, vms);
09123    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09124       return vm_intro_pt(chan, vms);
09125    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09126       return vm_intro_multilang(chan, vms, "n");
09127    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09128       return vm_intro_se(chan, vms);
09129    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09130       return vm_intro_multilang(chan, vms, "n");
09131    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09132       return vm_intro_vi(chan, vms);
09133    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09134       return vm_intro_zh(chan, vms);
09135    } else {                                             /* Default to ENGLISH */
09136       return vm_intro_en(chan, vms);
09137    }
09138 }
09139 
09140 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09141 {
09142    int res = 0;
09143    /* Play instructions and wait for new command */
09144    while (!res) {
09145       if (vms->starting) {
09146          if (vms->lastmsg > -1) {
09147             if (skipadvanced)
09148                res = ast_play_and_wait(chan, "vm-onefor-full");
09149             else
09150                res = ast_play_and_wait(chan, "vm-onefor");
09151             if (!res)
09152                res = vm_play_folder_name(chan, vms->vmbox);
09153          }
09154          if (!res) {
09155             if (skipadvanced)
09156                res = ast_play_and_wait(chan, "vm-opts-full");
09157             else
09158                res = ast_play_and_wait(chan, "vm-opts");
09159          }
09160       } else {
09161          /* Added for additional help */
09162          if (skipadvanced) {
09163             res = ast_play_and_wait(chan, "vm-onefor-full");
09164             if (!res)
09165                res = vm_play_folder_name(chan, vms->vmbox);
09166             res = ast_play_and_wait(chan, "vm-opts-full");
09167          }
09168          /* Logic:
09169           * If the current message is not the first OR
09170           * if we're listening to the first new message and there are
09171           * also urgent messages, then prompt for navigation to the
09172           * previous message
09173           */
09174          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09175             res = ast_play_and_wait(chan, "vm-prev");
09176          }
09177          if (!res && !skipadvanced)
09178             res = ast_play_and_wait(chan, "vm-advopts");
09179          if (!res)
09180             res = ast_play_and_wait(chan, "vm-repeat");
09181          /* Logic:
09182           * If we're not listening to the last message OR
09183           * we're listening to the last urgent message and there are
09184           * also new non-urgent messages, then prompt for navigation
09185           * to the next message
09186           */
09187          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09188             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09189             res = ast_play_and_wait(chan, "vm-next");
09190          }
09191          if (!res) {
09192             int curmsg_deleted;
09193 #ifdef IMAP_STORAGE
09194             ast_mutex_lock(&vms->lock);
09195 #endif
09196             curmsg_deleted = vms->deleted[vms->curmsg];
09197 #ifdef IMAP_STORAGE
09198             ast_mutex_unlock(&vms->lock);
09199 #endif
09200             if (!curmsg_deleted) {
09201                res = ast_play_and_wait(chan, "vm-delete");
09202             } else {
09203                res = ast_play_and_wait(chan, "vm-undelete");
09204             }
09205             if (!res) {
09206                res = ast_play_and_wait(chan, "vm-toforward");
09207             }
09208             if (!res) {
09209                res = ast_play_and_wait(chan, "vm-savemessage");
09210             }
09211          }
09212       }
09213       if (!res) {
09214          res = ast_play_and_wait(chan, "vm-helpexit");
09215       }
09216       if (!res)
09217          res = ast_waitfordigit(chan, 6000);
09218       if (!res) {
09219          vms->repeats++;
09220          if (vms->repeats > 2) {
09221             res = 't';
09222          }
09223       }
09224    }
09225    return res;
09226 }
09227 
09228 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09229 {
09230    int res = 0;
09231    /* Play instructions and wait for new command */
09232    while (!res) {
09233       if (vms->lastmsg > -1) {
09234          res = ast_play_and_wait(chan, "vm-listen");
09235          if (!res)
09236             res = vm_play_folder_name(chan, vms->vmbox);
09237          if (!res)
09238             res = ast_play_and_wait(chan, "press");
09239          if (!res)
09240             res = ast_play_and_wait(chan, "digits/1");
09241       }
09242       if (!res)
09243          res = ast_play_and_wait(chan, "vm-opts");
09244       if (!res) {
09245          vms->starting = 0;
09246          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09247       }
09248    }
09249    return res;
09250 }
09251 
09252 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09253 {
09254    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09255       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09256    } else {             /* Default to ENGLISH */
09257       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09258    }
09259 }
09260 
09261 
09262 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09263 {
09264    int cmd = 0;
09265    int duration = 0;
09266    int tries = 0;
09267    char newpassword[80] = "";
09268    char newpassword2[80] = "";
09269    char prefile[PATH_MAX] = "";
09270    unsigned char buf[256];
09271    int bytes = 0;
09272 
09273    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09274    if (ast_adsi_available(chan)) {
09275       bytes += adsi_logo(buf + bytes);
09276       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09277       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09278       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09279       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09280       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09281    }
09282 
09283    /* If forcename is set, have the user record their name */
09284    if (ast_test_flag(vmu, VM_FORCENAME)) {
09285       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09286       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09287          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09288          if (cmd < 0 || cmd == 't' || cmd == '#')
09289             return cmd;
09290       }
09291    }
09292 
09293    /* If forcegreetings is set, have the user record their greetings */
09294    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09295       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09296       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09297          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09298          if (cmd < 0 || cmd == 't' || cmd == '#')
09299             return cmd;
09300       }
09301 
09302       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09303       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09304          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09305          if (cmd < 0 || cmd == 't' || cmd == '#')
09306             return cmd;
09307       }
09308    }
09309 
09310    /*
09311     * Change the password last since new users will be able to skip over any steps this one comes before
09312     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09313     */
09314    for (;;) {
09315       newpassword[1] = '\0';
09316       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09317       if (cmd == '#')
09318          newpassword[0] = '\0';
09319       if (cmd < 0 || cmd == 't' || cmd == '#')
09320          return cmd;
09321       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09322       if (cmd < 0 || cmd == 't' || cmd == '#')
09323          return cmd;
09324       cmd = check_password(vmu, newpassword); /* perform password validation */
09325       if (cmd != 0) {
09326          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09327          cmd = ast_play_and_wait(chan, vm_invalid_password);
09328       } else {
09329          newpassword2[1] = '\0';
09330          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09331          if (cmd == '#')
09332             newpassword2[0] = '\0';
09333          if (cmd < 0 || cmd == 't' || cmd == '#')
09334             return cmd;
09335          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09336          if (cmd < 0 || cmd == 't' || cmd == '#')
09337             return cmd;
09338          if (!strcmp(newpassword, newpassword2))
09339             break;
09340          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09341          cmd = ast_play_and_wait(chan, vm_mismatch);
09342       }
09343       if (++tries == 3)
09344          return -1;
09345       if (cmd != 0) {
09346          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09347       }
09348    }
09349    if (pwdchange & PWDCHANGE_INTERNAL)
09350       vm_change_password(vmu, newpassword);
09351    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09352       vm_change_password_shell(vmu, newpassword);
09353 
09354    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09355    cmd = ast_play_and_wait(chan, vm_passchanged);
09356 
09357    return cmd;
09358 }
09359 
09360 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09361 {
09362    int cmd = 0;
09363    int retries = 0;
09364    int duration = 0;
09365    char newpassword[80] = "";
09366    char newpassword2[80] = "";
09367    char prefile[PATH_MAX] = "";
09368    unsigned char buf[256];
09369    int bytes = 0;
09370 
09371    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09372    if (ast_adsi_available(chan)) {
09373       bytes += adsi_logo(buf + bytes);
09374       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09375       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09376       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09377       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09378       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09379    }
09380    while ((cmd >= 0) && (cmd != 't')) {
09381       if (cmd)
09382          retries = 0;
09383       switch (cmd) {
09384       case '1': /* Record your unavailable message */
09385          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09386          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09387          break;
09388       case '2':  /* Record your busy message */
09389          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09390          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09391          break;
09392       case '3': /* Record greeting */
09393          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09394          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09395          break;
09396       case '4':  /* manage the temporary greeting */
09397          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09398          break;
09399       case '5': /* change password */
09400          if (vmu->password[0] == '-') {
09401             cmd = ast_play_and_wait(chan, "vm-no");
09402             break;
09403          }
09404          newpassword[1] = '\0';
09405          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09406          if (cmd == '#')
09407             newpassword[0] = '\0';
09408          else {
09409             if (cmd < 0)
09410                break;
09411             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09412                break;
09413             }
09414          }
09415          cmd = check_password(vmu, newpassword); /* perform password validation */
09416          if (cmd != 0) {
09417             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09418             cmd = ast_play_and_wait(chan, vm_invalid_password);
09419             if (!cmd) {
09420                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09421             }
09422             break;
09423          }
09424          newpassword2[1] = '\0';
09425          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09426          if (cmd == '#')
09427             newpassword2[0] = '\0';
09428          else {
09429             if (cmd < 0)
09430                break;
09431 
09432             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09433                break;
09434             }
09435          }
09436          if (strcmp(newpassword, newpassword2)) {
09437             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09438             cmd = ast_play_and_wait(chan, vm_mismatch);
09439             if (!cmd) {
09440                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09441             }
09442             break;
09443          }
09444 
09445          if (pwdchange & PWDCHANGE_INTERNAL) {
09446             vm_change_password(vmu, newpassword);
09447          }
09448          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09449             vm_change_password_shell(vmu, newpassword);
09450          }
09451 
09452          ast_debug(1, "User %s set password to %s of length %d\n",
09453             vms->username, newpassword, (int) strlen(newpassword));
09454          cmd = ast_play_and_wait(chan, vm_passchanged);
09455          break;
09456       case '*': 
09457          cmd = 't';
09458          break;
09459       default: 
09460          cmd = 0;
09461          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09462          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09463          if (ast_fileexists(prefile, NULL, NULL)) {
09464             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09465          }
09466          DISPOSE(prefile, -1);
09467          if (!cmd) {
09468             cmd = ast_play_and_wait(chan, "vm-options");
09469          }
09470          if (!cmd) {
09471             cmd = ast_waitfordigit(chan, 6000);
09472          }
09473          if (!cmd) {
09474             retries++;
09475          }
09476          if (retries > 3) {
09477             cmd = 't';
09478          }
09479          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09480       }
09481    }
09482    if (cmd == 't')
09483       cmd = 0;
09484    return cmd;
09485 }
09486 
09487 /*!
09488  * \brief The handler for 'record a temporary greeting'. 
09489  * \param chan
09490  * \param vmu
09491  * \param vms
09492  * \param fmtc
09493  * \param record_gain
09494  *
09495  * This is option 4 from the mailbox options menu.
09496  * This function manages the following promptings:
09497  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09498  * 2: remove (delete) the temporary greeting.
09499  * *: return to the main menu.
09500  *
09501  * \return zero on success, -1 on error.
09502  */
09503 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09504 {
09505    int cmd = 0;
09506    int retries = 0;
09507    int duration = 0;
09508    char prefile[PATH_MAX] = "";
09509    unsigned char buf[256];
09510    int bytes = 0;
09511 
09512    if (ast_adsi_available(chan)) {
09513       bytes += adsi_logo(buf + bytes);
09514       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09515       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09516       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09517       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09518       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09519    }
09520 
09521    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09522    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09523    while ((cmd >= 0) && (cmd != 't')) {
09524       if (cmd)
09525          retries = 0;
09526       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09527       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09528          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09529          if (cmd == -1) {
09530             break;
09531          }
09532          cmd = 't';  
09533       } else {
09534          switch (cmd) {
09535          case '1':
09536             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09537             break;
09538          case '2':
09539             DELETE(prefile, -1, prefile, vmu);
09540             ast_play_and_wait(chan, "vm-tempremoved");
09541             cmd = 't';  
09542             break;
09543          case '*': 
09544             cmd = 't';
09545             break;
09546          default:
09547             cmd = ast_play_and_wait(chan,
09548                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09549                   "vm-tempgreeting2" : "vm-tempgreeting");
09550             if (!cmd) {
09551                cmd = ast_waitfordigit(chan, 6000);
09552             }
09553             if (!cmd) {
09554                retries++;
09555             }
09556             if (retries > 3) {
09557                cmd = 't';
09558             }
09559             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09560          }
09561       }
09562       DISPOSE(prefile, -1);
09563    }
09564    if (cmd == 't')
09565       cmd = 0;
09566    return cmd;
09567 }
09568 
09569 /*!
09570  * \brief Greek syntax for 'You have N messages' greeting.
09571  * \param chan
09572  * \param vms
09573  * \param vmu
09574  *
09575  * \return zero on success, -1 on error.
09576  */   
09577 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09578 {
09579    int cmd = 0;
09580 
09581    if (vms->lastmsg > -1) {
09582       cmd = play_message(chan, vmu, vms);
09583    } else {
09584       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09585       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09586          if (!cmd) {
09587             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09588             cmd = ast_play_and_wait(chan, vms->fn);
09589          }
09590          if (!cmd)
09591             cmd = ast_play_and_wait(chan, "vm-messages");
09592       } else {
09593          if (!cmd)
09594             cmd = ast_play_and_wait(chan, "vm-messages");
09595          if (!cmd) {
09596             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09597             cmd = ast_play_and_wait(chan, vms->fn);
09598          }
09599       }
09600    } 
09601    return cmd;
09602 }
09603 
09604 /* Hebrew Syntax */
09605 static int vm_browse_messages_he(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       if (!strcasecmp(vms->fn, "INBOX")) {
09613          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09614       } else {
09615          cmd = ast_play_and_wait(chan, "vm-nomessages");
09616       }
09617    }
09618    return cmd;
09619 }
09620 
09621 /*! 
09622  * \brief Default English syntax for 'You have N messages' greeting.
09623  * \param chan
09624  * \param vms
09625  * \param vmu
09626  *
09627  * \return zero on success, -1 on error.
09628  */
09629 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09630 {
09631    int cmd = 0;
09632 
09633    if (vms->lastmsg > -1) {
09634       cmd = play_message(chan, vmu, vms);
09635    } else {
09636       cmd = ast_play_and_wait(chan, "vm-youhave");
09637       if (!cmd) 
09638          cmd = ast_play_and_wait(chan, "vm-no");
09639       if (!cmd) {
09640          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09641          cmd = ast_play_and_wait(chan, vms->fn);
09642       }
09643       if (!cmd)
09644          cmd = ast_play_and_wait(chan, "vm-messages");
09645    }
09646    return cmd;
09647 }
09648 
09649 /*! 
09650  *\brief Italian syntax for 'You have N messages' greeting.
09651  * \param chan
09652  * \param vms
09653  * \param vmu
09654  *
09655  * \return zero on success, -1 on error.
09656  */
09657 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09658 {
09659    int cmd;
09660 
09661    if (vms->lastmsg > -1) {
09662       cmd = play_message(chan, vmu, vms);
09663    } else {
09664       cmd = ast_play_and_wait(chan, "vm-no");
09665       if (!cmd)
09666          cmd = ast_play_and_wait(chan, "vm-message");
09667       if (!cmd) {
09668          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09669          cmd = ast_play_and_wait(chan, vms->fn);
09670       }
09671    }
09672    return cmd;
09673 }
09674 
09675 /*! 
09676  * \brief Spanish syntax for 'You have N messages' greeting.
09677  * \param chan
09678  * \param vms
09679  * \param vmu
09680  *
09681  * \return zero on success, -1 on error.
09682  */
09683 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09684 {
09685    int cmd;
09686 
09687    if (vms->lastmsg > -1) {
09688       cmd = play_message(chan, vmu, vms);
09689    } else {
09690       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09691       if (!cmd)
09692          cmd = ast_play_and_wait(chan, "vm-messages");
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    }
09698    return cmd;
09699 }
09700 
09701 /*! 
09702  * \brief Portuguese syntax for 'You have N messages' greeting.
09703  * \param chan
09704  * \param vms
09705  * \param vmu
09706  *
09707  * \return zero on success, -1 on error.
09708  */
09709 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09710 {
09711    int cmd;
09712 
09713    if (vms->lastmsg > -1) {
09714       cmd = play_message(chan, vmu, vms);
09715    } else {
09716       cmd = ast_play_and_wait(chan, "vm-no");
09717       if (!cmd) {
09718          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09719          cmd = ast_play_and_wait(chan, vms->fn);
09720       }
09721       if (!cmd)
09722          cmd = ast_play_and_wait(chan, "vm-messages");
09723    }
09724    return cmd;
09725 }
09726 
09727 /*! 
09728  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
09729  * \param chan
09730  * \param vms
09731  * \param vmu
09732  *
09733  * \return zero on success, -1 on error.
09734  */
09735 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09736 {
09737    int cmd;
09738 
09739    if (vms->lastmsg > -1) {
09740       cmd = play_message(chan, vmu, vms);
09741    } else {
09742       cmd = ast_play_and_wait(chan, "vm-you");
09743       if (!cmd) 
09744          cmd = ast_play_and_wait(chan, "vm-haveno");
09745       if (!cmd)
09746          cmd = ast_play_and_wait(chan, "vm-messages");
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 Vietnamese syntax for 'You have N messages' greeting.
09757  * \param chan
09758  * \param vms
09759  * \param vmu
09760  *
09761  * \return zero on success, -1 on error.
09762  */
09763 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09764 {
09765    int cmd = 0;
09766 
09767    if (vms->lastmsg > -1) {
09768       cmd = play_message(chan, vmu, vms);
09769    } else {
09770       cmd = ast_play_and_wait(chan, "vm-no");
09771       if (!cmd) {
09772          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09773          cmd = ast_play_and_wait(chan, vms->fn);
09774       }
09775    }
09776    return cmd;
09777 }
09778 
09779 /*!
09780  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09781  * \param chan The channel for the current user. We read the language property from this.
09782  * \param vms passed into the language-specific vm_browse_messages function.
09783  * \param vmu passed into the language-specific vm_browse_messages function.
09784  * 
09785  * The method to be invoked is determined by the value of language code property in the user's channel.
09786  * The default (when unable to match) is to use english.
09787  *
09788  * \return zero on success, -1 on error.
09789  */
09790 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09791 {
09792    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09793       return vm_browse_messages_es(chan, vms, vmu);
09794    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09795       return vm_browse_messages_gr(chan, vms, vmu);
09796    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09797       return vm_browse_messages_he(chan, vms, vmu);
09798    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09799       return vm_browse_messages_it(chan, vms, vmu);
09800    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09801       return vm_browse_messages_pt(chan, vms, vmu);
09802    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09803       return vm_browse_messages_vi(chan, vms, vmu);
09804    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09805       return vm_browse_messages_zh(chan, vms, vmu);
09806    } else {                                             /* Default to English syntax */
09807       return vm_browse_messages_en(chan, vms, vmu);
09808    }
09809 }
09810 
09811 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09812          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09813          int skipuser, int max_logins, int silent)
09814 {
09815    int useadsi = 0, valid = 0, logretries = 0;
09816    char password[AST_MAX_EXTENSION]="", *passptr;
09817    struct ast_vm_user vmus, *vmu = NULL;
09818 
09819    /* If ADSI is supported, setup login screen */
09820    adsi_begin(chan, &useadsi);
09821    if (!skipuser && useadsi)
09822       adsi_login(chan);
09823    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09824    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09825       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09826       return -1;
09827    }
09828 
09829    /* Authenticate them and get their mailbox/password */
09830 
09831    while (!valid && (logretries < max_logins)) {
09832       /* Prompt for, and read in the username */
09833       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09834          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09835          return -1;
09836       }
09837       if (ast_strlen_zero(mailbox)) {
09838          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09839             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09840          } else {
09841             ast_verb(3, "Username not entered\n"); 
09842             return -1;
09843          }
09844       } else if (mailbox[0] == '*') {
09845          /* user entered '*' */
09846          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09847          if (ast_exists_extension(chan, chan->context, "a", 1,
09848             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09849             return -1;
09850          }
09851          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09852          mailbox[0] = '\0';
09853       }
09854 
09855       if (useadsi)
09856          adsi_password(chan);
09857 
09858       if (!ast_strlen_zero(prefix)) {
09859          char fullusername[80] = "";
09860          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09861          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09862          ast_copy_string(mailbox, fullusername, mailbox_size);
09863       }
09864 
09865       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09866       vmu = find_user(&vmus, context, mailbox);
09867       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09868          /* saved password is blank, so don't bother asking */
09869          password[0] = '\0';
09870       } else {
09871          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09872          if (ast_streamfile(chan, vm_password, chan->language)) {
09873             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09874             return -1;
09875          }
09876          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09877             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09878             return -1;
09879          } else if (password[0] == '*') {
09880             /* user entered '*' */
09881             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09882             if (ast_exists_extension(chan, chan->context, "a", 1,
09883                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09884                mailbox[0] = '*';
09885                return -1;
09886             }
09887             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09888             mailbox[0] = '\0';
09889             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09890             vmu = NULL;
09891          }
09892       }
09893 
09894       if (vmu) {
09895          passptr = vmu->password;
09896          if (passptr[0] == '-') passptr++;
09897       }
09898       if (vmu && !strcmp(passptr, password))
09899          valid++;
09900       else {
09901          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09902          if (!ast_strlen_zero(prefix))
09903             mailbox[0] = '\0';
09904       }
09905       logretries++;
09906       if (!valid) {
09907          if (skipuser || logretries >= max_logins) {
09908             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09909             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09910                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09911                return -1;
09912             }
09913          } else {
09914             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09915             if (useadsi)
09916                adsi_login(chan);
09917             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09918                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09919                return -1;
09920             }
09921          }
09922          if (ast_waitstream(chan, "")) /* Channel is hung up */
09923             return -1;
09924       }
09925    }
09926    if (!valid && (logretries >= max_logins)) {
09927       ast_stopstream(chan);
09928       ast_play_and_wait(chan, "vm-goodbye");
09929       return -1;
09930    }
09931    if (vmu && !skipuser) {
09932       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09933    }
09934    return 0;
09935 }
09936 
09937 static int vm_execmain(struct ast_channel *chan, const char *data)
09938 {
09939    /* XXX This is, admittedly, some pretty horrendous code.  For some
09940       reason it just seemed a lot easier to do with GOTO's.  I feel
09941       like I'm back in my GWBASIC days. XXX */
09942    int res = -1;
09943    int cmd = 0;
09944    int valid = 0;
09945    char prefixstr[80] ="";
09946    char ext_context[256]="";
09947    int box;
09948    int useadsi = 0;
09949    int skipuser = 0;
09950    struct vm_state vms;
09951    struct ast_vm_user *vmu = NULL, vmus;
09952    char *context = NULL;
09953    int silentexit = 0;
09954    struct ast_flags flags = { 0 };
09955    signed char record_gain = 0;
09956    int play_auto = 0;
09957    int play_folder = 0;
09958    int in_urgent = 0;
09959 #ifdef IMAP_STORAGE
09960    int deleted = 0;
09961 #endif
09962 
09963    /* Add the vm_state to the active list and keep it active */
09964    memset(&vms, 0, sizeof(vms));
09965 
09966    vms.lastmsg = -1;
09967 
09968    memset(&vmus, 0, sizeof(vmus));
09969 
09970    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09971    if (chan->_state != AST_STATE_UP) {
09972       ast_debug(1, "Before ast_answer\n");
09973       ast_answer(chan);
09974    }
09975 
09976    if (!ast_strlen_zero(data)) {
09977       char *opts[OPT_ARG_ARRAY_SIZE];
09978       char *parse;
09979       AST_DECLARE_APP_ARGS(args,
09980          AST_APP_ARG(argv0);
09981          AST_APP_ARG(argv1);
09982       );
09983 
09984       parse = ast_strdupa(data);
09985 
09986       AST_STANDARD_APP_ARGS(args, parse);
09987 
09988       if (args.argc == 2) {
09989          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09990             return -1;
09991          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09992             int gain;
09993             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09994                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09995                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09996                   return -1;
09997                } else {
09998                   record_gain = (signed char) gain;
09999                }
10000             } else {
10001                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
10002             }
10003          }
10004          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
10005             play_auto = 1;
10006             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
10007                /* See if it is a folder name first */
10008                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
10009                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
10010                      play_folder = -1;
10011                   }
10012                } else {
10013                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
10014                }
10015             } else {
10016                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
10017             }
10018             if (play_folder > 9 || play_folder < 0) {
10019                ast_log(AST_LOG_WARNING,
10020                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
10021                   opts[OPT_ARG_PLAYFOLDER]);
10022                play_folder = 0;
10023             }
10024          }
10025       } else {
10026          /* old style options parsing */
10027          while (*(args.argv0)) {
10028             if (*(args.argv0) == 's')
10029                ast_set_flag(&flags, OPT_SILENT);
10030             else if (*(args.argv0) == 'p')
10031                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
10032             else 
10033                break;
10034             (args.argv0)++;
10035          }
10036 
10037       }
10038 
10039       valid = ast_test_flag(&flags, OPT_SILENT);
10040 
10041       if ((context = strchr(args.argv0, '@')))
10042          *context++ = '\0';
10043 
10044       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10045          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10046       else
10047          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10048 
10049       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10050          skipuser++;
10051       else
10052          valid = 0;
10053    }
10054 
10055    if (!valid)
10056       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10057 
10058    ast_debug(1, "After vm_authenticate\n");
10059 
10060    if (vms.username[0] == '*') {
10061       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10062 
10063       /* user entered '*' */
10064       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10065          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10066          res = 0; /* prevent hangup */
10067          goto out;
10068       }
10069    }
10070 
10071    if (!res) {
10072       valid = 1;
10073       if (!skipuser)
10074          vmu = &vmus;
10075    } else {
10076       res = 0;
10077    }
10078 
10079    /* If ADSI is supported, setup login screen */
10080    adsi_begin(chan, &useadsi);
10081 
10082    ast_test_suite_assert(valid);
10083    if (!valid) {
10084       goto out;
10085    }
10086    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10087 
10088 #ifdef IMAP_STORAGE
10089    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10090    pthread_setspecific(ts_vmstate.key, &vms);
10091 
10092    vms.interactive = 1;
10093    vms.updated = 1;
10094    if (vmu)
10095       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10096    vmstate_insert(&vms);
10097    init_vm_state(&vms);
10098 #endif
10099    
10100    /* Set language from config to override channel language */
10101    if (!ast_strlen_zero(vmu->language))
10102       ast_string_field_set(chan, language, vmu->language);
10103 
10104    /* Retrieve urgent, old and new message counts */
10105    ast_debug(1, "Before open_mailbox\n");
10106    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10107    if (res < 0)
10108       goto out;
10109    vms.oldmessages = vms.lastmsg + 1;
10110    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10111    /* check INBOX */
10112    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10113    if (res < 0)
10114       goto out;
10115    vms.newmessages = vms.lastmsg + 1;
10116    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10117    /* Start in Urgent */
10118    in_urgent = 1;
10119    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10120    if (res < 0)
10121       goto out;
10122    vms.urgentmessages = vms.lastmsg + 1;
10123    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10124 
10125    /* Select proper mailbox FIRST!! */
10126    if (play_auto) {
10127       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10128       if (vms.urgentmessages) {
10129          in_urgent = 1;
10130          res = open_mailbox(&vms, vmu, 11);
10131       } else {
10132          in_urgent = 0;
10133          res = open_mailbox(&vms, vmu, play_folder);
10134       }
10135       if (res < 0)
10136          goto out;
10137 
10138       /* If there are no new messages, inform the user and hangup */
10139       if (vms.lastmsg == -1) {
10140          in_urgent = 0;
10141          cmd = vm_browse_messages(chan, &vms, vmu);
10142          res = 0;
10143          goto out;
10144       }
10145    } else {
10146       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10147          /* If we only have old messages start here */
10148          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10149          in_urgent = 0;
10150          play_folder = 1;
10151          if (res < 0)
10152             goto out;
10153       } else if (!vms.urgentmessages && vms.newmessages) {
10154          /* If we have new messages but none are urgent */
10155          in_urgent = 0;
10156          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10157          if (res < 0)
10158             goto out;
10159       }
10160    }
10161 
10162    if (useadsi)
10163       adsi_status(chan, &vms);
10164    res = 0;
10165 
10166    /* Check to see if this is a new user */
10167    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10168       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10169       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10170          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10171       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10172       if ((cmd == 't') || (cmd == '#')) {
10173          /* Timeout */
10174          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10175          res = 0;
10176          goto out;
10177       } else if (cmd < 0) {
10178          /* Hangup */
10179          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10180          res = -1;
10181          goto out;
10182       }
10183    }
10184 #ifdef IMAP_STORAGE
10185       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10186       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10187          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10188          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10189       }
10190       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10191       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10192          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10193          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10194       }
10195 #endif
10196 
10197    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10198    if (play_auto) {
10199       cmd = '1';
10200    } else {
10201       cmd = vm_intro(chan, vmu, &vms);
10202    }
10203    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10204 
10205    vms.repeats = 0;
10206    vms.starting = 1;
10207    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10208       /* Run main menu */
10209       switch (cmd) {
10210       case '1': /* First message */
10211          vms.curmsg = 0;
10212          /* Fall through */
10213       case '5': /* Play current message */
10214          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10215          cmd = vm_browse_messages(chan, &vms, vmu);
10216          break;
10217       case '2': /* Change folders */
10218          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10219          if (useadsi)
10220             adsi_folders(chan, 0, "Change to folder...");
10221 
10222          cmd = get_folder2(chan, "vm-changeto", 0);
10223          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10224          if (cmd == '#') {
10225             cmd = 0;
10226          } else if (cmd > 0) {
10227             cmd = cmd - '0';
10228             res = close_mailbox(&vms, vmu);
10229             if (res == ERROR_LOCK_PATH)
10230                goto out;
10231             /* If folder is not urgent, set in_urgent to zero! */
10232             if (cmd != 11) in_urgent = 0;
10233             res = open_mailbox(&vms, vmu, cmd);
10234             if (res < 0)
10235                goto out;
10236             play_folder = cmd;
10237             cmd = 0;
10238          }
10239          if (useadsi)
10240             adsi_status2(chan, &vms);
10241 
10242          if (!cmd) {
10243             cmd = vm_play_folder_name(chan, vms.vmbox);
10244          }
10245 
10246          vms.starting = 1;
10247          vms.curmsg = 0;
10248          break;
10249       case '3': /* Advanced options */
10250          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10251          cmd = 0;
10252          vms.repeats = 0;
10253          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10254             switch (cmd) {
10255             case '1': /* Reply */
10256                if (vms.lastmsg > -1 && !vms.starting) {
10257                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10258                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10259                      res = cmd;
10260                      goto out;
10261                   }
10262                } else {
10263                   cmd = ast_play_and_wait(chan, "vm-sorry");
10264                }
10265                cmd = 't';
10266                break;
10267             case '2': /* Callback */
10268                if (!vms.starting)
10269                   ast_verb(3, "Callback Requested\n");
10270                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10271                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10272                   if (cmd == 9) {
10273                      silentexit = 1;
10274                      goto out;
10275                   } else if (cmd == ERROR_LOCK_PATH) {
10276                      res = cmd;
10277                      goto out;
10278                   }
10279                } else {
10280                   cmd = ast_play_and_wait(chan, "vm-sorry");
10281                }
10282                cmd = 't';
10283                break;
10284             case '3': /* Envelope */
10285                if (vms.lastmsg > -1 && !vms.starting) {
10286                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10287                   if (cmd == ERROR_LOCK_PATH) {
10288                      res = cmd;
10289                      goto out;
10290                   }
10291                } else {
10292                   cmd = ast_play_and_wait(chan, "vm-sorry");
10293                }
10294                cmd = 't';
10295                break;
10296             case '4': /* Dialout */
10297                if (!ast_strlen_zero(vmu->dialout)) {
10298                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10299                   if (cmd == 9) {
10300                      silentexit = 1;
10301                      goto out;
10302                   }
10303                } else {
10304                   cmd = ast_play_and_wait(chan, "vm-sorry");
10305                }
10306                cmd = 't';
10307                break;
10308 
10309             case '5': /* Leave VoiceMail */
10310                if (ast_test_flag(vmu, VM_SVMAIL)) {
10311                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10312                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10313                      res = cmd;
10314                      goto out;
10315                   }
10316                } else {
10317                   cmd = ast_play_and_wait(chan, "vm-sorry");
10318                }
10319                cmd = 't';
10320                break;
10321 
10322             case '*': /* Return to main menu */
10323                cmd = 't';
10324                break;
10325 
10326             default:
10327                cmd = 0;
10328                if (!vms.starting) {
10329                   cmd = ast_play_and_wait(chan, "vm-toreply");
10330                }
10331                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10332                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10333                }
10334                if (!cmd && !vms.starting) {
10335                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10336                }
10337                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10338                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10339                }
10340                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10341                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10342                }
10343                if (!cmd) {
10344                   cmd = ast_play_and_wait(chan, "vm-starmain");
10345                }
10346                if (!cmd) {
10347                   cmd = ast_waitfordigit(chan, 6000);
10348                }
10349                if (!cmd) {
10350                   vms.repeats++;
10351                }
10352                if (vms.repeats > 3) {
10353                   cmd = 't';
10354                }
10355                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10356             }
10357          }
10358          if (cmd == 't') {
10359             cmd = 0;
10360             vms.repeats = 0;
10361          }
10362          break;
10363       case '4': /* Go to the previous message */
10364          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10365          if (vms.curmsg > 0) {
10366             vms.curmsg--;
10367             cmd = play_message(chan, vmu, &vms);
10368          } else {
10369             /* Check if we were listening to new
10370                messages.  If so, go to Urgent messages
10371                instead of saying "no more messages"
10372             */
10373             if (in_urgent == 0 && vms.urgentmessages > 0) {
10374                /* Check for Urgent messages */
10375                in_urgent = 1;
10376                res = close_mailbox(&vms, vmu);
10377                if (res == ERROR_LOCK_PATH)
10378                   goto out;
10379                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10380                if (res < 0)
10381                   goto out;
10382                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10383                vms.curmsg = vms.lastmsg;
10384                if (vms.lastmsg < 0) {
10385                   cmd = ast_play_and_wait(chan, "vm-nomore");
10386                }
10387             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10388                vms.curmsg = vms.lastmsg;
10389                cmd = play_message(chan, vmu, &vms);
10390             } else {
10391                cmd = ast_play_and_wait(chan, "vm-nomore");
10392             }
10393          }
10394          break;
10395       case '6': /* Go to the next message */
10396          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10397          if (vms.curmsg < vms.lastmsg) {
10398             vms.curmsg++;
10399             cmd = play_message(chan, vmu, &vms);
10400          } else {
10401             if (in_urgent && vms.newmessages > 0) {
10402                /* Check if we were listening to urgent
10403                 * messages.  If so, go to regular new messages
10404                 * instead of saying "no more messages"
10405                 */
10406                in_urgent = 0;
10407                res = close_mailbox(&vms, vmu);
10408                if (res == ERROR_LOCK_PATH)
10409                   goto out;
10410                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10411                if (res < 0)
10412                   goto out;
10413                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10414                vms.curmsg = -1;
10415                if (vms.lastmsg < 0) {
10416                   cmd = ast_play_and_wait(chan, "vm-nomore");
10417                }
10418             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10419                vms.curmsg = 0;
10420                cmd = play_message(chan, vmu, &vms);
10421             } else {
10422                cmd = ast_play_and_wait(chan, "vm-nomore");
10423             }
10424          }
10425          break;
10426       case '7': /* Delete the current message */
10427          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10428             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10429             if (useadsi)
10430                adsi_delete(chan, &vms);
10431             if (vms.deleted[vms.curmsg]) {
10432                if (play_folder == 0) {
10433                   if (in_urgent) {
10434                      vms.urgentmessages--;
10435                   } else {
10436                      vms.newmessages--;
10437                   }
10438                }
10439                else if (play_folder == 1)
10440                   vms.oldmessages--;
10441                cmd = ast_play_and_wait(chan, "vm-deleted");
10442             } else {
10443                if (play_folder == 0) {
10444                   if (in_urgent) {
10445                      vms.urgentmessages++;
10446                   } else {
10447                      vms.newmessages++;
10448                   }
10449                }
10450                else if (play_folder == 1)
10451                   vms.oldmessages++;
10452                cmd = ast_play_and_wait(chan, "vm-undeleted");
10453             }
10454             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10455                if (vms.curmsg < vms.lastmsg) {
10456                   vms.curmsg++;
10457                   cmd = play_message(chan, vmu, &vms);
10458                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10459                   vms.curmsg = 0;
10460                   cmd = play_message(chan, vmu, &vms);
10461                } else {
10462                   /* Check if we were listening to urgent
10463                      messages.  If so, go to regular new messages
10464                      instead of saying "no more messages"
10465                   */
10466                   if (in_urgent == 1) {
10467                      /* Check for new messages */
10468                      in_urgent = 0;
10469                      res = close_mailbox(&vms, vmu);
10470                      if (res == ERROR_LOCK_PATH)
10471                         goto out;
10472                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10473                      if (res < 0)
10474                         goto out;
10475                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10476                      vms.curmsg = -1;
10477                      if (vms.lastmsg < 0) {
10478                         cmd = ast_play_and_wait(chan, "vm-nomore");
10479                      }
10480                   } else {
10481                      cmd = ast_play_and_wait(chan, "vm-nomore");
10482                   }
10483                }
10484             }
10485          } else /* Delete not valid if we haven't selected a message */
10486             cmd = 0;
10487 #ifdef IMAP_STORAGE
10488          deleted = 1;
10489 #endif
10490          break;
10491    
10492       case '8': /* Forward the current message */
10493          if (vms.lastmsg > -1) {
10494             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10495             if (cmd == ERROR_LOCK_PATH) {
10496                res = cmd;
10497                goto out;
10498             }
10499          } else {
10500             /* Check if we were listening to urgent
10501                messages.  If so, go to regular new messages
10502                instead of saying "no more messages"
10503             */
10504             if (in_urgent == 1 && vms.newmessages > 0) {
10505                /* Check for new messages */
10506                in_urgent = 0;
10507                res = close_mailbox(&vms, vmu);
10508                if (res == ERROR_LOCK_PATH)
10509                   goto out;
10510                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10511                if (res < 0)
10512                   goto out;
10513                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10514                vms.curmsg = -1;
10515                if (vms.lastmsg < 0) {
10516                   cmd = ast_play_and_wait(chan, "vm-nomore");
10517                }
10518             } else {
10519                cmd = ast_play_and_wait(chan, "vm-nomore");
10520             }
10521          }
10522          break;
10523       case '9': /* Save message to folder */
10524          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10525          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10526             /* No message selected */
10527             cmd = 0;
10528             break;
10529          }
10530          if (useadsi)
10531             adsi_folders(chan, 1, "Save to folder...");
10532          cmd = get_folder2(chan, "vm-savefolder", 1);
10533          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10534          box = 0; /* Shut up compiler */
10535          if (cmd == '#') {
10536             cmd = 0;
10537             break;
10538          } else if (cmd > 0) {
10539             box = cmd = cmd - '0';
10540             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10541             if (cmd == ERROR_LOCK_PATH) {
10542                res = cmd;
10543                goto out;
10544 #ifndef IMAP_STORAGE
10545             } else if (!cmd) {
10546                vms.deleted[vms.curmsg] = 1;
10547 #endif
10548             } else {
10549                vms.deleted[vms.curmsg] = 0;
10550                vms.heard[vms.curmsg] = 0;
10551             }
10552          }
10553          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10554          if (useadsi)
10555             adsi_message(chan, &vms);
10556          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10557          if (!cmd) {
10558             cmd = ast_play_and_wait(chan, "vm-message");
10559             if (!cmd) 
10560                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10561             if (!cmd)
10562                cmd = ast_play_and_wait(chan, "vm-savedto");
10563             if (!cmd)
10564                cmd = vm_play_folder_name(chan, vms.fn);
10565          } else {
10566             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10567          }
10568          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10569             if (vms.curmsg < vms.lastmsg) {
10570                vms.curmsg++;
10571                cmd = play_message(chan, vmu, &vms);
10572             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10573                vms.curmsg = 0;
10574                cmd = play_message(chan, vmu, &vms);
10575             } else {
10576                /* Check if we were listening to urgent
10577                   messages.  If so, go to regular new messages
10578                   instead of saying "no more messages"
10579                */
10580                if (in_urgent == 1 && vms.newmessages > 0) {
10581                   /* Check for new messages */
10582                   in_urgent = 0;
10583                   res = close_mailbox(&vms, vmu);
10584                   if (res == ERROR_LOCK_PATH)
10585                      goto out;
10586                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10587                   if (res < 0)
10588                      goto out;
10589                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10590                   vms.curmsg = -1;
10591                   if (vms.lastmsg < 0) {
10592                      cmd = ast_play_and_wait(chan, "vm-nomore");
10593                   }
10594                } else {
10595                   cmd = ast_play_and_wait(chan, "vm-nomore");
10596                }
10597             }
10598          }
10599          break;
10600       case '*': /* Help */
10601          if (!vms.starting) {
10602             cmd = ast_play_and_wait(chan, "vm-onefor");
10603             if (!strncasecmp(chan->language, "he", 2)) {
10604                cmd = ast_play_and_wait(chan, "vm-for");
10605             }
10606             if (!cmd)
10607                cmd = vm_play_folder_name(chan, vms.vmbox);
10608             if (!cmd)
10609                cmd = ast_play_and_wait(chan, "vm-opts");
10610             if (!cmd)
10611                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10612          } else
10613             cmd = 0;
10614          break;
10615       case '0': /* Mailbox options */
10616          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10617          if (useadsi)
10618             adsi_status(chan, &vms);
10619          break;
10620       default: /* Nothing */
10621          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10622          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10623          break;
10624       }
10625    }
10626    if ((cmd == 't') || (cmd == '#')) {
10627       /* Timeout */
10628       res = 0;
10629    } else {
10630       /* Hangup */
10631       res = -1;
10632    }
10633 
10634 out:
10635    if (res > -1) {
10636       ast_stopstream(chan);
10637       adsi_goodbye(chan);
10638       if (valid && res != OPERATOR_EXIT) {
10639          if (silentexit)
10640             res = ast_play_and_wait(chan, "vm-dialout");
10641          else 
10642             res = ast_play_and_wait(chan, "vm-goodbye");
10643       }
10644       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10645          res = 0;
10646       }
10647       if (useadsi)
10648          ast_adsi_unload_session(chan);
10649    }
10650    if (vmu)
10651       close_mailbox(&vms, vmu);
10652    if (valid) {
10653       int new = 0, old = 0, urgent = 0;
10654       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10655       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10656       /* Urgent flag not passwd to externnotify here */
10657       run_externnotify(vmu->context, vmu->mailbox, NULL);
10658       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10659       queue_mwi_event(ext_context, urgent, new, old);
10660    }
10661 #ifdef IMAP_STORAGE
10662    /* expunge message - use UID Expunge if supported on IMAP server*/
10663    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10664    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10665       ast_mutex_lock(&vms.lock);
10666 #ifdef HAVE_IMAP_TK2006
10667       if (LEVELUIDPLUS (vms.mailstream)) {
10668          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10669       } else 
10670 #endif
10671          mail_expunge(vms.mailstream);
10672       ast_mutex_unlock(&vms.lock);
10673    }
10674    /*  before we delete the state, we should copy pertinent info
10675     *  back to the persistent model */
10676    if (vmu) {
10677       vmstate_delete(&vms);
10678    }
10679 #endif
10680    if (vmu)
10681       free_user(vmu);
10682 
10683 #ifdef IMAP_STORAGE
10684    pthread_setspecific(ts_vmstate.key, NULL);
10685 #endif
10686    return res;
10687 }
10688 
10689 static int vm_exec(struct ast_channel *chan, const char *data)
10690 {
10691    int res = 0;
10692    char *tmp;
10693    struct leave_vm_options leave_options;
10694    struct ast_flags flags = { 0 };
10695    char *opts[OPT_ARG_ARRAY_SIZE];
10696    AST_DECLARE_APP_ARGS(args,
10697       AST_APP_ARG(argv0);
10698       AST_APP_ARG(argv1);
10699    );
10700    
10701    memset(&leave_options, 0, sizeof(leave_options));
10702 
10703    if (chan->_state != AST_STATE_UP)
10704       ast_answer(chan);
10705 
10706    if (!ast_strlen_zero(data)) {
10707       tmp = ast_strdupa(data);
10708       AST_STANDARD_APP_ARGS(args, tmp);
10709       if (args.argc == 2) {
10710          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10711             return -1;
10712          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10713          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10714             int gain;
10715 
10716             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10717                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10718                return -1;
10719             } else {
10720                leave_options.record_gain = (signed char) gain;
10721             }
10722          }
10723          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10724             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10725                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10726          }
10727       }
10728    } else {
10729       char temp[256];
10730       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10731       if (res < 0)
10732          return res;
10733       if (ast_strlen_zero(temp))
10734          return 0;
10735       args.argv0 = ast_strdupa(temp);
10736    }
10737 
10738    res = leave_voicemail(chan, args.argv0, &leave_options);
10739    if (res == 't') {
10740       ast_play_and_wait(chan, "vm-goodbye");
10741       res = 0;
10742    }
10743 
10744    if (res == OPERATOR_EXIT) {
10745       res = 0;
10746    }
10747 
10748    if (res == ERROR_LOCK_PATH) {
10749       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10750       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10751       res = 0;
10752    }
10753 
10754    return res;
10755 }
10756 
10757 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10758 {
10759    struct ast_vm_user *vmu;
10760 
10761    if (!ast_strlen_zero(box) && box[0] == '*') {
10762       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10763             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10764             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10765             "\n\tand will be ignored.\n", box, context);
10766       return NULL;
10767    }
10768 
10769    AST_LIST_TRAVERSE(&users, vmu, list) {
10770       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10771          if (strcasecmp(vmu->context, context)) {
10772             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10773                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10774                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10775                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10776          }
10777          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10778          return NULL;
10779       }
10780       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10781          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10782          return NULL;
10783       }
10784    }
10785    
10786    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10787       return NULL;
10788    
10789    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10790    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10791 
10792    AST_LIST_INSERT_TAIL(&users, vmu, list);
10793    
10794    return vmu;
10795 }
10796 
10797 static int append_mailbox(const char *context, const char *box, const char *data)
10798 {
10799    /* Assumes lock is already held */
10800    char *tmp;
10801    char *stringp;
10802    char *s;
10803    struct ast_vm_user *vmu;
10804    char *mailbox_full;
10805    int new = 0, old = 0, urgent = 0;
10806    char secretfn[PATH_MAX] = "";
10807 
10808    tmp = ast_strdupa(data);
10809 
10810    if (!(vmu = find_or_create(context, box)))
10811       return -1;
10812 
10813    populate_defaults(vmu);
10814 
10815    stringp = tmp;
10816    if ((s = strsep(&stringp, ","))) {
10817       if (!ast_strlen_zero(s) && s[0] == '*') {
10818          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10819             "\n\tmust be reset in voicemail.conf.\n", box);
10820       }
10821       /* assign password regardless of validity to prevent NULL password from being assigned */
10822       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10823    }
10824    if (stringp && (s = strsep(&stringp, ","))) {
10825       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10826    }
10827    if (stringp && (s = strsep(&stringp, ","))) {
10828       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10829    }
10830    if (stringp && (s = strsep(&stringp, ","))) {
10831       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10832    }
10833    if (stringp && (s = strsep(&stringp, ","))) {
10834       apply_options(vmu, s);
10835    }
10836 
10837    switch (vmu->passwordlocation) {
10838    case OPT_PWLOC_SPOOLDIR:
10839       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10840       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10841    }
10842 
10843    mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10844    strcpy(mailbox_full, box);
10845    strcat(mailbox_full, "@");
10846    strcat(mailbox_full, context);
10847 
10848    inboxcount2(mailbox_full, &urgent, &new, &old);
10849    queue_mwi_event(mailbox_full, urgent, new, old);
10850 
10851    return 0;
10852 }
10853 
10854 AST_TEST_DEFINE(test_voicemail_vmuser)
10855 {
10856    int res = 0;
10857    struct ast_vm_user *vmu;
10858    /* language parameter seems to only be used for display in manager action */
10859    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10860       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10861       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10862       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10863       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10864       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10865       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10866       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10867       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10868 #ifdef IMAP_STORAGE
10869    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10870       "imapfolder=INBOX|imapvmshareid=6000";
10871 #endif
10872 
10873    switch (cmd) {
10874    case TEST_INIT:
10875       info->name = "vmuser";
10876       info->category = "/apps/app_voicemail/";
10877       info->summary = "Vmuser unit test";
10878       info->description =
10879          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10880       return AST_TEST_NOT_RUN;
10881    case TEST_EXECUTE:
10882       break;
10883    }
10884 
10885    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10886       return AST_TEST_NOT_RUN;
10887    }
10888    populate_defaults(vmu);
10889    ast_set_flag(vmu, VM_ALLOCED);
10890 
10891    apply_options(vmu, options_string);
10892 
10893    if (!ast_test_flag(vmu, VM_ATTACH)) {
10894       ast_test_status_update(test, "Parse failure for attach option\n");
10895       res = 1;
10896    }
10897    if (strcasecmp(vmu->attachfmt, "wav49")) {
10898       ast_test_status_update(test, "Parse failure for attachftm option\n");
10899       res = 1;
10900    }
10901    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10902       ast_test_status_update(test, "Parse failure for serveremail option\n");
10903       res = 1;
10904    }
10905    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10906       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10907       res = 1;
10908    }
10909    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10910       ast_test_status_update(test, "Parse failure for emailbody option\n");
10911       res = 1;
10912    }
10913    if (strcasecmp(vmu->zonetag, "central")) {
10914       ast_test_status_update(test, "Parse failure for tz option\n");
10915       res = 1;
10916    }
10917    if (!ast_test_flag(vmu, VM_DELETE)) {
10918       ast_test_status_update(test, "Parse failure for delete option\n");
10919       res = 1;
10920    }
10921    if (!ast_test_flag(vmu, VM_SAYCID)) {
10922       ast_test_status_update(test, "Parse failure for saycid option\n");
10923       res = 1;
10924    }
10925    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10926       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10927       res = 1;
10928    }
10929    if (!ast_test_flag(vmu, VM_REVIEW)) {
10930       ast_test_status_update(test, "Parse failure for review option\n");
10931       res = 1;
10932    }
10933    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10934       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10935       res = 1;
10936    }
10937    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10938       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10939       res = 1;
10940    }
10941    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10942       ast_test_status_update(test, "Parse failure for operator option\n");
10943       res = 1;
10944    }
10945    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10946       ast_test_status_update(test, "Parse failure for envelope option\n");
10947       res = 1;
10948    }
10949    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10950       ast_test_status_update(test, "Parse failure for moveheard option\n");
10951       res = 1;
10952    }
10953    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10954       ast_test_status_update(test, "Parse failure for sayduration option\n");
10955       res = 1;
10956    }
10957    if (vmu->saydurationm != 5) {
10958       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10959       res = 1;
10960    }
10961    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10962       ast_test_status_update(test, "Parse failure for forcename option\n");
10963       res = 1;
10964    }
10965    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10966       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10967       res = 1;
10968    }
10969    if (strcasecmp(vmu->callback, "somecontext")) {
10970       ast_test_status_update(test, "Parse failure for callbacks option\n");
10971       res = 1;
10972    }
10973    if (strcasecmp(vmu->dialout, "somecontext2")) {
10974       ast_test_status_update(test, "Parse failure for dialout option\n");
10975       res = 1;
10976    }
10977    if (strcasecmp(vmu->exit, "somecontext3")) {
10978       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10979       res = 1;
10980    }
10981    if (vmu->minsecs != 10) {
10982       ast_test_status_update(test, "Parse failure for minsecs option\n");
10983       res = 1;
10984    }
10985    if (vmu->maxsecs != 100) {
10986       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10987       res = 1;
10988    }
10989    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10990       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10991       res = 1;
10992    }
10993    if (vmu->maxdeletedmsg != 50) {
10994       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10995       res = 1;
10996    }
10997    if (vmu->volgain != 1.3) {
10998       ast_test_status_update(test, "Parse failure for volgain option\n");
10999       res = 1;
11000    }
11001    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
11002       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
11003       res = 1;
11004    }
11005 #ifdef IMAP_STORAGE
11006    apply_options(vmu, option_string2);
11007 
11008    if (strcasecmp(vmu->imapuser, "imapuser")) {
11009       ast_test_status_update(test, "Parse failure for imapuser option\n");
11010       res = 1;
11011    }
11012    if (strcasecmp(vmu->imappassword, "imappasswd")) {
11013       ast_test_status_update(test, "Parse failure for imappasswd option\n");
11014       res = 1;
11015    }
11016    if (strcasecmp(vmu->imapfolder, "INBOX")) {
11017       ast_test_status_update(test, "Parse failure for imapfolder option\n");
11018       res = 1;
11019    }
11020    if (strcasecmp(vmu->imapvmshareid, "6000")) {
11021       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
11022       res = 1;
11023    }
11024 #endif
11025 
11026    free_user(vmu);
11027    return res ? AST_TEST_FAIL : AST_TEST_PASS;
11028 }
11029 
11030 static int vm_box_exists(struct ast_channel *chan, const char *data) 
11031 {
11032    struct ast_vm_user svm;
11033    char *context, *box;
11034    AST_DECLARE_APP_ARGS(args,
11035       AST_APP_ARG(mbox);
11036       AST_APP_ARG(options);
11037    );
11038    static int dep_warning = 0;
11039 
11040    if (ast_strlen_zero(data)) {
11041       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
11042       return -1;
11043    }
11044 
11045    if (!dep_warning) {
11046       dep_warning = 1;
11047       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
11048    }
11049 
11050    box = ast_strdupa(data);
11051 
11052    AST_STANDARD_APP_ARGS(args, box);
11053 
11054    if (args.options) {
11055    }
11056 
11057    if ((context = strchr(args.mbox, '@'))) {
11058       *context = '\0';
11059       context++;
11060    }
11061 
11062    if (find_user(&svm, context, args.mbox)) {
11063       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11064    } else
11065       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11066 
11067    return 0;
11068 }
11069 
11070 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11071 {
11072    struct ast_vm_user svm;
11073    AST_DECLARE_APP_ARGS(arg,
11074       AST_APP_ARG(mbox);
11075       AST_APP_ARG(context);
11076    );
11077 
11078    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11079 
11080    if (ast_strlen_zero(arg.mbox)) {
11081       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11082       return -1;
11083    }
11084 
11085    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11086    return 0;
11087 }
11088 
11089 static struct ast_custom_function mailbox_exists_acf = {
11090    .name = "MAILBOX_EXISTS",
11091    .read = acf_mailbox_exists,
11092 };
11093 
11094 static int vmauthenticate(struct ast_channel *chan, const char *data)
11095 {
11096    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11097    struct ast_vm_user vmus;
11098    char *options = NULL;
11099    int silent = 0, skipuser = 0;
11100    int res = -1;
11101    
11102    if (data) {
11103       s = ast_strdupa(data);
11104       user = strsep(&s, ",");
11105       options = strsep(&s, ",");
11106       if (user) {
11107          s = user;
11108          user = strsep(&s, "@");
11109          context = strsep(&s, "");
11110          if (!ast_strlen_zero(user))
11111             skipuser++;
11112          ast_copy_string(mailbox, user, sizeof(mailbox));
11113       }
11114    }
11115 
11116    if (options) {
11117       silent = (strchr(options, 's')) != NULL;
11118    }
11119 
11120    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11121       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11122       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11123       ast_play_and_wait(chan, "auth-thankyou");
11124       res = 0;
11125    } else if (mailbox[0] == '*') {
11126       /* user entered '*' */
11127       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11128          res = 0; /* prevent hangup */
11129       }
11130    }
11131 
11132    return res;
11133 }
11134 
11135 static char *show_users_realtime(int fd, const char *context)
11136 {
11137    struct ast_config *cfg;
11138    const char *cat = NULL;
11139 
11140    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11141       "context", context, SENTINEL))) {
11142       return CLI_FAILURE;
11143    }
11144 
11145    ast_cli(fd,
11146       "\n"
11147       "=============================================================\n"
11148       "=== Configured Voicemail Users ==============================\n"
11149       "=============================================================\n"
11150       "===\n");
11151 
11152    while ((cat = ast_category_browse(cfg, cat))) {
11153       struct ast_variable *var = NULL;
11154       ast_cli(fd,
11155          "=== Mailbox ...\n"
11156          "===\n");
11157       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11158          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11159       ast_cli(fd,
11160          "===\n"
11161          "=== ---------------------------------------------------------\n"
11162          "===\n");
11163    }
11164 
11165    ast_cli(fd,
11166       "=============================================================\n"
11167       "\n");
11168 
11169    ast_config_destroy(cfg);
11170 
11171    return CLI_SUCCESS;
11172 }
11173 
11174 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11175 {
11176    int which = 0;
11177    int wordlen;
11178    struct ast_vm_user *vmu;
11179    const char *context = "";
11180 
11181    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11182    if (pos > 4)
11183       return NULL;
11184    if (pos == 3)
11185       return (state == 0) ? ast_strdup("for") : NULL;
11186    wordlen = strlen(word);
11187    AST_LIST_TRAVERSE(&users, vmu, list) {
11188       if (!strncasecmp(word, vmu->context, wordlen)) {
11189          if (context && strcmp(context, vmu->context) && ++which > state)
11190             return ast_strdup(vmu->context);
11191          /* ignore repeated contexts ? */
11192          context = vmu->context;
11193       }
11194    }
11195    return NULL;
11196 }
11197 
11198 /*! \brief Show a list of voicemail users in the CLI */
11199 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11200 {
11201    struct ast_vm_user *vmu;
11202 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11203    const char *context = NULL;
11204    int users_counter = 0;
11205 
11206    switch (cmd) {
11207    case CLI_INIT:
11208       e->command = "voicemail show users";
11209       e->usage =
11210          "Usage: voicemail show users [for <context>]\n"
11211          "       Lists all mailboxes currently set up\n";
11212       return NULL;
11213    case CLI_GENERATE:
11214       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11215    }  
11216 
11217    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11218       return CLI_SHOWUSAGE;
11219    if (a->argc == 5) {
11220       if (strcmp(a->argv[3],"for"))
11221          return CLI_SHOWUSAGE;
11222       context = a->argv[4];
11223    }
11224 
11225    if (ast_check_realtime("voicemail")) {
11226       if (!context) {
11227          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11228          return CLI_SHOWUSAGE;
11229       }
11230       return show_users_realtime(a->fd, context);
11231    }
11232 
11233    AST_LIST_LOCK(&users);
11234    if (AST_LIST_EMPTY(&users)) {
11235       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11236       AST_LIST_UNLOCK(&users);
11237       return CLI_FAILURE;
11238    }
11239    if (!context) {
11240       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11241    } else {
11242       int count = 0;
11243       AST_LIST_TRAVERSE(&users, vmu, list) {
11244          if (!strcmp(context, vmu->context)) {
11245             count++;
11246             break;
11247          }
11248       }
11249       if (count) {
11250          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11251       } else {
11252          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11253          AST_LIST_UNLOCK(&users);
11254          return CLI_FAILURE;
11255       }
11256    }
11257    AST_LIST_TRAVERSE(&users, vmu, list) {
11258       int newmsgs = 0, oldmsgs = 0;
11259       char count[12], tmp[256] = "";
11260 
11261       if (!context || !strcmp(context, vmu->context)) {
11262          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11263          inboxcount(tmp, &newmsgs, &oldmsgs);
11264          snprintf(count, sizeof(count), "%d", newmsgs);
11265          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11266          users_counter++;
11267       }
11268    }
11269    AST_LIST_UNLOCK(&users);
11270    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11271    return CLI_SUCCESS;
11272 }
11273 
11274 /*! \brief Show a list of voicemail zones in the CLI */
11275 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11276 {
11277    struct vm_zone *zone;
11278 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11279    char *res = CLI_SUCCESS;
11280 
11281    switch (cmd) {
11282    case CLI_INIT:
11283       e->command = "voicemail show zones";
11284       e->usage =
11285          "Usage: voicemail show zones\n"
11286          "       Lists zone message formats\n";
11287       return NULL;
11288    case CLI_GENERATE:
11289       return NULL;
11290    }
11291 
11292    if (a->argc != 3)
11293       return CLI_SHOWUSAGE;
11294 
11295    AST_LIST_LOCK(&zones);
11296    if (!AST_LIST_EMPTY(&zones)) {
11297       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11298       AST_LIST_TRAVERSE(&zones, zone, list) {
11299          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11300       }
11301    } else {
11302       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11303       res = CLI_FAILURE;
11304    }
11305    AST_LIST_UNLOCK(&zones);
11306 
11307    return res;
11308 }
11309 
11310 /*! \brief Reload voicemail configuration from the CLI */
11311 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11312 {
11313    switch (cmd) {
11314    case CLI_INIT:
11315       e->command = "voicemail reload";
11316       e->usage =
11317          "Usage: voicemail reload\n"
11318          "       Reload voicemail configuration\n";
11319       return NULL;
11320    case CLI_GENERATE:
11321       return NULL;
11322    }
11323 
11324    if (a->argc != 2)
11325       return CLI_SHOWUSAGE;
11326 
11327    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11328    load_config(1);
11329    
11330    return CLI_SUCCESS;
11331 }
11332 
11333 static struct ast_cli_entry cli_voicemail[] = {
11334    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11335    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11336    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11337 };
11338 
11339 #ifdef IMAP_STORAGE
11340    #define DATA_EXPORT_VM_USERS(USER)              \
11341       USER(ast_vm_user, context, AST_DATA_STRING)        \
11342       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11343       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11344       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11345       USER(ast_vm_user, email, AST_DATA_STRING)       \
11346       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11347       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11348       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11349       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11350       USER(ast_vm_user, language, AST_DATA_STRING)       \
11351       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11352       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11353       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11354       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11355       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11356       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11357       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11358       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11359       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11360       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11361       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11362       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11363       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11364       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11365       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11366 #else
11367    #define DATA_EXPORT_VM_USERS(USER)              \
11368       USER(ast_vm_user, context, AST_DATA_STRING)        \
11369       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11370       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11371       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11372       USER(ast_vm_user, email, AST_DATA_STRING)       \
11373       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11374       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11375       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11376       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11377       USER(ast_vm_user, language, AST_DATA_STRING)       \
11378       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11379       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11380       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11381       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11382       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11383       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11384       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11385       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11386       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11387       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11388       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11389       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11390 #endif
11391 
11392 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11393 
11394 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11395    ZONE(vm_zone, name, AST_DATA_STRING)      \
11396    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11397    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11398 
11399 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11400 
11401 /*!
11402  * \internal
11403  * \brief Add voicemail user to the data_root.
11404  * \param[in] search The search tree.
11405  * \param[in] data_root The main result node.
11406  * \param[in] user The voicemail user.
11407  */
11408 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11409     struct ast_data *data_root, struct ast_vm_user *user)
11410 {
11411    struct ast_data *data_user, *data_zone;
11412    struct ast_data *data_state;
11413    struct vm_zone *zone = NULL;
11414    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11415    char ext_context[256] = "";
11416 
11417    data_user = ast_data_add_node(data_root, "user");
11418    if (!data_user) {
11419       return -1;
11420    }
11421 
11422    ast_data_add_structure(ast_vm_user, data_user, user);
11423 
11424    AST_LIST_LOCK(&zones);
11425    AST_LIST_TRAVERSE(&zones, zone, list) {
11426       if (!strcmp(zone->name, user->zonetag)) {
11427          break;
11428       }
11429    }
11430    AST_LIST_UNLOCK(&zones);
11431 
11432    /* state */
11433    data_state = ast_data_add_node(data_user, "state");
11434    if (!data_state) {
11435       return -1;
11436    }
11437    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11438    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11439    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11440    ast_data_add_int(data_state, "newmsg", newmsg);
11441    ast_data_add_int(data_state, "oldmsg", oldmsg);
11442 
11443    if (zone) {
11444       data_zone = ast_data_add_node(data_user, "zone");
11445       ast_data_add_structure(vm_zone, data_zone, zone);
11446    }
11447 
11448    if (!ast_data_search_match(search, data_user)) {
11449       ast_data_remove_node(data_root, data_user);
11450    }
11451 
11452    return 0;
11453 }
11454 
11455 static int vm_users_data_provider_get(const struct ast_data_search *search,
11456    struct ast_data *data_root)
11457 {
11458    struct ast_vm_user *user;
11459 
11460    AST_LIST_LOCK(&users);
11461    AST_LIST_TRAVERSE(&users, user, list) {
11462       vm_users_data_provider_get_helper(search, data_root, user);
11463    }
11464    AST_LIST_UNLOCK(&users);
11465 
11466    return 0;
11467 }
11468 
11469 static const struct ast_data_handler vm_users_data_provider = {
11470    .version = AST_DATA_HANDLER_VERSION,
11471    .get = vm_users_data_provider_get
11472 };
11473 
11474 static const struct ast_data_entry vm_data_providers[] = {
11475    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11476 };
11477 
11478 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11479 {
11480    int new = 0, old = 0, urgent = 0;
11481 
11482    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11483 
11484    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11485       mwi_sub->old_urgent = urgent;
11486       mwi_sub->old_new = new;
11487       mwi_sub->old_old = old;
11488       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11489       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11490    }
11491 }
11492 
11493 static void poll_subscribed_mailboxes(void)
11494 {
11495    struct mwi_sub *mwi_sub;
11496 
11497    AST_RWLIST_RDLOCK(&mwi_subs);
11498    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11499       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11500          poll_subscribed_mailbox(mwi_sub);
11501       }
11502    }
11503    AST_RWLIST_UNLOCK(&mwi_subs);
11504 }
11505 
11506 static void *mb_poll_thread(void *data)
11507 {
11508    while (poll_thread_run) {
11509       struct timespec ts = { 0, };
11510       struct timeval wait;
11511 
11512       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11513       ts.tv_sec = wait.tv_sec;
11514       ts.tv_nsec = wait.tv_usec * 1000;
11515 
11516       ast_mutex_lock(&poll_lock);
11517       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11518       ast_mutex_unlock(&poll_lock);
11519 
11520       if (!poll_thread_run)
11521          break;
11522 
11523       poll_subscribed_mailboxes();
11524    }
11525 
11526    return NULL;
11527 }
11528 
11529 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11530 {
11531    ast_free(mwi_sub);
11532 }
11533 
11534 static int handle_unsubscribe(void *datap)
11535 {
11536    struct mwi_sub *mwi_sub;
11537    uint32_t *uniqueid = datap;
11538    
11539    AST_RWLIST_WRLOCK(&mwi_subs);
11540    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11541       if (mwi_sub->uniqueid == *uniqueid) {
11542          AST_LIST_REMOVE_CURRENT(entry);
11543          break;
11544       }
11545    }
11546    AST_RWLIST_TRAVERSE_SAFE_END
11547    AST_RWLIST_UNLOCK(&mwi_subs);
11548 
11549    if (mwi_sub)
11550       mwi_sub_destroy(mwi_sub);
11551 
11552    ast_free(uniqueid);  
11553    return 0;
11554 }
11555 
11556 static int handle_subscribe(void *datap)
11557 {
11558    unsigned int len;
11559    struct mwi_sub *mwi_sub;
11560    struct mwi_sub_task *p = datap;
11561 
11562    len = sizeof(*mwi_sub);
11563    if (!ast_strlen_zero(p->mailbox))
11564       len += strlen(p->mailbox);
11565 
11566    if (!ast_strlen_zero(p->context))
11567       len += strlen(p->context) + 1; /* Allow for seperator */
11568 
11569    if (!(mwi_sub = ast_calloc(1, len)))
11570       return -1;
11571 
11572    mwi_sub->uniqueid = p->uniqueid;
11573    if (!ast_strlen_zero(p->mailbox))
11574       strcpy(mwi_sub->mailbox, p->mailbox);
11575 
11576    if (!ast_strlen_zero(p->context)) {
11577       strcat(mwi_sub->mailbox, "@");
11578       strcat(mwi_sub->mailbox, p->context);
11579    }
11580 
11581    AST_RWLIST_WRLOCK(&mwi_subs);
11582    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11583    AST_RWLIST_UNLOCK(&mwi_subs);
11584    ast_free((void *) p->mailbox);
11585    ast_free((void *) p->context);
11586    ast_free(p);
11587    poll_subscribed_mailbox(mwi_sub);
11588    return 0;
11589 }
11590 
11591 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11592 {
11593    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11594 
11595    if (!uniqueid) {
11596       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11597       return;
11598    }
11599 
11600    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11601       ast_free(uniqueid);
11602       return;
11603    }
11604 
11605    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11606       ast_free(uniqueid);
11607       return;
11608    }
11609 
11610    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11611    *uniqueid = u;
11612    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11613       ast_free(uniqueid);
11614    }
11615 }
11616 
11617 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11618 {
11619    struct mwi_sub_task *mwist;
11620    
11621    if (ast_event_get_type(event) != AST_EVENT_SUB)
11622       return;
11623 
11624    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11625       return;
11626 
11627    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11628       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11629       return;
11630    }
11631    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11632    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11633    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11634    
11635    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11636       ast_free(mwist);
11637    }
11638 }
11639 
11640 static void start_poll_thread(void)
11641 {
11642    int errcode;
11643    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11644       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11645       AST_EVENT_IE_END);
11646 
11647    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11648       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11649       AST_EVENT_IE_END);
11650 
11651    if (mwi_sub_sub)
11652       ast_event_report_subs(mwi_sub_sub);
11653 
11654    poll_thread_run = 1;
11655 
11656    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11657       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11658    }
11659 }
11660 
11661 static void stop_poll_thread(void)
11662 {
11663    poll_thread_run = 0;
11664 
11665    if (mwi_sub_sub) {
11666       ast_event_unsubscribe(mwi_sub_sub);
11667       mwi_sub_sub = NULL;
11668    }
11669 
11670    if (mwi_unsub_sub) {
11671       ast_event_unsubscribe(mwi_unsub_sub);
11672       mwi_unsub_sub = NULL;
11673    }
11674 
11675    ast_mutex_lock(&poll_lock);
11676    ast_cond_signal(&poll_cond);
11677    ast_mutex_unlock(&poll_lock);
11678 
11679    pthread_join(poll_thread, NULL);
11680 
11681    poll_thread = AST_PTHREADT_NULL;
11682 }
11683 
11684 /*! \brief Manager list voicemail users command */
11685 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11686 {
11687    struct ast_vm_user *vmu = NULL;
11688    const char *id = astman_get_header(m, "ActionID");
11689    char actionid[128] = "";
11690 
11691    if (!ast_strlen_zero(id))
11692       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11693 
11694    AST_LIST_LOCK(&users);
11695 
11696    if (AST_LIST_EMPTY(&users)) {
11697       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11698       AST_LIST_UNLOCK(&users);
11699       return RESULT_SUCCESS;
11700    }
11701    
11702    astman_send_ack(s, m, "Voicemail user list will follow");
11703    
11704    AST_LIST_TRAVERSE(&users, vmu, list) {
11705       char dirname[256];
11706 
11707 #ifdef IMAP_STORAGE
11708       int new, old;
11709       inboxcount(vmu->mailbox, &new, &old);
11710 #endif
11711       
11712       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11713       astman_append(s,
11714          "%s"
11715          "Event: VoicemailUserEntry\r\n"
11716          "VMContext: %s\r\n"
11717          "VoiceMailbox: %s\r\n"
11718          "Fullname: %s\r\n"
11719          "Email: %s\r\n"
11720          "Pager: %s\r\n"
11721          "ServerEmail: %s\r\n"
11722          "MailCommand: %s\r\n"
11723          "Language: %s\r\n"
11724          "TimeZone: %s\r\n"
11725          "Callback: %s\r\n"
11726          "Dialout: %s\r\n"
11727          "UniqueID: %s\r\n"
11728          "ExitContext: %s\r\n"
11729          "SayDurationMinimum: %d\r\n"
11730          "SayEnvelope: %s\r\n"
11731          "SayCID: %s\r\n"
11732          "AttachMessage: %s\r\n"
11733          "AttachmentFormat: %s\r\n"
11734          "DeleteMessage: %s\r\n"
11735          "VolumeGain: %.2f\r\n"
11736          "CanReview: %s\r\n"
11737          "CallOperator: %s\r\n"
11738          "MaxMessageCount: %d\r\n"
11739          "MaxMessageLength: %d\r\n"
11740          "NewMessageCount: %d\r\n"
11741 #ifdef IMAP_STORAGE
11742          "OldMessageCount: %d\r\n"
11743          "IMAPUser: %s\r\n"
11744 #endif
11745          "\r\n",
11746          actionid,
11747          vmu->context,
11748          vmu->mailbox,
11749          vmu->fullname,
11750          vmu->email,
11751          vmu->pager,
11752          ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11753          mailcmd,
11754          vmu->language,
11755          vmu->zonetag,
11756          vmu->callback,
11757          vmu->dialout,
11758          vmu->uniqueid,
11759          vmu->exit,
11760          vmu->saydurationm,
11761          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11762          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11763          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11764          vmu->attachfmt,
11765          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11766          vmu->volgain,
11767          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11768          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11769          vmu->maxmsg,
11770          vmu->maxsecs,
11771 #ifdef IMAP_STORAGE
11772          new, old, vmu->imapuser
11773 #else
11774          count_messages(vmu, dirname)
11775 #endif
11776          );
11777    }     
11778    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11779 
11780    AST_LIST_UNLOCK(&users);
11781 
11782    return RESULT_SUCCESS;
11783 }
11784 
11785 /*! \brief Free the users structure. */
11786 static void free_vm_users(void) 
11787 {
11788    struct ast_vm_user *current;
11789    AST_LIST_LOCK(&users);
11790    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11791       ast_set_flag(current, VM_ALLOCED);
11792       free_user(current);
11793    }
11794    AST_LIST_UNLOCK(&users);
11795 }
11796 
11797 /*! \brief Free the zones structure. */
11798 static void free_vm_zones(void)
11799 {
11800    struct vm_zone *zcur;
11801    AST_LIST_LOCK(&zones);
11802    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11803       free_zone(zcur);
11804    AST_LIST_UNLOCK(&zones);
11805 }
11806 
11807 static const char *substitute_escapes(const char *value)
11808 {
11809    char *current;
11810 
11811    /* Add 16 for fudge factor */
11812    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11813 
11814    ast_str_reset(str);
11815    
11816    /* Substitute strings \r, \n, and \t into the appropriate characters */
11817    for (current = (char *) value; *current; current++) {
11818       if (*current == '\\') {
11819          current++;
11820          if (!*current) {
11821             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11822             break;
11823          }
11824          switch (*current) {
11825          case '\\':
11826             ast_str_append(&str, 0, "\\");
11827             break;
11828          case 'r':
11829             ast_str_append(&str, 0, "\r");
11830             break;
11831          case 'n':
11832 #ifdef IMAP_STORAGE
11833             if (!str->used || str->str[str->used - 1] != '\r') {
11834                ast_str_append(&str, 0, "\r");
11835             }
11836 #endif
11837             ast_str_append(&str, 0, "\n");
11838             break;
11839          case 't':
11840             ast_str_append(&str, 0, "\t");
11841             break;
11842          default:
11843             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11844             break;
11845          }
11846       } else {
11847          ast_str_append(&str, 0, "%c", *current);
11848       }
11849    }
11850 
11851    return ast_str_buffer(str);
11852 }
11853 
11854 static int load_config(int reload)
11855 {
11856    struct ast_config *cfg, *ucfg;
11857    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11858    int res;
11859 
11860    ast_unload_realtime("voicemail");
11861    ast_unload_realtime("voicemail_data");
11862 
11863    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11864       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11865          return 0;
11866       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11867          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11868          ucfg = NULL;
11869       }
11870       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11871       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11872          ast_config_destroy(ucfg);
11873          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11874          return 0;
11875       }
11876    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11877       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11878       return 0;
11879    } else {
11880       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11881       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11882          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11883          ucfg = NULL;
11884       }
11885    }
11886 
11887    res = actual_load_config(reload, cfg, ucfg);
11888 
11889    ast_config_destroy(cfg);
11890    ast_config_destroy(ucfg);
11891 
11892    return res;
11893 }
11894 
11895 #ifdef TEST_FRAMEWORK
11896 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11897 {
11898    ast_unload_realtime("voicemail");
11899    ast_unload_realtime("voicemail_data");
11900    return actual_load_config(reload, cfg, ucfg);
11901 }
11902 #endif
11903 
11904 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11905 {
11906    struct ast_vm_user *current;
11907    char *cat;
11908    struct ast_variable *var;
11909    const char *val;
11910    char *q, *stringp, *tmp;
11911    int x;
11912    int tmpadsi[4];
11913    char secretfn[PATH_MAX] = "";
11914 
11915 #ifdef IMAP_STORAGE
11916    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11917 #endif
11918    /* set audio control prompts */
11919    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11920    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11921    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11922    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11923    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11924 
11925    /* Free all the users structure */  
11926    free_vm_users();
11927 
11928    /* Free all the zones structure */
11929    free_vm_zones();
11930 
11931    AST_LIST_LOCK(&users);  
11932 
11933    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11934    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11935 
11936    if (cfg) {
11937       /* General settings */
11938 
11939       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11940          val = "default";
11941       ast_copy_string(userscontext, val, sizeof(userscontext));
11942       /* Attach voice message to mail message ? */
11943       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11944          val = "yes";
11945       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11946 
11947       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11948          val = "no";
11949       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11950 
11951       volgain = 0.0;
11952       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11953          sscanf(val, "%30lf", &volgain);
11954 
11955 #ifdef ODBC_STORAGE
11956       strcpy(odbc_database, "asterisk");
11957       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11958          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11959       }
11960       strcpy(odbc_table, "voicemessages");
11961       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11962          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11963       }
11964 #endif      
11965       /* Mail command */
11966       strcpy(mailcmd, SENDMAIL);
11967       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11968          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11969 
11970       maxsilence = 0;
11971       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11972          maxsilence = atoi(val);
11973          if (maxsilence > 0)
11974             maxsilence *= 1000;
11975       }
11976       
11977       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11978          maxmsg = MAXMSG;
11979       } else {
11980          maxmsg = atoi(val);
11981          if (maxmsg < 0) {
11982             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11983             maxmsg = MAXMSG;
11984          } else if (maxmsg > MAXMSGLIMIT) {
11985             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11986             maxmsg = MAXMSGLIMIT;
11987          }
11988       }
11989 
11990       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11991          maxdeletedmsg = 0;
11992       } else {
11993          if (sscanf(val, "%30d", &x) == 1)
11994             maxdeletedmsg = x;
11995          else if (ast_true(val))
11996             maxdeletedmsg = MAXMSG;
11997          else
11998             maxdeletedmsg = 0;
11999 
12000          if (maxdeletedmsg < 0) {
12001             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
12002             maxdeletedmsg = MAXMSG;
12003          } else if (maxdeletedmsg > MAXMSGLIMIT) {
12004             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
12005             maxdeletedmsg = MAXMSGLIMIT;
12006          }
12007       }
12008 
12009       /* Load date format config for voicemail mail */
12010       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
12011          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
12012       }
12013 
12014       /* Load date format config for voicemail pager mail */
12015       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
12016          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
12017       }
12018 
12019       /* External password changing command */
12020       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
12021          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12022          pwdchange = PWDCHANGE_EXTERNAL;
12023       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
12024          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12025          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
12026       }
12027  
12028       /* External password validation command */
12029       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
12030          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
12031          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
12032       }
12033 
12034 #ifdef IMAP_STORAGE
12035       /* IMAP server address */
12036       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
12037          ast_copy_string(imapserver, val, sizeof(imapserver));
12038       } else {
12039          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
12040       }
12041       /* IMAP server port */
12042       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
12043          ast_copy_string(imapport, val, sizeof(imapport));
12044       } else {
12045          ast_copy_string(imapport, "143", sizeof(imapport));
12046       }
12047       /* IMAP server flags */
12048       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
12049          ast_copy_string(imapflags, val, sizeof(imapflags));
12050       }
12051       /* IMAP server master username */
12052       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12053          ast_copy_string(authuser, val, sizeof(authuser));
12054       }
12055       /* IMAP server master password */
12056       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12057          ast_copy_string(authpassword, val, sizeof(authpassword));
12058       }
12059       /* Expunge on exit */
12060       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12061          if (ast_false(val))
12062             expungeonhangup = 0;
12063          else
12064             expungeonhangup = 1;
12065       } else {
12066          expungeonhangup = 1;
12067       }
12068       /* IMAP voicemail folder */
12069       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12070          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12071       } else {
12072          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12073       }
12074       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12075          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12076       }
12077       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12078          imapgreetings = ast_true(val);
12079       } else {
12080          imapgreetings = 0;
12081       }
12082       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12083          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12084       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12085          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12086          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12087       } else {
12088          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12089       }
12090 
12091       /* There is some very unorthodox casting done here. This is due
12092        * to the way c-client handles the argument passed in. It expects a 
12093        * void pointer and casts the pointer directly to a long without
12094        * first dereferencing it. */
12095       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12096          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12097       } else {
12098          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12099       }
12100 
12101       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12102          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12103       } else {
12104          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12105       }
12106 
12107       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12108          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12109       } else {
12110          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12111       }
12112 
12113       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12114          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12115       } else {
12116          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12117       }
12118 
12119       /* Increment configuration version */
12120       imapversion++;
12121 #endif
12122       /* External voicemail notify application */
12123       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12124          ast_copy_string(externnotify, val, sizeof(externnotify));
12125          ast_debug(1, "found externnotify: %s\n", externnotify);
12126       } else {
12127          externnotify[0] = '\0';
12128       }
12129 
12130       /* SMDI voicemail notification */
12131       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12132          ast_debug(1, "Enabled SMDI voicemail notification\n");
12133          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12134             smdi_iface = ast_smdi_interface_find(val);
12135          } else {
12136             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12137             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12138          }
12139          if (!smdi_iface) {
12140             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12141          } 
12142       }
12143 
12144       /* Silence treshold */
12145       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12146       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12147          silencethreshold = atoi(val);
12148       
12149       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12150          val = ASTERISK_USERNAME;
12151       ast_copy_string(serveremail, val, sizeof(serveremail));
12152       
12153       vmmaxsecs = 0;
12154       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12155          if (sscanf(val, "%30d", &x) == 1) {
12156             vmmaxsecs = x;
12157          } else {
12158             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12159          }
12160       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12161          static int maxmessage_deprecate = 0;
12162          if (maxmessage_deprecate == 0) {
12163             maxmessage_deprecate = 1;
12164             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12165          }
12166          if (sscanf(val, "%30d", &x) == 1) {
12167             vmmaxsecs = x;
12168          } else {
12169             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12170          }
12171       }
12172 
12173       vmminsecs = 0;
12174       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12175          if (sscanf(val, "%30d", &x) == 1) {
12176             vmminsecs = x;
12177             if (maxsilence / 1000 >= vmminsecs) {
12178                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12179             }
12180          } else {
12181             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12182          }
12183       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12184          static int maxmessage_deprecate = 0;
12185          if (maxmessage_deprecate == 0) {
12186             maxmessage_deprecate = 1;
12187             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12188          }
12189          if (sscanf(val, "%30d", &x) == 1) {
12190             vmminsecs = x;
12191             if (maxsilence / 1000 >= vmminsecs) {
12192                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12193             }
12194          } else {
12195             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12196          }
12197       }
12198 
12199       val = ast_variable_retrieve(cfg, "general", "format");
12200       if (!val) {
12201          val = "wav";   
12202       } else {
12203          tmp = ast_strdupa(val);
12204          val = ast_format_str_reduce(tmp);
12205          if (!val) {
12206             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12207             val = "wav";
12208          }
12209       }
12210       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12211 
12212       skipms = 3000;
12213       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12214          if (sscanf(val, "%30d", &x) == 1) {
12215             maxgreet = x;
12216          } else {
12217             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12218          }
12219       }
12220 
12221       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12222          if (sscanf(val, "%30d", &x) == 1) {
12223             skipms = x;
12224          } else {
12225             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12226          }
12227       }
12228 
12229       maxlogins = 3;
12230       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12231          if (sscanf(val, "%30d", &x) == 1) {
12232             maxlogins = x;
12233          } else {
12234             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12235          }
12236       }
12237 
12238       minpassword = MINPASSWORD;
12239       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12240          if (sscanf(val, "%30d", &x) == 1) {
12241             minpassword = x;
12242          } else {
12243             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12244          }
12245       }
12246 
12247       /* Force new user to record name ? */
12248       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12249          val = "no";
12250       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12251 
12252       /* Force new user to record greetings ? */
12253       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12254          val = "no";
12255       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12256 
12257       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12258          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12259          stringp = ast_strdupa(val);
12260          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12261             if (!ast_strlen_zero(stringp)) {
12262                q = strsep(&stringp, ",");
12263                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12264                   q++;
12265                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12266                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12267             } else {
12268                cidinternalcontexts[x][0] = '\0';
12269             }
12270          }
12271       }
12272       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12273          ast_debug(1, "VM Review Option disabled globally\n");
12274          val = "no";
12275       }
12276       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12277 
12278       /* Temporary greeting reminder */
12279       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12280          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12281          val = "no";
12282       } else {
12283          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12284       }
12285       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12286       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12287          ast_debug(1, "VM next message wrap disabled globally\n");
12288          val = "no";
12289       }
12290       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12291 
12292       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12293          ast_debug(1, "VM Operator break disabled globally\n");
12294          val = "no";
12295       }
12296       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12297 
12298       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12299          ast_debug(1, "VM CID Info before msg disabled globally\n");
12300          val = "no";
12301       } 
12302       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12303 
12304       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12305          ast_debug(1, "Send Voicemail msg disabled globally\n");
12306          val = "no";
12307       }
12308       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12309    
12310       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12311          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12312          val = "yes";
12313       }
12314       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12315 
12316       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12317          ast_debug(1, "Move Heard enabled globally\n");
12318          val = "yes";
12319       }
12320       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12321 
12322       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12323          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12324          val = "no";
12325       }
12326       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12327 
12328       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12329          ast_debug(1, "Duration info before msg enabled globally\n");
12330          val = "yes";
12331       }
12332       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12333 
12334       saydurationminfo = 2;
12335       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12336          if (sscanf(val, "%30d", &x) == 1) {
12337             saydurationminfo = x;
12338          } else {
12339             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12340          }
12341       }
12342 
12343       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12344          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12345          val = "no";
12346       }
12347       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12348 
12349       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12350          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12351          ast_debug(1, "found dialout context: %s\n", dialcontext);
12352       } else {
12353          dialcontext[0] = '\0';  
12354       }
12355       
12356       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12357          ast_copy_string(callcontext, val, sizeof(callcontext));
12358          ast_debug(1, "found callback context: %s\n", callcontext);
12359       } else {
12360          callcontext[0] = '\0';
12361       }
12362 
12363       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12364          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12365          ast_debug(1, "found operator context: %s\n", exitcontext);
12366       } else {
12367          exitcontext[0] = '\0';
12368       }
12369       
12370       /* load password sounds configuration */
12371       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12372          ast_copy_string(vm_password, val, sizeof(vm_password));
12373       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12374          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12375       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12376          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12377       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12378          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12379       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12380          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12381       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12382          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12383       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12384          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12385       }
12386       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12387          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12388       }
12389       /* load configurable audio prompts */
12390       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12391          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12392       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12393          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12394       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12395          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12396       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12397          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12398       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12399          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12400 
12401       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12402          val = "no";
12403       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12404 
12405       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12406          val = "voicemail.conf";
12407       }
12408       if (!(strcmp(val, "spooldir"))) {
12409          passwordlocation = OPT_PWLOC_SPOOLDIR;
12410       } else {
12411          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12412       }
12413 
12414       poll_freq = DEFAULT_POLL_FREQ;
12415       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12416          if (sscanf(val, "%30u", &poll_freq) != 1) {
12417             poll_freq = DEFAULT_POLL_FREQ;
12418             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12419          }
12420       }
12421 
12422       poll_mailboxes = 0;
12423       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12424          poll_mailboxes = ast_true(val);
12425 
12426       memset(fromstring, 0, sizeof(fromstring));
12427       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12428       strcpy(charset, "ISO-8859-1");
12429       if (emailbody) {
12430          ast_free(emailbody);
12431          emailbody = NULL;
12432       }
12433       if (emailsubject) {
12434          ast_free(emailsubject);
12435          emailsubject = NULL;
12436       }
12437       if (pagerbody) {
12438          ast_free(pagerbody);
12439          pagerbody = NULL;
12440       }
12441       if (pagersubject) {
12442          ast_free(pagersubject);
12443          pagersubject = NULL;
12444       }
12445       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12446          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12447       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12448          ast_copy_string(fromstring, val, sizeof(fromstring));
12449       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12450          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12451       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12452          ast_copy_string(charset, val, sizeof(charset));
12453       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12454          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12455          for (x = 0; x < 4; x++) {
12456             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12457          }
12458       }
12459       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12460          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12461          for (x = 0; x < 4; x++) {
12462             memcpy(&adsisec[x], &tmpadsi[x], 1);
12463          }
12464       }
12465       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12466          if (atoi(val)) {
12467             adsiver = atoi(val);
12468          }
12469       }
12470       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12471          ast_copy_string(zonetag, val, sizeof(zonetag));
12472       }
12473       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12474          ast_copy_string(locale, val, sizeof(locale));
12475       }
12476       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12477          emailsubject = ast_strdup(substitute_escapes(val));
12478       }
12479       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12480          emailbody = ast_strdup(substitute_escapes(val));
12481       }
12482       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12483          pagersubject = ast_strdup(substitute_escapes(val));
12484       }
12485       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12486          pagerbody = ast_strdup(substitute_escapes(val));
12487       }
12488 
12489       /* load mailboxes from users.conf */
12490       if (ucfg) { 
12491          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12492             if (!strcasecmp(cat, "general")) {
12493                continue;
12494             }
12495             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12496                continue;
12497             if ((current = find_or_create(userscontext, cat))) {
12498                populate_defaults(current);
12499                apply_options_full(current, ast_variable_browse(ucfg, cat));
12500                ast_copy_string(current->context, userscontext, sizeof(current->context));
12501                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12502                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12503                }
12504 
12505                switch (current->passwordlocation) {
12506                case OPT_PWLOC_SPOOLDIR:
12507                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12508                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12509                }
12510             }
12511          }
12512       }
12513 
12514       /* load mailboxes from voicemail.conf */
12515       cat = ast_category_browse(cfg, NULL);
12516       while (cat) {
12517          if (strcasecmp(cat, "general")) {
12518             var = ast_variable_browse(cfg, cat);
12519             if (strcasecmp(cat, "zonemessages")) {
12520                /* Process mailboxes in this context */
12521                while (var) {
12522                   append_mailbox(cat, var->name, var->value);
12523                   var = var->next;
12524                }
12525             } else {
12526                /* Timezones in this context */
12527                while (var) {
12528                   struct vm_zone *z;
12529                   if ((z = ast_malloc(sizeof(*z)))) {
12530                      char *msg_format, *tzone;
12531                      msg_format = ast_strdupa(var->value);
12532                      tzone = strsep(&msg_format, "|,");
12533                      if (msg_format) {
12534                         ast_copy_string(z->name, var->name, sizeof(z->name));
12535                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12536                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12537                         AST_LIST_LOCK(&zones);
12538                         AST_LIST_INSERT_HEAD(&zones, z, list);
12539                         AST_LIST_UNLOCK(&zones);
12540                      } else {
12541                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12542                         ast_free(z);
12543                      }
12544                   } else {
12545                      AST_LIST_UNLOCK(&users);
12546                      return -1;
12547                   }
12548                   var = var->next;
12549                }
12550             }
12551          }
12552          cat = ast_category_browse(cfg, cat);
12553       }
12554 
12555       AST_LIST_UNLOCK(&users);
12556 
12557       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12558          start_poll_thread();
12559       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12560          stop_poll_thread();;
12561 
12562       return 0;
12563    } else {
12564       AST_LIST_UNLOCK(&users);
12565       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12566       return 0;
12567    }
12568 }
12569 
12570 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12571 {
12572    int res = -1;
12573    char dir[PATH_MAX];
12574    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12575    ast_debug(2, "About to try retrieving name file %s\n", dir);
12576    RETRIEVE(dir, -1, mailbox, context);
12577    if (ast_fileexists(dir, NULL, NULL)) {
12578       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12579    }
12580    DISPOSE(dir, -1);
12581    return res;
12582 }
12583 
12584 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12585    struct ast_config *pwconf;
12586    struct ast_flags config_flags = { 0 };
12587 
12588    pwconf = ast_config_load(secretfn, config_flags);
12589    if (valid_config(pwconf)) {
12590       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12591       if (val) {
12592          ast_copy_string(password, val, passwordlen);
12593          ast_config_destroy(pwconf);
12594          return;
12595       }
12596       ast_config_destroy(pwconf);
12597    }
12598    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12599 }
12600 
12601 static int write_password_to_file(const char *secretfn, const char *password) {
12602    struct ast_config *conf;
12603    struct ast_category *cat;
12604    struct ast_variable *var;
12605    int res = -1;
12606 
12607    if (!(conf = ast_config_new())) {
12608       ast_log(LOG_ERROR, "Error creating new config structure\n");
12609       return res;
12610    }
12611    if (!(cat = ast_category_new("general", "", 1))) {
12612       ast_log(LOG_ERROR, "Error creating new category structure\n");
12613       ast_config_destroy(conf);
12614       return res;
12615    }
12616    if (!(var = ast_variable_new("password", password, ""))) {
12617       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12618       ast_config_destroy(conf);
12619       ast_category_destroy(cat);
12620       return res;
12621    }
12622    ast_category_append(conf, cat);
12623    ast_variable_append(cat, var);
12624    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12625       res = 0;
12626    } else {
12627       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12628    }
12629 
12630    ast_config_destroy(conf);
12631    return res;
12632 }
12633 
12634 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12635 {
12636    char *context;
12637    char *args_copy;
12638    int res;
12639 
12640    if (ast_strlen_zero(data)) {
12641       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12642       return -1;
12643    }
12644 
12645    args_copy = ast_strdupa(data);
12646    if ((context = strchr(args_copy, '@'))) {
12647       *context++ = '\0';
12648    } else {
12649       context = "default";
12650    }
12651 
12652    if ((res = sayname(chan, args_copy, context) < 0)) {
12653       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12654       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12655       if (!res) {
12656          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12657       }
12658    }
12659 
12660    return res;
12661 }
12662 
12663 #ifdef TEST_FRAMEWORK
12664 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12665 {
12666    return 0;
12667 }
12668 
12669 static struct ast_frame *fake_read(struct ast_channel *ast)
12670 {
12671    return &ast_null_frame;
12672 }
12673 
12674 AST_TEST_DEFINE(test_voicemail_vmsayname)
12675 {
12676    char dir[PATH_MAX];
12677    char dir2[PATH_MAX];
12678    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12679    static const char TEST_EXTENSION[] = "1234";
12680 
12681    struct ast_channel *test_channel1 = NULL;
12682    int res = -1;
12683 
12684    static const struct ast_channel_tech fake_tech = {
12685       .write = fake_write,
12686       .read = fake_read,
12687    };
12688 
12689    switch (cmd) {
12690    case TEST_INIT:
12691       info->name = "vmsayname_exec";
12692       info->category = "/apps/app_voicemail/";
12693       info->summary = "Vmsayname unit test";
12694       info->description =
12695          "This tests passing various parameters to vmsayname";
12696       return AST_TEST_NOT_RUN;
12697    case TEST_EXECUTE:
12698       break;
12699    }
12700 
12701    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12702         NULL, NULL, 0, 0, "TestChannel1"))) {
12703       goto exit_vmsayname_test;
12704    }
12705 
12706    /* normally this is done in the channel driver */
12707    test_channel1->nativeformats = AST_FORMAT_GSM;
12708    test_channel1->writeformat = AST_FORMAT_GSM;
12709    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12710    test_channel1->readformat = AST_FORMAT_GSM;
12711    test_channel1->rawreadformat = AST_FORMAT_GSM;
12712    test_channel1->tech = &fake_tech;
12713 
12714    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12715    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12716    if (!(res = vmsayname_exec(test_channel1, dir))) {
12717       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12718       if (ast_fileexists(dir, NULL, NULL)) {
12719          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12720          res = -1;
12721          goto exit_vmsayname_test;
12722       } else {
12723          /* no greeting already exists as expected, let's create one to fully test sayname */
12724          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12725             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12726             goto exit_vmsayname_test;
12727          }
12728          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12729          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12730          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12731          if ((res = symlink(dir, dir2))) {
12732             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12733             goto exit_vmsayname_test;
12734          }
12735          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12736          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12737          res = vmsayname_exec(test_channel1, dir);
12738 
12739          /* TODO: there may be a better way to do this */
12740          unlink(dir2);
12741          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12742          rmdir(dir2);
12743          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12744          rmdir(dir2);
12745       }
12746    }
12747 
12748 exit_vmsayname_test:
12749 
12750    if (test_channel1) {
12751       ast_hangup(test_channel1);
12752    }
12753 
12754    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12755 }
12756 
12757 AST_TEST_DEFINE(test_voicemail_msgcount)
12758 {
12759    int i, j, res = AST_TEST_PASS, syserr;
12760    struct ast_vm_user *vmu;
12761    struct ast_vm_user svm;
12762    struct vm_state vms;
12763 #ifdef IMAP_STORAGE
12764    struct ast_channel *chan = NULL;
12765 #endif
12766    struct {
12767       char dir[256];
12768       char file[256];
12769       char txtfile[256];
12770    } tmp[3];
12771    char syscmd[256];
12772    const char origweasels[] = "tt-weasels";
12773    const char testcontext[] = "test";
12774    const char testmailbox[] = "00000000";
12775    const char testspec[] = "00000000@test";
12776    FILE *txt;
12777    int new, old, urgent;
12778    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12779    const int folder2mbox[3] = { 1, 11, 0 };
12780    const int expected_results[3][12] = {
12781       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12782       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12783       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12784       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12785    };
12786 
12787    switch (cmd) {
12788    case TEST_INIT:
12789       info->name = "test_voicemail_msgcount";
12790       info->category = "/apps/app_voicemail/";
12791       info->summary = "Test Voicemail status checks";
12792       info->description =
12793          "Verify that message counts are correct when retrieved through the public API";
12794       return AST_TEST_NOT_RUN;
12795    case TEST_EXECUTE:
12796       break;
12797    }
12798 
12799    /* Make sure the original path was completely empty */
12800    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12801    if ((syserr = ast_safe_system(syscmd))) {
12802       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12803          syserr > 0 ? strerror(syserr) : "unable to fork()");
12804       return AST_TEST_FAIL;
12805    }
12806 
12807 #ifdef IMAP_STORAGE
12808    if (!(chan = ast_dummy_channel_alloc())) {
12809       ast_test_status_update(test, "Unable to create dummy channel\n");
12810       return AST_TEST_FAIL;
12811    }
12812 #endif
12813 
12814    if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
12815       !(vmu = find_or_create(testcontext, testmailbox))) {
12816       ast_test_status_update(test, "Cannot create vmu structure\n");
12817       ast_unreplace_sigchld();
12818 #ifdef IMAP_STORAGE
12819       chan = ast_channel_unref(chan);
12820 #endif
12821       return AST_TEST_FAIL;
12822    }
12823 
12824    populate_defaults(vmu);
12825    memset(&vms, 0, sizeof(vms));
12826 
12827    /* Create temporary voicemail */
12828    for (i = 0; i < 3; i++) {
12829       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12830       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12831       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12832 
12833       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12834          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12835             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12836          if ((syserr = ast_safe_system(syscmd))) {
12837             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12838                syserr > 0 ? strerror(syserr) : "unable to fork()");
12839             ast_unreplace_sigchld();
12840 #ifdef IMAP_STORAGE
12841             chan = ast_channel_unref(chan);
12842 #endif
12843             return AST_TEST_FAIL;
12844          }
12845       }
12846 
12847       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12848          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12849          fclose(txt);
12850       } else {
12851          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12852          res = AST_TEST_FAIL;
12853          break;
12854       }
12855       open_mailbox(&vms, vmu, folder2mbox[i]);
12856       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12857 
12858       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12859       for (j = 0; j < 3; j++) {
12860          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12861          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12862             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12863                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12864             res = AST_TEST_FAIL;
12865          }
12866       }
12867 
12868       new = old = urgent = 0;
12869       if (ast_app_inboxcount(testspec, &new, &old)) {
12870          ast_test_status_update(test, "inboxcount returned failure\n");
12871          res = AST_TEST_FAIL;
12872       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12873          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12874             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12875          res = AST_TEST_FAIL;
12876       }
12877 
12878       new = old = urgent = 0;
12879       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12880          ast_test_status_update(test, "inboxcount2 returned failure\n");
12881          res = AST_TEST_FAIL;
12882       } else if (old != expected_results[i][6 + 0] ||
12883             urgent != expected_results[i][6 + 1] ||
12884                new != expected_results[i][6 + 2]    ) {
12885          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12886             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12887          res = AST_TEST_FAIL;
12888       }
12889 
12890       new = old = urgent = 0;
12891       for (j = 0; j < 3; j++) {
12892          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12893             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12894                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12895             res = AST_TEST_FAIL;
12896          }
12897       }
12898    }
12899 
12900    for (i = 0; i < 3; i++) {
12901       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12902        * server, in which case, the rm below will not affect the
12903        * voicemails. */
12904       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12905       DISPOSE(tmp[i].dir, 0);
12906    }
12907 
12908    if (vms.deleted) {
12909       ast_free(vms.deleted);
12910    }
12911    if (vms.heard) {
12912       ast_free(vms.heard);
12913    }
12914 
12915 #ifdef IMAP_STORAGE
12916    chan = ast_channel_unref(chan);
12917 #endif
12918 
12919    /* And remove test directory */
12920    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12921    if ((syserr = ast_safe_system(syscmd))) {
12922       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12923          syserr > 0 ? strerror(syserr) : "unable to fork()");
12924    }
12925 
12926    return res;
12927 }
12928 
12929 AST_TEST_DEFINE(test_voicemail_notify_endl)
12930 {
12931    int res = AST_TEST_PASS;
12932    char testcontext[] = "test";
12933    char testmailbox[] = "00000000";
12934    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12935    char attach[256], attach2[256];
12936    char buf[256] = ""; /* No line should actually be longer than 80 */
12937    struct ast_channel *chan = NULL;
12938    struct ast_vm_user *vmu, vmus = {
12939       .flags = 0,
12940    };
12941    FILE *file;
12942    struct {
12943       char *name;
12944       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12945       void *location;
12946       union {
12947          int intval;
12948          char *strval;
12949       } u;
12950    } test_items[] = {
12951       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12952       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12953       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12954       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12955       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12956       { "attach2", STRPTR, attach2, .u.strval = "" },
12957       { "attach", STRPTR, attach, .u.strval = "" },
12958    };
12959    int which;
12960 
12961    switch (cmd) {
12962    case TEST_INIT:
12963       info->name = "test_voicemail_notify_endl";
12964       info->category = "/apps/app_voicemail/";
12965       info->summary = "Test Voicemail notification end-of-line";
12966       info->description =
12967          "Verify that notification emails use a consistent end-of-line character";
12968       return AST_TEST_NOT_RUN;
12969    case TEST_EXECUTE:
12970       break;
12971    }
12972 
12973    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12974    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12975 
12976    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12977       !(vmu = find_or_create(testcontext, testmailbox))) {
12978       ast_test_status_update(test, "Cannot create vmu structure\n");
12979       return AST_TEST_NOT_RUN;
12980    }
12981 
12982    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12983       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12984       return AST_TEST_NOT_RUN;
12985    }
12986 
12987    populate_defaults(vmu);
12988    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12989 #ifdef IMAP_STORAGE
12990    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12991 #endif
12992 
12993    file = tmpfile();
12994    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12995       /* Kill previous test, if any */
12996       rewind(file);
12997       if (ftruncate(fileno(file), 0)) {
12998          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12999          res = AST_TEST_FAIL;
13000          break;
13001       }
13002 
13003       /* Make each change, in order, to the test mailbox */
13004       if (test_items[which].type == INT) {
13005          *((int *) test_items[which].location) = test_items[which].u.intval;
13006       } else if (test_items[which].type == FLAGVAL) {
13007          if (ast_test_flag(vmu, test_items[which].u.intval)) {
13008             ast_clear_flag(vmu, test_items[which].u.intval);
13009          } else {
13010             ast_set_flag(vmu, test_items[which].u.intval);
13011          }
13012       } else if (test_items[which].type == STATIC) {
13013          strcpy(test_items[which].location, test_items[which].u.strval);
13014       } else if (test_items[which].type == STRPTR) {
13015          test_items[which].location = test_items[which].u.strval;
13016       }
13017 
13018       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
13019       rewind(file);
13020       while (fgets(buf, sizeof(buf), file)) {
13021          if (
13022 #ifdef IMAP_STORAGE
13023          buf[strlen(buf) - 2] != '\r'
13024 #else
13025          buf[strlen(buf) - 2] == '\r'
13026 #endif
13027          || buf[strlen(buf) - 1] != '\n') {
13028             res = AST_TEST_FAIL;
13029          }
13030       }
13031    }
13032    fclose(file);
13033    return res;
13034 }
13035 
13036 AST_TEST_DEFINE(test_voicemail_load_config)
13037 {
13038    int res = AST_TEST_PASS;
13039    struct ast_vm_user *vmu;
13040    struct ast_config *cfg;
13041    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
13042    int fd;
13043    FILE *file;
13044    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
13045 
13046    switch (cmd) {
13047    case TEST_INIT:
13048       info->name = "test_voicemail_load_config";
13049       info->category = "/apps/app_voicemail/";
13050       info->summary = "Test loading Voicemail config";
13051       info->description =
13052          "Verify that configuration is loaded consistently. "
13053          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13054          "some options were loaded after the mailboxes were instantiated, causing "
13055          "those options not to be set correctly.";
13056       return AST_TEST_NOT_RUN;
13057    case TEST_EXECUTE:
13058       break;
13059    }
13060 
13061    /* build a config file by hand... */
13062    if ((fd = mkstemp(config_filename)) < 0) {
13063       return AST_TEST_FAIL;
13064    }
13065    if (!(file = fdopen(fd, "w"))) {
13066       close(fd);
13067       unlink(config_filename);
13068       return AST_TEST_FAIL;
13069    }
13070    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13071    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13072    fputs("00000002 => 9999,Mrs. Test\n", file);
13073    fclose(file);
13074 
13075    if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
13076       res = AST_TEST_FAIL;
13077       goto cleanup;
13078    }
13079 
13080    load_config_from_memory(1, cfg, NULL);
13081    ast_config_destroy(cfg);
13082 
13083 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13084    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13085    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13086 
13087    AST_LIST_LOCK(&users);
13088    AST_LIST_TRAVERSE(&users, vmu, list) {
13089       if (!strcmp(vmu->mailbox, "00000001")) {
13090          if (0); /* trick to get CHECK to work */
13091          CHECK(vmu, callback, "othercontext")
13092          CHECK(vmu, locale, "nl_NL.UTF-8")
13093          CHECK(vmu, zonetag, "central")
13094       } else if (!strcmp(vmu->mailbox, "00000002")) {
13095          if (0); /* trick to get CHECK to work */
13096          CHECK(vmu, callback, "somecontext")
13097          CHECK(vmu, locale, "de_DE.UTF-8")
13098          CHECK(vmu, zonetag, "european")
13099       }
13100    }
13101    AST_LIST_UNLOCK(&users);
13102 
13103 #undef CHECK
13104 
13105    /* restore config */
13106    load_config(1); /* this might say "Failed to load configuration file." */
13107 
13108 cleanup:
13109    unlink(config_filename);
13110    return res;
13111 }
13112 
13113 #endif /* defined(TEST_FRAMEWORK) */
13114 
13115 static int reload(void)
13116 {
13117    return load_config(1);
13118 }
13119 
13120 static int unload_module(void)
13121 {
13122    int res;
13123 
13124    res = ast_unregister_application(app);
13125    res |= ast_unregister_application(app2);
13126    res |= ast_unregister_application(app3);
13127    res |= ast_unregister_application(app4);
13128    res |= ast_unregister_application(sayname_app);
13129    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13130    res |= ast_manager_unregister("VoicemailUsersList");
13131    res |= ast_data_unregister(NULL);
13132 #ifdef TEST_FRAMEWORK
13133    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13134    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13135    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13136    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13137    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13138 #endif
13139    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13140    ast_uninstall_vm_functions();
13141    ao2_ref(inprocess_container, -1);
13142 
13143    if (poll_thread != AST_PTHREADT_NULL)
13144       stop_poll_thread();
13145 
13146    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13147    ast_unload_realtime("voicemail");
13148    ast_unload_realtime("voicemail_data");
13149 
13150    free_vm_users();
13151    free_vm_zones();
13152    return res;
13153 }
13154 
13155 static int load_module(void)
13156 {
13157    int res;
13158    my_umask = umask(0);
13159    umask(my_umask);
13160 
13161    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13162       return AST_MODULE_LOAD_DECLINE;
13163    }
13164 
13165    /* compute the location of the voicemail spool directory */
13166    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13167    
13168    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13169       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13170    }
13171 
13172    if ((res = load_config(0)))
13173       return res;
13174 
13175    res = ast_register_application_xml(app, vm_exec);
13176    res |= ast_register_application_xml(app2, vm_execmain);
13177    res |= ast_register_application_xml(app3, vm_box_exists);
13178    res |= ast_register_application_xml(app4, vmauthenticate);
13179    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13180    res |= ast_custom_function_register(&mailbox_exists_acf);
13181    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13182 #ifdef TEST_FRAMEWORK
13183    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13184    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13185    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13186    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13187    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13188 #endif
13189 
13190    if (res)
13191       return res;
13192 
13193    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13194    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13195 
13196    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13197    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13198    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13199 
13200    return res;
13201 }
13202 
13203 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13204 {
13205    int cmd = 0;
13206    char destination[80] = "";
13207    int retries = 0;
13208 
13209    if (!num) {
13210       ast_verb(3, "Destination number will be entered manually\n");
13211       while (retries < 3 && cmd != 't') {
13212          destination[1] = '\0';
13213          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13214          if (!cmd)
13215             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13216          if (!cmd)
13217             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13218          if (!cmd) {
13219             cmd = ast_waitfordigit(chan, 6000);
13220             if (cmd)
13221                destination[0] = cmd;
13222          }
13223          if (!cmd) {
13224             retries++;
13225          } else {
13226 
13227             if (cmd < 0)
13228                return 0;
13229             if (cmd == '*') {
13230                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13231                return 0;
13232             }
13233             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13234                retries++;
13235             else
13236                cmd = 't';
13237          }
13238          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13239       }
13240       if (retries >= 3) {
13241          return 0;
13242       }
13243       
13244    } else {
13245       if (option_verbose > 2)
13246          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13247       ast_copy_string(destination, num, sizeof(destination));
13248    }
13249 
13250    if (!ast_strlen_zero(destination)) {
13251       if (destination[strlen(destination) -1 ] == '*')
13252          return 0; 
13253       if (option_verbose > 2)
13254          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13255       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13256       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13257       chan->priority = 0;
13258       return 9;
13259    }
13260    return 0;
13261 }
13262 
13263 /*!
13264  * \brief The advanced options within a message.
13265  * \param chan
13266  * \param vmu 
13267  * \param vms
13268  * \param msg
13269  * \param option
13270  * \param record_gain
13271  *
13272  * Provides handling for the play message envelope, call the person back, or reply to message. 
13273  *
13274  * \return zero on success, -1 on error.
13275  */
13276 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)
13277 {
13278    int res = 0;
13279    char filename[PATH_MAX];
13280    struct ast_config *msg_cfg = NULL;
13281    const char *origtime, *context;
13282    char *name, *num;
13283    int retries = 0;
13284    char *cid;
13285    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13286 
13287    vms->starting = 0; 
13288 
13289    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13290 
13291    /* Retrieve info from VM attribute file */
13292    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13293    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13294    msg_cfg = ast_config_load(filename, config_flags);
13295    DISPOSE(vms->curdir, vms->curmsg);
13296    if (!valid_config(msg_cfg)) {
13297       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13298       return 0;
13299    }
13300 
13301    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13302       ast_config_destroy(msg_cfg);
13303       return 0;
13304    }
13305 
13306    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13307 
13308    context = ast_variable_retrieve(msg_cfg, "message", "context");
13309    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13310       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13311    switch (option) {
13312    case 3: /* Play message envelope */
13313       if (!res)
13314          res = play_message_datetime(chan, vmu, origtime, filename);
13315       if (!res)
13316          res = play_message_callerid(chan, vms, cid, context, 0);
13317 
13318       res = 't';
13319       break;
13320 
13321    case 2:  /* Call back */
13322 
13323       if (ast_strlen_zero(cid))
13324          break;
13325 
13326       ast_callerid_parse(cid, &name, &num);
13327       while ((res > -1) && (res != 't')) {
13328          switch (res) {
13329          case '1':
13330             if (num) {
13331                /* Dial the CID number */
13332                res = dialout(chan, vmu, num, vmu->callback);
13333                if (res) {
13334                   ast_config_destroy(msg_cfg);
13335                   return 9;
13336                }
13337             } else {
13338                res = '2';
13339             }
13340             break;
13341 
13342          case '2':
13343             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13344             if (!ast_strlen_zero(vmu->dialout)) {
13345                res = dialout(chan, vmu, NULL, vmu->dialout);
13346                if (res) {
13347                   ast_config_destroy(msg_cfg);
13348                   return 9;
13349                }
13350             } else {
13351                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13352                res = ast_play_and_wait(chan, "vm-sorry");
13353             }
13354             ast_config_destroy(msg_cfg);
13355             return res;
13356          case '*':
13357             res = 't';
13358             break;
13359          case '3':
13360          case '4':
13361          case '5':
13362          case '6':
13363          case '7':
13364          case '8':
13365          case '9':
13366          case '0':
13367 
13368             res = ast_play_and_wait(chan, "vm-sorry");
13369             retries++;
13370             break;
13371          default:
13372             if (num) {
13373                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13374                res = ast_play_and_wait(chan, "vm-num-i-have");
13375                if (!res)
13376                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13377                if (!res)
13378                   res = ast_play_and_wait(chan, "vm-tocallnum");
13379                /* Only prompt for a caller-specified number if there is a dialout context specified */
13380                if (!ast_strlen_zero(vmu->dialout)) {
13381                   if (!res)
13382                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13383                }
13384             } else {
13385                res = ast_play_and_wait(chan, "vm-nonumber");
13386                if (!ast_strlen_zero(vmu->dialout)) {
13387                   if (!res)
13388                      res = ast_play_and_wait(chan, "vm-toenternumber");
13389                }
13390             }
13391             if (!res) {
13392                res = ast_play_and_wait(chan, "vm-star-cancel");
13393             }
13394             if (!res) {
13395                res = ast_waitfordigit(chan, 6000);
13396             }
13397             if (!res) {
13398                retries++;
13399                if (retries > 3) {
13400                   res = 't';
13401                }
13402             }
13403             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13404             break; 
13405             
13406          }
13407          if (res == 't')
13408             res = 0;
13409          else if (res == '*')
13410             res = -1;
13411       }
13412       break;
13413       
13414    case 1:  /* Reply */
13415       /* Send reply directly to sender */
13416       if (ast_strlen_zero(cid))
13417          break;
13418 
13419       ast_callerid_parse(cid, &name, &num);
13420       if (!num) {
13421          ast_verb(3, "No CID number available, no reply sent\n");
13422          if (!res)
13423             res = ast_play_and_wait(chan, "vm-nonumber");
13424          ast_config_destroy(msg_cfg);
13425          return res;
13426       } else {
13427          struct ast_vm_user vmu2;
13428          if (find_user(&vmu2, vmu->context, num)) {
13429             struct leave_vm_options leave_options;
13430             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13431             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13432 
13433             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13434             
13435             memset(&leave_options, 0, sizeof(leave_options));
13436             leave_options.record_gain = record_gain;
13437             res = leave_voicemail(chan, mailbox, &leave_options);
13438             if (!res)
13439                res = 't';
13440             ast_config_destroy(msg_cfg);
13441             return res;
13442          } else {
13443             /* Sender has no mailbox, can't reply */
13444             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13445             ast_play_and_wait(chan, "vm-nobox");
13446             res = 't';
13447             ast_config_destroy(msg_cfg);
13448             return res;
13449          }
13450       } 
13451       res = 0;
13452 
13453       break;
13454    }
13455 
13456    ast_config_destroy(msg_cfg);
13457 
13458 #ifndef IMAP_STORAGE
13459    if (!res) {
13460       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13461       vms->heard[msg] = 1;
13462       res = wait_file(chan, vms, vms->fn);
13463    }
13464 #endif
13465    return res;
13466 }
13467 
13468 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13469          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13470          signed char record_gain, struct vm_state *vms, char *flag)
13471 {
13472    /* Record message & let caller review or re-record it, or set options if applicable */
13473    int res = 0;
13474    int cmd = 0;
13475    int max_attempts = 3;
13476    int attempts = 0;
13477    int recorded = 0;
13478    int msg_exists = 0;
13479    signed char zero_gain = 0;
13480    char tempfile[PATH_MAX];
13481    char *acceptdtmf = "#";
13482    char *canceldtmf = "";
13483    int canceleddtmf = 0;
13484 
13485    /* Note that urgent and private are for flagging messages as such in the future */
13486 
13487    /* barf if no pointer passed to store duration in */
13488    if (duration == NULL) {
13489       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13490       return -1;
13491    }
13492 
13493    if (!outsidecaller)
13494       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13495    else
13496       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13497 
13498    cmd = '3';  /* Want to start by recording */
13499 
13500    while ((cmd >= 0) && (cmd != 't')) {
13501       switch (cmd) {
13502       case '1':
13503          if (!msg_exists) {
13504             /* In this case, 1 is to record a message */
13505             cmd = '3';
13506             break;
13507          } else {
13508             /* Otherwise 1 is to save the existing message */
13509             ast_verb(3, "Saving message as is\n");
13510             if (!outsidecaller) 
13511                ast_filerename(tempfile, recordfile, NULL);
13512             ast_stream_and_wait(chan, "vm-msgsaved", "");
13513             if (!outsidecaller) {
13514                /* Saves to IMAP server only if imapgreeting=yes */
13515                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13516                DISPOSE(recordfile, -1);
13517             }
13518             cmd = 't';
13519             return res;
13520          }
13521       case '2':
13522          /* Review */
13523          ast_verb(3, "Reviewing the message\n");
13524          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13525          break;
13526       case '3':
13527          msg_exists = 0;
13528          /* Record */
13529          if (recorded == 1) 
13530             ast_verb(3, "Re-recording the message\n");
13531          else  
13532             ast_verb(3, "Recording the message\n");
13533          
13534          if (recorded && outsidecaller) {
13535             cmd = ast_play_and_wait(chan, INTRO);
13536             cmd = ast_play_and_wait(chan, "beep");
13537          }
13538          recorded = 1;
13539          /* 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 */
13540          if (record_gain)
13541             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13542          if (ast_test_flag(vmu, VM_OPERATOR))
13543             canceldtmf = "0";
13544          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13545          if (strchr(canceldtmf, cmd)) {
13546          /* need this flag here to distinguish between pressing '0' during message recording or after */
13547             canceleddtmf = 1;
13548          }
13549          if (record_gain)
13550             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13551          if (cmd == -1) {
13552             /* User has hung up, no options to give */
13553             if (!outsidecaller) {
13554                /* user was recording a greeting and they hung up, so let's delete the recording. */
13555                ast_filedelete(tempfile, NULL);
13556             }     
13557             return cmd;
13558          }
13559          if (cmd == '0') {
13560             break;
13561          } else if (cmd == '*') {
13562             break;
13563 #if 0
13564          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13565             /* Message is too short */
13566             ast_verb(3, "Message too short\n");
13567             cmd = ast_play_and_wait(chan, "vm-tooshort");
13568             cmd = ast_filedelete(tempfile, NULL);
13569             break;
13570          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13571             /* Message is all silence */
13572             ast_verb(3, "Nothing recorded\n");
13573             cmd = ast_filedelete(tempfile, NULL);
13574             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13575             if (!cmd)
13576                cmd = ast_play_and_wait(chan, "vm-speakup");
13577             break;
13578 #endif
13579          } else {
13580             /* If all is well, a message exists */
13581             msg_exists = 1;
13582             cmd = 0;
13583          }
13584          break;
13585       case '4':
13586          if (outsidecaller) {  /* only mark vm messages */
13587             /* Mark Urgent */
13588             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13589                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13590                res = ast_play_and_wait(chan, "vm-marked-urgent");
13591                strcpy(flag, "Urgent");
13592             } else if (flag) {
13593                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13594                res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13595                strcpy(flag, "");
13596             } else {
13597                ast_play_and_wait(chan, "vm-sorry");
13598             }
13599             cmd = 0;
13600          } else {
13601             cmd = ast_play_and_wait(chan, "vm-sorry");
13602          }
13603          break;
13604       case '5':
13605       case '6':
13606       case '7':
13607       case '8':
13608       case '9':
13609       case '*':
13610       case '#':
13611          cmd = ast_play_and_wait(chan, "vm-sorry");
13612          break;
13613 #if 0 
13614 /*  XXX Commented out for the moment because of the dangers of deleting
13615     a message while recording (can put the message numbers out of sync) */
13616       case '*':
13617          /* Cancel recording, delete message, offer to take another message*/
13618          cmd = ast_play_and_wait(chan, "vm-deleted");
13619          cmd = ast_filedelete(tempfile, NULL);
13620          if (outsidecaller) {
13621             res = vm_exec(chan, NULL);
13622             return res;
13623          }
13624          else
13625             return 1;
13626 #endif
13627       case '0':
13628          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13629             cmd = ast_play_and_wait(chan, "vm-sorry");
13630             break;
13631          }
13632          if (msg_exists || recorded) {
13633             cmd = ast_play_and_wait(chan, "vm-saveoper");
13634             if (!cmd)
13635                cmd = ast_waitfordigit(chan, 3000);
13636             if (cmd == '1') {
13637                ast_filerename(tempfile, recordfile, NULL);
13638                ast_play_and_wait(chan, "vm-msgsaved");
13639                cmd = '0';
13640             } else if (cmd == '4') {
13641                if (flag) {
13642                   ast_play_and_wait(chan, "vm-marked-urgent");
13643                   strcpy(flag, "Urgent");
13644                }
13645                ast_play_and_wait(chan, "vm-msgsaved");
13646                cmd = '0';
13647             } else {
13648                ast_play_and_wait(chan, "vm-deleted");
13649                DELETE(tempfile, -1, tempfile, vmu);
13650                cmd = '0';
13651             }
13652          }
13653          return cmd;
13654       default:
13655          /* If the caller is an ouside caller, and the review option is enabled,
13656             allow them to review the message, but let the owner of the box review
13657             their OGM's */
13658          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13659             return cmd;
13660          if (msg_exists) {
13661             cmd = ast_play_and_wait(chan, "vm-review");
13662             if (!cmd && outsidecaller) {
13663                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13664                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13665                } else if (flag) {
13666                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13667                }
13668             }
13669          } else {
13670             cmd = ast_play_and_wait(chan, "vm-torerecord");
13671             if (!cmd)
13672                cmd = ast_waitfordigit(chan, 600);
13673          }
13674          
13675          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13676             cmd = ast_play_and_wait(chan, "vm-reachoper");
13677             if (!cmd)
13678                cmd = ast_waitfordigit(chan, 600);
13679          }
13680 #if 0
13681          if (!cmd)
13682             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13683 #endif
13684          if (!cmd)
13685             cmd = ast_waitfordigit(chan, 6000);
13686          if (!cmd) {
13687             attempts++;
13688          }
13689          if (attempts > max_attempts) {
13690             cmd = 't';
13691          }
13692       }
13693    }
13694    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13695       /* Hang up or timeout, so delete the recording. */
13696       ast_filedelete(tempfile, NULL);
13697    }
13698 
13699    if (cmd != 't' && outsidecaller)
13700       ast_play_and_wait(chan, "vm-goodbye");
13701 
13702    return cmd;
13703 }
13704 
13705 /* This is a workaround so that menuselect displays a proper description
13706  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13707  */
13708 
13709 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13710       .load = load_module,
13711       .unload = unload_module,
13712       .reload = reload,
13713       .nonoptreq = "res_adsi,res_smdi",
13714       );