Wed Oct 29 05:02:07 2014

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

Generated on 29 Oct 2014 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1