Mon Oct 8 12:38:57 2012

Asterisk developer's documentation


app_voicemail_imapstorage.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: 369652 $")
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 mailcmd[160];               /*!< Configurable mail command */
00652    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00653    char zonetag[80];                /*!< Time zone */
00654    char locale[20];                 /*!< The locale (for presentation of date/time) */
00655    char callback[80];
00656    char dialout[80];
00657    char uniqueid[80];               /*!< Unique integer identifier */
00658    char exit[80];
00659    char attachfmt[20];              /*!< Attachment format */
00660    unsigned int flags;              /*!< VM_ flags */ 
00661    int saydurationm;
00662    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00663    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00664    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00665    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00666    int passwordlocation;            /*!< Storage location of the password */
00667 #ifdef IMAP_STORAGE
00668    char imapuser[80];               /*!< IMAP server login */
00669    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00670    char imapfolder[64];             /*!< IMAP voicemail folder */
00671    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00672    int imapversion;                 /*!< If configuration changes, use the new values */
00673 #endif
00674    double volgain;                  /*!< Volume gain for voicemails sent via email */
00675    AST_LIST_ENTRY(ast_vm_user) list;
00676 };
00677 
00678 /*! Voicemail time zones */
00679 struct vm_zone {
00680    AST_LIST_ENTRY(vm_zone) list;
00681    char name[80];
00682    char timezone[80];
00683    char msg_format[512];
00684 };
00685 
00686 #define VMSTATE_MAX_MSG_ARRAY 256
00687 
00688 /*! Voicemail mailbox state */
00689 struct vm_state {
00690    char curbox[80];
00691    char username[80];
00692    char context[80];
00693    char curdir[PATH_MAX];
00694    char vmbox[PATH_MAX];
00695    char fn[PATH_MAX];
00696    char intro[PATH_MAX];
00697    int *deleted;
00698    int *heard;
00699    int dh_arraysize; /* used for deleted / heard allocation */
00700    int curmsg;
00701    int lastmsg;
00702    int newmessages;
00703    int oldmessages;
00704    int urgentmessages;
00705    int starting;
00706    int repeats;
00707 #ifdef IMAP_STORAGE
00708    ast_mutex_t lock;
00709    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00710    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00711    MAILSTREAM *mailstream;
00712    int vmArrayIndex;
00713    char imapuser[80];                   /*!< IMAP server login */
00714    char imapfolder[64];                 /*!< IMAP voicemail folder */
00715    int imapversion;
00716    int interactive;
00717    char introfn[PATH_MAX];              /*!< Name of prepended file */
00718    unsigned int quota_limit;
00719    unsigned int quota_usage;
00720    struct vm_state *persist_vms;
00721 #endif
00722 };
00723 
00724 #ifdef ODBC_STORAGE
00725 static char odbc_database[80];
00726 static char odbc_table[80];
00727 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00728 #define DISPOSE(a,b) remove_file(a,b)
00729 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00730 #define EXISTS(a,b,c,d) (message_exists(a,b))
00731 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00732 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00733 #define DELETE(a,b,c,d) (delete_file(a,b))
00734 #else
00735 #ifdef IMAP_STORAGE
00736 #define DISPOSE(a,b) (imap_remove_file(a,b))
00737 #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))
00738 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00739 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00740 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00741 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00742 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00743 #else
00744 #define RETRIEVE(a,b,c,d)
00745 #define DISPOSE(a,b)
00746 #define STORE(a,b,c,d,e,f,g,h,i,j)
00747 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00748 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00749 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00750 #define DELETE(a,b,c,d) (vm_delete(c))
00751 #endif
00752 #endif
00753 
00754 static char VM_SPOOL_DIR[PATH_MAX];
00755 
00756 static char ext_pass_cmd[128];
00757 static char ext_pass_check_cmd[128];
00758 
00759 static int my_umask;
00760 
00761 #define PWDCHANGE_INTERNAL (1 << 1)
00762 #define PWDCHANGE_EXTERNAL (1 << 2)
00763 static int pwdchange = PWDCHANGE_INTERNAL;
00764 
00765 #ifdef ODBC_STORAGE
00766 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00767 #else
00768 # ifdef IMAP_STORAGE
00769 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00770 # else
00771 # define tdesc "Comedian Mail (Voicemail System)"
00772 # endif
00773 #endif
00774 
00775 static char userscontext[AST_MAX_EXTENSION] = "default";
00776 
00777 static char *addesc = "Comedian Mail";
00778 
00779 /* Leave a message */
00780 static char *app = "VoiceMail";
00781 
00782 /* Check mail, control, etc */
00783 static char *app2 = "VoiceMailMain";
00784 
00785 static char *app3 = "MailboxExists";
00786 static char *app4 = "VMAuthenticate";
00787 
00788 static char *sayname_app = "VMSayName";
00789 
00790 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00791 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00792 static char zonetag[80];
00793 static char locale[20];
00794 static int maxsilence;
00795 static int maxmsg;
00796 static int maxdeletedmsg;
00797 static int silencethreshold = 128;
00798 static char serveremail[80];
00799 static char mailcmd[160];  /* Configurable mail cmd */
00800 static char externnotify[160]; 
00801 static struct ast_smdi_interface *smdi_iface = NULL;
00802 static char vmfmts[80];
00803 static double volgain;
00804 static int vmminsecs;
00805 static int vmmaxsecs;
00806 static int maxgreet;
00807 static int skipms;
00808 static int maxlogins;
00809 static int minpassword;
00810 static int passwordlocation;
00811 
00812 /*! Poll mailboxes for changes since there is something external to
00813  *  app_voicemail that may change them. */
00814 static unsigned int poll_mailboxes;
00815 
00816 /*! Polling frequency */
00817 static unsigned int poll_freq;
00818 /*! By default, poll every 30 seconds */
00819 #define DEFAULT_POLL_FREQ 30
00820 
00821 AST_MUTEX_DEFINE_STATIC(poll_lock);
00822 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00823 static pthread_t poll_thread = AST_PTHREADT_NULL;
00824 static unsigned char poll_thread_run;
00825 
00826 /*! Subscription to ... MWI event subscriptions */
00827 static struct ast_event_sub *mwi_sub_sub;
00828 /*! Subscription to ... MWI event un-subscriptions */
00829 static struct ast_event_sub *mwi_unsub_sub;
00830 
00831 /*!
00832  * \brief An MWI subscription
00833  *
00834  * This is so we can keep track of which mailboxes are subscribed to.
00835  * This way, we know which mailboxes to poll when the pollmailboxes
00836  * option is being used.
00837  */
00838 struct mwi_sub {
00839    AST_RWLIST_ENTRY(mwi_sub) entry;
00840    int old_urgent;
00841    int old_new;
00842    int old_old;
00843    uint32_t uniqueid;
00844    char mailbox[1];
00845 };
00846 
00847 struct mwi_sub_task {
00848    const char *mailbox;
00849    const char *context;
00850    uint32_t uniqueid;
00851 };
00852 
00853 static struct ast_taskprocessor *mwi_subscription_tps;
00854 
00855 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00856 
00857 /* custom audio control prompts for voicemail playback */
00858 static char listen_control_forward_key[12];
00859 static char listen_control_reverse_key[12];
00860 static char listen_control_pause_key[12];
00861 static char listen_control_restart_key[12];
00862 static char listen_control_stop_key[12];
00863 
00864 /* custom password sounds */
00865 static char vm_password[80] = "vm-password";
00866 static char vm_newpassword[80] = "vm-newpassword";
00867 static char vm_passchanged[80] = "vm-passchanged";
00868 static char vm_reenterpassword[80] = "vm-reenterpassword";
00869 static char vm_mismatch[80] = "vm-mismatch";
00870 static char vm_invalid_password[80] = "vm-invalid-password";
00871 static char vm_pls_try_again[80] = "vm-pls-try-again";
00872 
00873 /*
00874  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00875  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00876  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00877  * app.c's __ast_play_and_record function
00878  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00879  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00880  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00881  * more effort than either of the other two.
00882  */
00883 static char vm_prepend_timeout[80] = "vm-then-pound";
00884 
00885 static struct ast_flags globalflags = {0};
00886 
00887 static int saydurationminfo;
00888 
00889 static char dialcontext[AST_MAX_CONTEXT] = "";
00890 static char callcontext[AST_MAX_CONTEXT] = "";
00891 static char exitcontext[AST_MAX_CONTEXT] = "";
00892 
00893 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00894 
00895 
00896 static char *emailbody = NULL;
00897 static char *emailsubject = NULL;
00898 static char *pagerbody = NULL;
00899 static char *pagersubject = NULL;
00900 static char fromstring[100];
00901 static char pagerfromstring[100];
00902 static char charset[32] = "ISO-8859-1";
00903 
00904 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00905 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00906 static int adsiver = 1;
00907 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00908 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
00909 
00910 /* Forward declarations - generic */
00911 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00912 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);
00913 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00914 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00915          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
00916          signed char record_gain, struct vm_state *vms, char *flag);
00917 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00918 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00919 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);
00920 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);
00921 static void apply_options(struct ast_vm_user *vmu, const char *options);
00922 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);
00923 static int is_valid_dtmf(const char *key);
00924 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
00925 static int write_password_to_file(const char *secretfn, const char *password);
00926 static const char *substitute_escapes(const char *value);
00927 static void free_user(struct ast_vm_user *vmu);
00928 
00929 struct ao2_container *inprocess_container;
00930 
00931 struct inprocess {
00932    int count;
00933    char *context;
00934    char mailbox[0];
00935 };
00936 
00937 static int inprocess_hash_fn(const void *obj, const int flags)
00938 {
00939    const struct inprocess *i = obj;
00940    return atoi(i->mailbox);
00941 }
00942 
00943 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00944 {
00945    struct inprocess *i = obj, *j = arg;
00946    if (strcmp(i->mailbox, j->mailbox)) {
00947       return 0;
00948    }
00949    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00950 }
00951 
00952 static int inprocess_count(const char *context, const char *mailbox, int delta)
00953 {
00954    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00955    arg->context = arg->mailbox + strlen(mailbox) + 1;
00956    strcpy(arg->mailbox, mailbox); /* SAFE */
00957    strcpy(arg->context, context); /* SAFE */
00958    ao2_lock(inprocess_container);
00959    if ((i = ao2_find(inprocess_container, arg, 0))) {
00960       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00961       ao2_unlock(inprocess_container);
00962       ao2_ref(i, -1);
00963       return ret;
00964    }
00965    if (delta < 0) {
00966       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00967    }
00968    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00969       ao2_unlock(inprocess_container);
00970       return 0;
00971    }
00972    i->context = i->mailbox + strlen(mailbox) + 1;
00973    strcpy(i->mailbox, mailbox); /* SAFE */
00974    strcpy(i->context, context); /* SAFE */
00975    i->count = delta;
00976    ao2_link(inprocess_container, i);
00977    ao2_unlock(inprocess_container);
00978    ao2_ref(i, -1);
00979    return 0;
00980 }
00981 
00982 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00983 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00984 #endif
00985 
00986 /*!
00987  * \brief Strips control and non 7-bit clean characters from input string.
00988  *
00989  * \note To map control and none 7-bit characters to a 7-bit clean characters
00990  *  please use ast_str_encode_mine().
00991  */
00992 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00993 {
00994    char *bufptr = buf;
00995    for (; *input; input++) {
00996       if (*input < 32) {
00997          continue;
00998       }
00999       *bufptr++ = *input;
01000       if (bufptr == buf + buflen - 1) {
01001          break;
01002       }
01003    }
01004    *bufptr = '\0';
01005    return buf;
01006 }
01007 
01008 
01009 /*!
01010  * \brief Sets default voicemail system options to a voicemail user.
01011  *
01012  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01013  * - all the globalflags
01014  * - the saydurationminfo
01015  * - the callcontext
01016  * - the dialcontext
01017  * - the exitcontext
01018  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01019  * - volume gain
01020  * - emailsubject, emailbody set to NULL
01021  */
01022 static void populate_defaults(struct ast_vm_user *vmu)
01023 {
01024    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01025    vmu->passwordlocation = passwordlocation;
01026    if (saydurationminfo) {
01027       vmu->saydurationm = saydurationminfo;
01028    }
01029    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01030    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01031    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01032    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01033    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01034    if (vmminsecs) {
01035       vmu->minsecs = vmminsecs;
01036    }
01037    if (vmmaxsecs) {
01038       vmu->maxsecs = vmmaxsecs;
01039    }
01040    if (maxmsg) {
01041       vmu->maxmsg = maxmsg;
01042    }
01043    if (maxdeletedmsg) {
01044       vmu->maxdeletedmsg = maxdeletedmsg;
01045    }
01046    vmu->volgain = volgain;
01047    ast_free(vmu->emailsubject);
01048    vmu->emailsubject = NULL;
01049    ast_free(vmu->emailbody);
01050    vmu->emailbody = NULL;
01051 #ifdef IMAP_STORAGE
01052    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01053 #endif
01054 }
01055 
01056 /*!
01057  * \brief Sets a a specific property value.
01058  * \param vmu The voicemail user object to work with.
01059  * \param var The name of the property to be set.
01060  * \param value The value to be set to the property.
01061  * 
01062  * The property name must be one of the understood properties. See the source for details.
01063  */
01064 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01065 {
01066    int x;
01067    if (!strcasecmp(var, "attach")) {
01068       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01069    } else if (!strcasecmp(var, "attachfmt")) {
01070       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01071    } else if (!strcasecmp(var, "serveremail")) {
01072       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01073    } else if (!strcasecmp(var, "emailbody")) {
01074       vmu->emailbody = ast_strdup(substitute_escapes(value));
01075    } else if (!strcasecmp(var, "emailsubject")) {
01076       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01077    } else if (!strcasecmp(var, "language")) {
01078       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01079    } else if (!strcasecmp(var, "tz")) {
01080       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01081    } else if (!strcasecmp(var, "locale")) {
01082       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01083 #ifdef IMAP_STORAGE
01084    } else if (!strcasecmp(var, "imapuser")) {
01085       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01086       vmu->imapversion = imapversion;
01087    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01088       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01089       vmu->imapversion = imapversion;
01090    } else if (!strcasecmp(var, "imapfolder")) {
01091       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01092    } else if (!strcasecmp(var, "imapvmshareid")) {
01093       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01094       vmu->imapversion = imapversion;
01095 #endif
01096    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01097       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01098    } else if (!strcasecmp(var, "saycid")){
01099       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01100    } else if (!strcasecmp(var, "sendvoicemail")){
01101       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01102    } else if (!strcasecmp(var, "review")){
01103       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01104    } else if (!strcasecmp(var, "tempgreetwarn")){
01105       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01106    } else if (!strcasecmp(var, "messagewrap")){
01107       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01108    } else if (!strcasecmp(var, "operator")) {
01109       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01110    } else if (!strcasecmp(var, "envelope")){
01111       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01112    } else if (!strcasecmp(var, "moveheard")){
01113       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01114    } else if (!strcasecmp(var, "sayduration")){
01115       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01116    } else if (!strcasecmp(var, "saydurationm")){
01117       if (sscanf(value, "%30d", &x) == 1) {
01118          vmu->saydurationm = x;
01119       } else {
01120          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01121       }
01122    } else if (!strcasecmp(var, "forcename")){
01123       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01124    } else if (!strcasecmp(var, "forcegreetings")){
01125       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01126    } else if (!strcasecmp(var, "callback")) {
01127       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01128    } else if (!strcasecmp(var, "dialout")) {
01129       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01130    } else if (!strcasecmp(var, "exitcontext")) {
01131       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01132    } else if (!strcasecmp(var, "minsecs")) {
01133       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01134          vmu->minsecs = x;
01135       } else {
01136          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01137          vmu->minsecs = vmminsecs;
01138       }
01139    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01140       vmu->maxsecs = atoi(value);
01141       if (vmu->maxsecs <= 0) {
01142          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01143          vmu->maxsecs = vmmaxsecs;
01144       } else {
01145          vmu->maxsecs = atoi(value);
01146       }
01147       if (!strcasecmp(var, "maxmessage"))
01148          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01149    } else if (!strcasecmp(var, "maxmsg")) {
01150       vmu->maxmsg = atoi(value);
01151       /* Accept maxmsg=0 (Greetings only voicemail) */
01152       if (vmu->maxmsg < 0) {
01153          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01154          vmu->maxmsg = MAXMSG;
01155       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01156          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01157          vmu->maxmsg = MAXMSGLIMIT;
01158       }
01159    } else if (!strcasecmp(var, "nextaftercmd")) {
01160       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01161    } else if (!strcasecmp(var, "backupdeleted")) {
01162       if (sscanf(value, "%30d", &x) == 1)
01163          vmu->maxdeletedmsg = x;
01164       else if (ast_true(value))
01165          vmu->maxdeletedmsg = MAXMSG;
01166       else
01167          vmu->maxdeletedmsg = 0;
01168 
01169       if (vmu->maxdeletedmsg < 0) {
01170          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01171          vmu->maxdeletedmsg = MAXMSG;
01172       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01173          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01174          vmu->maxdeletedmsg = MAXMSGLIMIT;
01175       }
01176    } else if (!strcasecmp(var, "volgain")) {
01177       sscanf(value, "%30lf", &vmu->volgain);
01178    } else if (!strcasecmp(var, "passwordlocation")) {
01179       if (!strcasecmp(value, "spooldir")) {
01180          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01181       } else {
01182          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01183       }
01184    } else if (!strcasecmp(var, "options")) {
01185       apply_options(vmu, value);
01186    }
01187 }
01188 
01189 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01190 {
01191    int fds[2], pid = 0;
01192 
01193    memset(buf, 0, len);
01194 
01195    if (pipe(fds)) {
01196       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01197    } else {
01198       /* good to go*/
01199       pid = ast_safe_fork(0);
01200 
01201       if (pid < 0) {
01202          /* ok maybe not */
01203          close(fds[0]);
01204          close(fds[1]);
01205          snprintf(buf, len, "FAILURE: Fork failed");
01206       } else if (pid) {
01207          /* parent */
01208          close(fds[1]);
01209          if (read(fds[0], buf, len) < 0) {
01210             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01211          }
01212          close(fds[0]);
01213       } else {
01214          /*  child */
01215          AST_DECLARE_APP_ARGS(arg,
01216             AST_APP_ARG(v)[20];
01217          );
01218          char *mycmd = ast_strdupa(command);
01219 
01220          close(fds[0]);
01221          dup2(fds[1], STDOUT_FILENO);
01222          close(fds[1]);
01223          ast_close_fds_above_n(STDOUT_FILENO);
01224 
01225          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01226 
01227          execv(arg.v[0], arg.v); 
01228          printf("FAILURE: %s", strerror(errno));
01229          _exit(0);
01230       }
01231    }
01232    return buf;
01233 }
01234 
01235 /*!
01236  * \brief Check that password meets minimum required length
01237  * \param vmu The voicemail user to change the password for.
01238  * \param password The password string to check
01239  *
01240  * \return zero on ok, 1 on not ok.
01241  */
01242 static int check_password(struct ast_vm_user *vmu, char *password)
01243 {
01244    /* check minimum length */
01245    if (strlen(password) < minpassword)
01246       return 1;
01247    /* check that password does not contain '*' character */
01248    if (!ast_strlen_zero(password) && password[0] == '*')
01249       return 1;
01250    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01251       char cmd[255], buf[255];
01252 
01253       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
01254 
01255       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01256       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01257          ast_debug(5, "Result: %s\n", buf);
01258          if (!strncasecmp(buf, "VALID", 5)) {
01259             ast_debug(3, "Passed password check: '%s'\n", buf);
01260             return 0;
01261          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01262             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01263             return 0;
01264          } else {
01265             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01266             return 1;
01267          }
01268       }
01269    }
01270    return 0;
01271 }
01272 
01273 /*! 
01274  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01275  * \param vmu The voicemail user to change the password for.
01276  * \param password The new value to be set to the password for this user.
01277  * 
01278  * This only works if there is a realtime engine configured.
01279  * This is called from the (top level) vm_change_password.
01280  *
01281  * \return zero on success, -1 on error.
01282  */
01283 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01284 {
01285    int res = -1;
01286    if (!strcmp(vmu->password, password)) {
01287       /* No change (but an update would return 0 rows updated, so we opt out here) */
01288       return 0;
01289    }
01290 
01291    if (strlen(password) > 10) {
01292       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01293    }
01294    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01295       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01296       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01297       res = 0;
01298    }
01299    return res;
01300 }
01301 
01302 /*!
01303  * \brief Destructively Parse options and apply.
01304  */
01305 static void apply_options(struct ast_vm_user *vmu, const char *options)
01306 {  
01307    char *stringp;
01308    char *s;
01309    char *var, *value;
01310    stringp = ast_strdupa(options);
01311    while ((s = strsep(&stringp, "|"))) {
01312       value = s;
01313       if ((var = strsep(&value, "=")) && value) {
01314          apply_option(vmu, var, value);
01315       }
01316    }  
01317 }
01318 
01319 /*!
01320  * \brief Loads the options specific to a voicemail user.
01321  * 
01322  * This is called when a vm_user structure is being set up, such as from load_options.
01323  */
01324 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01325 {
01326    for (; var; var = var->next) {
01327       if (!strcasecmp(var->name, "vmsecret")) {
01328          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01329       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01330          if (ast_strlen_zero(retval->password)) {
01331             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01332                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01333                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01334             } else {
01335                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01336             }
01337          }
01338       } else if (!strcasecmp(var->name, "uniqueid")) {
01339          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01340       } else if (!strcasecmp(var->name, "pager")) {
01341          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01342       } else if (!strcasecmp(var->name, "email")) {
01343          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01344       } else if (!strcasecmp(var->name, "fullname")) {
01345          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01346       } else if (!strcasecmp(var->name, "context")) {
01347          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01348       } else if (!strcasecmp(var->name, "emailsubject")) {
01349          ast_free(retval->emailsubject);
01350          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01351       } else if (!strcasecmp(var->name, "emailbody")) {
01352          ast_free(retval->emailbody);
01353          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01354 #ifdef IMAP_STORAGE
01355       } else if (!strcasecmp(var->name, "imapuser")) {
01356          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01357          retval->imapversion = imapversion;
01358       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01359          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01360          retval->imapversion = imapversion;
01361       } else if (!strcasecmp(var->name, "imapfolder")) {
01362          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01363       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01364          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01365          retval->imapversion = imapversion;
01366 #endif
01367       } else
01368          apply_option(retval, var->name, var->value);
01369    }
01370 }
01371 
01372 /*!
01373  * \brief Determines if a DTMF key entered is valid.
01374  * \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.
01375  *
01376  * Tests the character entered against the set of valid DTMF characters. 
01377  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01378  */
01379 static int is_valid_dtmf(const char *key)
01380 {
01381    int i;
01382    char *local_key = ast_strdupa(key);
01383 
01384    for (i = 0; i < strlen(key); ++i) {
01385       if (!strchr(VALID_DTMF, *local_key)) {
01386          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01387          return 0;
01388       }
01389       local_key++;
01390    }
01391    return 1;
01392 }
01393 
01394 /*!
01395  * \brief Finds a voicemail user from the realtime engine.
01396  * \param ivm
01397  * \param context
01398  * \param mailbox
01399  *
01400  * 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.
01401  *
01402  * \return The ast_vm_user structure for the user that was found.
01403  */
01404 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01405 {
01406    struct ast_variable *var;
01407    struct ast_vm_user *retval;
01408 
01409    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01410       if (!ivm)
01411          ast_set_flag(retval, VM_ALLOCED);   
01412       else
01413          memset(retval, 0, sizeof(*retval));
01414       if (mailbox) 
01415          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01416       populate_defaults(retval);
01417       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01418          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01419       else
01420          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01421       if (var) {
01422          apply_options_full(retval, var);
01423          ast_variables_destroy(var);
01424       } else { 
01425          if (!ivm) 
01426             free_user(retval);
01427          retval = NULL;
01428       }  
01429    } 
01430    return retval;
01431 }
01432 
01433 /*!
01434  * \brief Finds a voicemail user from the users file or the realtime engine.
01435  * \param ivm
01436  * \param context
01437  * \param mailbox
01438  * 
01439  * \return The ast_vm_user structure for the user that was found.
01440  */
01441 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01442 {
01443    /* This function could be made to generate one from a database, too */
01444    struct ast_vm_user *vmu = NULL, *cur;
01445    AST_LIST_LOCK(&users);
01446 
01447    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01448       context = "default";
01449 
01450    AST_LIST_TRAVERSE(&users, cur, list) {
01451 #ifdef IMAP_STORAGE
01452       if (cur->imapversion != imapversion) {
01453          continue;
01454       }
01455 #endif
01456       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01457          break;
01458       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01459          break;
01460    }
01461    if (cur) {
01462       /* Make a copy, so that on a reload, we have no race */
01463       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01464          *vmu = *cur;
01465          if (!ivm) {
01466             vmu->emailbody = ast_strdup(cur->emailbody);
01467             vmu->emailsubject = ast_strdup(cur->emailsubject);
01468          }
01469          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01470          AST_LIST_NEXT(vmu, list) = NULL;
01471       }
01472    } else
01473       vmu = find_user_realtime(ivm, context, mailbox);
01474    AST_LIST_UNLOCK(&users);
01475    return vmu;
01476 }
01477 
01478 /*!
01479  * \brief Resets a user password to a specified password.
01480  * \param context
01481  * \param mailbox
01482  * \param newpass
01483  *
01484  * This does the actual change password work, called by the vm_change_password() function.
01485  *
01486  * \return zero on success, -1 on error.
01487  */
01488 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01489 {
01490    /* This function could be made to generate one from a database, too */
01491    struct ast_vm_user *cur;
01492    int res = -1;
01493    AST_LIST_LOCK(&users);
01494    AST_LIST_TRAVERSE(&users, cur, list) {
01495       if ((!context || !strcasecmp(context, cur->context)) &&
01496          (!strcasecmp(mailbox, cur->mailbox)))
01497             break;
01498    }
01499    if (cur) {
01500       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01501       res = 0;
01502    }
01503    AST_LIST_UNLOCK(&users);
01504    return res;
01505 }
01506 
01507 /*! 
01508  * \brief The handler for the change password option.
01509  * \param vmu The voicemail user to work with.
01510  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01511  * 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.
01512  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01513  */
01514 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01515 {
01516    struct ast_config   *cfg = NULL;
01517    struct ast_variable *var = NULL;
01518    struct ast_category *cat = NULL;
01519    char *category = NULL, *value = NULL, *new = NULL;
01520    const char *tmp = NULL;
01521    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01522    char secretfn[PATH_MAX] = "";
01523    int found = 0;
01524 
01525    if (!change_password_realtime(vmu, newpassword))
01526       return;
01527 
01528    /* check if we should store the secret in the spool directory next to the messages */
01529    switch (vmu->passwordlocation) {
01530    case OPT_PWLOC_SPOOLDIR:
01531       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01532       if (write_password_to_file(secretfn, newpassword) == 0) {
01533          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01534          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01535          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01536          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01537          break;
01538       } else {
01539          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01540       }
01541       /* Fall-through */
01542    case OPT_PWLOC_VOICEMAILCONF:
01543       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01544          while ((category = ast_category_browse(cfg, category))) {
01545             if (!strcasecmp(category, vmu->context)) {
01546                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01547                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01548                   break;
01549                }
01550                value = strstr(tmp, ",");
01551                if (!value) {
01552                   new = alloca(strlen(newpassword)+1);
01553                   sprintf(new, "%s", newpassword);
01554                } else {
01555                   new = alloca((strlen(value) + strlen(newpassword) + 1));
01556                   sprintf(new, "%s%s", newpassword, value);
01557                }
01558                if (!(cat = ast_category_get(cfg, category))) {
01559                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01560                   break;
01561                }
01562                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01563                found = 1;
01564             }
01565          }
01566          /* save the results */
01567          if (found) {
01568             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01569             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01570             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01571             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01572             break;
01573          }
01574       }
01575       /* Fall-through */
01576    case OPT_PWLOC_USERSCONF:
01577       /* check users.conf and update the password stored for the mailbox */
01578       /* if no vmsecret entry exists create one. */
01579       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01580          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01581          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01582             ast_debug(4, "users.conf: %s\n", category);
01583             if (!strcasecmp(category, vmu->mailbox)) {
01584                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01585                   ast_debug(3, "looks like we need to make vmsecret!\n");
01586                   var = ast_variable_new("vmsecret", newpassword, "");
01587                } else {
01588                   var = NULL;
01589                }
01590                new = alloca(strlen(newpassword) + 1);
01591                sprintf(new, "%s", newpassword);
01592                if (!(cat = ast_category_get(cfg, category))) {
01593                   ast_debug(4, "failed to get category!\n");
01594                   ast_free(var);
01595                   break;
01596                }
01597                if (!var) {
01598                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01599                } else {
01600                   ast_variable_append(cat, var);
01601                }
01602                found = 1;
01603                break;
01604             }
01605          }
01606          /* save the results and clean things up */
01607          if (found) {
01608             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01609             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01610             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01611             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01612          }
01613       }
01614    }
01615 }
01616 
01617 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01618 {
01619    char buf[255];
01620    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01621    ast_debug(1, "External password: %s\n",buf);
01622    if (!ast_safe_system(buf)) {
01623       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01624       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01625       /* Reset the password in memory, too */
01626       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01627    }
01628 }
01629 
01630 /*! 
01631  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01632  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01633  * \param len The length of the path string that was written out.
01634  * \param context
01635  * \param ext 
01636  * \param folder 
01637  * 
01638  * The path is constructed as 
01639  *    VM_SPOOL_DIRcontext/ext/folder
01640  *
01641  * \return zero on success, -1 on error.
01642  */
01643 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01644 {
01645    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01646 }
01647 
01648 /*! 
01649  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01650  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01651  * \param len The length of the path string that was written out.
01652  * \param dir 
01653  * \param num 
01654  * 
01655  * The path is constructed as 
01656  *    VM_SPOOL_DIRcontext/ext/folder
01657  *
01658  * \return zero on success, -1 on error.
01659  */
01660 static int make_file(char *dest, const int len, const char *dir, const int num)
01661 {
01662    return snprintf(dest, len, "%s/msg%04d", dir, num);
01663 }
01664 
01665 /* same as mkstemp, but return a FILE * */
01666 static FILE *vm_mkftemp(char *template)
01667 {
01668    FILE *p = NULL;
01669    int pfd = mkstemp(template);
01670    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01671    if (pfd > -1) {
01672       p = fdopen(pfd, "w+");
01673       if (!p) {
01674          close(pfd);
01675          pfd = -1;
01676       }
01677    }
01678    return p;
01679 }
01680 
01681 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01682  * \param dest    String. base directory.
01683  * \param len     Length of dest.
01684  * \param context String. Ignored if is null or empty string.
01685  * \param ext     String. Ignored if is null or empty string.
01686  * \param folder  String. Ignored if is null or empty string. 
01687  * \return -1 on failure, 0 on success.
01688  */
01689 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01690 {
01691    mode_t   mode = VOICEMAIL_DIR_MODE;
01692    int res;
01693 
01694    make_dir(dest, len, context, ext, folder);
01695    if ((res = ast_mkdir(dest, mode))) {
01696       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01697       return -1;
01698    }
01699    return 0;
01700 }
01701 
01702 static const char * const mailbox_folders[] = {
01703 #ifdef IMAP_STORAGE
01704    imapfolder,
01705 #else
01706    "INBOX",
01707 #endif
01708    "Old",
01709    "Work",
01710    "Family",
01711    "Friends",
01712    "Cust1",
01713    "Cust2",
01714    "Cust3",
01715    "Cust4",
01716    "Cust5",
01717    "Deleted",
01718    "Urgent",
01719 };
01720 
01721 static const char *mbox(struct ast_vm_user *vmu, int id)
01722 {
01723 #ifdef IMAP_STORAGE
01724    if (vmu && id == 0) {
01725       return vmu->imapfolder;
01726    }
01727 #endif
01728    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01729 }
01730 
01731 static int get_folder_by_name(const char *name)
01732 {
01733    size_t i;
01734 
01735    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01736       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01737          return i;
01738       }
01739    }
01740 
01741    return -1;
01742 }
01743 
01744 static void free_user(struct ast_vm_user *vmu)
01745 {
01746    if (ast_test_flag(vmu, VM_ALLOCED)) {
01747 
01748       ast_free(vmu->emailbody);
01749       vmu->emailbody = NULL;
01750 
01751       ast_free(vmu->emailsubject);
01752       vmu->emailsubject = NULL;
01753 
01754       ast_free(vmu);
01755    }
01756 }
01757 
01758 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01759 
01760    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01761 
01762    /* remove old allocation */
01763    if (vms->deleted) {
01764       ast_free(vms->deleted);
01765       vms->deleted = NULL;
01766    }
01767    if (vms->heard) {
01768       ast_free(vms->heard);
01769       vms->heard = NULL;
01770    }
01771    vms->dh_arraysize = 0;
01772 
01773    if (arraysize > 0) {
01774       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01775          return -1;
01776       }
01777       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01778          ast_free(vms->deleted);
01779          vms->deleted = NULL;
01780          return -1;
01781       }
01782       vms->dh_arraysize = arraysize;
01783    }
01784 
01785    return 0;
01786 }
01787 
01788 /* All IMAP-specific functions should go in this block. This
01789  * keeps them from being spread out all over the code */
01790 #ifdef IMAP_STORAGE
01791 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01792 {
01793    char arg[10];
01794    struct vm_state *vms;
01795    unsigned long messageNum;
01796 
01797    /* If greetings aren't stored in IMAP, just delete the file */
01798    if (msgnum < 0 && !imapgreetings) {
01799       ast_filedelete(file, NULL);
01800       return;
01801    }
01802 
01803    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01804       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);
01805       return;
01806    }
01807 
01808    /* find real message number based on msgnum */
01809    /* this may be an index into vms->msgArray based on the msgnum. */
01810    messageNum = vms->msgArray[msgnum];
01811    if (messageNum == 0) {
01812       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01813       return;
01814    }
01815    if (option_debug > 2)
01816       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01817    /* delete message */
01818    snprintf (arg, sizeof(arg), "%lu", messageNum);
01819    ast_mutex_lock(&vms->lock);
01820    mail_setflag (vms->mailstream, arg, "\\DELETED");
01821    mail_expunge(vms->mailstream);
01822    ast_mutex_unlock(&vms->lock);
01823 }
01824 
01825 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01826 {
01827    struct vm_state *vms_p;
01828    char *file, *filename;
01829    char *attachment;
01830    int i;
01831    BODY *body;
01832 
01833    /* This function is only used for retrieval of IMAP greetings
01834     * regular messages are not retrieved this way, nor are greetings
01835     * if they are stored locally*/
01836    if (msgnum > -1 || !imapgreetings) {
01837       return 0;
01838    } else {
01839       file = strrchr(ast_strdupa(dir), '/');
01840       if (file)
01841          *file++ = '\0';
01842       else {
01843          ast_debug (1, "Failed to procure file name from directory passed.\n");
01844          return -1;
01845       }
01846    }
01847 
01848    /* check if someone is accessing this box right now... */
01849    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01850       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01851       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01852       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01853       * that's all we need to do.
01854       */
01855       if (!(vms_p = create_vm_state_from_user(vmu))) {
01856          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01857          return -1;
01858       }
01859    }
01860 
01861    /* Greetings will never have a prepended message */
01862    *vms_p->introfn = '\0';
01863 
01864    ast_mutex_lock(&vms_p->lock);
01865    init_mailstream(vms_p, GREETINGS_FOLDER);
01866    if (!vms_p->mailstream) {
01867       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01868       ast_mutex_unlock(&vms_p->lock);
01869       return -1;
01870    }
01871 
01872    /*XXX Yuck, this could probably be done a lot better */
01873    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01874       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01875       /* We have the body, now we extract the file name of the first attachment. */
01876       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01877          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01878       } else {
01879          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01880          ast_mutex_unlock(&vms_p->lock);
01881          return -1;
01882       }
01883       filename = strsep(&attachment, ".");
01884       if (!strcmp(filename, file)) {
01885          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01886          vms_p->msgArray[vms_p->curmsg] = i + 1;
01887          save_body(body, vms_p, "2", attachment, 0);
01888          ast_mutex_unlock(&vms_p->lock);
01889          return 0;
01890       }
01891    }
01892    ast_mutex_unlock(&vms_p->lock);
01893 
01894    return -1;
01895 }
01896 
01897 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01898 {
01899    BODY *body;
01900    char *header_content;
01901    char *attachedfilefmt;
01902    char buf[80];
01903    struct vm_state *vms;
01904    char text_file[PATH_MAX];
01905    FILE *text_file_ptr;
01906    int res = 0;
01907    struct ast_vm_user *vmu;
01908 
01909    if (!(vmu = find_user(NULL, context, mailbox))) {
01910       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01911       return -1;
01912    }
01913    
01914    if (msgnum < 0) {
01915       if (imapgreetings) {
01916          res = imap_retrieve_greeting(dir, msgnum, vmu);
01917          goto exit;
01918       } else {
01919          res = 0;
01920          goto exit;
01921       }
01922    }
01923 
01924    /* Before anything can happen, we need a vm_state so that we can
01925     * actually access the imap server through the vms->mailstream
01926     */
01927    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01928       /* This should not happen. If it does, then I guess we'd
01929        * need to create the vm_state, extract which mailbox to
01930        * open, and then set up the msgArray so that the correct
01931        * IMAP message could be accessed. If I have seen correctly
01932        * though, the vms should be obtainable from the vmstates list
01933        * and should have its msgArray properly set up.
01934        */
01935       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01936       res = -1;
01937       goto exit;
01938    }
01939    
01940    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01941    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01942 
01943    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01944    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01945       res = 0;
01946       goto exit;
01947    }
01948 
01949    if (option_debug > 2)
01950       ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01951    if (vms->msgArray[msgnum] == 0) {
01952       ast_log(LOG_WARNING, "Trying to access unknown message\n");
01953       res = -1;
01954       goto exit;
01955    }
01956 
01957    /* This will only work for new messages... */
01958    ast_mutex_lock(&vms->lock);
01959    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01960    ast_mutex_unlock(&vms->lock);
01961    /* empty string means no valid header */
01962    if (ast_strlen_zero(header_content)) {
01963       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
01964       res = -1;
01965       goto exit;
01966    }
01967 
01968    ast_mutex_lock(&vms->lock);
01969    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
01970    ast_mutex_unlock(&vms->lock);
01971 
01972    /* We have the body, now we extract the file name of the first attachment. */
01973    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01974       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01975    } else {
01976       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01977       res = -1;
01978       goto exit;
01979    }
01980    
01981    /* Find the format of the attached file */
01982 
01983    strsep(&attachedfilefmt, ".");
01984    if (!attachedfilefmt) {
01985       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01986       res = -1;
01987       goto exit;
01988    }
01989    
01990    save_body(body, vms, "2", attachedfilefmt, 0);
01991    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01992       *vms->introfn = '\0';
01993    }
01994 
01995    /* Get info from headers!! */
01996    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01997 
01998    if (!(text_file_ptr = fopen(text_file, "w"))) {
01999       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
02000    }
02001 
02002    fprintf(text_file_ptr, "%s\n", "[message]");
02003 
02004    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
02005    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
02006    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
02007    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02008    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
02009    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02010    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
02011    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02012    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
02013    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02014    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
02015    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02016    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
02017    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02018    fclose(text_file_ptr);
02019 
02020 exit:
02021    free_user(vmu);
02022    return res;
02023 }
02024 
02025 static int folder_int(const char *folder)
02026 {
02027    /*assume a NULL folder means INBOX*/
02028    if (!folder) {
02029       return 0;
02030    }
02031    if (!strcasecmp(folder, imapfolder)) {
02032       return 0;
02033    } else if (!strcasecmp(folder, "Old")) {
02034       return 1;
02035    } else if (!strcasecmp(folder, "Work")) {
02036       return 2;
02037    } else if (!strcasecmp(folder, "Family")) {
02038       return 3;
02039    } else if (!strcasecmp(folder, "Friends")) {
02040       return 4;
02041    } else if (!strcasecmp(folder, "Cust1")) {
02042       return 5;
02043    } else if (!strcasecmp(folder, "Cust2")) {
02044       return 6;
02045    } else if (!strcasecmp(folder, "Cust3")) {
02046       return 7;
02047    } else if (!strcasecmp(folder, "Cust4")) {
02048       return 8;
02049    } else if (!strcasecmp(folder, "Cust5")) {
02050       return 9;
02051    } else if (!strcasecmp(folder, "Urgent")) {
02052       return 11;
02053    } else { /*assume they meant INBOX if folder is not found otherwise*/
02054       return 0;
02055    }
02056 }
02057 
02058 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02059 {
02060    SEARCHPGM *pgm;
02061    SEARCHHEADER *hdr;
02062 
02063    struct ast_vm_user *vmu, vmus;
02064    struct vm_state *vms_p;
02065    int ret = 0;
02066    int fold = folder_int(folder);
02067    int urgent = 0;
02068    
02069    /* If URGENT, then look at INBOX */
02070    if (fold == 11) {
02071       fold = NEW_FOLDER;
02072       urgent = 1;
02073    }
02074 
02075    if (ast_strlen_zero(mailbox))
02076       return 0;
02077 
02078    /* We have to get the user before we can open the stream! */
02079    vmu = find_user(&vmus, context, mailbox);
02080    if (!vmu) {
02081       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02082       return -1;
02083    } else {
02084       /* No IMAP account available */
02085       if (vmu->imapuser[0] == '\0') {
02086          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02087          return -1;
02088       }
02089    }
02090    
02091    /* No IMAP account available */
02092    if (vmu->imapuser[0] == '\0') {
02093       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02094       free_user(vmu);
02095       return -1;
02096    }
02097 
02098    /* check if someone is accessing this box right now... */
02099    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02100    if (!vms_p) {
02101       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02102    }
02103    if (vms_p) {
02104       ast_debug(3, "Returning before search - user is logged in\n");
02105       if (fold == 0) { /* INBOX */
02106          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02107       }
02108       if (fold == 1) { /* Old messages */
02109          return vms_p->oldmessages;
02110       }
02111    }
02112 
02113    /* add one if not there... */
02114    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02115    if (!vms_p) {
02116       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02117    }
02118 
02119    if (!vms_p) {
02120       vms_p = create_vm_state_from_user(vmu);
02121    }
02122    ret = init_mailstream(vms_p, fold);
02123    if (!vms_p->mailstream) {
02124       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02125       return -1;
02126    }
02127    if (ret == 0) {
02128       ast_mutex_lock(&vms_p->lock);
02129       pgm = mail_newsearchpgm ();
02130       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02131       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02132       pgm->header = hdr;
02133       if (fold != OLD_FOLDER) {
02134          pgm->unseen = 1;
02135          pgm->seen = 0;
02136       }
02137       /* In the special case where fold is 1 (old messages) we have to do things a bit
02138        * differently. Old messages are stored in the INBOX but are marked as "seen"
02139        */
02140       else {
02141          pgm->unseen = 0;
02142          pgm->seen = 1;
02143       }
02144       /* look for urgent messages */
02145       if (fold == NEW_FOLDER) {
02146          if (urgent) {
02147             pgm->flagged = 1;
02148             pgm->unflagged = 0;
02149          } else {
02150             pgm->flagged = 0;
02151             pgm->unflagged = 1;
02152          }
02153       }
02154       pgm->undeleted = 1;
02155       pgm->deleted = 0;
02156 
02157       vms_p->vmArrayIndex = 0;
02158       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02159       if (fold == 0 && urgent == 0)
02160          vms_p->newmessages = vms_p->vmArrayIndex;
02161       if (fold == 1)
02162          vms_p->oldmessages = vms_p->vmArrayIndex;
02163       if (fold == 0 && urgent == 1)
02164          vms_p->urgentmessages = vms_p->vmArrayIndex;
02165       /*Freeing the searchpgm also frees the searchhdr*/
02166       mail_free_searchpgm(&pgm);
02167       ast_mutex_unlock(&vms_p->lock);
02168       vms_p->updated = 0;
02169       return vms_p->vmArrayIndex;
02170    } else {
02171       ast_mutex_lock(&vms_p->lock);
02172       mail_ping(vms_p->mailstream);
02173       ast_mutex_unlock(&vms_p->lock);
02174    }
02175    return 0;
02176 }
02177 
02178 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02179 {
02180    /* Check if mailbox is full */
02181    check_quota(vms, vmu->imapfolder);
02182    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02183       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02184       ast_play_and_wait(chan, "vm-mailboxfull");
02185       return -1;
02186    }
02187    
02188    /* Check if we have exceeded maxmsg */
02189    ast_debug(3, "Checking message number quota: mailbox has %d messages, maximum is set to %d, current messages %d\n", msgnum, vmu->maxmsg, inprocess_count(vmu->mailbox, vmu->context, 0));
02190    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02191       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02192       ast_play_and_wait(chan, "vm-mailboxfull");
02193       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02194       return -1;
02195    }
02196 
02197    return 0;
02198 }
02199 
02200 /*!
02201  * \brief Gets the number of messages that exist in a mailbox folder.
02202  * \param context
02203  * \param mailbox
02204  * \param folder
02205  * 
02206  * This method is used when IMAP backend is used.
02207  * \return The number of messages in this mailbox folder (zero or more).
02208  */
02209 static int messagecount(const char *context, const char *mailbox, const char *folder)
02210 {
02211    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02212       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02213    } else {
02214       return __messagecount(context, mailbox, folder);
02215    }
02216 }
02217 
02218 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag)
02219 {
02220    char *myserveremail = serveremail;
02221    char fn[PATH_MAX];
02222    char introfn[PATH_MAX];
02223    char mailbox[256];
02224    char *stringp;
02225    FILE *p = NULL;
02226    char tmp[80] = "/tmp/astmail-XXXXXX";
02227    long len;
02228    void *buf;
02229    int tempcopy = 0;
02230    STRING str;
02231    int ret; /* for better error checking */
02232    char *imap_flags = NIL;
02233    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02234    int box = NEW_FOLDER;
02235 
02236    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02237    if (msgnum < 0) {
02238       if(!imapgreetings) {
02239          return 0;
02240       } else {
02241          box = GREETINGS_FOLDER;
02242       }
02243    }
02244    
02245    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02246       return -1;
02247    }
02248 
02249    /* Set urgent flag for IMAP message */
02250    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02251       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02252       imap_flags = "\\FLAGGED";
02253    }
02254    
02255    /* Attach only the first format */
02256    fmt = ast_strdupa(fmt);
02257    stringp = fmt;
02258    strsep(&stringp, "|");
02259 
02260    if (!ast_strlen_zero(vmu->serveremail))
02261       myserveremail = vmu->serveremail;
02262 
02263    if (msgnum > -1)
02264       make_file(fn, sizeof(fn), dir, msgnum);
02265    else
02266       ast_copy_string (fn, dir, sizeof(fn));
02267 
02268    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02269    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02270       *introfn = '\0';
02271    }
02272    
02273    if (ast_strlen_zero(vmu->email)) {
02274       /* We need the vmu->email to be set when we call make_email_file, but
02275        * if we keep it set, a duplicate e-mail will be created. So at the end
02276        * of this function, we will revert back to an empty string if tempcopy
02277        * is 1.
02278        */
02279       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02280       tempcopy = 1;
02281    }
02282 
02283    if (!strcmp(fmt, "wav49"))
02284       fmt = "WAV";
02285    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02286 
02287    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02288       command hangs. */
02289    if (!(p = vm_mkftemp(tmp))) {
02290       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02291       if (tempcopy)
02292          *(vmu->email) = '\0';
02293       return -1;
02294    }
02295 
02296    if (msgnum < 0 && imapgreetings) {
02297       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02298          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02299          return -1;
02300       }
02301       imap_delete_old_greeting(fn, vms);
02302    }
02303 
02304    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02305       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
02306       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
02307       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
02308    /* read mail file to memory */
02309    len = ftell(p);
02310    rewind(p);
02311    if (!(buf = ast_malloc(len + 1))) {
02312       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02313       fclose(p);
02314       if (tempcopy)
02315          *(vmu->email) = '\0';
02316       return -1;
02317    }
02318    if (fread(buf, len, 1, p) < len) {
02319       if (ferror(p)) {
02320          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02321          return -1;
02322       }
02323    }
02324    ((char *) buf)[len] = '\0';
02325    INIT(&str, mail_string, buf, len);
02326    ret = init_mailstream(vms, box);
02327    if (ret == 0) {
02328       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02329       ast_mutex_lock(&vms->lock);
02330       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02331          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02332       ast_mutex_unlock(&vms->lock);
02333       fclose(p);
02334       unlink(tmp);
02335       ast_free(buf);
02336    } else {
02337       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02338       fclose(p);
02339       unlink(tmp);
02340       ast_free(buf);
02341       return -1;
02342    }
02343    ast_debug(3, "%s stored\n", fn);
02344    
02345    if (tempcopy)
02346       *(vmu->email) = '\0';
02347    inprocess_count(vmu->mailbox, vmu->context, -1);
02348    return 0;
02349 
02350 }
02351 
02352 /*!
02353  * \brief Gets the number of messages that exist in the inbox folder.
02354  * \param mailbox_context
02355  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02356  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02357  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02358  * 
02359  * This method is used when IMAP backend is used.
02360  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02361  *
02362  * \return zero on success, -1 on error.
02363  */
02364 
02365 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02366 {
02367    char tmp[PATH_MAX] = "";
02368    char *mailboxnc;
02369    char *context;
02370    char *mb;
02371    char *cur;
02372    if (newmsgs)
02373       *newmsgs = 0;
02374    if (oldmsgs)
02375       *oldmsgs = 0;
02376    if (urgentmsgs)
02377       *urgentmsgs = 0;
02378 
02379    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02380    /* If no mailbox, return immediately */
02381    if (ast_strlen_zero(mailbox_context))
02382       return 0;
02383    
02384    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02385    context = strchr(tmp, '@');
02386    if (strchr(mailbox_context, ',')) {
02387       int tmpnew, tmpold, tmpurgent;
02388       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02389       mb = tmp;
02390       while ((cur = strsep(&mb, ", "))) {
02391          if (!ast_strlen_zero(cur)) {
02392             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02393                return -1;
02394             else {
02395                if (newmsgs)
02396                   *newmsgs += tmpnew; 
02397                if (oldmsgs)
02398                   *oldmsgs += tmpold;
02399                if (urgentmsgs)
02400                   *urgentmsgs += tmpurgent;
02401             }
02402          }
02403       }
02404       return 0;
02405    }
02406    if (context) {
02407       *context = '\0';
02408       mailboxnc = tmp;
02409       context++;
02410    } else {
02411       context = "default";
02412       mailboxnc = (char *) mailbox_context;
02413    }
02414 
02415    if (newmsgs) {
02416       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02417       if (!vmu) {
02418          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02419          return -1;
02420       }
02421       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02422          return -1;
02423       }
02424    }
02425    if (oldmsgs) {
02426       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02427          return -1;
02428       }
02429    }
02430    if (urgentmsgs) {
02431       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02432          return -1;
02433       }
02434    }
02435    return 0;
02436 }
02437 
02438 /** 
02439  * \brief Determines if the given folder has messages.
02440  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02441  * \param folder the folder to look in
02442  *
02443  * This function is used when the mailbox is stored in an IMAP back end.
02444  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02445  * \return 1 if the folder has one or more messages. zero otherwise.
02446  */
02447 
02448 static int has_voicemail(const char *mailbox, const char *folder)
02449 {
02450    char tmp[256], *tmp2, *box, *context;
02451    ast_copy_string(tmp, mailbox, sizeof(tmp));
02452    tmp2 = tmp;
02453    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02454       while ((box = strsep(&tmp2, ",&"))) {
02455          if (!ast_strlen_zero(box)) {
02456             if (has_voicemail(box, folder)) {
02457                return 1;
02458             }
02459          }
02460       }
02461    }
02462    if ((context = strchr(tmp, '@'))) {
02463       *context++ = '\0';
02464    } else {
02465       context = "default";
02466    }
02467    return __messagecount(context, tmp, folder) ? 1 : 0;
02468 }
02469 
02470 /*!
02471  * \brief Copies a message from one mailbox to another.
02472  * \param chan
02473  * \param vmu
02474  * \param imbox
02475  * \param msgnum
02476  * \param duration
02477  * \param recip
02478  * \param fmt
02479  * \param dir
02480  *
02481  * This works with IMAP storage based mailboxes.
02482  *
02483  * \return zero on success, -1 on error.
02484  */
02485 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)
02486 {
02487    struct vm_state *sendvms = NULL, *destvms = NULL;
02488    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02489    if (msgnum >= recip->maxmsg) {
02490       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02491       return -1;
02492    }
02493    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02494       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02495       return -1;
02496    }
02497    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02498       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02499       return -1;
02500    }
02501    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02502    ast_mutex_lock(&sendvms->lock);
02503    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02504       ast_mutex_unlock(&sendvms->lock);
02505       return 0;
02506    }
02507    ast_mutex_unlock(&sendvms->lock);
02508    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02509    return -1;
02510 }
02511 
02512 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02513 {
02514    char tmp[256], *t = tmp;
02515    size_t left = sizeof(tmp);
02516    
02517    if (box == OLD_FOLDER) {
02518       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02519    } else {
02520       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02521    }
02522 
02523    if (box == NEW_FOLDER) {
02524       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02525    } else {
02526       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02527    }
02528 
02529    /* Build up server information */
02530    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02531 
02532    /* Add authentication user if present */
02533    if (!ast_strlen_zero(authuser))
02534       ast_build_string(&t, &left, "/authuser=%s", authuser);
02535 
02536    /* Add flags if present */
02537    if (!ast_strlen_zero(imapflags))
02538       ast_build_string(&t, &left, "/%s", imapflags);
02539 
02540    /* End with username */
02541 #if 1
02542    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02543 #else
02544    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02545 #endif
02546    if (box == NEW_FOLDER || box == OLD_FOLDER)
02547       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02548    else if (box == GREETINGS_FOLDER)
02549       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02550    else {   /* Other folders such as Friends, Family, etc... */
02551       if (!ast_strlen_zero(imapparentfolder)) {
02552          /* imapparentfolder would typically be set to INBOX */
02553          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02554       } else {
02555          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02556       }
02557    }
02558 }
02559 
02560 static int init_mailstream(struct vm_state *vms, int box)
02561 {
02562    MAILSTREAM *stream = NIL;
02563    long debug;
02564    char tmp[256];
02565    
02566    if (!vms) {
02567       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02568       return -1;
02569    }
02570    if (option_debug > 2)
02571       ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
02572    if (vms->mailstream == NIL || !vms->mailstream) {
02573       if (option_debug)
02574          ast_log(LOG_DEBUG, "mailstream not set.\n");
02575    } else {
02576       stream = vms->mailstream;
02577    }
02578    /* debug = T;  user wants protocol telemetry? */
02579    debug = NIL;  /* NO protocol telemetry? */
02580 
02581    if (delimiter == '\0') {      /* did not probe the server yet */
02582       char *cp;
02583 #ifdef USE_SYSTEM_IMAP
02584 #include <imap/linkage.c>
02585 #elif defined(USE_SYSTEM_CCLIENT)
02586 #include <c-client/linkage.c>
02587 #else
02588 #include "linkage.c"
02589 #endif
02590       /* Connect to INBOX first to get folders delimiter */
02591       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02592       ast_mutex_lock(&vms->lock);
02593       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02594       ast_mutex_unlock(&vms->lock);
02595       if (stream == NIL) {
02596          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02597          return -1;
02598       }
02599       get_mailbox_delimiter(stream);
02600       /* update delimiter in imapfolder */
02601       for (cp = vms->imapfolder; *cp; cp++)
02602          if (*cp == '/')
02603             *cp = delimiter;
02604    }
02605    /* Now connect to the target folder */
02606    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02607    if (option_debug > 2)
02608       ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
02609    ast_mutex_lock(&vms->lock);
02610    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02611    ast_mutex_unlock(&vms->lock);
02612    if (vms->mailstream == NIL) {
02613       return -1;
02614    } else {
02615       return 0;
02616    }
02617 }
02618 
02619 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02620 {
02621    SEARCHPGM *pgm;
02622    SEARCHHEADER *hdr;
02623    int ret, urgent = 0;
02624 
02625    /* If Urgent, then look at INBOX */
02626    if (box == 11) {
02627       box = NEW_FOLDER;
02628       urgent = 1;
02629    }
02630 
02631    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02632    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02633    vms->imapversion = vmu->imapversion;
02634    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02635 
02636    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02637       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02638       return -1;
02639    }
02640    
02641    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02642    
02643    /* Check Quota */
02644    if  (box == 0)  {
02645       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02646       check_quota(vms, (char *) mbox(vmu, box));
02647    }
02648 
02649    ast_mutex_lock(&vms->lock);
02650    pgm = mail_newsearchpgm();
02651 
02652    /* Check IMAP folder for Asterisk messages only... */
02653    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02654    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02655    pgm->header = hdr;
02656    pgm->deleted = 0;
02657    pgm->undeleted = 1;
02658 
02659    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02660    if (box == NEW_FOLDER && urgent == 1) {
02661       pgm->unseen = 1;
02662       pgm->seen = 0;
02663       pgm->flagged = 1;
02664       pgm->unflagged = 0;
02665    } else if (box == NEW_FOLDER && urgent == 0) {
02666       pgm->unseen = 1;
02667       pgm->seen = 0;
02668       pgm->flagged = 0;
02669       pgm->unflagged = 1;
02670    } else if (box == OLD_FOLDER) {
02671       pgm->seen = 1;
02672       pgm->unseen = 0;
02673    }
02674 
02675    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02676 
02677    vms->vmArrayIndex = 0;
02678    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02679    vms->lastmsg = vms->vmArrayIndex - 1;
02680    mail_free_searchpgm(&pgm);
02681    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02682     * ensure to allocate enough space to account for all of them. Warn if old messages
02683     * have not been checked first as that is required.
02684     */
02685    if (box == 0 && !vms->dh_arraysize) {
02686       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02687    }
02688    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02689       ast_mutex_unlock(&vms->lock);
02690       return -1;
02691    }
02692 
02693    ast_mutex_unlock(&vms->lock);
02694    return 0;
02695 }
02696 
02697 static void write_file(char *filename, char *buffer, unsigned long len)
02698 {
02699    FILE *output;
02700 
02701    output = fopen (filename, "w");
02702    if (fwrite(buffer, len, 1, output) != 1) {
02703       if (ferror(output)) {
02704          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02705       }
02706    }
02707    fclose (output);
02708 }
02709 
02710 static void update_messages_by_imapuser(const char *user, unsigned long number)
02711 {
02712    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02713 
02714    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02715       return;
02716    }
02717 
02718    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02719    vms->msgArray[vms->vmArrayIndex++] = number;
02720 }
02721 
02722 void mm_searched(MAILSTREAM *stream, unsigned long number)
02723 {
02724    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02725 
02726    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02727       return;
02728 
02729    update_messages_by_imapuser(user, number);
02730 }
02731 
02732 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02733 {
02734    struct ast_variable *var;
02735    struct ast_vm_user *vmu;
02736 
02737    vmu = ast_calloc(1, sizeof *vmu);
02738    if (!vmu)
02739       return NULL;
02740    ast_set_flag(vmu, VM_ALLOCED);
02741    populate_defaults(vmu);
02742 
02743    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02744    if (var) {
02745       apply_options_full(vmu, var);
02746       ast_variables_destroy(var);
02747       return vmu;
02748    } else {
02749       ast_free(vmu);
02750       return NULL;
02751    }
02752 }
02753 
02754 /* Interfaces to C-client */
02755 
02756 void mm_exists(MAILSTREAM * stream, unsigned long number)
02757 {
02758    /* mail_ping will callback here if new mail! */
02759    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02760    if (number == 0) return;
02761    set_update(stream);
02762 }
02763 
02764 
02765 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02766 {
02767    /* mail_ping will callback here if expunged mail! */
02768    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02769    if (number == 0) return;
02770    set_update(stream);
02771 }
02772 
02773 
02774 void mm_flags(MAILSTREAM * stream, unsigned long number)
02775 {
02776    /* mail_ping will callback here if read mail! */
02777    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02778    if (number == 0) return;
02779    set_update(stream);
02780 }
02781 
02782 
02783 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02784 {
02785    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02786    mm_log (string, errflg);
02787 }
02788 
02789 
02790 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02791 {
02792    if (delimiter == '\0') {
02793       delimiter = delim;
02794    }
02795 
02796    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02797    if (attributes & LATT_NOINFERIORS)
02798       ast_debug(5, "no inferiors\n");
02799    if (attributes & LATT_NOSELECT)
02800       ast_debug(5, "no select\n");
02801    if (attributes & LATT_MARKED)
02802       ast_debug(5, "marked\n");
02803    if (attributes & LATT_UNMARKED)
02804       ast_debug(5, "unmarked\n");
02805 }
02806 
02807 
02808 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02809 {
02810    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
02811    if (attributes & LATT_NOINFERIORS)
02812       ast_debug(5, "no inferiors\n");
02813    if (attributes & LATT_NOSELECT)
02814       ast_debug(5, "no select\n");
02815    if (attributes & LATT_MARKED)
02816       ast_debug(5, "marked\n");
02817    if (attributes & LATT_UNMARKED)
02818       ast_debug(5, "unmarked\n");
02819 }
02820 
02821 
02822 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02823 {
02824    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02825    if (status->flags & SA_MESSAGES)
02826       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02827    if (status->flags & SA_RECENT)
02828       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02829    if (status->flags & SA_UNSEEN)
02830       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02831    if (status->flags & SA_UIDVALIDITY)
02832       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02833    if (status->flags & SA_UIDNEXT)
02834       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02835    ast_log(AST_LOG_NOTICE, "\n");
02836 }
02837 
02838 
02839 void mm_log(char *string, long errflg)
02840 {
02841    switch ((short) errflg) {
02842       case NIL:
02843          ast_debug(1, "IMAP Info: %s\n", string);
02844          break;
02845       case PARSE:
02846       case WARN:
02847          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02848          break;
02849       case ERROR:
02850          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02851          break;
02852    }
02853 }
02854 
02855 
02856 void mm_dlog(char *string)
02857 {
02858    ast_log(AST_LOG_NOTICE, "%s\n", string);
02859 }
02860 
02861 
02862 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02863 {
02864    struct ast_vm_user *vmu;
02865 
02866    ast_debug(4, "Entering callback mm_login\n");
02867 
02868    ast_copy_string(user, mb->user, MAILTMPLEN);
02869 
02870    /* We should only do this when necessary */
02871    if (!ast_strlen_zero(authpassword)) {
02872       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02873    } else {
02874       AST_LIST_TRAVERSE(&users, vmu, list) {
02875          if (!strcasecmp(mb->user, vmu->imapuser)) {
02876             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02877             break;
02878          }
02879       }
02880       if (!vmu) {
02881          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02882             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02883             free_user(vmu);
02884          }
02885       }
02886    }
02887 }
02888 
02889 
02890 void mm_critical(MAILSTREAM * stream)
02891 {
02892 }
02893 
02894 
02895 void mm_nocritical(MAILSTREAM * stream)
02896 {
02897 }
02898 
02899 
02900 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02901 {
02902    kill (getpid (), SIGSTOP);
02903    return NIL;
02904 }
02905 
02906 
02907 void mm_fatal(char *string)
02908 {
02909    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02910 }
02911 
02912 /* C-client callback to handle quota */
02913 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02914 {
02915    struct vm_state *vms;
02916    char *mailbox = stream->mailbox, *user;
02917    char buf[1024] = "";
02918    unsigned long usage = 0, limit = 0;
02919    
02920    while (pquota) {
02921       usage = pquota->usage;
02922       limit = pquota->limit;
02923       pquota = pquota->next;
02924    }
02925    
02926    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)))) {
02927       ast_log(AST_LOG_ERROR, "No state found.\n");
02928       return;
02929    }
02930 
02931    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02932 
02933    vms->quota_usage = usage;
02934    vms->quota_limit = limit;
02935 }
02936 
02937 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02938 {
02939    char *start, *eol_pnt;
02940    int taglen;
02941 
02942    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02943       return NULL;
02944 
02945    taglen = strlen(tag) + 1;
02946    if (taglen < 1)
02947       return NULL;
02948 
02949    if (!(start = strstr(header, tag)))
02950       return NULL;
02951 
02952    /* Since we can be called multiple times we should clear our buffer */
02953    memset(buf, 0, len);
02954 
02955    ast_copy_string(buf, start+taglen, len);
02956    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02957       *eol_pnt = '\0';
02958    return buf;
02959 }
02960 
02961 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02962 {
02963    char *start, *quote, *eol_pnt;
02964 
02965    if (ast_strlen_zero(mailbox))
02966       return NULL;
02967 
02968    if (!(start = strstr(mailbox, "/user=")))
02969       return NULL;
02970 
02971    ast_copy_string(buf, start+6, len);
02972 
02973    if (!(quote = strchr(buf, '\"'))) {
02974       if (!(eol_pnt = strchr(buf, '/')))
02975          eol_pnt = strchr(buf,'}');
02976       *eol_pnt = '\0';
02977       return buf;
02978    } else {
02979       eol_pnt = strchr(buf+1,'\"');
02980       *eol_pnt = '\0';
02981       return buf+1;
02982    }
02983 }
02984 
02985 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02986 {
02987    struct vm_state *vms_p;
02988 
02989    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02990    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02991       return vms_p;
02992    }
02993    if (option_debug > 4)
02994       ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
02995    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02996       return NULL;
02997    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02998    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
02999    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
03000    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
03001    vms_p->mailstream = NIL; /* save for access from interactive entry point */
03002    vms_p->imapversion = vmu->imapversion;
03003    if (option_debug > 4)
03004       ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
03005    vms_p->updated = 1;
03006    /* set mailbox to INBOX! */
03007    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03008    init_vm_state(vms_p);
03009    vmstate_insert(vms_p);
03010    return vms_p;
03011 }
03012 
03013 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03014 {
03015    struct vmstate *vlist = NULL;
03016 
03017    if (interactive) {
03018       struct vm_state *vms;
03019       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03020       vms = pthread_getspecific(ts_vmstate.key);
03021       return vms;
03022    }
03023 
03024    AST_LIST_LOCK(&vmstates);
03025    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03026       if (!vlist->vms) {
03027          ast_debug(3, "error: vms is NULL for %s\n", user);
03028          continue;
03029       }
03030       if (vlist->vms->imapversion != imapversion) {
03031          continue;
03032       }
03033       if (!vlist->vms->imapuser) {
03034          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03035          continue;
03036       }
03037 
03038       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03039          AST_LIST_UNLOCK(&vmstates);
03040          return vlist->vms;
03041       }
03042    }
03043    AST_LIST_UNLOCK(&vmstates);
03044 
03045    ast_debug(3, "%s not found in vmstates\n", user);
03046 
03047    return NULL;
03048 }
03049 
03050 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03051 {
03052 
03053    struct vmstate *vlist = NULL;
03054    const char *local_context = S_OR(context, "default");
03055 
03056    if (interactive) {
03057       struct vm_state *vms;
03058       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03059       vms = pthread_getspecific(ts_vmstate.key);
03060       return vms;
03061    }
03062 
03063    AST_LIST_LOCK(&vmstates);
03064    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03065       if (!vlist->vms) {
03066          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03067          continue;
03068       }
03069       if (vlist->vms->imapversion != imapversion) {
03070          continue;
03071       }
03072       if (!vlist->vms->username || !vlist->vms->context) {
03073          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03074          continue;
03075       }
03076 
03077       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);
03078       
03079       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03080          ast_debug(3, "Found it!\n");
03081          AST_LIST_UNLOCK(&vmstates);
03082          return vlist->vms;
03083       }
03084    }
03085    AST_LIST_UNLOCK(&vmstates);
03086 
03087    ast_debug(3, "%s not found in vmstates\n", mailbox);
03088 
03089    return NULL;
03090 }
03091 
03092 static void vmstate_insert(struct vm_state *vms) 
03093 {
03094    struct vmstate *v;
03095    struct vm_state *altvms;
03096 
03097    /* If interactive, it probably already exists, and we should
03098       use the one we already have since it is more up to date.
03099       We can compare the username to find the duplicate */
03100    if (vms->interactive == 1) {
03101       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03102       if (altvms) {  
03103          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03104          vms->newmessages = altvms->newmessages;
03105          vms->oldmessages = altvms->oldmessages;
03106          vms->vmArrayIndex = altvms->vmArrayIndex;
03107          vms->lastmsg = altvms->lastmsg;
03108          vms->curmsg = altvms->curmsg;
03109          /* get a pointer to the persistent store */
03110          vms->persist_vms = altvms;
03111          /* Reuse the mailstream? */
03112 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03113          vms->mailstream = altvms->mailstream;
03114 #else
03115          vms->mailstream = NIL;
03116 #endif
03117       }
03118       return;
03119    }
03120 
03121    if (!(v = ast_calloc(1, sizeof(*v))))
03122       return;
03123    
03124    v->vms = vms;
03125 
03126    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03127 
03128    AST_LIST_LOCK(&vmstates);
03129    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03130    AST_LIST_UNLOCK(&vmstates);
03131 }
03132 
03133 static void vmstate_delete(struct vm_state *vms) 
03134 {
03135    struct vmstate *vc = NULL;
03136    struct vm_state *altvms = NULL;
03137 
03138    /* If interactive, we should copy pertinent info
03139       back to the persistent state (to make update immediate) */
03140    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03141       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03142       altvms->newmessages = vms->newmessages;
03143       altvms->oldmessages = vms->oldmessages;
03144       altvms->updated = 1;
03145       vms->mailstream = mail_close(vms->mailstream);
03146 
03147       /* Interactive states are not stored within the persistent list */
03148       return;
03149    }
03150    
03151    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03152    
03153    AST_LIST_LOCK(&vmstates);
03154    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03155       if (vc->vms == vms) {
03156          AST_LIST_REMOVE_CURRENT(list);
03157          break;
03158       }
03159    }
03160    AST_LIST_TRAVERSE_SAFE_END
03161    AST_LIST_UNLOCK(&vmstates);
03162    
03163    if (vc) {
03164       ast_mutex_destroy(&vc->vms->lock);
03165       ast_free(vc);
03166    }
03167    else
03168       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03169 }
03170 
03171 static void set_update(MAILSTREAM * stream) 
03172 {
03173    struct vm_state *vms;
03174    char *mailbox = stream->mailbox, *user;
03175    char buf[1024] = "";
03176 
03177    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03178       if (user && option_debug > 2)
03179          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03180       return;
03181    }
03182 
03183    ast_debug(3, "User %s mailbox set for update.\n", user);
03184 
03185    vms->updated = 1; /* Set updated flag since mailbox changed */
03186 }
03187 
03188 static void init_vm_state(struct vm_state *vms) 
03189 {
03190    int x;
03191    vms->vmArrayIndex = 0;
03192    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03193       vms->msgArray[x] = 0;
03194    }
03195    ast_mutex_init(&vms->lock);
03196 }
03197 
03198 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
03199 {
03200    char *body_content;
03201    char *body_decoded;
03202    char *fn = is_intro ? vms->introfn : vms->fn;
03203    unsigned long len;
03204    unsigned long newlen;
03205    char filename[256];
03206    
03207    if (!body || body == NIL)
03208       return -1;
03209 
03210    ast_mutex_lock(&vms->lock);
03211    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03212    ast_mutex_unlock(&vms->lock);
03213    if (body_content != NIL) {
03214       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03215       /* ast_debug(1,body_content); */
03216       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03217       /* If the body of the file is empty, return an error */
03218       if (!newlen) {
03219          return -1;
03220       }
03221       write_file(filename, (char *) body_decoded, newlen);
03222    } else {
03223       ast_debug(5, "Body of message is NULL.\n");
03224       return -1;
03225    }
03226    return 0;
03227 }
03228 
03229 /*! 
03230  * \brief Get delimiter via mm_list callback 
03231  * \param stream
03232  *
03233  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03234  */
03235 /* MUTEX should already be held */
03236 static void get_mailbox_delimiter(MAILSTREAM *stream) {
03237    char tmp[50];
03238    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
03239    mail_list(stream, tmp, "*");
03240 }
03241 
03242 /*! 
03243  * \brief Check Quota for user 
03244  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03245  * \param mailbox the mailbox to check the quota for.
03246  *
03247  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03248  */
03249 static void check_quota(struct vm_state *vms, char *mailbox) {
03250    ast_mutex_lock(&vms->lock);
03251    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03252    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03253    if (vms && vms->mailstream != NULL) {
03254       imap_getquotaroot(vms->mailstream, mailbox);
03255    } else {
03256       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03257    }
03258    ast_mutex_unlock(&vms->lock);
03259 }
03260 
03261 #endif /* IMAP_STORAGE */
03262 
03263 /*! \brief Lock file path
03264  * only return failure if ast_lock_path returns 'timeout',
03265  * not if the path does not exist or any other reason
03266  */
03267 static int vm_lock_path(const char *path)
03268 {
03269    switch (ast_lock_path(path)) {
03270    case AST_LOCK_TIMEOUT:
03271       return -1;
03272    default:
03273       return 0;
03274    }
03275 }
03276 
03277 
03278 #ifdef ODBC_STORAGE
03279 struct generic_prepare_struct {
03280    char *sql;
03281    int argc;
03282    char **argv;
03283 };
03284 
03285 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03286 {
03287    struct generic_prepare_struct *gps = data;
03288    int res, i;
03289    SQLHSTMT stmt;
03290 
03291    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03292    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03293       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03294       return NULL;
03295    }
03296    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03297    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03298       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03299       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03300       return NULL;
03301    }
03302    for (i = 0; i < gps->argc; i++)
03303       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03304 
03305    return stmt;
03306 }
03307 
03308 /*!
03309  * \brief Retrieves a file from an ODBC data store.
03310  * \param dir the path to the file to be retreived.
03311  * \param msgnum the message number, such as within a mailbox folder.
03312  * 
03313  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03314  * 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.
03315  *
03316  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03317  * The output is the message information file with the name msgnum and the extension .txt
03318  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03319  * 
03320  * \return 0 on success, -1 on error.
03321  */
03322 static int retrieve_file(char *dir, int msgnum)
03323 {
03324    int x = 0;
03325    int res;
03326    int fd = -1;
03327    size_t fdlen = 0;
03328    void *fdm = MAP_FAILED;
03329    SQLSMALLINT colcount = 0;
03330    SQLHSTMT stmt;
03331    char sql[PATH_MAX];
03332    char fmt[80]="";
03333    char *c;
03334    char coltitle[256];
03335    SQLSMALLINT collen;
03336    SQLSMALLINT datatype;
03337    SQLSMALLINT decimaldigits;
03338    SQLSMALLINT nullable;
03339    SQLULEN colsize;
03340    SQLLEN colsize2;
03341    FILE *f = NULL;
03342    char rowdata[80];
03343    char fn[PATH_MAX];
03344    char full_fn[PATH_MAX];
03345    char msgnums[80];
03346    char *argv[] = { dir, msgnums };
03347    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03348 
03349    struct odbc_obj *obj;
03350    obj = ast_odbc_request_obj(odbc_database, 0);
03351    if (obj) {
03352       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03353       c = strchr(fmt, '|');
03354       if (c)
03355          *c = '\0';
03356       if (!strcasecmp(fmt, "wav49"))
03357          strcpy(fmt, "WAV");
03358       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03359       if (msgnum > -1)
03360          make_file(fn, sizeof(fn), dir, msgnum);
03361       else
03362          ast_copy_string(fn, dir, sizeof(fn));
03363 
03364       /* Create the information file */
03365       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03366       
03367       if (!(f = fopen(full_fn, "w+"))) {
03368          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03369          goto yuck;
03370       }
03371       
03372       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03373       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03374       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03375       if (!stmt) {
03376          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03377          ast_odbc_release_obj(obj);
03378          goto yuck;
03379       }
03380       res = SQLFetch(stmt);
03381       if (res == SQL_NO_DATA) {
03382          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03383          ast_odbc_release_obj(obj);
03384          goto yuck;
03385       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03386          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03387          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03388          ast_odbc_release_obj(obj);
03389          goto yuck;
03390       }
03391       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03392       if (fd < 0) {
03393          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03394          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03395          ast_odbc_release_obj(obj);
03396          goto yuck;
03397       }
03398       res = SQLNumResultCols(stmt, &colcount);
03399       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03400          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03401          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03402          ast_odbc_release_obj(obj);
03403          goto yuck;
03404       }
03405       if (f) 
03406          fprintf(f, "[message]\n");
03407       for (x = 0; x < colcount; x++) {
03408          rowdata[0] = '\0';
03409          colsize = 0;
03410          collen = sizeof(coltitle);
03411          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03412                   &datatype, &colsize, &decimaldigits, &nullable);
03413          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03414             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03415             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03416             ast_odbc_release_obj(obj);
03417             goto yuck;
03418          }
03419          if (!strcasecmp(coltitle, "recording")) {
03420             off_t offset;
03421             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03422             fdlen = colsize2;
03423             if (fd > -1) {
03424                char tmp[1]="";
03425                lseek(fd, fdlen - 1, SEEK_SET);
03426                if (write(fd, tmp, 1) != 1) {
03427                   close(fd);
03428                   fd = -1;
03429                   continue;
03430                }
03431                /* Read out in small chunks */
03432                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03433                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03434                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03435                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03436                      ast_odbc_release_obj(obj);
03437                      goto yuck;
03438                   } else {
03439                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03440                      munmap(fdm, CHUNKSIZE);
03441                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03442                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03443                         unlink(full_fn);
03444                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03445                         ast_odbc_release_obj(obj);
03446                         goto yuck;
03447                      }
03448                   }
03449                }
03450                if (truncate(full_fn, fdlen) < 0) {
03451                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03452                }
03453             }
03454          } else {
03455             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03456             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03457                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03458                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03459                ast_odbc_release_obj(obj);
03460                goto yuck;
03461             }
03462             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03463                fprintf(f, "%s=%s\n", coltitle, rowdata);
03464          }
03465       }
03466       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03467       ast_odbc_release_obj(obj);
03468    } else
03469       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03470 yuck:
03471    if (f)
03472       fclose(f);
03473    if (fd > -1)
03474       close(fd);
03475    return x - 1;
03476 }
03477 
03478 /*!
03479  * \brief Determines the highest message number in use for a given user and mailbox folder.
03480  * \param vmu 
03481  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03482  *
03483  * This method is used when mailboxes are stored in an ODBC back end.
03484  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03485  *
03486  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03487 
03488  */
03489 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03490 {
03491    int x = 0;
03492    int res;
03493    SQLHSTMT stmt;
03494    char sql[PATH_MAX];
03495    char rowdata[20];
03496    char *argv[] = { dir };
03497    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03498 
03499    struct odbc_obj *obj;
03500    obj = ast_odbc_request_obj(odbc_database, 0);
03501    if (obj) {
03502       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03503 
03504       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03505       if (!stmt) {
03506          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03507          ast_odbc_release_obj(obj);
03508          goto yuck;
03509       }
03510       res = SQLFetch(stmt);
03511       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03512          if (res == SQL_NO_DATA) {
03513             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03514          } else {
03515             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03516          }
03517 
03518          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03519          ast_odbc_release_obj(obj);
03520          goto yuck;
03521       }
03522       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03523       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03524          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03525          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03526          ast_odbc_release_obj(obj);
03527          goto yuck;
03528       }
03529       if (sscanf(rowdata, "%30d", &x) != 1)
03530          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03531       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03532       ast_odbc_release_obj(obj);
03533       return x;
03534    } else
03535       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03536 yuck:
03537    return x - 1;
03538 }
03539 
03540 /*!
03541  * \brief Determines if the specified message exists.
03542  * \param dir the folder the mailbox folder to look for messages. 
03543  * \param msgnum the message index to query for.
03544  *
03545  * This method is used when mailboxes are stored in an ODBC back end.
03546  *
03547  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03548  */
03549 static int message_exists(char *dir, int msgnum)
03550 {
03551    int x = 0;
03552    int res;
03553    SQLHSTMT stmt;
03554    char sql[PATH_MAX];
03555    char rowdata[20];
03556    char msgnums[20];
03557    char *argv[] = { dir, msgnums };
03558    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03559 
03560    struct odbc_obj *obj;
03561    obj = ast_odbc_request_obj(odbc_database, 0);
03562    if (obj) {
03563       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03564       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03565       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03566       if (!stmt) {
03567          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03568          ast_odbc_release_obj(obj);
03569          goto yuck;
03570       }
03571       res = SQLFetch(stmt);
03572       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03573          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03574          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03575          ast_odbc_release_obj(obj);
03576          goto yuck;
03577       }
03578       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03579       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03580          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03581          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03582          ast_odbc_release_obj(obj);
03583          goto yuck;
03584       }
03585       if (sscanf(rowdata, "%30d", &x) != 1)
03586          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03587       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03588       ast_odbc_release_obj(obj);
03589    } else
03590       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03591 yuck:
03592    return x;
03593 }
03594 
03595 /*!
03596  * \brief returns the number of messages found.
03597  * \param vmu
03598  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03599  *
03600  * This method is used when mailboxes are stored in an ODBC back end.
03601  *
03602  * \return The count of messages being zero or more, less than zero on error.
03603  */
03604 static int count_messages(struct ast_vm_user *vmu, char *dir)
03605 {
03606    int x = 0;
03607    int res;
03608    SQLHSTMT stmt;
03609    char sql[PATH_MAX];
03610    char rowdata[20];
03611    char *argv[] = { dir };
03612    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03613 
03614    struct odbc_obj *obj;
03615    obj = ast_odbc_request_obj(odbc_database, 0);
03616    if (obj) {
03617       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03618       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03619       if (!stmt) {
03620          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03621          ast_odbc_release_obj(obj);
03622          goto yuck;
03623       }
03624       res = SQLFetch(stmt);
03625       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03626          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03627          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03628          ast_odbc_release_obj(obj);
03629          goto yuck;
03630       }
03631       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03632       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03633          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03634          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03635          ast_odbc_release_obj(obj);
03636          goto yuck;
03637       }
03638       if (sscanf(rowdata, "%30d", &x) != 1)
03639          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03640       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03641       ast_odbc_release_obj(obj);
03642       return x;
03643    } else
03644       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03645 yuck:
03646    return x - 1;
03647 
03648 }
03649 
03650 /*!
03651  * \brief Deletes a message from the mailbox folder.
03652  * \param sdir The mailbox folder to work in.
03653  * \param smsg The message index to be deleted.
03654  *
03655  * This method is used when mailboxes are stored in an ODBC back end.
03656  * The specified message is directly deleted from the database 'voicemessages' table.
03657  * 
03658  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03659  */
03660 static void delete_file(const char *sdir, int smsg)
03661 {
03662    SQLHSTMT stmt;
03663    char sql[PATH_MAX];
03664    char msgnums[20];
03665    char *argv[] = { NULL, msgnums };
03666    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03667    struct odbc_obj *obj;
03668 
03669    argv[0] = ast_strdupa(sdir);
03670 
03671    obj = ast_odbc_request_obj(odbc_database, 0);
03672    if (obj) {
03673       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03674       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03675       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03676       if (!stmt)
03677          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03678       else
03679          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03680       ast_odbc_release_obj(obj);
03681    } else
03682       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03683    return;  
03684 }
03685 
03686 /*!
03687  * \brief Copies a voicemail from one mailbox to another.
03688  * \param sdir the folder for which to look for the message to be copied.
03689  * \param smsg the index of the message to be copied.
03690  * \param ddir the destination folder to copy the message into.
03691  * \param dmsg the index to be used for the copied message.
03692  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03693  * \param dmailboxcontext The context for the destination user.
03694  *
03695  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03696  */
03697 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03698 {
03699    SQLHSTMT stmt;
03700    char sql[512];
03701    char msgnums[20];
03702    char msgnumd[20];
03703    struct odbc_obj *obj;
03704    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03705    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03706 
03707    delete_file(ddir, dmsg);
03708    obj = ast_odbc_request_obj(odbc_database, 0);
03709    if (obj) {
03710       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03711       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03712       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);
03713       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03714       if (!stmt)
03715          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03716       else
03717          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03718       ast_odbc_release_obj(obj);
03719    } else
03720       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03721    return;  
03722 }
03723 
03724 struct insert_data {
03725    char *sql;
03726    const char *dir;
03727    const char *msgnums;
03728    void *data;
03729    SQLLEN datalen;
03730    SQLLEN indlen;
03731    const char *context;
03732    const char *macrocontext;
03733    const char *callerid;
03734    const char *origtime;
03735    const char *duration;
03736    const char *mailboxuser;
03737    const char *mailboxcontext;
03738    const char *category;
03739    const char *flag;
03740 };
03741 
03742 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03743 {
03744    struct insert_data *data = vdata;
03745    int res;
03746    SQLHSTMT stmt;
03747 
03748    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03749    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03750       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03751       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03752       return NULL;
03753    }
03754 
03755    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
03756    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
03757    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
03758    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
03759    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
03760    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
03761    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
03762    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
03763    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
03764    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
03765    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
03766    if (!ast_strlen_zero(data->category)) {
03767       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
03768    }
03769    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
03770    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03771       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03772       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03773       return NULL;
03774    }
03775 
03776    return stmt;
03777 }
03778 
03779 /*!
03780  * \brief Stores a voicemail into the database.
03781  * \param dir the folder the mailbox folder to store the message.
03782  * \param mailboxuser the user owning the mailbox folder.
03783  * \param mailboxcontext
03784  * \param msgnum the message index for the message to be stored.
03785  *
03786  * This method is used when mailboxes are stored in an ODBC back end.
03787  * The message sound file and information file is looked up on the file system. 
03788  * A SQL query is invoked to store the message into the (MySQL) database.
03789  *
03790  * \return the zero on success -1 on error.
03791  */
03792 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03793 {
03794    int res = 0;
03795    int fd = -1;
03796    void *fdm = MAP_FAILED;
03797    off_t fdlen = -1;
03798    SQLHSTMT stmt;
03799    char sql[PATH_MAX];
03800    char msgnums[20];
03801    char fn[PATH_MAX];
03802    char full_fn[PATH_MAX];
03803    char fmt[80]="";
03804    char *c;
03805    struct ast_config *cfg = NULL;
03806    struct odbc_obj *obj;
03807    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03808       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03809    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03810 
03811    delete_file(dir, msgnum);
03812    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03813       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03814       return -1;
03815    }
03816 
03817    do {
03818       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03819       c = strchr(fmt, '|');
03820       if (c)
03821          *c = '\0';
03822       if (!strcasecmp(fmt, "wav49"))
03823          strcpy(fmt, "WAV");
03824       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03825       if (msgnum > -1)
03826          make_file(fn, sizeof(fn), dir, msgnum);
03827       else
03828          ast_copy_string(fn, dir, sizeof(fn));
03829       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03830       cfg = ast_config_load(full_fn, config_flags);
03831       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03832       fd = open(full_fn, O_RDWR);
03833       if (fd < 0) {
03834          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03835          res = -1;
03836          break;
03837       }
03838       if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
03839          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03840             idata.context = "";
03841          }
03842          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03843             idata.macrocontext = "";
03844          }
03845          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03846             idata.callerid = "";
03847          }
03848          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03849             idata.origtime = "";
03850          }
03851          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03852             idata.duration = "";
03853          }
03854          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03855             idata.category = "";
03856          }
03857          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03858             idata.flag = "";
03859          }
03860       }
03861       fdlen = lseek(fd, 0, SEEK_END);
03862       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
03863          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
03864          res = -1;
03865          break;
03866       }
03867       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
03868       if (fdm == MAP_FAILED) {
03869          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
03870          res = -1;
03871          break;
03872       } 
03873       idata.data = fdm;
03874       idata.datalen = idata.indlen = fdlen;
03875 
03876       if (!ast_strlen_zero(idata.category)) 
03877          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
03878       else
03879          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
03880 
03881       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03882          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03883       } else {
03884          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03885          res = -1;
03886       }
03887    } while (0);
03888    if (obj) {
03889       ast_odbc_release_obj(obj);
03890    }
03891    if (cfg)
03892       ast_config_destroy(cfg);
03893    if (fdm != MAP_FAILED)
03894       munmap(fdm, fdlen);
03895    if (fd > -1)
03896       close(fd);
03897    return res;
03898 }
03899 
03900 /*!
03901  * \brief Renames a message in a mailbox folder.
03902  * \param sdir The folder of the message to be renamed.
03903  * \param smsg The index of the message to be renamed.
03904  * \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.
03905  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03906  * \param ddir The destination folder for the message to be renamed into
03907  * \param dmsg The destination message for the message to be renamed.
03908  *
03909  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03910  * 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.
03911  * 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.
03912  */
03913 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03914 {
03915    SQLHSTMT stmt;
03916    char sql[PATH_MAX];
03917    char msgnums[20];
03918    char msgnumd[20];
03919    struct odbc_obj *obj;
03920    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03921    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03922 
03923    delete_file(ddir, dmsg);
03924    obj = ast_odbc_request_obj(odbc_database, 0);
03925    if (obj) {
03926       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03927       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03928       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
03929       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03930       if (!stmt)
03931          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03932       else
03933          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03934       ast_odbc_release_obj(obj);
03935    } else
03936       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03937    return;  
03938 }
03939 
03940 /*!
03941  * \brief Removes a voicemail message file.
03942  * \param dir the path to the message file.
03943  * \param msgnum the unique number for the message within the mailbox.
03944  *
03945  * Removes the message content file and the information file.
03946  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03947  * Typical use is to clean up after a RETRIEVE operation. 
03948  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03949  * \return zero on success, -1 on error.
03950  */
03951 static int remove_file(char *dir, int msgnum)
03952 {
03953    char fn[PATH_MAX];
03954    char full_fn[PATH_MAX];
03955    char msgnums[80];
03956    
03957    if (msgnum > -1) {
03958       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03959       make_file(fn, sizeof(fn), dir, msgnum);
03960    } else
03961       ast_copy_string(fn, dir, sizeof(fn));
03962    ast_filedelete(fn, NULL);  
03963    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03964    unlink(full_fn);
03965    return 0;
03966 }
03967 #else
03968 #ifndef IMAP_STORAGE
03969 /*!
03970  * \brief Find all .txt files - even if they are not in sequence from 0000.
03971  * \param vmu
03972  * \param dir
03973  *
03974  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03975  *
03976  * \return the count of messages, zero or more.
03977  */
03978 static int count_messages(struct ast_vm_user *vmu, char *dir)
03979 {
03980 
03981    int vmcount = 0;
03982    DIR *vmdir = NULL;
03983    struct dirent *vment = NULL;
03984 
03985    if (vm_lock_path(dir))
03986       return ERROR_LOCK_PATH;
03987 
03988    if ((vmdir = opendir(dir))) {
03989       while ((vment = readdir(vmdir))) {
03990          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03991             vmcount++;
03992          }
03993       }
03994       closedir(vmdir);
03995    }
03996    ast_unlock_path(dir);
03997    
03998    return vmcount;
03999 }
04000 
04001 /*!
04002  * \brief Renames a message in a mailbox folder.
04003  * \param sfn The path to the mailbox information and data file to be renamed.
04004  * \param dfn The path for where the message data and information files will be renamed to.
04005  *
04006  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04007  */
04008 static void rename_file(char *sfn, char *dfn)
04009 {
04010    char stxt[PATH_MAX];
04011    char dtxt[PATH_MAX];
04012    ast_filerename(sfn, dfn, NULL);
04013    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04014    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04015    if (ast_check_realtime("voicemail_data")) {
04016       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04017    }
04018    rename(stxt, dtxt);
04019 }
04020 
04021 /*! 
04022  * \brief Determines the highest message number in use for a given user and mailbox folder.
04023  * \param vmu 
04024  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04025  *
04026  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04027  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04028  *
04029  * \note Should always be called with a lock already set on dir.
04030  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04031  */
04032 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04033 {
04034    int x;
04035    unsigned char map[MAXMSGLIMIT] = "";
04036    DIR *msgdir;
04037    struct dirent *msgdirent;
04038    int msgdirint;
04039    char extension[4];
04040    int stopcount = 0;
04041 
04042    /* Reading the entire directory into a file map scales better than
04043     * doing a stat repeatedly on a predicted sequence.  I suspect this
04044     * is partially due to stat(2) internally doing a readdir(2) itself to
04045     * find each file. */
04046    if (!(msgdir = opendir(dir))) {
04047       return -1;
04048    }
04049 
04050    while ((msgdirent = readdir(msgdir))) {
04051       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04052          map[msgdirint] = 1;
04053          stopcount++;
04054          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04055       }
04056    }
04057    closedir(msgdir);
04058 
04059    for (x = 0; x < vmu->maxmsg; x++) {
04060       if (map[x] == 1) {
04061          stopcount--;
04062       } else if (map[x] == 0 && !stopcount) {
04063          break;
04064       }
04065    }
04066 
04067    return x - 1;
04068 }
04069 
04070 #endif /* #ifndef IMAP_STORAGE */
04071 #endif /* #else of #ifdef ODBC_STORAGE */
04072 #ifndef IMAP_STORAGE
04073 /*!
04074  * \brief Utility function to copy a file.
04075  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04076  * \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.
04077  *
04078  * 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.
04079  * The copy operation copies up to 4096 bytes at once.
04080  *
04081  * \return zero on success, -1 on error.
04082  */
04083 static int copy(char *infile, char *outfile)
04084 {
04085    int ifd;
04086    int ofd;
04087    int res;
04088    int len;
04089    char buf[4096];
04090 
04091 #ifdef HARDLINK_WHEN_POSSIBLE
04092    /* Hard link if possible; saves disk space & is faster */
04093    if (link(infile, outfile)) {
04094 #endif
04095       if ((ifd = open(infile, O_RDONLY)) < 0) {
04096          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04097          return -1;
04098       }
04099       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04100          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04101          close(ifd);
04102          return -1;
04103       }
04104       do {
04105          len = read(ifd, buf, sizeof(buf));
04106          if (len < 0) {
04107             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04108             close(ifd);
04109             close(ofd);
04110             unlink(outfile);
04111          } else if (len) {
04112             res = write(ofd, buf, len);
04113             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04114                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04115                close(ifd);
04116                close(ofd);
04117                unlink(outfile);
04118             }
04119          }
04120       } while (len);
04121       close(ifd);
04122       close(ofd);
04123       return 0;
04124 #ifdef HARDLINK_WHEN_POSSIBLE
04125    } else {
04126       /* Hard link succeeded */
04127       return 0;
04128    }
04129 #endif
04130 }
04131 
04132 /*!
04133  * \brief Copies a voicemail information (envelope) file.
04134  * \param frompath
04135  * \param topath 
04136  *
04137  * Every voicemail has the data (.wav) file, and the information file.
04138  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04139  * This is used by the COPY macro when not using IMAP storage.
04140  */
04141 static void copy_plain_file(char *frompath, char *topath)
04142 {
04143    char frompath2[PATH_MAX], topath2[PATH_MAX];
04144    struct ast_variable *tmp,*var = NULL;
04145    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04146    ast_filecopy(frompath, topath, NULL);
04147    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04148    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04149    if (ast_check_realtime("voicemail_data")) {
04150       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04151       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04152       for (tmp = var; tmp; tmp = tmp->next) {
04153          if (!strcasecmp(tmp->name, "origmailbox")) {
04154             origmailbox = tmp->value;
04155          } else if (!strcasecmp(tmp->name, "context")) {
04156             context = tmp->value;
04157          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04158             macrocontext = tmp->value;
04159          } else if (!strcasecmp(tmp->name, "exten")) {
04160             exten = tmp->value;
04161          } else if (!strcasecmp(tmp->name, "priority")) {
04162             priority = tmp->value;
04163          } else if (!strcasecmp(tmp->name, "callerchan")) {
04164             callerchan = tmp->value;
04165          } else if (!strcasecmp(tmp->name, "callerid")) {
04166             callerid = tmp->value;
04167          } else if (!strcasecmp(tmp->name, "origdate")) {
04168             origdate = tmp->value;
04169          } else if (!strcasecmp(tmp->name, "origtime")) {
04170             origtime = tmp->value;
04171          } else if (!strcasecmp(tmp->name, "category")) {
04172             category = tmp->value;
04173          } else if (!strcasecmp(tmp->name, "duration")) {
04174             duration = tmp->value;
04175          }
04176       }
04177       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);
04178    }
04179    copy(frompath2, topath2);
04180    ast_variables_destroy(var);
04181 }
04182 #endif
04183 
04184 /*! 
04185  * \brief Removes the voicemail sound and information file.
04186  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04187  *
04188  * This is used by the DELETE macro when voicemails are stored on the file system.
04189  *
04190  * \return zero on success, -1 on error.
04191  */
04192 static int vm_delete(char *file)
04193 {
04194    char *txt;
04195    int txtsize = 0;
04196 
04197    txtsize = (strlen(file) + 5)*sizeof(char);
04198    txt = alloca(txtsize);
04199    /* Sprintf here would safe because we alloca'd exactly the right length,
04200     * but trying to eliminate all sprintf's anyhow
04201     */
04202    if (ast_check_realtime("voicemail_data")) {
04203       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04204    }
04205    snprintf(txt, txtsize, "%s.txt", file);
04206    unlink(txt);
04207    return ast_filedelete(file, NULL);
04208 }
04209 
04210 /*!
04211  * \brief utility used by inchar(), for base_encode()
04212  */
04213 static int inbuf(struct baseio *bio, FILE *fi)
04214 {
04215    int l;
04216 
04217    if (bio->ateof)
04218       return 0;
04219 
04220    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04221       if (ferror(fi))
04222          return -1;
04223 
04224       bio->ateof = 1;
04225       return 0;
04226    }
04227 
04228    bio->iolen = l;
04229    bio->iocp = 0;
04230 
04231    return 1;
04232 }
04233 
04234 /*!
04235  * \brief utility used by base_encode()
04236  */
04237 static int inchar(struct baseio *bio, FILE *fi)
04238 {
04239    if (bio->iocp>=bio->iolen) {
04240       if (!inbuf(bio, fi))
04241          return EOF;
04242    }
04243 
04244    return bio->iobuf[bio->iocp++];
04245 }
04246 
04247 /*!
04248  * \brief utility used by base_encode()
04249  */
04250 static int ochar(struct baseio *bio, int c, FILE *so)
04251 {
04252    if (bio->linelength >= BASELINELEN) {
04253       if (fputs(ENDL, so) == EOF) {
04254          return -1;
04255       }
04256 
04257       bio->linelength = 0;
04258    }
04259 
04260    if (putc(((unsigned char) c), so) == EOF) {
04261       return -1;
04262    }
04263 
04264    bio->linelength++;
04265 
04266    return 1;
04267 }
04268 
04269 /*!
04270  * \brief Performs a base 64 encode algorithm on the contents of a File
04271  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04272  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04273  *
04274  * 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 ?
04275  *
04276  * \return zero on success, -1 on error.
04277  */
04278 static int base_encode(char *filename, FILE *so)
04279 {
04280    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04281       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04282       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04283       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04284    int i, hiteof = 0;
04285    FILE *fi;
04286    struct baseio bio;
04287 
04288    memset(&bio, 0, sizeof(bio));
04289    bio.iocp = BASEMAXINLINE;
04290 
04291    if (!(fi = fopen(filename, "rb"))) {
04292       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04293       return -1;
04294    }
04295 
04296    while (!hiteof){
04297       unsigned char igroup[3], ogroup[4];
04298       int c, n;
04299 
04300       memset(igroup, 0, sizeof(igroup));
04301 
04302       for (n = 0; n < 3; n++) {
04303          if ((c = inchar(&bio, fi)) == EOF) {
04304             hiteof = 1;
04305             break;
04306          }
04307 
04308          igroup[n] = (unsigned char) c;
04309       }
04310 
04311       if (n > 0) {
04312          ogroup[0]= dtable[igroup[0] >> 2];
04313          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04314          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04315          ogroup[3]= dtable[igroup[2] & 0x3F];
04316 
04317          if (n < 3) {
04318             ogroup[3] = '=';
04319 
04320             if (n < 2)
04321                ogroup[2] = '=';
04322          }
04323 
04324          for (i = 0; i < 4; i++)
04325             ochar(&bio, ogroup[i], so);
04326       }
04327    }
04328 
04329    fclose(fi);
04330    
04331    if (fputs(ENDL, so) == EOF) {
04332       return 0;
04333    }
04334 
04335    return 1;
04336 }
04337 
04338 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)
04339 {
04340    char callerid[256];
04341    char num[12];
04342    char fromdir[256], fromfile[256];
04343    struct ast_config *msg_cfg;
04344    const char *origcallerid, *origtime;
04345    char origcidname[80], origcidnum[80], origdate[80];
04346    int inttime;
04347    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04348 
04349    /* Prepare variables for substitution in email body and subject */
04350    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04351    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04352    snprintf(num, sizeof(num), "%d", msgnum);
04353    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04354    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04355    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04356    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04357       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04358    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04359    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04360    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04361    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04362    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04363 
04364    /* Retrieve info from VM attribute file */
04365    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04366    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04367    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04368       strcat(fromfile, ".txt");
04369    }
04370    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
04371       if (option_debug > 0) {
04372          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
04373       }
04374       return;
04375    }
04376 
04377    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04378       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04379       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04380       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04381       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04382    }
04383 
04384    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04385       struct timeval tv = { inttime, };
04386       struct ast_tm tm;
04387       ast_localtime(&tv, &tm, NULL);
04388       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04389       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04390    }
04391    ast_config_destroy(msg_cfg);
04392 }
04393 
04394 /*!
04395  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04396  * \param from The string to work with.
04397  * \param buf The buffer into which to write the modified quoted string.
04398  * \param maxlen Always zero, but see \see ast_str
04399  * 
04400  * \return The destination string with quotes wrapped on it (the to field).
04401  */
04402 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04403 {
04404    const char *ptr;
04405 
04406    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04407    ast_str_set(buf, maxlen, "\"");
04408    for (ptr = from; *ptr; ptr++) {
04409       if (*ptr == '"' || *ptr == '\\') {
04410          ast_str_append(buf, maxlen, "\\%c", *ptr);
04411       } else {
04412          ast_str_append(buf, maxlen, "%c", *ptr);
04413       }
04414    }
04415    ast_str_append(buf, maxlen, "\"");
04416 
04417    return ast_str_buffer(*buf);
04418 }
04419 
04420 /*! \brief
04421  * fill in *tm for current time according to the proper timezone, if any.
04422  * \return tm so it can be used as a function argument.
04423  */
04424 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04425 {
04426    const struct vm_zone *z = NULL;
04427    struct timeval t = ast_tvnow();
04428 
04429    /* Does this user have a timezone specified? */
04430    if (!ast_strlen_zero(vmu->zonetag)) {
04431       /* Find the zone in the list */
04432       AST_LIST_LOCK(&zones);
04433       AST_LIST_TRAVERSE(&zones, z, list) {
04434          if (!strcmp(z->name, vmu->zonetag))
04435             break;
04436       }
04437       AST_LIST_UNLOCK(&zones);
04438    }
04439    ast_localtime(&t, tm, z ? z->timezone : NULL);
04440    return tm;
04441 }
04442 
04443 /*!\brief Check if the string would need encoding within the MIME standard, to
04444  * avoid confusing certain mail software that expects messages to be 7-bit
04445  * clean.
04446  */
04447 static int check_mime(const char *str)
04448 {
04449    for (; *str; str++) {
04450       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04451          return 1;
04452       }
04453    }
04454    return 0;
04455 }
04456 
04457 /*!\brief Encode a string according to the MIME rules for encoding strings
04458  * that are not 7-bit clean or contain control characters.
04459  *
04460  * Additionally, if the encoded string would exceed the MIME limit of 76
04461  * characters per line, then the encoding will be broken up into multiple
04462  * sections, separated by a space character, in order to facilitate
04463  * breaking up the associated header across multiple lines.
04464  *
04465  * \param end An expandable buffer for holding the result
04466  * \param maxlen Always zero, but see \see ast_str
04467  * \param start A string to be encoded
04468  * \param preamble The length of the first line already used for this string,
04469  * to ensure that each line maintains a maximum length of 76 chars.
04470  * \param postamble the length of any additional characters appended to the
04471  * line, used to ensure proper field wrapping.
04472  * \retval The encoded string.
04473  */
04474 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04475 {
04476    struct ast_str *tmp = ast_str_alloca(80);
04477    int first_section = 1;
04478 
04479    ast_str_reset(*end);
04480    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04481    for (; *start; start++) {
04482       int need_encoding = 0;
04483       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04484          need_encoding = 1;
04485       }
04486       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04487          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04488          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04489          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04490          /* Start new line */
04491          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04492          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04493          first_section = 0;
04494       }
04495       if (need_encoding && *start == ' ') {
04496          ast_str_append(&tmp, -1, "_");
04497       } else if (need_encoding) {
04498          ast_str_append(&tmp, -1, "=%hhX", *start);
04499       } else {
04500          ast_str_append(&tmp, -1, "%c", *start);
04501       }
04502    }
04503    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04504    return ast_str_buffer(*end);
04505 }
04506 
04507 /*!
04508  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04509  * \param p The output file to generate the email contents into.
04510  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04511  * \param vmu The voicemail user who is sending the voicemail.
04512  * \param msgnum The message index in the mailbox folder.
04513  * \param context 
04514  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04515  * \param fromfolder
04516  * \param cidnum The caller ID number.
04517  * \param cidname The caller ID name.
04518  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04519  * \param attach2 
04520  * \param format The message sound file format. i.e. .wav
04521  * \param duration The time of the message content, in seconds.
04522  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04523  * \param chan
04524  * \param category
04525  * \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.
04526  * \param flag
04527  *
04528  * 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.
04529  */
04530 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)
04531 {
04532    char date[256];
04533    char host[MAXHOSTNAMELEN] = "";
04534    char who[256];
04535    char bound[256];
04536    char dur[256];
04537    struct ast_tm tm;
04538    char enc_cidnum[256] = "", enc_cidname[256] = "";
04539    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04540    char *greeting_attachment; 
04541    char filename[256];
04542 
04543    if (!str1 || !str2) {
04544       ast_free(str1);
04545       ast_free(str2);
04546       return;
04547    }
04548 
04549    if (cidnum) {
04550       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04551    }
04552    if (cidname) {
04553       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04554    }
04555    gethostname(host, sizeof(host) - 1);
04556 
04557    if (strchr(srcemail, '@')) {
04558       ast_copy_string(who, srcemail, sizeof(who));
04559    } else {
04560       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04561    }
04562 
04563    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04564    if (greeting_attachment) {
04565       *greeting_attachment++ = '\0';
04566    }
04567 
04568    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04569    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04570    fprintf(p, "Date: %s" ENDL, date);
04571 
04572    /* Set date format for voicemail mail */
04573    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04574 
04575    if (!ast_strlen_zero(fromstring)) {
04576       struct ast_channel *ast;
04577       if ((ast = ast_dummy_channel_alloc())) {
04578          char *ptr;
04579          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04580          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04581 
04582          if (check_mime(ast_str_buffer(str1))) {
04583             int first_line = 1;
04584             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04585             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04586                *ptr = '\0';
04587                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04588                first_line = 0;
04589                /* Substring is smaller, so this will never grow */
04590                ast_str_set(&str2, 0, "%s", ptr + 1);
04591             }
04592             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04593          } else {
04594             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04595          }
04596          ast = ast_channel_unref(ast);
04597       } else {
04598          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04599       }
04600    } else {
04601       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04602    }
04603 
04604    if (check_mime(vmu->fullname)) {
04605       int first_line = 1;
04606       char *ptr;
04607       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04608       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04609          *ptr = '\0';
04610          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04611          first_line = 0;
04612          /* Substring is smaller, so this will never grow */
04613          ast_str_set(&str2, 0, "%s", ptr + 1);
04614       }
04615       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04616    } else {
04617       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04618    }
04619 
04620    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04621       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04622       struct ast_channel *ast;
04623       if ((ast = ast_dummy_channel_alloc())) {
04624          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04625          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04626          if (check_mime(ast_str_buffer(str1))) {
04627             int first_line = 1;
04628             char *ptr;
04629             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04630             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04631                *ptr = '\0';
04632                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04633                first_line = 0;
04634                /* Substring is smaller, so this will never grow */
04635                ast_str_set(&str2, 0, "%s", ptr + 1);
04636             }
04637             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04638          } else {
04639             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04640          }
04641          ast = ast_channel_unref(ast);
04642       } else {
04643          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04644       }
04645    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04646       if (ast_strlen_zero(flag)) {
04647          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04648       } else {
04649          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04650       }
04651    } else {
04652       if (ast_strlen_zero(flag)) {
04653          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04654       } else {
04655          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04656       }
04657    }
04658 
04659    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04660       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04661    if (imap) {
04662       /* additional information needed for IMAP searching */
04663       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04664       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04665       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04666       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04667 #ifdef IMAP_STORAGE
04668       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04669 #else
04670       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04671 #endif
04672       /* flag added for Urgent */
04673       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04674       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04675       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04676       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04677       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04678       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04679       if (!ast_strlen_zero(category)) {
04680          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04681       } else {
04682          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04683       }
04684       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04685       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04686       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
04687    }
04688    if (!ast_strlen_zero(cidnum)) {
04689       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04690    }
04691    if (!ast_strlen_zero(cidname)) {
04692       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04693    }
04694    fprintf(p, "MIME-Version: 1.0" ENDL);
04695    if (attach_user_voicemail) {
04696       /* Something unique. */
04697       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
04698          (int) getpid(), (unsigned int) ast_random());
04699 
04700       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04701       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04702       fprintf(p, "--%s" ENDL, bound);
04703    }
04704    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04705    if (emailbody || vmu->emailbody) {
04706       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
04707       struct ast_channel *ast;
04708       if ((ast = ast_dummy_channel_alloc())) {
04709          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04710          ast_str_substitute_variables(&str1, 0, ast, e_body);
04711 #ifdef IMAP_STORAGE
04712             {
04713                /* Convert body to native line terminators for IMAP backend */
04714                char *line = ast_str_buffer(str1), *next;
04715                do {
04716                   /* Terminate line before outputting it to the file */
04717                   if ((next = strchr(line, '\n'))) {
04718                      *next++ = '\0';
04719                   }
04720                   fprintf(p, "%s" ENDL, line);
04721                   line = next;
04722                } while (!ast_strlen_zero(line));
04723             }
04724 #else
04725          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
04726 #endif
04727          ast = ast_channel_unref(ast);
04728       } else {
04729          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04730       }
04731    } else if (msgnum > -1) {
04732       if (strcmp(vmu->mailbox, mailbox)) {
04733          /* Forwarded type */
04734          struct ast_config *msg_cfg;
04735          const char *v;
04736          int inttime;
04737          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04738          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04739          /* Retrieve info from VM attribute file */
04740          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04741          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04742          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04743             strcat(fromfile, ".txt");
04744          }
04745          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04746             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04747                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04748             }
04749 
04750             /* You might be tempted to do origdate, except that a) it's in the wrong
04751              * format, and b) it's missing for IMAP recordings. */
04752             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04753                struct timeval tv = { inttime, };
04754                struct ast_tm tm;
04755                ast_localtime(&tv, &tm, NULL);
04756                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04757             }
04758             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04759                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04760                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04761                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04762                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04763                date, origcallerid, origdate);
04764             ast_config_destroy(msg_cfg);
04765          } else {
04766             goto plain_message;
04767          }
04768       } else {
04769 plain_message:
04770          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04771             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04772             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04773             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04774             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04775       }
04776    } else {
04777       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04778             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04779    }
04780 
04781    if (imap || attach_user_voicemail) {
04782       if (!ast_strlen_zero(attach2)) {
04783          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04784          ast_debug(5, "creating second attachment filename %s\n", filename);
04785          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04786          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04787          ast_debug(5, "creating attachment filename %s\n", filename);
04788          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04789       } else {
04790          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04791          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04792          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04793       }
04794    }
04795    ast_free(str1);
04796    ast_free(str2);
04797 }
04798 
04799 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)
04800 {
04801    char tmpdir[256], newtmp[256];
04802    char fname[256];
04803    char tmpcmd[256];
04804    int tmpfd = -1;
04805    int soxstatus = 0;
04806 
04807    /* Eww. We want formats to tell us their own MIME type */
04808    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04809 
04810    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04811       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04812       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04813       tmpfd = mkstemp(newtmp);
04814       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04815       ast_debug(3, "newtmp: %s\n", newtmp);
04816       if (tmpfd > -1) {
04817          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04818          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04819             attach = newtmp;
04820             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04821          } else {
04822             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04823                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04824             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04825          }
04826       }
04827    }
04828    fprintf(p, "--%s" ENDL, bound);
04829    if (msgnum > -1)
04830       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04831    else
04832       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04833    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04834    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04835    if (msgnum > -1)
04836       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04837    else
04838       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04839    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04840    base_encode(fname, p);
04841    if (last)
04842       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04843    if (tmpfd > -1) {
04844       if (soxstatus == 0) {
04845          unlink(fname);
04846       }
04847       close(tmpfd);
04848       unlink(newtmp);
04849    }
04850    return 0;
04851 }
04852 
04853 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)
04854 {
04855    FILE *p = NULL;
04856    char tmp[80] = "/tmp/astmail-XXXXXX";
04857    char tmp2[256];
04858    char *stringp;
04859 
04860    if (vmu && ast_strlen_zero(vmu->email)) {
04861       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04862       return(0);
04863    }
04864 
04865    /* Mail only the first format */
04866    format = ast_strdupa(format);
04867    stringp = format;
04868    strsep(&stringp, "|");
04869 
04870    if (!strcmp(format, "wav49"))
04871       format = "WAV";
04872    ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
04873    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04874       command hangs */
04875    if ((p = vm_mkftemp(tmp)) == NULL) {
04876       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04877       return -1;
04878    } else {
04879       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04880       fclose(p);
04881       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04882       ast_safe_system(tmp2);
04883       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04884    }
04885    return 0;
04886 }
04887 
04888 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)
04889 {
04890    char enc_cidnum[256], enc_cidname[256];
04891    char date[256];
04892    char host[MAXHOSTNAMELEN] = "";
04893    char who[256];
04894    char dur[PATH_MAX];
04895    char tmp[80] = "/tmp/astmail-XXXXXX";
04896    char tmp2[PATH_MAX];
04897    struct ast_tm tm;
04898    FILE *p;
04899    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04900 
04901    if (!str1 || !str2) {
04902       ast_free(str1);
04903       ast_free(str2);
04904       return -1;
04905    }
04906 
04907    if (cidnum) {
04908       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04909    }
04910    if (cidname) {
04911       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04912    }
04913 
04914    if ((p = vm_mkftemp(tmp)) == NULL) {
04915       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04916       ast_free(str1);
04917       ast_free(str2);
04918       return -1;
04919    }
04920    gethostname(host, sizeof(host)-1);
04921    if (strchr(srcemail, '@')) {
04922       ast_copy_string(who, srcemail, sizeof(who));
04923    } else {
04924       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04925    }
04926    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04927    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04928    fprintf(p, "Date: %s\n", date);
04929 
04930    /* Reformat for custom pager format */
04931    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
04932 
04933    if (!ast_strlen_zero(pagerfromstring)) {
04934       struct ast_channel *ast;
04935       if ((ast = ast_dummy_channel_alloc())) {
04936          char *ptr;
04937          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04938          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
04939 
04940          if (check_mime(ast_str_buffer(str1))) {
04941             int first_line = 1;
04942             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04943             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04944                *ptr = '\0';
04945                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04946                first_line = 0;
04947                /* Substring is smaller, so this will never grow */
04948                ast_str_set(&str2, 0, "%s", ptr + 1);
04949             }
04950             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04951          } else {
04952             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04953          }
04954          ast = ast_channel_unref(ast);
04955       } else {
04956          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04957       }
04958    } else {
04959       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04960    }
04961 
04962    if (check_mime(vmu->fullname)) {
04963       int first_line = 1;
04964       char *ptr;
04965       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
04966       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04967          *ptr = '\0';
04968          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04969          first_line = 0;
04970          /* Substring is smaller, so this will never grow */
04971          ast_str_set(&str2, 0, "%s", ptr + 1);
04972       }
04973       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
04974    } else {
04975       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
04976    }
04977 
04978    if (!ast_strlen_zero(pagersubject)) {
04979       struct ast_channel *ast;
04980       if ((ast = ast_dummy_channel_alloc())) {
04981          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04982          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
04983          if (check_mime(ast_str_buffer(str1))) {
04984             int first_line = 1;
04985             char *ptr;
04986             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04987             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04988                *ptr = '\0';
04989                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04990                first_line = 0;
04991                /* Substring is smaller, so this will never grow */
04992                ast_str_set(&str2, 0, "%s", ptr + 1);
04993             }
04994             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04995          } else {
04996             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04997          }
04998          ast = ast_channel_unref(ast);
04999       } else {
05000          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05001       }
05002    } else {
05003       if (ast_strlen_zero(flag)) {
05004          fprintf(p, "Subject: New VM\n\n");
05005       } else {
05006          fprintf(p, "Subject: New %s VM\n\n", flag);
05007       }
05008    }
05009 
05010    if (pagerbody) {
05011       struct ast_channel *ast;
05012       if ((ast = ast_dummy_channel_alloc())) {
05013          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05014          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05015          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05016          ast = ast_channel_unref(ast);
05017       } else {
05018          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05019       }
05020    } else {
05021       fprintf(p, "New %s long %s msg in box %s\n"
05022             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05023    }
05024 
05025    fclose(p);
05026    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05027    ast_safe_system(tmp2);
05028    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05029    ast_free(str1);
05030    ast_free(str2);
05031    return 0;
05032 }
05033 
05034 /*!
05035  * \brief Gets the current date and time, as formatted string.
05036  * \param s The buffer to hold the output formatted date.
05037  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05038  * 
05039  * The date format string used is "%a %b %e %r UTC %Y".
05040  * 
05041  * \return zero on success, -1 on error.
05042  */
05043 static int get_date(char *s, int len)
05044 {
05045    struct ast_tm tm;
05046    struct timeval t = ast_tvnow();
05047    
05048    ast_localtime(&t, &tm, "UTC");
05049 
05050    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05051 }
05052 
05053 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05054 {
05055    int res;
05056    char fn[PATH_MAX];
05057    char dest[PATH_MAX];
05058 
05059    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05060 
05061    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05062       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05063       return -1;
05064    }
05065 
05066    RETRIEVE(fn, -1, ext, context);
05067    if (ast_fileexists(fn, NULL, NULL) > 0) {
05068       res = ast_stream_and_wait(chan, fn, ecodes);
05069       if (res) {
05070          DISPOSE(fn, -1);
05071          return res;
05072       }
05073    } else {
05074       /* Dispose just in case */
05075       DISPOSE(fn, -1);
05076       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05077       if (res)
05078          return res;
05079       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
05080       if (res)
05081          return res;
05082    }
05083    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05084    return res;
05085 }
05086 
05087 static void free_zone(struct vm_zone *z)
05088 {
05089    ast_free(z);
05090 }
05091 
05092 #ifdef ODBC_STORAGE
05093 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05094 {
05095    int x = -1;
05096    int res;
05097    SQLHSTMT stmt = NULL;
05098    char sql[PATH_MAX];
05099    char rowdata[20];
05100    char tmp[PATH_MAX] = "";
05101    struct odbc_obj *obj = NULL;
05102    char *context;
05103    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05104 
05105    if (newmsgs)
05106       *newmsgs = 0;
05107    if (oldmsgs)
05108       *oldmsgs = 0;
05109    if (urgentmsgs)
05110       *urgentmsgs = 0;
05111 
05112    /* If no mailbox, return immediately */
05113    if (ast_strlen_zero(mailbox))
05114       return 0;
05115 
05116    ast_copy_string(tmp, mailbox, sizeof(tmp));
05117 
05118    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05119       int u, n, o;
05120       char *next, *remaining = tmp;
05121       while ((next = strsep(&remaining, " ,"))) {
05122          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05123             return -1;
05124          }
05125          if (urgentmsgs) {
05126             *urgentmsgs += u;
05127          }
05128          if (newmsgs) {
05129             *newmsgs += n;
05130          }
05131          if (oldmsgs) {
05132             *oldmsgs += o;
05133          }
05134       }
05135       return 0;
05136    }
05137 
05138    context = strchr(tmp, '@');
05139    if (context) {
05140       *context = '\0';
05141       context++;
05142    } else
05143       context = "default";
05144 
05145    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05146       do {
05147          if (newmsgs) {
05148             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05149             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05150                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05151                break;
05152             }
05153             res = SQLFetch(stmt);
05154             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05155                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05156                break;
05157             }
05158             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05159             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05160                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05161                break;
05162             }
05163             *newmsgs = atoi(rowdata);
05164             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05165          }
05166 
05167          if (oldmsgs) {
05168             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05169             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05170                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05171                break;
05172             }
05173             res = SQLFetch(stmt);
05174             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05175                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05176                break;
05177             }
05178             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05179             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05180                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05181                break;
05182             }
05183             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05184             *oldmsgs = atoi(rowdata);
05185          }
05186 
05187          if (urgentmsgs) {
05188             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05189             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05190                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05191                break;
05192             }
05193             res = SQLFetch(stmt);
05194             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05195                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05196                break;
05197             }
05198             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05199             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05200                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05201                break;
05202             }
05203             *urgentmsgs = atoi(rowdata);
05204          }
05205 
05206          x = 0;
05207       } while (0);
05208    } else {
05209       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05210    }
05211 
05212    if (stmt) {
05213       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05214    }
05215    if (obj) {
05216       ast_odbc_release_obj(obj);
05217    }
05218    return x;
05219 }
05220 
05221 /*!
05222  * \brief Gets the number of messages that exist in a mailbox folder.
05223  * \param context
05224  * \param mailbox
05225  * \param folder
05226  * 
05227  * This method is used when ODBC backend is used.
05228  * \return The number of messages in this mailbox folder (zero or more).
05229  */
05230 static int messagecount(const char *context, const char *mailbox, const char *folder)
05231 {
05232    struct odbc_obj *obj = NULL;
05233    int nummsgs = 0;
05234    int res;
05235    SQLHSTMT stmt = NULL;
05236    char sql[PATH_MAX];
05237    char rowdata[20];
05238    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05239    if (!folder)
05240       folder = "INBOX";
05241    /* If no mailbox, return immediately */
05242    if (ast_strlen_zero(mailbox))
05243       return 0;
05244 
05245    obj = ast_odbc_request_obj(odbc_database, 0);
05246    if (obj) {
05247       if (!strcmp(folder, "INBOX")) {
05248          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);
05249       } else {
05250          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05251       }
05252       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05253       if (!stmt) {
05254          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05255          goto yuck;
05256       }
05257       res = SQLFetch(stmt);
05258       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05259          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05260          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05261          goto yuck;
05262       }
05263       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05264       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05265          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05266          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05267          goto yuck;
05268       }
05269       nummsgs = atoi(rowdata);
05270       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05271    } else
05272       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05273 
05274 yuck:
05275    if (obj)
05276       ast_odbc_release_obj(obj);
05277    return nummsgs;
05278 }
05279 
05280 /** 
05281  * \brief Determines if the given folder has messages.
05282  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05283  * 
05284  * This function is used when the mailbox is stored in an ODBC back end.
05285  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05286  * \return 1 if the folder has one or more messages. zero otherwise.
05287  */
05288 static int has_voicemail(const char *mailbox, const char *folder)
05289 {
05290    char tmp[256], *tmp2 = tmp, *box, *context;
05291    ast_copy_string(tmp, mailbox, sizeof(tmp));
05292    while ((context = box = strsep(&tmp2, ",&"))) {
05293       strsep(&context, "@");
05294       if (ast_strlen_zero(context))
05295          context = "default";
05296       if (messagecount(context, box, folder))
05297          return 1;
05298    }
05299    return 0;
05300 }
05301 #endif
05302 #ifndef IMAP_STORAGE
05303 /*! 
05304  * \brief Copies a message from one mailbox to another.
05305  * \param chan
05306  * \param vmu
05307  * \param imbox
05308  * \param msgnum
05309  * \param duration
05310  * \param recip
05311  * \param fmt
05312  * \param dir
05313  * \param flag
05314  *
05315  * This is only used by file storage based mailboxes.
05316  *
05317  * \return zero on success, -1 on error.
05318  */
05319 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)
05320 {
05321    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05322    const char *frombox = mbox(vmu, imbox);
05323    const char *userfolder;
05324    int recipmsgnum;
05325    int res = 0;
05326 
05327    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05328 
05329    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05330       userfolder = "Urgent";
05331    } else {
05332       userfolder = "INBOX";
05333    }
05334 
05335    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05336 
05337    if (!dir)
05338       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05339    else
05340       ast_copy_string(fromdir, dir, sizeof(fromdir));
05341 
05342    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05343    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05344 
05345    if (vm_lock_path(todir))
05346       return ERROR_LOCK_PATH;
05347 
05348    recipmsgnum = last_message_index(recip, todir) + 1;
05349    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05350       make_file(topath, sizeof(topath), todir, recipmsgnum);
05351 #ifndef ODBC_STORAGE
05352       if (EXISTS(fromdir, msgnum, frompath, chan->language)) { 
05353          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05354       } else {
05355 #endif
05356          /* If we are prepending a message for ODBC, then the message already
05357           * exists in the database, but we want to force copying from the
05358           * filesystem (since only the FS contains the prepend). */
05359          copy_plain_file(frompath, topath);
05360          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
05361          vm_delete(topath);
05362 #ifndef ODBC_STORAGE
05363       }
05364 #endif
05365    } else {
05366       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05367       res = -1;
05368    }
05369    ast_unlock_path(todir);
05370    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05371       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05372       S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05373       flag);
05374    
05375    return res;
05376 }
05377 #endif
05378 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05379 
05380 static int messagecount(const char *context, const char *mailbox, const char *folder)
05381 {
05382    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05383 }
05384 
05385 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05386 {
05387    DIR *dir;
05388    struct dirent *de;
05389    char fn[256];
05390    int ret = 0;
05391 
05392    /* If no mailbox, return immediately */
05393    if (ast_strlen_zero(mailbox))
05394       return 0;
05395 
05396    if (ast_strlen_zero(folder))
05397       folder = "INBOX";
05398    if (ast_strlen_zero(context))
05399       context = "default";
05400 
05401    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05402 
05403    if (!(dir = opendir(fn)))
05404       return 0;
05405 
05406    while ((de = readdir(dir))) {
05407       if (!strncasecmp(de->d_name, "msg", 3)) {
05408          if (shortcircuit) {
05409             ret = 1;
05410             break;
05411          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05412             ret++;
05413          }
05414       }
05415    }
05416 
05417    closedir(dir);
05418 
05419    return ret;
05420 }
05421 
05422 /** 
05423  * \brief Determines if the given folder has messages.
05424  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05425  * \param folder the folder to look in
05426  *
05427  * This function is used when the mailbox is stored in a filesystem back end.
05428  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05429  * \return 1 if the folder has one or more messages. zero otherwise.
05430  */
05431 static int has_voicemail(const char *mailbox, const char *folder)
05432 {
05433    char tmp[256], *tmp2 = tmp, *box, *context;
05434    ast_copy_string(tmp, mailbox, sizeof(tmp));
05435    if (ast_strlen_zero(folder)) {
05436       folder = "INBOX";
05437    }
05438    while ((box = strsep(&tmp2, ",&"))) {
05439       if ((context = strchr(box, '@')))
05440          *context++ = '\0';
05441       else
05442          context = "default";
05443       if (__has_voicemail(context, box, folder, 1))
05444          return 1;
05445       /* If we are checking INBOX, we should check Urgent as well */
05446       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05447          return 1;
05448       }
05449    }
05450    return 0;
05451 }
05452 
05453 
05454 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05455 {
05456    char tmp[256];
05457    char *context;
05458 
05459    /* If no mailbox, return immediately */
05460    if (ast_strlen_zero(mailbox))
05461       return 0;
05462 
05463    if (newmsgs)
05464       *newmsgs = 0;
05465    if (oldmsgs)
05466       *oldmsgs = 0;
05467    if (urgentmsgs)
05468       *urgentmsgs = 0;
05469 
05470    if (strchr(mailbox, ',')) {
05471       int tmpnew, tmpold, tmpurgent;
05472       char *mb, *cur;
05473 
05474       ast_copy_string(tmp, mailbox, sizeof(tmp));
05475       mb = tmp;
05476       while ((cur = strsep(&mb, ", "))) {
05477          if (!ast_strlen_zero(cur)) {
05478             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05479                return -1;
05480             else {
05481                if (newmsgs)
05482                   *newmsgs += tmpnew; 
05483                if (oldmsgs)
05484                   *oldmsgs += tmpold;
05485                if (urgentmsgs)
05486                   *urgentmsgs += tmpurgent;
05487             }
05488          }
05489       }
05490       return 0;
05491    }
05492 
05493    ast_copy_string(tmp, mailbox, sizeof(tmp));
05494    
05495    if ((context = strchr(tmp, '@')))
05496       *context++ = '\0';
05497    else
05498       context = "default";
05499 
05500    if (newmsgs)
05501       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05502    if (oldmsgs)
05503       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05504    if (urgentmsgs)
05505       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05506 
05507    return 0;
05508 }
05509 
05510 #endif
05511 
05512 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05513 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05514 {
05515    int urgentmsgs = 0;
05516    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05517    if (newmsgs) {
05518       *newmsgs += urgentmsgs;
05519    }
05520    return res;
05521 }
05522 
05523 static void run_externnotify(char *context, char *extension, const char *flag)
05524 {
05525    char arguments[255];
05526    char ext_context[256] = "";
05527    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05528    struct ast_smdi_mwi_message *mwi_msg;
05529 
05530    if (!ast_strlen_zero(context))
05531       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05532    else
05533       ast_copy_string(ext_context, extension, sizeof(ext_context));
05534 
05535    if (smdi_iface) {
05536       if (ast_app_has_voicemail(ext_context, NULL)) 
05537          ast_smdi_mwi_set(smdi_iface, extension);
05538       else
05539          ast_smdi_mwi_unset(smdi_iface, extension);
05540 
05541       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05542          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05543          if (!strncmp(mwi_msg->cause, "INV", 3))
05544             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05545          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05546             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05547          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05548          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05549       } else {
05550          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05551       }
05552    }
05553 
05554    if (!ast_strlen_zero(externnotify)) {
05555       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05556          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05557       } else {
05558          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
05559          ast_debug(1, "Executing %s\n", arguments);
05560          ast_safe_system(arguments);
05561       }
05562    }
05563 }
05564 
05565 /*!
05566  * \brief Variables used for saving a voicemail.
05567  *
05568  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05569  */
05570 struct leave_vm_options {
05571    unsigned int flags;
05572    signed char record_gain;
05573    char *exitcontext;
05574 };
05575 
05576 /*!
05577  * \brief Prompts the user and records a voicemail to a mailbox.
05578  * \param chan
05579  * \param ext
05580  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05581  * 
05582  * 
05583  * 
05584  * \return zero on success, -1 on error.
05585  */
05586 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05587 {
05588 #ifdef IMAP_STORAGE
05589    int newmsgs, oldmsgs;
05590 #else
05591    char urgdir[PATH_MAX];
05592 #endif
05593    char txtfile[PATH_MAX];
05594    char tmptxtfile[PATH_MAX];
05595    struct vm_state *vms = NULL;
05596    char callerid[256];
05597    FILE *txt;
05598    char date[256];
05599    int txtdes;
05600    int res = 0;
05601    int msgnum;
05602    int duration = 0;
05603    int sound_duration = 0;
05604    int ausemacro = 0;
05605    int ousemacro = 0;
05606    int ouseexten = 0;
05607    char tmpdur[16];
05608    char priority[16];
05609    char origtime[16];
05610    char dir[PATH_MAX];
05611    char tmpdir[PATH_MAX];
05612    char fn[PATH_MAX];
05613    char prefile[PATH_MAX] = "";
05614    char tempfile[PATH_MAX] = "";
05615    char ext_context[256] = "";
05616    char fmt[80];
05617    char *context;
05618    char ecodes[17] = "#";
05619    struct ast_str *tmp = ast_str_create(16);
05620    char *tmpptr;
05621    struct ast_vm_user *vmu;
05622    struct ast_vm_user svm;
05623    const char *category = NULL;
05624    const char *code;
05625    const char *alldtmf = "0123456789ABCD*#";
05626    char flag[80];
05627 
05628    if (!tmp) {
05629       return -1;
05630    }
05631 
05632    ast_str_set(&tmp, 0, "%s", ext);
05633    ext = ast_str_buffer(tmp);
05634    if ((context = strchr(ext, '@'))) {
05635       *context++ = '\0';
05636       tmpptr = strchr(context, '&');
05637    } else {
05638       tmpptr = strchr(ext, '&');
05639    }
05640 
05641    if (tmpptr)
05642       *tmpptr++ = '\0';
05643 
05644    ast_channel_lock(chan);
05645    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05646       category = ast_strdupa(category);
05647    }
05648    ast_channel_unlock(chan);
05649 
05650    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05651       ast_copy_string(flag, "Urgent", sizeof(flag));
05652    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05653       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05654    } else {
05655       flag[0] = '\0';
05656    }
05657 
05658    ast_debug(3, "Before find_user\n");
05659    if (!(vmu = find_user(&svm, context, ext))) {
05660       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05661       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05662       ast_free(tmp);
05663       return res;
05664    }
05665    /* Setup pre-file if appropriate */
05666    if (strcmp(vmu->context, "default"))
05667       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05668    else
05669       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05670 
05671    /* Set the path to the prefile. Will be one of 
05672       VM_SPOOL_DIRcontext/ext/busy
05673       VM_SPOOL_DIRcontext/ext/unavail
05674       Depending on the flag set in options.
05675    */
05676    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05677       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05678    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05679       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05680    }
05681    /* Set the path to the tmpfile as
05682       VM_SPOOL_DIR/context/ext/temp
05683       and attempt to create the folder structure.
05684    */
05685    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05686    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05687       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05688       ast_free(tmp);
05689       return -1;
05690    }
05691    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05692    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05693       ast_copy_string(prefile, tempfile, sizeof(prefile));
05694 
05695    DISPOSE(tempfile, -1);
05696    /* It's easier just to try to make it than to check for its existence */
05697 #ifndef IMAP_STORAGE
05698    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05699 #else
05700    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
05701    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
05702       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
05703    }
05704 #endif
05705 
05706    /* Check current or macro-calling context for special extensions */
05707    if (ast_test_flag(vmu, VM_OPERATOR)) {
05708       if (!ast_strlen_zero(vmu->exit)) {
05709          if (ast_exists_extension(chan, vmu->exit, "o", 1,
05710             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05711             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05712             ouseexten = 1;
05713          }
05714       } else if (ast_exists_extension(chan, chan->context, "o", 1,
05715          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05716          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05717          ouseexten = 1;
05718       } else if (!ast_strlen_zero(chan->macrocontext)
05719          && ast_exists_extension(chan, chan->macrocontext, "o", 1,
05720             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05721          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05722          ousemacro = 1;
05723       }
05724    }
05725 
05726    if (!ast_strlen_zero(vmu->exit)) {
05727       if (ast_exists_extension(chan, vmu->exit, "a", 1,
05728          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05729          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05730       }
05731    } else if (ast_exists_extension(chan, chan->context, "a", 1,
05732       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05733       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05734    } else if (!ast_strlen_zero(chan->macrocontext)
05735       && ast_exists_extension(chan, chan->macrocontext, "a", 1,
05736          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05737       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05738       ausemacro = 1;
05739    }
05740 
05741    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05742       for (code = alldtmf; *code; code++) {
05743          char e[2] = "";
05744          e[0] = *code;
05745          if (strchr(ecodes, e[0]) == NULL
05746             && ast_canmatch_extension(chan,
05747                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
05748                e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
05749             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05750          }
05751       }
05752    }
05753 
05754    /* Play the beginning intro if desired */
05755    if (!ast_strlen_zero(prefile)) {
05756 #ifdef ODBC_STORAGE
05757       int success = 
05758 #endif
05759          RETRIEVE(prefile, -1, ext, context);
05760       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05761          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05762             res = ast_waitstream(chan, ecodes);
05763 #ifdef ODBC_STORAGE
05764          if (success == -1) {
05765             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05766             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05767             store_file(prefile, vmu->mailbox, vmu->context, -1);
05768          }
05769 #endif
05770       } else {
05771          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05772          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05773       }
05774       DISPOSE(prefile, -1);
05775       if (res < 0) {
05776          ast_debug(1, "Hang up during prefile playback\n");
05777          free_user(vmu);
05778          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05779          ast_free(tmp);
05780          return -1;
05781       }
05782    }
05783    if (res == '#') {
05784       /* On a '#' we skip the instructions */
05785       ast_set_flag(options, OPT_SILENT);
05786       res = 0;
05787    }
05788    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
05789    if (vmu->maxmsg == 0) {
05790       if (option_debug > 2)
05791          ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
05792       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05793       goto leave_vm_out;
05794    }
05795    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05796       res = ast_stream_and_wait(chan, INTRO, ecodes);
05797       if (res == '#') {
05798          ast_set_flag(options, OPT_SILENT);
05799          res = 0;
05800       }
05801    }
05802    if (res > 0)
05803       ast_stopstream(chan);
05804    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05805     other than the operator -- an automated attendant or mailbox login for example */
05806    if (res == '*') {
05807       chan->exten[0] = 'a';
05808       chan->exten[1] = '\0';
05809       if (!ast_strlen_zero(vmu->exit)) {
05810          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05811       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05812          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05813       }
05814       chan->priority = 0;
05815       free_user(vmu);
05816       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05817       ast_free(tmp);
05818       return 0;
05819    }
05820 
05821    /* Check for a '0' here */
05822    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05823    transfer:
05824       if (ouseexten || ousemacro) {
05825          chan->exten[0] = 'o';
05826          chan->exten[1] = '\0';
05827          if (!ast_strlen_zero(vmu->exit)) {
05828             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05829          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05830             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05831          }
05832          ast_play_and_wait(chan, "transfer");
05833          chan->priority = 0;
05834          free_user(vmu);
05835          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05836       }
05837       ast_free(tmp);
05838       return OPERATOR_EXIT;
05839    }
05840 
05841    /* Allow all other digits to exit Voicemail and return to the dialplan */
05842    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05843       if (!ast_strlen_zero(options->exitcontext)) {
05844          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05845       }
05846       free_user(vmu);
05847       ast_free(tmp);
05848       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05849       return res;
05850    }
05851 
05852    if (res < 0) {
05853       free_user(vmu);
05854       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05855       ast_free(tmp);
05856       return -1;
05857    }
05858    /* The meat of recording the message...  All the announcements and beeps have been played*/
05859    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05860    if (!ast_strlen_zero(fmt)) {
05861       msgnum = 0;
05862 
05863 #ifdef IMAP_STORAGE
05864       /* Is ext a mailbox? */
05865       /* must open stream for this user to get info! */
05866       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05867       if (res < 0) {
05868          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05869          ast_free(tmp);
05870          return -1;
05871       }
05872       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05873       /* It is possible under certain circumstances that inboxcount did not
05874        * create a vm_state when it was needed. This is a catchall which will
05875        * rarely be used.
05876        */
05877          if (!(vms = create_vm_state_from_user(vmu))) {
05878             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05879             ast_free(tmp);
05880             return -1;
05881          }
05882       }
05883       vms->newmessages++;
05884       
05885       /* here is a big difference! We add one to it later */
05886       msgnum = newmsgs + oldmsgs;
05887       ast_debug(3, "Messagecount set to %d\n", msgnum);
05888       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05889       /* set variable for compatibility */
05890       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05891 
05892       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
05893          goto leave_vm_out;
05894       }
05895 #else
05896       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05897          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05898          if (!res)
05899             res = ast_waitstream(chan, "");
05900          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05901          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05902          inprocess_count(vmu->mailbox, vmu->context, -1);
05903          goto leave_vm_out;
05904       }
05905 
05906 #endif
05907       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05908       txtdes = mkstemp(tmptxtfile);
05909       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05910       if (txtdes < 0) {
05911          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05912          if (!res)
05913             res = ast_waitstream(chan, "");
05914          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05915          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05916          inprocess_count(vmu->mailbox, vmu->context, -1);
05917          goto leave_vm_out;
05918       }
05919 
05920       /* Now play the beep once we have the message number for our next message. */
05921       if (res >= 0) {
05922          /* Unless we're *really* silent, try to send the beep */
05923          res = ast_stream_and_wait(chan, "beep", "");
05924       }
05925             
05926       /* Store information in real-time storage */
05927       if (ast_check_realtime("voicemail_data")) {
05928          snprintf(priority, sizeof(priority), "%d", chan->priority);
05929          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
05930          get_date(date, sizeof(date));
05931          ast_callerid_merge(callerid, sizeof(callerid),
05932             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05933             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05934             "Unknown");
05935          ast_store_realtime("voicemail_data",
05936             "origmailbox", ext,
05937             "context", chan->context,
05938             "macrocontext", chan->macrocontext,
05939             "exten", chan->exten,
05940             "priority", priority,
05941             "callerchan", chan->name,
05942             "callerid", callerid,
05943             "origdate", date,
05944             "origtime", origtime,
05945             "category", S_OR(category, ""),
05946             "filename", tmptxtfile,
05947             SENTINEL);
05948       }
05949 
05950       /* Store information */
05951       txt = fdopen(txtdes, "w+");
05952       if (txt) {
05953          get_date(date, sizeof(date));
05954          ast_callerid_merge(callerid, sizeof(callerid),
05955             S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
05956             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
05957             "Unknown");
05958          fprintf(txt, 
05959             ";\n"
05960             "; Message Information file\n"
05961             ";\n"
05962             "[message]\n"
05963             "origmailbox=%s\n"
05964             "context=%s\n"
05965             "macrocontext=%s\n"
05966             "exten=%s\n"
05967             "rdnis=%s\n"
05968             "priority=%d\n"
05969             "callerchan=%s\n"
05970             "callerid=%s\n"
05971             "origdate=%s\n"
05972             "origtime=%ld\n"
05973             "category=%s\n",
05974             ext,
05975             chan->context,
05976             chan->macrocontext, 
05977             chan->exten,
05978             S_COR(chan->redirecting.from.number.valid,
05979                chan->redirecting.from.number.str, "unknown"),
05980             chan->priority,
05981             chan->name,
05982             callerid,
05983             date, (long) time(NULL),
05984             category ? category : "");
05985       } else {
05986          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05987          inprocess_count(vmu->mailbox, vmu->context, -1);
05988          if (ast_check_realtime("voicemail_data")) {
05989             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05990          }
05991          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05992          goto leave_vm_out;
05993       }
05994       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
05995 
05996       if (txt) {
05997          fprintf(txt, "flag=%s\n", flag);
05998          if (sound_duration < vmu->minsecs) {
05999             fclose(txt);
06000             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
06001             ast_filedelete(tmptxtfile, NULL);
06002             unlink(tmptxtfile);
06003             if (ast_check_realtime("voicemail_data")) {
06004                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06005             }
06006             inprocess_count(vmu->mailbox, vmu->context, -1);
06007          } else {
06008             fprintf(txt, "duration=%d\n", duration);
06009             fclose(txt);
06010             if (vm_lock_path(dir)) {
06011                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06012                /* Delete files */
06013                ast_filedelete(tmptxtfile, NULL);
06014                unlink(tmptxtfile);
06015                inprocess_count(vmu->mailbox, vmu->context, -1);
06016             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06017                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06018                unlink(tmptxtfile);
06019                ast_unlock_path(dir);
06020                inprocess_count(vmu->mailbox, vmu->context, -1);
06021                if (ast_check_realtime("voicemail_data")) {
06022                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06023                }
06024             } else {
06025 #ifndef IMAP_STORAGE
06026                msgnum = last_message_index(vmu, dir) + 1;
06027 #endif
06028                make_file(fn, sizeof(fn), dir, msgnum);
06029 
06030                /* assign a variable with the name of the voicemail file */ 
06031 #ifndef IMAP_STORAGE
06032                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06033 #else
06034                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06035 #endif
06036 
06037                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06038                ast_filerename(tmptxtfile, fn, NULL);
06039                rename(tmptxtfile, txtfile);
06040                inprocess_count(vmu->mailbox, vmu->context, -1);
06041 
06042                /* Properly set permissions on voicemail text descriptor file.
06043                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06044                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06045                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06046 
06047                ast_unlock_path(dir);
06048                if (ast_check_realtime("voicemail_data")) {
06049                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06050                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06051                }
06052                /* We must store the file first, before copying the message, because
06053                 * ODBC storage does the entire copy with SQL.
06054                 */
06055                if (ast_fileexists(fn, NULL, NULL) > 0) {
06056                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
06057                }
06058 
06059                /* Are there to be more recipients of this message? */
06060                while (tmpptr) {
06061                   struct ast_vm_user recipu, *recip;
06062                   char *exten, *cntx;
06063 
06064                   exten = strsep(&tmpptr, "&");
06065                   cntx = strchr(exten, '@');
06066                   if (cntx) {
06067                      *cntx = '\0';
06068                      cntx++;
06069                   }
06070                   if ((recip = find_user(&recipu, cntx, exten))) {
06071                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
06072                      free_user(recip);
06073                   }
06074                }
06075 #ifndef IMAP_STORAGE
06076                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06077                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06078                   char sfn[PATH_MAX];
06079                   char dfn[PATH_MAX];
06080                   int x;
06081                   /* It's easier just to try to make it than to check for its existence */
06082                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06083                   x = last_message_index(vmu, urgdir) + 1;
06084                   make_file(sfn, sizeof(sfn), dir, msgnum);
06085                   make_file(dfn, sizeof(dfn), urgdir, x);
06086                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06087                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06088                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06089                   ast_copy_string(fn, dfn, sizeof(fn));
06090                   msgnum = x;
06091                }
06092 #endif
06093                /* Notification needs to happen after the copy, though. */
06094                if (ast_fileexists(fn, NULL, NULL)) {
06095 #ifdef IMAP_STORAGE
06096                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06097                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06098                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06099                      flag);
06100 #else
06101                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06102                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
06103                      S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
06104                      flag);
06105 #endif
06106                }
06107 
06108                /* Disposal needs to happen after the optional move and copy */
06109                if (ast_fileexists(fn, NULL, NULL)) {
06110                   DISPOSE(dir, msgnum);
06111                }
06112             }
06113          }
06114       } else {
06115          inprocess_count(vmu->mailbox, vmu->context, -1);
06116       }
06117       if (res == '0') {
06118          goto transfer;
06119       } else if (res > 0 && res != 't')
06120          res = 0;
06121 
06122       if (sound_duration < vmu->minsecs)
06123          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06124          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06125       else
06126          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06127    } else
06128       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06129 leave_vm_out:
06130    free_user(vmu);
06131 
06132 #ifdef IMAP_STORAGE
06133    /* expunge message - use UID Expunge if supported on IMAP server*/
06134    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06135    if (expungeonhangup == 1) {
06136       ast_mutex_lock(&vms->lock);
06137 #ifdef HAVE_IMAP_TK2006
06138       if (LEVELUIDPLUS (vms->mailstream)) {
06139          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06140       } else 
06141 #endif
06142          mail_expunge(vms->mailstream);
06143       ast_mutex_unlock(&vms->lock);
06144    }
06145 #endif
06146 
06147    ast_free(tmp);
06148    return res;
06149 }
06150 
06151 #if !defined(IMAP_STORAGE)
06152 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06153 {
06154    /* we know the actual number of messages, so stop process when number is hit */
06155 
06156    int x, dest;
06157    char sfn[PATH_MAX];
06158    char dfn[PATH_MAX];
06159 
06160    if (vm_lock_path(dir)) {
06161       return ERROR_LOCK_PATH;
06162    }
06163 
06164    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06165       make_file(sfn, sizeof(sfn), dir, x);
06166       if (EXISTS(dir, x, sfn, NULL)) {
06167 
06168          if (x != dest) {
06169             make_file(dfn, sizeof(dfn), dir, dest);
06170             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06171          }
06172 
06173          dest++;
06174       }
06175    }
06176    ast_unlock_path(dir);
06177 
06178    return dest;
06179 }
06180 #endif
06181 
06182 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06183 {
06184    int d;
06185    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06186    return d;
06187 }
06188 
06189 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
06190 {
06191 #ifdef IMAP_STORAGE
06192    /* we must use mbox(x) folder names, and copy the message there */
06193    /* simple. huh? */
06194    char sequence[10];
06195    char mailbox[256];
06196    int res;
06197 
06198    /* get the real IMAP message number for this message */
06199    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06200    
06201    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06202    ast_mutex_lock(&vms->lock);
06203    /* if save to Old folder, put in INBOX as read */
06204    if (box == OLD_FOLDER) {
06205       mail_setflag(vms->mailstream, sequence, "\\Seen");
06206       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06207    } else if (box == NEW_FOLDER) {
06208       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06209       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06210    }
06211    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06212       ast_mutex_unlock(&vms->lock);
06213       return 0;
06214    }
06215    /* Create the folder if it don't exist */
06216    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06217    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06218    if (mail_create(vms->mailstream, mailbox) == NIL) 
06219       ast_debug(5, "Folder exists.\n");
06220    else
06221       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06222    res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06223    ast_mutex_unlock(&vms->lock);
06224    return res;
06225 #else
06226    char *dir = vms->curdir;
06227    char *username = vms->username;
06228    char *context = vmu->context;
06229    char sfn[PATH_MAX];
06230    char dfn[PATH_MAX];
06231    char ddir[PATH_MAX];
06232    const char *dbox = mbox(vmu, box);
06233    int x, i;
06234    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06235 
06236    if (vm_lock_path(ddir))
06237       return ERROR_LOCK_PATH;
06238 
06239    x = last_message_index(vmu, ddir) + 1;
06240 
06241    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06242       x--;
06243       for (i = 1; i <= x; i++) {
06244          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06245          make_file(sfn, sizeof(sfn), ddir, i);
06246          make_file(dfn, sizeof(dfn), ddir, i - 1);
06247          if (EXISTS(ddir, i, sfn, NULL)) {
06248             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06249          } else
06250             break;
06251       }
06252    } else {
06253       if (x >= vmu->maxmsg) {
06254          ast_unlock_path(ddir);
06255          return -1;
06256       }
06257    }
06258    make_file(sfn, sizeof(sfn), dir, msg);
06259    make_file(dfn, sizeof(dfn), ddir, x);
06260    if (strcmp(sfn, dfn)) {
06261       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06262    }
06263    ast_unlock_path(ddir);
06264 #endif
06265    return 0;
06266 }
06267 
06268 static int adsi_logo(unsigned char *buf)
06269 {
06270    int bytes = 0;
06271    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06272    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06273    return bytes;
06274 }
06275 
06276 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06277 {
06278    unsigned char buf[256];
06279    int bytes = 0;
06280    int x;
06281    char num[5];
06282 
06283    *useadsi = 0;
06284    bytes += ast_adsi_data_mode(buf + bytes);
06285    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06286 
06287    bytes = 0;
06288    bytes += adsi_logo(buf);
06289    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06290 #ifdef DISPLAY
06291    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06292 #endif
06293    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06294    bytes += ast_adsi_data_mode(buf + bytes);
06295    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06296 
06297    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06298       bytes = 0;
06299       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06300       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06301       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06302       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06303       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06304       return 0;
06305    }
06306 
06307 #ifdef DISPLAY
06308    /* Add a dot */
06309    bytes = 0;
06310    bytes += ast_adsi_logo(buf);
06311    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06312    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06313    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06314    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06315 #endif
06316    bytes = 0;
06317    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06318    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06319    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06320    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06321    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06322    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06323    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06324 
06325 #ifdef DISPLAY
06326    /* Add another dot */
06327    bytes = 0;
06328    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06329    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06330 
06331    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06332    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06333 #endif
06334 
06335    bytes = 0;
06336    /* These buttons we load but don't use yet */
06337    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
06338    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
06339    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
06340    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
06341    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
06342    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
06343    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06344 
06345 #ifdef DISPLAY
06346    /* Add another dot */
06347    bytes = 0;
06348    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
06349    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06350    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06351 #endif
06352 
06353    bytes = 0;
06354    for (x = 0; x < 5; x++) {
06355       snprintf(num, sizeof(num), "%d", x);
06356       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
06357    }
06358    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
06359    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06360 
06361 #ifdef DISPLAY
06362    /* Add another dot */
06363    bytes = 0;
06364    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
06365    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06366    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06367 #endif
06368 
06369    if (ast_adsi_end_download(chan)) {
06370       bytes = 0;
06371       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
06372       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06373       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06374       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06375       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06376       return 0;
06377    }
06378    bytes = 0;
06379    bytes += ast_adsi_download_disconnect(buf + bytes);
06380    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06381    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06382 
06383    ast_debug(1, "Done downloading scripts...\n");
06384 
06385 #ifdef DISPLAY
06386    /* Add last dot */
06387    bytes = 0;
06388    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
06389    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06390 #endif
06391    ast_debug(1, "Restarting session...\n");
06392 
06393    bytes = 0;
06394    /* Load the session now */
06395    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
06396       *useadsi = 1;
06397       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
06398    } else
06399       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
06400 
06401    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06402    return 0;
06403 }
06404 
06405 static void adsi_begin(struct ast_channel *chan, int *useadsi)
06406 {
06407    int x;
06408    if (!ast_adsi_available(chan))
06409       return;
06410    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
06411    if (x < 0)
06412       return;
06413    if (!x) {
06414       if (adsi_load_vmail(chan, useadsi)) {
06415          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
06416          return;
06417       }
06418    } else
06419       *useadsi = 1;
06420 }
06421 
06422 static void adsi_login(struct ast_channel *chan)
06423 {
06424    unsigned char buf[256];
06425    int bytes = 0;
06426    unsigned char keys[8];
06427    int x;
06428    if (!ast_adsi_available(chan))
06429       return;
06430 
06431    for (x = 0; x < 8; x++)
06432       keys[x] = 0;
06433    /* Set one key for next */
06434    keys[3] = ADSI_KEY_APPS + 3;
06435 
06436    bytes += adsi_logo(buf + bytes);
06437    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
06438    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
06439    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06440    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
06441    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
06442    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
06443    bytes += ast_adsi_set_keys(buf + bytes, keys);
06444    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06445    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06446 }
06447 
06448 static void adsi_password(struct ast_channel *chan)
06449 {
06450    unsigned char buf[256];
06451    int bytes = 0;
06452    unsigned char keys[8];
06453    int x;
06454    if (!ast_adsi_available(chan))
06455       return;
06456 
06457    for (x = 0; x < 8; x++)
06458       keys[x] = 0;
06459    /* Set one key for next */
06460    keys[3] = ADSI_KEY_APPS + 3;
06461 
06462    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06463    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
06464    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
06465    bytes += ast_adsi_set_keys(buf + bytes, keys);
06466    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06467    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06468 }
06469 
06470 static void adsi_folders(struct ast_channel *chan, int start, char *label)
06471 {
06472    unsigned char buf[256];
06473    int bytes = 0;
06474    unsigned char keys[8];
06475    int x, y;
06476 
06477    if (!ast_adsi_available(chan))
06478       return;
06479 
06480    for (x = 0; x < 5; x++) {
06481       y = ADSI_KEY_APPS + 12 + start + x;
06482       if (y > ADSI_KEY_APPS + 12 + 4)
06483          y = 0;
06484       keys[x] = ADSI_KEY_SKT | y;
06485    }
06486    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
06487    keys[6] = 0;
06488    keys[7] = 0;
06489 
06490    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
06491    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
06492    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06493    bytes += ast_adsi_set_keys(buf + bytes, keys);
06494    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06495 
06496    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06497 }
06498 
06499 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
06500 {
06501    int bytes = 0;
06502    unsigned char buf[256]; 
06503    char buf1[256], buf2[256];
06504    char fn2[PATH_MAX];
06505 
06506    char cid[256] = "";
06507    char *val;
06508    char *name, *num;
06509    char datetime[21] = "";
06510    FILE *f;
06511 
06512    unsigned char keys[8];
06513 
06514    int x;
06515 
06516    if (!ast_adsi_available(chan))
06517       return;
06518 
06519    /* Retrieve important info */
06520    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
06521    f = fopen(fn2, "r");
06522    if (f) {
06523       while (!feof(f)) {   
06524          if (!fgets((char *) buf, sizeof(buf), f)) {
06525             continue;
06526          }
06527          if (!feof(f)) {
06528             char *stringp = NULL;
06529             stringp = (char *) buf;
06530             strsep(&stringp, "=");
06531             val = strsep(&stringp, "=");
06532             if (!ast_strlen_zero(val)) {
06533                if (!strcmp((char *) buf, "callerid"))
06534                   ast_copy_string(cid, val, sizeof(cid));
06535                if (!strcmp((char *) buf, "origdate"))
06536                   ast_copy_string(datetime, val, sizeof(datetime));
06537             }
06538          }
06539       }
06540       fclose(f);
06541    }
06542    /* New meaning for keys */
06543    for (x = 0; x < 5; x++)
06544       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06545    keys[6] = 0x0;
06546    keys[7] = 0x0;
06547 
06548    if (!vms->curmsg) {
06549       /* No prev key, provide "Folder" instead */
06550       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06551    }
06552    if (vms->curmsg >= vms->lastmsg) {
06553       /* If last message ... */
06554       if (vms->curmsg) {
06555          /* but not only message, provide "Folder" instead */
06556          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06557          bytes += ast_adsi_voice_mode(buf + bytes, 0);
06558 
06559       } else {
06560          /* Otherwise if only message, leave blank */
06561          keys[3] = 1;
06562       }
06563    }
06564 
06565    if (!ast_strlen_zero(cid)) {
06566       ast_callerid_parse(cid, &name, &num);
06567       if (!name)
06568          name = num;
06569    } else
06570       name = "Unknown Caller";
06571 
06572    /* If deleted, show "undeleted" */
06573 #ifdef IMAP_STORAGE
06574    ast_mutex_lock(&vms->lock);
06575 #endif
06576    if (vms->deleted[vms->curmsg]) {
06577       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06578    }
06579 #ifdef IMAP_STORAGE
06580    ast_mutex_unlock(&vms->lock);
06581 #endif
06582 
06583    /* Except "Exit" */
06584    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06585    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
06586       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
06587    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
06588 
06589    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06590    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06591    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
06592    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
06593    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06594    bytes += ast_adsi_set_keys(buf + bytes, keys);
06595    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06596 
06597    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06598 }
06599 
06600 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
06601 {
06602    int bytes = 0;
06603    unsigned char buf[256];
06604    unsigned char keys[8];
06605 
06606    int x;
06607 
06608    if (!ast_adsi_available(chan))
06609       return;
06610 
06611    /* New meaning for keys */
06612    for (x = 0; x < 5; x++)
06613       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
06614 
06615    keys[6] = 0x0;
06616    keys[7] = 0x0;
06617 
06618    if (!vms->curmsg) {
06619       /* No prev key, provide "Folder" instead */
06620       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06621    }
06622    if (vms->curmsg >= vms->lastmsg) {
06623       /* If last message ... */
06624       if (vms->curmsg) {
06625          /* but not only message, provide "Folder" instead */
06626          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
06627       } else {
06628          /* Otherwise if only message, leave blank */
06629          keys[3] = 1;
06630       }
06631    }
06632 
06633    /* If deleted, show "undeleted" */
06634 #ifdef IMAP_STORAGE
06635    ast_mutex_lock(&vms->lock);
06636 #endif
06637    if (vms->deleted[vms->curmsg]) {
06638       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
06639    }
06640 #ifdef IMAP_STORAGE
06641    ast_mutex_unlock(&vms->lock);
06642 #endif
06643 
06644    /* Except "Exit" */
06645    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
06646    bytes += ast_adsi_set_keys(buf + bytes, keys);
06647    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06648 
06649    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06650 }
06651 
06652 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
06653 {
06654    unsigned char buf[256] = "";
06655    char buf1[256] = "", buf2[256] = "";
06656    int bytes = 0;
06657    unsigned char keys[8];
06658    int x;
06659 
06660    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06661    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06662    if (!ast_adsi_available(chan))
06663       return;
06664    if (vms->newmessages) {
06665       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06666       if (vms->oldmessages) {
06667          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06668          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06669       } else {
06670          snprintf(buf2, sizeof(buf2), "%s.", newm);
06671       }
06672    } else if (vms->oldmessages) {
06673       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06674       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06675    } else {
06676       strcpy(buf1, "You have no messages.");
06677       buf2[0] = ' ';
06678       buf2[1] = '\0';
06679    }
06680    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06681    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06682    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06683 
06684    for (x = 0; x < 6; x++)
06685       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06686    keys[6] = 0;
06687    keys[7] = 0;
06688 
06689    /* Don't let them listen if there are none */
06690    if (vms->lastmsg < 0)
06691       keys[0] = 1;
06692    bytes += ast_adsi_set_keys(buf + bytes, keys);
06693 
06694    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06695 
06696    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06697 }
06698 
06699 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06700 {
06701    unsigned char buf[256] = "";
06702    char buf1[256] = "", buf2[256] = "";
06703    int bytes = 0;
06704    unsigned char keys[8];
06705    int x;
06706 
06707    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06708 
06709    if (!ast_adsi_available(chan))
06710       return;
06711 
06712    /* Original command keys */
06713    for (x = 0; x < 6; x++)
06714       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06715 
06716    keys[6] = 0;
06717    keys[7] = 0;
06718 
06719    if ((vms->lastmsg + 1) < 1)
06720       keys[0] = 0;
06721 
06722    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06723       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06724 
06725    if (vms->lastmsg + 1)
06726       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06727    else
06728       strcpy(buf2, "no messages.");
06729    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06730    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06731    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06732    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06733    bytes += ast_adsi_set_keys(buf + bytes, keys);
06734 
06735    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06736 
06737    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06738    
06739 }
06740 
06741 /*
06742 static void adsi_clear(struct ast_channel *chan)
06743 {
06744    char buf[256];
06745    int bytes=0;
06746    if (!ast_adsi_available(chan))
06747       return;
06748    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06749    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06750 
06751    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06752 }
06753 */
06754 
06755 static void adsi_goodbye(struct ast_channel *chan)
06756 {
06757    unsigned char buf[256];
06758    int bytes = 0;
06759 
06760    if (!ast_adsi_available(chan))
06761       return;
06762    bytes += adsi_logo(buf + bytes);
06763    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06764    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06765    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06766    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06767 
06768    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06769 }
06770 
06771 /*!\brief get_folder: Folder menu
06772  * Plays "press 1 for INBOX messages" etc.
06773  * Should possibly be internationalized
06774  */
06775 static int get_folder(struct ast_channel *chan, int start)
06776 {
06777    int x;
06778    int d;
06779    char fn[PATH_MAX];
06780    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06781    if (d)
06782       return d;
06783    for (x = start; x < 5; x++) { /* For all folders */
06784       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06785          return d;
06786       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06787       if (d)
06788          return d;
06789       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
06790 
06791       /* The inbox folder can have its name changed under certain conditions
06792        * so this checks if the sound file exists for the inbox folder name and
06793        * if it doesn't, plays the default name instead. */
06794       if (x == 0) {
06795          if (ast_fileexists(fn, NULL, NULL)) {
06796             d = vm_play_folder_name(chan, fn);
06797          } else {
06798             ast_verb(1, "failed to find %s\n", fn);
06799             d = vm_play_folder_name(chan, "vm-INBOX");
06800          }
06801       } else {
06802          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
06803          d = vm_play_folder_name(chan, fn);
06804       }
06805 
06806       if (d)
06807          return d;
06808       d = ast_waitfordigit(chan, 500);
06809       if (d)
06810          return d;
06811    }
06812 
06813    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06814    if (d)
06815       return d;
06816    d = ast_waitfordigit(chan, 4000);
06817    return d;
06818 }
06819 
06820 /*!
06821  * \brief plays a prompt and waits for a keypress.
06822  * \param chan
06823  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06824  * \param start Does not appear to be used at this time.
06825  *
06826  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06827  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06828  * prompting for the number inputs that correspond to the available folders.
06829  * 
06830  * \return zero on success, or -1 on error.
06831  */
06832 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06833 {
06834    int res = 0;
06835    int loops = 0;
06836 
06837    res = ast_play_and_wait(chan, fn);  /* Folder name */
06838    while (((res < '0') || (res > '9')) &&
06839          (res != '#') && (res >= 0) &&
06840          loops < 4) {
06841       res = get_folder(chan, 0);
06842       loops++;
06843    }
06844    if (loops == 4) { /* give up */
06845       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
06846       return '#';
06847    }
06848    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
06849    return res;
06850 }
06851 
06852 /*!
06853  * \brief presents the option to prepend to an existing message when forwarding it.
06854  * \param chan
06855  * \param vmu
06856  * \param curdir
06857  * \param curmsg
06858  * \param vm_fmts
06859  * \param context
06860  * \param record_gain
06861  * \param duration
06862  * \param vms
06863  * \param flag 
06864  *
06865  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06866  *
06867  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06868  * \return zero on success, -1 on error.
06869  */
06870 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06871          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06872 {
06873    int cmd = 0;
06874    int retries = 0, prepend_duration = 0, already_recorded = 0;
06875    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06876    char textfile[PATH_MAX];
06877    struct ast_config *msg_cfg;
06878    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06879 #ifndef IMAP_STORAGE
06880    signed char zero_gain = 0;
06881 #endif
06882    const char *duration_str;
06883 
06884    /* Must always populate duration correctly */
06885    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06886    strcpy(textfile, msgfile);
06887    strcpy(backup, msgfile);
06888    strcpy(backup_textfile, msgfile);
06889    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06890    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06891    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06892 
06893    if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06894       *duration = atoi(duration_str);
06895    } else {
06896       *duration = 0;
06897    }
06898 
06899    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06900       if (cmd)
06901          retries = 0;
06902       switch (cmd) {
06903       case '1': 
06904 
06905 #ifdef IMAP_STORAGE
06906          /* Record new intro file */
06907          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06908          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06909          ast_play_and_wait(chan, INTRO);
06910          ast_play_and_wait(chan, "beep");
06911          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
06912          if (cmd == -1) {
06913             break;
06914          }
06915          cmd = 't';
06916 #else
06917 
06918          /* prepend a message to the current message, update the metadata and return */
06919 
06920          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06921          strcpy(textfile, msgfile);
06922          strncat(textfile, ".txt", sizeof(textfile) - 1);
06923          *duration = 0;
06924 
06925          /* if we can't read the message metadata, stop now */
06926          if (!msg_cfg) {
06927             cmd = 0;
06928             break;
06929          }
06930 
06931          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06932 #ifndef IMAP_STORAGE
06933          if (already_recorded) {
06934             ast_filecopy(backup, msgfile, NULL);
06935             copy(backup_textfile, textfile);
06936          }
06937          else {
06938             ast_filecopy(msgfile, backup, NULL);
06939             copy(textfile, backup_textfile);
06940          }
06941 #endif
06942          already_recorded = 1;
06943 
06944          if (record_gain)
06945             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06946 
06947          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
06948 
06949          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
06950             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
06951             ast_stream_and_wait(chan, vm_prepend_timeout, "");
06952             ast_filerename(backup, msgfile, NULL);
06953          }
06954 
06955          if (record_gain)
06956             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06957 
06958          
06959          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06960             *duration = atoi(duration_str);
06961 
06962          if (prepend_duration) {
06963             struct ast_category *msg_cat;
06964             /* need enough space for a maximum-length message duration */
06965             char duration_buf[12];
06966 
06967             *duration += prepend_duration;
06968             msg_cat = ast_category_get(msg_cfg, "message");
06969             snprintf(duration_buf, 11, "%ld", *duration);
06970             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06971                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
06972             }
06973          }
06974 
06975 #endif
06976          break;
06977       case '2': 
06978          /* NULL out introfile so we know there is no intro! */
06979 #ifdef IMAP_STORAGE
06980          *vms->introfn = '\0';
06981 #endif
06982          cmd = 't';
06983          break;
06984       case '*':
06985          cmd = '*';
06986          break;
06987       default: 
06988          /* If time_out and return to menu, reset already_recorded */
06989          already_recorded = 0;
06990 
06991          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
06992             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06993          if (!cmd) {
06994             cmd = ast_play_and_wait(chan, "vm-starmain");
06995             /* "press star to return to the main menu" */
06996          }
06997          if (!cmd) {
06998             cmd = ast_waitfordigit(chan, 6000);
06999          }
07000          if (!cmd) {
07001             retries++;
07002          }
07003          if (retries > 3) {
07004             cmd = '*'; /* Let's cancel this beast */
07005          }
07006          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07007       }
07008    }
07009 
07010    if (msg_cfg)
07011       ast_config_destroy(msg_cfg);
07012    if (prepend_duration)
07013       *duration = prepend_duration;
07014 
07015    if (already_recorded && cmd == -1) {
07016       /* restore original message if prepention cancelled */
07017       ast_filerename(backup, msgfile, NULL);
07018       rename(backup_textfile, textfile);
07019    }
07020 
07021    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07022       cmd = 0;
07023    return cmd;
07024 }
07025 
07026 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07027 {
07028    struct ast_event *event;
07029    char *mailbox, *context;
07030 
07031    /* Strip off @default */
07032    context = mailbox = ast_strdupa(box);
07033    strsep(&context, "@");
07034    if (ast_strlen_zero(context))
07035       context = "default";
07036 
07037    if (!(event = ast_event_new(AST_EVENT_MWI,
07038          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07039          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07040          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07041          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07042          AST_EVENT_IE_END))) {
07043       return;
07044    }
07045 
07046    ast_event_queue_and_cache(event);
07047 }
07048 
07049 /*!
07050  * \brief Sends email notification that a user has a new voicemail waiting for them.
07051  * \param chan
07052  * \param vmu
07053  * \param vms
07054  * \param msgnum
07055  * \param duration
07056  * \param fmt
07057  * \param cidnum The Caller ID phone number value.
07058  * \param cidname The Caller ID name value.
07059  * \param flag
07060  *
07061  * \return zero on success, -1 on error.
07062  */
07063 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)
07064 {
07065    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07066    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07067    const char *category;
07068    char *myserveremail = serveremail;
07069 
07070    ast_channel_lock(chan);
07071    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07072       category = ast_strdupa(category);
07073    }
07074    ast_channel_unlock(chan);
07075 
07076 #ifndef IMAP_STORAGE
07077    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07078 #else
07079    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07080 #endif
07081    make_file(fn, sizeof(fn), todir, msgnum);
07082    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07083 
07084    if (!ast_strlen_zero(vmu->attachfmt)) {
07085       if (strstr(fmt, vmu->attachfmt))
07086          fmt = vmu->attachfmt;
07087       else
07088          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);
07089    }
07090 
07091    /* Attach only the first format */
07092    fmt = ast_strdupa(fmt);
07093    stringp = fmt;
07094    strsep(&stringp, "|");
07095 
07096    if (!ast_strlen_zero(vmu->serveremail))
07097       myserveremail = vmu->serveremail;
07098 
07099    if (!ast_strlen_zero(vmu->email)) {
07100       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07101 
07102       if (attach_user_voicemail)
07103          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07104 
07105       /* XXX possible imap issue, should category be NULL XXX */
07106       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
07107 
07108       if (attach_user_voicemail)
07109          DISPOSE(todir, msgnum);
07110    }
07111 
07112    if (!ast_strlen_zero(vmu->pager)) {
07113       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07114    }
07115 
07116    if (ast_test_flag(vmu, VM_DELETE))
07117       DELETE(todir, msgnum, fn, vmu);
07118 
07119    /* Leave voicemail for someone */
07120    if (ast_app_has_voicemail(ext_context, NULL)) 
07121       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07122 
07123    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07124 
07125    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);
07126    run_externnotify(vmu->context, vmu->mailbox, flag);
07127 
07128 #ifdef IMAP_STORAGE
07129    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07130    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07131       vm_imap_delete(NULL, vms->curmsg, vmu);
07132       vms->newmessages--;  /* Fix new message count */
07133    }
07134 #endif
07135 
07136    return 0;
07137 }
07138 
07139 /*!
07140  * \brief Sends a voicemail message to a mailbox recipient.
07141  * \param chan
07142  * \param context
07143  * \param vms
07144  * \param sender
07145  * \param fmt
07146  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07147  *             Will be 0 when called to forward an existing message (option 8)
07148  *             Will be 1 when called to leave a message (option 3->5)
07149  * \param record_gain 
07150  * \param urgent
07151  *
07152  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07153  * 
07154  * When in the leave message mode (is_new_message == 1):
07155  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07156  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07157  *
07158  * When in the forward message mode (is_new_message == 0):
07159  *   - retreives the current message to be forwarded
07160  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07161  *   - determines the target mailbox and folders
07162  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07163  *
07164  * \return zero on success, -1 on error.
07165  */
07166 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)
07167 {
07168 #ifdef IMAP_STORAGE
07169    int todircount = 0;
07170    struct vm_state *dstvms;
07171 #endif
07172    char username[70]="";
07173    char fn[PATH_MAX]; /* for playback of name greeting */
07174    char ecodes[16] = "#";
07175    int res = 0, cmd = 0;
07176    struct ast_vm_user *receiver = NULL, *vmtmp;
07177    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07178    char *stringp;
07179    const char *s;
07180    int saved_messages = 0;
07181    int valid_extensions = 0;
07182    char *dir;
07183    int curmsg;
07184    char urgent_str[7] = "";
07185    int prompt_played = 0;
07186 #ifndef IMAP_STORAGE
07187    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07188 #endif
07189    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07190       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07191    }
07192 
07193    if (vms == NULL) return -1;
07194    dir = vms->curdir;
07195    curmsg = vms->curmsg;
07196 
07197    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07198    while (!res && !valid_extensions) {
07199       int use_directory = 0;
07200       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07201          int done = 0;
07202          int retries = 0;
07203          cmd = 0;
07204          while ((cmd >= 0) && !done ){
07205             if (cmd)
07206                retries = 0;
07207             switch (cmd) {
07208             case '1': 
07209                use_directory = 0;
07210                done = 1;
07211                break;
07212             case '2': 
07213                use_directory = 1;
07214                done = 1;
07215                break;
07216             case '*': 
07217                cmd = 't';
07218                done = 1;
07219                break;
07220             default: 
07221                /* Press 1 to enter an extension press 2 to use the directory */
07222                cmd = ast_play_and_wait(chan, "vm-forward");
07223                if (!cmd) {
07224                   cmd = ast_waitfordigit(chan, 3000);
07225                }
07226                if (!cmd) {
07227                   retries++;
07228                }
07229                if (retries > 3) {
07230                   cmd = 't';
07231                   done = 1;
07232                }
07233                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07234             }
07235          }
07236          if (cmd < 0 || cmd == 't')
07237             break;
07238       }
07239       
07240       if (use_directory) {
07241          /* use app_directory */
07242          
07243          char old_context[sizeof(chan->context)];
07244          char old_exten[sizeof(chan->exten)];
07245          int old_priority;
07246          struct ast_app* directory_app;
07247 
07248          directory_app = pbx_findapp("Directory");
07249          if (directory_app) {
07250             char vmcontext[256];
07251             /* make backup copies */
07252             memcpy(old_context, chan->context, sizeof(chan->context));
07253             memcpy(old_exten, chan->exten, sizeof(chan->exten));
07254             old_priority = chan->priority;
07255             
07256             /* call the the Directory, changes the channel */
07257             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07258             res = pbx_exec(chan, directory_app, vmcontext);
07259             
07260             ast_copy_string(username, chan->exten, sizeof(username));
07261             
07262             /* restore the old context, exten, and priority */
07263             memcpy(chan->context, old_context, sizeof(chan->context));
07264             memcpy(chan->exten, old_exten, sizeof(chan->exten));
07265             chan->priority = old_priority;
07266          } else {
07267             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07268             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07269          }
07270       } else {
07271          /* Ask for an extension */
07272          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07273          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
07274          prompt_played++;
07275          if (res || prompt_played > 4)
07276             break;
07277          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
07278             break;
07279       }
07280       
07281       /* start all over if no username */
07282       if (ast_strlen_zero(username))
07283          continue;
07284       stringp = username;
07285       s = strsep(&stringp, "*");
07286       /* start optimistic */
07287       valid_extensions = 1;
07288       while (s) {
07289          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
07290             int oldmsgs;
07291             int newmsgs;
07292             int capacity;
07293             if (inboxcount(s, &newmsgs, &oldmsgs)) {
07294                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
07295                /* Shouldn't happen, but allow trying another extension if it does */
07296                res = ast_play_and_wait(chan, "pbx-invalid");
07297                valid_extensions = 0;
07298                break;
07299             }
07300             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
07301             if ((newmsgs + oldmsgs) >= capacity) {
07302                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
07303                res = ast_play_and_wait(chan, "vm-mailboxfull");
07304                valid_extensions = 0;
07305                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07306                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07307                   free_user(vmtmp);
07308                }
07309                inprocess_count(receiver->mailbox, receiver->context, -1);
07310                break;
07311             }
07312             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
07313          } else {
07314             /* XXX Optimization for the future.  When we encounter a single bad extension,
07315              * bailing out on all of the extensions may not be the way to go.  We should
07316              * probably just bail on that single extension, then allow the user to enter
07317              * several more. XXX
07318              */
07319             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07320                free_user(receiver);
07321             }
07322             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
07323             /* "I am sorry, that's not a valid extension.  Please try again." */
07324             res = ast_play_and_wait(chan, "pbx-invalid");
07325             valid_extensions = 0;
07326             break;
07327          }
07328 
07329          /* play name if available, else play extension number */
07330          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
07331          RETRIEVE(fn, -1, s, receiver->context);
07332          if (ast_fileexists(fn, NULL, NULL) > 0) {
07333             res = ast_stream_and_wait(chan, fn, ecodes);
07334             if (res) {
07335                DISPOSE(fn, -1);
07336                return res;
07337             }
07338          } else {
07339             res = ast_say_digit_str(chan, s, ecodes, chan->language);
07340          }
07341          DISPOSE(fn, -1);
07342 
07343          s = strsep(&stringp, "*");
07344       }
07345       /* break from the loop of reading the extensions */
07346       if (valid_extensions)
07347          break;
07348    }
07349    /* check if we're clear to proceed */
07350    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
07351       return res;
07352    if (is_new_message == 1) {
07353       struct leave_vm_options leave_options;
07354       char mailbox[AST_MAX_EXTENSION * 2 + 2];
07355       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
07356 
07357       /* Send VoiceMail */
07358       memset(&leave_options, 0, sizeof(leave_options));
07359       leave_options.record_gain = record_gain;
07360       cmd = leave_voicemail(chan, mailbox, &leave_options);
07361    } else {
07362       /* Forward VoiceMail */
07363       long duration = 0;
07364       struct vm_state vmstmp;
07365       int copy_msg_result = 0;
07366       memcpy(&vmstmp, vms, sizeof(vmstmp));
07367 
07368       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
07369 
07370       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
07371       if (!cmd) {
07372          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
07373 #ifdef IMAP_STORAGE
07374             int attach_user_voicemail;
07375             char *myserveremail = serveremail;
07376             
07377             /* get destination mailbox */
07378             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
07379             if (!dstvms) {
07380                dstvms = create_vm_state_from_user(vmtmp);
07381             }
07382             if (dstvms) {
07383                init_mailstream(dstvms, 0);
07384                if (!dstvms->mailstream) {
07385                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
07386                } else {
07387                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
07388                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
07389                }
07390             } else {
07391                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
07392             }
07393             if (!ast_strlen_zero(vmtmp->serveremail))
07394                myserveremail = vmtmp->serveremail;
07395             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
07396             /* NULL category for IMAP storage */
07397             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
07398                dstvms->curbox,
07399                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
07400                S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
07401                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
07402                NULL, urgent_str);
07403 #else
07404             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
07405 #endif
07406             saved_messages++;
07407             AST_LIST_REMOVE_CURRENT(list);
07408             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07409             free_user(vmtmp);
07410             if (res)
07411                break;
07412          }
07413          AST_LIST_TRAVERSE_SAFE_END;
07414          if (saved_messages > 0 && !copy_msg_result) {
07415             /* give confirmation that the message was saved */
07416             /* commented out since we can't forward batches yet
07417             if (saved_messages == 1)
07418                res = ast_play_and_wait(chan, "vm-message");
07419             else
07420                res = ast_play_and_wait(chan, "vm-messages");
07421             if (!res)
07422                res = ast_play_and_wait(chan, "vm-saved"); */
07423 #ifdef IMAP_STORAGE
07424             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
07425             if (ast_strlen_zero(vmstmp.introfn))
07426 #endif
07427             res = ast_play_and_wait(chan, "vm-msgsaved");
07428          }
07429 #ifndef IMAP_STORAGE
07430          else {
07431             /* with IMAP, mailbox full warning played by imap_check_limits */
07432             res = ast_play_and_wait(chan, "vm-mailboxfull");
07433          }
07434          /* Restore original message without prepended message if backup exists */
07435          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07436          strcpy(textfile, msgfile);
07437          strcpy(backup, msgfile);
07438          strcpy(backup_textfile, msgfile);
07439          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07440          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07441          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07442          if (ast_fileexists(backup, NULL, NULL) > 0) {
07443             ast_filerename(backup, msgfile, NULL);
07444             rename(backup_textfile, textfile);
07445          }
07446 #endif
07447       }
07448       DISPOSE(dir, curmsg);
07449 #ifndef IMAP_STORAGE
07450       if (cmd) { /* assuming hangup, cleanup backup file */
07451          make_file(msgfile, sizeof(msgfile), dir, curmsg);
07452          strcpy(textfile, msgfile);
07453          strcpy(backup_textfile, msgfile);
07454          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07455          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07456          rename(backup_textfile, textfile);
07457       }
07458 #endif
07459    }
07460 
07461    /* If anything failed above, we still have this list to free */
07462    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
07463       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
07464       free_user(vmtmp);
07465    }
07466    return res ? res : cmd;
07467 }
07468 
07469 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
07470 {
07471    int res;
07472    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
07473       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
07474    return res;
07475 }
07476 
07477 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
07478 {
07479    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
07480    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);
07481 }
07482 
07483 static int play_message_category(struct ast_channel *chan, const char *category)
07484 {
07485    int res = 0;
07486 
07487    if (!ast_strlen_zero(category))
07488       res = ast_play_and_wait(chan, category);
07489 
07490    if (res) {
07491       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
07492       res = 0;
07493    }
07494 
07495    return res;
07496 }
07497 
07498 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
07499 {
07500    int res = 0;
07501    struct vm_zone *the_zone = NULL;
07502    time_t t;
07503 
07504    if (ast_get_time_t(origtime, &t, 0, NULL)) {
07505       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
07506       return 0;
07507    }
07508 
07509    /* Does this user have a timezone specified? */
07510    if (!ast_strlen_zero(vmu->zonetag)) {
07511       /* Find the zone in the list */
07512       struct vm_zone *z;
07513       AST_LIST_LOCK(&zones);
07514       AST_LIST_TRAVERSE(&zones, z, list) {
07515          if (!strcmp(z->name, vmu->zonetag)) {
07516             the_zone = z;
07517             break;
07518          }
07519       }
07520       AST_LIST_UNLOCK(&zones);
07521    }
07522 
07523 /* No internal variable parsing for now, so we'll comment it out for the time being */
07524 #if 0
07525    /* Set the DIFF_* variables */
07526    ast_localtime(&t, &time_now, NULL);
07527    tv_now = ast_tvnow();
07528    ast_localtime(&tv_now, &time_then, NULL);
07529 
07530    /* Day difference */
07531    if (time_now.tm_year == time_then.tm_year)
07532       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
07533    else
07534       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
07535    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
07536 
07537    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
07538 #endif
07539    if (the_zone) {
07540       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
07541    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
07542       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07543    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
07544       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
07545    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
07546       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);
07547    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
07548       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
07549    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
07550       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
07551    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
07552       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
07553    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
07554       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);
07555    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
07556       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
07557    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
07558       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
07559    } else if (!strncasecmp(chan->language, "vi", 2)) {     /* VIETNAMESE syntax */
07560       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);
07561    } else {
07562       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
07563    }
07564 #if 0
07565    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
07566 #endif
07567    return res;
07568 }
07569 
07570 
07571 
07572 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
07573 {
07574    int res = 0;
07575    int i;
07576    char *callerid, *name;
07577    char prefile[PATH_MAX] = "";
07578    
07579 
07580    /* If voicemail cid is not enabled, or we didn't get cid or context from
07581     * the attribute file, leave now.
07582     *
07583     * TODO Still need to change this so that if this function is called by the
07584     * message envelope (and someone is explicitly requesting to hear the CID),
07585     * it does not check to see if CID is enabled in the config file.
07586     */
07587    if ((cid == NULL)||(context == NULL))
07588       return res;
07589 
07590    /* Strip off caller ID number from name */
07591    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
07592    ast_callerid_parse(cid, &name, &callerid);
07593    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
07594       /* Check for internal contexts and only */
07595       /* say extension when the call didn't come from an internal context in the list */
07596       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
07597          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
07598          if ((strcmp(cidinternalcontexts[i], context) == 0))
07599             break;
07600       }
07601       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
07602          if (!res) {
07603             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
07604             if (!ast_strlen_zero(prefile)) {
07605             /* See if we can find a recorded name for this person instead of their extension number */
07606                if (ast_fileexists(prefile, NULL, NULL) > 0) {
07607                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
07608                   if (!callback)
07609                      res = wait_file2(chan, vms, "vm-from");
07610                   res = ast_stream_and_wait(chan, prefile, "");
07611                } else {
07612                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
07613                   /* Say "from extension" as one saying to sound smoother */
07614                   if (!callback)
07615                      res = wait_file2(chan, vms, "vm-from-extension");
07616                   res = ast_say_digit_str(chan, callerid, "", chan->language);
07617                }
07618             }
07619          }
07620       } else if (!res) {
07621          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
07622          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
07623          if (!callback)
07624             res = wait_file2(chan, vms, "vm-from-phonenumber");
07625          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
07626       }
07627    } else {
07628       /* Number unknown */
07629       ast_debug(1, "VM-CID: From an unknown number\n");
07630       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
07631       res = wait_file2(chan, vms, "vm-unknown-caller");
07632    }
07633    return res;
07634 }
07635 
07636 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
07637 {
07638    int res = 0;
07639    int durationm;
07640    int durations;
07641    /* Verify that we have a duration for the message */
07642    if (duration == NULL)
07643       return res;
07644 
07645    /* Convert from seconds to minutes */
07646    durations = atoi(duration);
07647    durationm = (durations / 60);
07648 
07649    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
07650 
07651    if ((!res) && (durationm >= minduration)) {
07652       res = wait_file2(chan, vms, "vm-duration");
07653 
07654       /* POLISH syntax */
07655       if (!strncasecmp(chan->language, "pl", 2)) {
07656          div_t num = div(durationm, 10);
07657 
07658          if (durationm == 1) {
07659             res = ast_play_and_wait(chan, "digits/1z");
07660             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
07661          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07662             if (num.rem == 2) {
07663                if (!num.quot) {
07664                   res = ast_play_and_wait(chan, "digits/2-ie");
07665                } else {
07666                   res = say_and_wait(chan, durationm - 2 , chan->language);
07667                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07668                }
07669             } else {
07670                res = say_and_wait(chan, durationm, chan->language);
07671             }
07672             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
07673          } else {
07674             res = say_and_wait(chan, durationm, chan->language);
07675             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
07676          }
07677       /* DEFAULT syntax */
07678       } else {
07679          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
07680          res = wait_file2(chan, vms, "vm-minutes");
07681       }
07682    }
07683    return res;
07684 }
07685 
07686 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07687 {
07688    int res = 0;
07689    char filename[256], *cid;
07690    const char *origtime, *context, *category, *duration, *flag;
07691    struct ast_config *msg_cfg;
07692    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07693 
07694    vms->starting = 0;
07695    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07696    adsi_message(chan, vms);
07697    if (!vms->curmsg) {
07698       res = wait_file2(chan, vms, "vm-first");  /* "First" */
07699    } else if (vms->curmsg == vms->lastmsg) {
07700       res = wait_file2(chan, vms, "vm-last");      /* "last" */
07701    }
07702 
07703    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
07704    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
07705    msg_cfg = ast_config_load(filename, config_flags);
07706    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
07707       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07708       return 0;
07709    }
07710    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
07711 
07712    /* Play the word urgent if we are listening to urgent messages */
07713    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
07714       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
07715    }
07716 
07717    if (!res) {
07718       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
07719       /* POLISH syntax */
07720       if (!strncasecmp(chan->language, "pl", 2)) {
07721          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07722             int ten, one;
07723             char nextmsg[256];
07724             ten = (vms->curmsg + 1) / 10;
07725             one = (vms->curmsg + 1) % 10;
07726 
07727             if (vms->curmsg < 20) {
07728                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
07729                res = wait_file2(chan, vms, nextmsg);
07730             } else {
07731                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
07732                res = wait_file2(chan, vms, nextmsg);
07733                if (one > 0) {
07734                   if (!res) {
07735                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
07736                      res = wait_file2(chan, vms, nextmsg);
07737                   }
07738                }
07739             }
07740          }
07741          if (!res)
07742             res = wait_file2(chan, vms, "vm-message");
07743       /* HEBREW syntax */
07744       } else if (!strncasecmp(chan->language, "he", 2)) {
07745          if (!vms->curmsg) {
07746             res = wait_file2(chan, vms, "vm-message");
07747             res = wait_file2(chan, vms, "vm-first");
07748          } else if (vms->curmsg == vms->lastmsg) {
07749             res = wait_file2(chan, vms, "vm-message");
07750             res = wait_file2(chan, vms, "vm-last");
07751          } else {
07752             res = wait_file2(chan, vms, "vm-message");
07753             res = wait_file2(chan, vms, "vm-number");
07754             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07755          }
07756       /* VIETNAMESE syntax */
07757       } else if (!strncasecmp(chan->language, "vi", 2)) {
07758          if (!vms->curmsg) {
07759             res = wait_file2(chan, vms, "vm-message");
07760             res = wait_file2(chan, vms, "vm-first");
07761          } else if (vms->curmsg == vms->lastmsg) {
07762             res = wait_file2(chan, vms, "vm-message");
07763             res = wait_file2(chan, vms, "vm-last");
07764          } else {
07765             res = wait_file2(chan, vms, "vm-message");
07766             res = wait_file2(chan, vms, "vm-number");
07767             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
07768          }
07769       } else {
07770          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07771             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07772          } else { /* DEFAULT syntax */
07773             res = wait_file2(chan, vms, "vm-message");
07774          }
07775          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07776             if (!res) {
07777                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
07778                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07779             }
07780          }
07781       }
07782    }
07783 
07784    if (!msg_cfg) {
07785       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07786       return 0;
07787    }
07788 
07789    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07790       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07791       DISPOSE(vms->curdir, vms->curmsg);
07792       ast_config_destroy(msg_cfg);
07793       return 0;
07794    }
07795 
07796    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07797    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07798    category = ast_variable_retrieve(msg_cfg, "message", "category");
07799 
07800    context = ast_variable_retrieve(msg_cfg, "message", "context");
07801    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
07802       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
07803    if (!res) {
07804       res = play_message_category(chan, category);
07805    }
07806    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
07807       res = play_message_datetime(chan, vmu, origtime, filename);
07808    }
07809    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
07810       res = play_message_callerid(chan, vms, cid, context, 0);
07811    }
07812    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
07813       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07814    }
07815    /* Allow pressing '1' to skip envelope / callerid */
07816    if (res == '1') {
07817       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07818       res = 0;
07819    }
07820    ast_config_destroy(msg_cfg);
07821 
07822    if (!res) {
07823       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07824 #ifdef IMAP_STORAGE
07825       ast_mutex_lock(&vms->lock);
07826 #endif
07827       vms->heard[vms->curmsg] = 1;
07828 #ifdef IMAP_STORAGE
07829       ast_mutex_unlock(&vms->lock);
07830       /*IMAP storage stores any prepended message from a forward
07831        * as a separate file from the rest of the message
07832        */
07833       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07834          wait_file(chan, vms, vms->introfn);
07835       }
07836 #endif
07837       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07838          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07839          res = 0;
07840       }
07841       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07842    }
07843    DISPOSE(vms->curdir, vms->curmsg);
07844    return res;
07845 }
07846 
07847 #ifdef IMAP_STORAGE
07848 static int imap_remove_file(char *dir, int msgnum)
07849 {
07850    char fn[PATH_MAX];
07851    char full_fn[PATH_MAX];
07852    char intro[PATH_MAX] = {0,};
07853    
07854    if (msgnum > -1) {
07855       make_file(fn, sizeof(fn), dir, msgnum);
07856       snprintf(intro, sizeof(intro), "%sintro", fn);
07857    } else
07858       ast_copy_string(fn, dir, sizeof(fn));
07859    
07860    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07861       ast_filedelete(fn, NULL);
07862       if (!ast_strlen_zero(intro)) {
07863          ast_filedelete(intro, NULL);
07864       }
07865       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07866       unlink(full_fn);
07867    }
07868    return 0;
07869 }
07870 
07871 
07872 
07873 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07874 {
07875    char *file, *filename;
07876    char *attachment;
07877    char arg[10];
07878    int i;
07879    BODY* body;
07880 
07881    file = strrchr(ast_strdupa(dir), '/');
07882    if (file) {
07883       *file++ = '\0';
07884    } else {
07885       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07886       return -1;
07887    }
07888 
07889    ast_mutex_lock(&vms->lock);
07890    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07891       mail_fetchstructure(vms->mailstream, i + 1, &body);
07892       /* We have the body, now we extract the file name of the first attachment. */
07893       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07894          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07895       } else {
07896          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07897          ast_mutex_unlock(&vms->lock);
07898          return -1;
07899       }
07900       filename = strsep(&attachment, ".");
07901       if (!strcmp(filename, file)) {
07902          sprintf(arg, "%d", i + 1);
07903          mail_setflag(vms->mailstream, arg, "\\DELETED");
07904       }
07905    }
07906    mail_expunge(vms->mailstream);
07907    ast_mutex_unlock(&vms->lock);
07908    return 0;
07909 }
07910 
07911 #elif !defined(IMAP_STORAGE)
07912 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07913 {
07914    int count_msg, last_msg;
07915 
07916    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
07917 
07918    /* Rename the member vmbox HERE so that we don't try to return before
07919     * we know what's going on.
07920     */
07921    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07922 
07923    /* Faster to make the directory than to check if it exists. */
07924    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07925 
07926    /* traverses directory using readdir (or select query for ODBC) */
07927    count_msg = count_messages(vmu, vms->curdir);
07928    if (count_msg < 0) {
07929       return count_msg;
07930    } else {
07931       vms->lastmsg = count_msg - 1;
07932    }
07933 
07934    if (vm_allocate_dh(vms, vmu, count_msg)) {
07935       return -1;
07936    }
07937 
07938    /*
07939    The following test is needed in case sequencing gets messed up.
07940    There appears to be more than one way to mess up sequence, so
07941    we will not try to find all of the root causes--just fix it when
07942    detected.
07943    */
07944 
07945    if (vm_lock_path(vms->curdir)) {
07946       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07947       return ERROR_LOCK_PATH;
07948    }
07949 
07950    /* for local storage, checks directory for messages up to maxmsg limit */
07951    last_msg = last_message_index(vmu, vms->curdir);
07952    ast_unlock_path(vms->curdir);
07953 
07954    if (last_msg < -1) {
07955       return last_msg;
07956    } else if (vms->lastmsg != last_msg) {
07957       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);
07958       resequence_mailbox(vmu, vms->curdir, count_msg);
07959    }
07960 
07961    return 0;
07962 }
07963 #endif
07964 
07965 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07966 {
07967    int x = 0;
07968    int last_msg_idx = 0;
07969 
07970 #ifndef IMAP_STORAGE
07971    int res = 0, nummsg;
07972    char fn2[PATH_MAX];
07973 #endif
07974 
07975    if (vms->lastmsg <= -1) {
07976       goto done;
07977    }
07978 
07979    vms->curmsg = -1;
07980 #ifndef IMAP_STORAGE
07981    /* Get the deleted messages fixed */
07982    if (vm_lock_path(vms->curdir)) {
07983       return ERROR_LOCK_PATH;
07984    }
07985 
07986    /* update count as message may have arrived while we've got mailbox open */
07987    last_msg_idx = last_message_index(vmu, vms->curdir);
07988    if (last_msg_idx != vms->lastmsg) {
07989       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
07990    }
07991 
07992    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
07993    for (x = 0; x < last_msg_idx + 1; x++) {
07994       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
07995          /* Save this message.  It's not in INBOX or hasn't been heard */
07996          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07997          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
07998             break;
07999          }
08000          vms->curmsg++;
08001          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
08002          if (strcmp(vms->fn, fn2)) {
08003             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
08004          }
08005       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
08006          /* Move to old folder before deleting */
08007          res = save_to_folder(vmu, vms, x, 1);
08008          if (res == ERROR_LOCK_PATH) {
08009             /* If save failed do not delete the message */
08010             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
08011             vms->deleted[x] = 0;
08012             vms->heard[x] = 0;
08013             --x;
08014          }
08015       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08016          /* Move to deleted folder */
08017          res = save_to_folder(vmu, vms, x, 10);
08018          if (res == ERROR_LOCK_PATH) {
08019             /* If save failed do not delete the message */
08020             vms->deleted[x] = 0;
08021             vms->heard[x] = 0;
08022             --x;
08023          }
08024       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08025          /* If realtime storage enabled - we should explicitly delete this message,
08026          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08027          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08028          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08029             DELETE(vms->curdir, x, vms->fn, vmu);
08030          }
08031       }
08032    }
08033 
08034    /* Delete ALL remaining messages */
08035    nummsg = x - 1;
08036    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08037       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08038       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08039          DELETE(vms->curdir, x, vms->fn, vmu);
08040       }
08041    }
08042    ast_unlock_path(vms->curdir);
08043 #else /* defined(IMAP_STORAGE) */
08044    ast_mutex_lock(&vms->lock);
08045    if (vms->deleted) {
08046       /* Since we now expunge after each delete, deleting in reverse order
08047        * ensures that no reordering occurs between each step. */
08048       last_msg_idx = vms->dh_arraysize;
08049       for (x = last_msg_idx - 1; x >= 0; x--) {
08050          if (vms->deleted[x]) {
08051             ast_debug(3, "IMAP delete of %d\n", x);
08052             DELETE(vms->curdir, x, vms->fn, vmu);
08053          }
08054       }
08055    }
08056 #endif
08057 
08058 done:
08059    if (vms->deleted) {
08060       ast_free(vms->deleted);
08061       vms->deleted = NULL;
08062    }
08063    if (vms->heard) {
08064       ast_free(vms->heard);
08065       vms->heard = NULL;
08066    }
08067    vms->dh_arraysize = 0;
08068 #ifdef IMAP_STORAGE
08069    ast_mutex_unlock(&vms->lock);
08070 #endif
08071 
08072    return 0;
08073 }
08074 
08075 /* In Greek even though we CAN use a syntax like "friends messages"
08076  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08077  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08078  * syntax for the above three categories which is more elegant.
08079  */
08080 
08081 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08082 {
08083    int cmd;
08084    char *buf;
08085 
08086    buf = alloca(strlen(box) + 2);
08087    strcpy(buf, box);
08088    strcat(buf, "s");
08089 
08090    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08091       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08092       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08093    } else {
08094       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08095       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08096    }
08097 }
08098 
08099 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08100 {
08101    int cmd;
08102 
08103    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08104       if (!strcasecmp(box, "vm-INBOX"))
08105          cmd = ast_play_and_wait(chan, "vm-new-e");
08106       else
08107          cmd = ast_play_and_wait(chan, "vm-old-e");
08108       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08109    } else {
08110       cmd = ast_play_and_wait(chan, "vm-messages");
08111       return cmd ? cmd : ast_play_and_wait(chan, box);
08112    }
08113 }
08114 
08115 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08116 {
08117    int cmd;
08118 
08119    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08120       cmd = ast_play_and_wait(chan, "vm-messages");
08121       return cmd ? cmd : ast_play_and_wait(chan, box);
08122    } else {
08123       cmd = ast_play_and_wait(chan, box);
08124       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08125    }
08126 }
08127 
08128 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08129 {
08130    int cmd;
08131 
08132    if (  !strncasecmp(chan->language, "it", 2) ||
08133         !strncasecmp(chan->language, "es", 2) ||
08134         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08135       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08136       return cmd ? cmd : ast_play_and_wait(chan, box);
08137    } else if (!strncasecmp(chan->language, "gr", 2)) {
08138       return vm_play_folder_name_gr(chan, box);
08139    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
08140       return ast_play_and_wait(chan, box);
08141    } else if (!strncasecmp(chan->language, "pl", 2)) {
08142       return vm_play_folder_name_pl(chan, box);
08143    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
08144       return vm_play_folder_name_ua(chan, box);
08145    } else if (!strncasecmp(chan->language, "vi", 2)) {
08146       return ast_play_and_wait(chan, box);
08147    } else {  /* Default English */
08148       cmd = ast_play_and_wait(chan, box);
08149       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08150    }
08151 }
08152 
08153 /* GREEK SYNTAX
08154    In greek the plural for old/new is
08155    different so we need the following files
08156    We also need vm-denExeteMynhmata because
08157    this syntax is different.
08158 
08159    -> vm-Olds.wav : "Palia"
08160    -> vm-INBOXs.wav : "Nea"
08161    -> vm-denExeteMynhmata : "den exete mynhmata"
08162 */
08163 
08164 
08165 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08166 {
08167    int res = 0;
08168 
08169    if (vms->newmessages) {
08170       res = ast_play_and_wait(chan, "vm-youhave");
08171       if (!res) 
08172          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
08173       if (!res) {
08174          if ((vms->newmessages == 1)) {
08175             res = ast_play_and_wait(chan, "vm-INBOX");
08176             if (!res)
08177                res = ast_play_and_wait(chan, "vm-message");
08178          } else {
08179             res = ast_play_and_wait(chan, "vm-INBOXs");
08180             if (!res)
08181                res = ast_play_and_wait(chan, "vm-messages");
08182          }
08183       }
08184    } else if (vms->oldmessages){
08185       res = ast_play_and_wait(chan, "vm-youhave");
08186       if (!res)
08187          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
08188       if ((vms->oldmessages == 1)){
08189          res = ast_play_and_wait(chan, "vm-Old");
08190          if (!res)
08191             res = ast_play_and_wait(chan, "vm-message");
08192       } else {
08193          res = ast_play_and_wait(chan, "vm-Olds");
08194          if (!res)
08195             res = ast_play_and_wait(chan, "vm-messages");
08196       }
08197    } else if (!vms->oldmessages && !vms->newmessages) 
08198       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08199    return res;
08200 }
08201 
08202 /* Version of vm_intro() designed to work for many languages.
08203  *
08204  * It is hoped that this function can prevent the proliferation of 
08205  * language-specific vm_intro() functions and in time replace the language-
08206  * specific functions which already exist.  An examination of the language-
08207  * specific functions revealed that they all corrected the same deficiencies
08208  * in vm_intro_en() (which was the default function). Namely:
08209  *
08210  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08211  *     wording of the voicemail greeting hides this problem.  For example,
08212  *     vm-INBOX contains only the word "new".  This means that both of these
08213  *     sequences produce valid utterances:
08214  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08215  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08216  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08217  *     in many languages) the first utterance becomes "you have 1 the new message".
08218  *  2) The function contains hardcoded rules for pluralizing the word "message".
08219  *     These rules are correct for English, but not for many other languages.
08220  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08221  *     required in many languages.
08222  *  4) The gender of the word for "message" is not specified. This is a problem
08223  *     because in many languages the gender of the number in phrases such
08224  *     as "you have one new message" must match the gender of the word
08225  *     meaning "message".
08226  *
08227  * Fixing these problems for each new language has meant duplication of effort.
08228  * This new function solves the problems in the following general ways:
08229  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08230  *     and vm-Old respectively for those languages where it makes sense.
08231  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08232  *     on vm-message.
08233  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08234  *     prefix on vm-new and vm-old (none for English).
08235  *  4) Pass the gender of the language's word for "message" as an agument to
08236  *     this function which is can in turn pass on to the functions which 
08237  *     say numbers and put endings on nounds and adjectives.
08238  *
08239  * All languages require these messages:
08240  *  vm-youhave    "You have..."
08241  *  vm-and     "and"
08242  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08243  *
08244  * To use it for English, you will need these additional sound files:
08245  *  vm-new     "new"
08246  *  vm-message    "message", singular
08247  *  vm-messages      "messages", plural
08248  *
08249  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08250  *
08251  *  vm-newn    "novoye" (singular, neuter)
08252  *  vm-newx    "novikh" (counting plural form, genative plural)
08253  *  vm-message    "sobsheniye" (singular form)
08254  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08255  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08256  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08257  *  digits/2n     "dva" (neuter singular)
08258  */
08259 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
08260 {
08261    int res;
08262    int lastnum = 0;
08263 
08264    res = ast_play_and_wait(chan, "vm-youhave");
08265 
08266    if (!res && vms->newmessages) {
08267       lastnum = vms->newmessages;
08268 
08269       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08270          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
08271       }
08272 
08273       if (!res && vms->oldmessages) {
08274          res = ast_play_and_wait(chan, "vm-and");
08275       }
08276    }
08277 
08278    if (!res && vms->oldmessages) {
08279       lastnum = vms->oldmessages;
08280 
08281       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
08282          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
08283       }
08284    }
08285 
08286    if (!res) {
08287       if (lastnum == 0) {
08288          res = ast_play_and_wait(chan, "vm-no");
08289       }
08290       if (!res) {
08291          res = ast_say_counted_noun(chan, lastnum, "vm-message");
08292       }
08293    }
08294 
08295    return res;
08296 }
08297 
08298 /* Default Hebrew syntax */
08299 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
08300 {
08301    int res = 0;
08302 
08303    /* Introduce messages they have */
08304    if (!res) {
08305       if ((vms->newmessages) || (vms->oldmessages)) {
08306          res = ast_play_and_wait(chan, "vm-youhave");
08307       }
08308       /*
08309        * The word "shtei" refers to the number 2 in hebrew when performing a count
08310        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
08311        * an element, this is one of them.
08312        */
08313       if (vms->newmessages) {
08314          if (!res) {
08315             if (vms->newmessages == 1) {
08316                res = ast_play_and_wait(chan, "vm-INBOX1");
08317             } else {
08318                if (vms->newmessages == 2) {
08319                   res = ast_play_and_wait(chan, "vm-shtei");
08320                } else {
08321                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08322                }
08323                res = ast_play_and_wait(chan, "vm-INBOX");
08324             }
08325          }
08326          if (vms->oldmessages && !res) {
08327             res = ast_play_and_wait(chan, "vm-and");
08328             if (vms->oldmessages == 1) {
08329                res = ast_play_and_wait(chan, "vm-Old1");
08330             } else {
08331                if (vms->oldmessages == 2) {
08332                   res = ast_play_and_wait(chan, "vm-shtei");
08333                } else {
08334                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08335                }
08336                res = ast_play_and_wait(chan, "vm-Old");
08337             }
08338          }
08339       }
08340       if (!res && vms->oldmessages && !vms->newmessages) {
08341          if (!res) {
08342             if (vms->oldmessages == 1) {
08343                res = ast_play_and_wait(chan, "vm-Old1");
08344             } else {
08345                if (vms->oldmessages == 2) {
08346                   res = ast_play_and_wait(chan, "vm-shtei");
08347                } else {
08348                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
08349                }
08350                res = ast_play_and_wait(chan, "vm-Old");
08351             }
08352          }
08353       }
08354       if (!res) {
08355          if (!vms->oldmessages && !vms->newmessages) {
08356             if (!res) {
08357                res = ast_play_and_wait(chan, "vm-nomessages");
08358             }
08359          }
08360       }
08361    }
08362    return res;
08363 }
08364    
08365 /* Default English syntax */
08366 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
08367 {
08368    int res;
08369 
08370    /* Introduce messages they have */
08371    res = ast_play_and_wait(chan, "vm-youhave");
08372    if (!res) {
08373       if (vms->urgentmessages) {
08374          res = say_and_wait(chan, vms->urgentmessages, chan->language);
08375          if (!res)
08376             res = ast_play_and_wait(chan, "vm-Urgent");
08377          if ((vms->oldmessages || vms->newmessages) && !res) {
08378             res = ast_play_and_wait(chan, "vm-and");
08379          } else if (!res) {
08380             if ((vms->urgentmessages == 1))
08381                res = ast_play_and_wait(chan, "vm-message");
08382             else
08383                res = ast_play_and_wait(chan, "vm-messages");
08384          }
08385       }
08386       if (vms->newmessages) {
08387          res = say_and_wait(chan, vms->newmessages, chan->language);
08388          if (!res)
08389             res = ast_play_and_wait(chan, "vm-INBOX");
08390          if (vms->oldmessages && !res)
08391             res = ast_play_and_wait(chan, "vm-and");
08392          else if (!res) {
08393             if ((vms->newmessages == 1))
08394                res = ast_play_and_wait(chan, "vm-message");
08395             else
08396                res = ast_play_and_wait(chan, "vm-messages");
08397          }
08398             
08399       }
08400       if (!res && vms->oldmessages) {
08401          res = say_and_wait(chan, vms->oldmessages, chan->language);
08402          if (!res)
08403             res = ast_play_and_wait(chan, "vm-Old");
08404          if (!res) {
08405             if (vms->oldmessages == 1)
08406                res = ast_play_and_wait(chan, "vm-message");
08407             else
08408                res = ast_play_and_wait(chan, "vm-messages");
08409          }
08410       }
08411       if (!res) {
08412          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
08413             res = ast_play_and_wait(chan, "vm-no");
08414             if (!res)
08415                res = ast_play_and_wait(chan, "vm-messages");
08416          }
08417       }
08418    }
08419    return res;
08420 }
08421 
08422 /* ITALIAN syntax */
08423 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
08424 {
08425    /* Introduce messages they have */
08426    int res;
08427    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
08428       res = ast_play_and_wait(chan, "vm-no") ||
08429          ast_play_and_wait(chan, "vm-message");
08430    else
08431       res = ast_play_and_wait(chan, "vm-youhave");
08432    if (!res && vms->newmessages) {
08433       res = (vms->newmessages == 1) ?
08434          ast_play_and_wait(chan, "digits/un") ||
08435          ast_play_and_wait(chan, "vm-nuovo") ||
08436          ast_play_and_wait(chan, "vm-message") :
08437          /* 2 or more new messages */
08438          say_and_wait(chan, vms->newmessages, chan->language) ||
08439          ast_play_and_wait(chan, "vm-nuovi") ||
08440          ast_play_and_wait(chan, "vm-messages");
08441       if (!res && vms->oldmessages)
08442          res = ast_play_and_wait(chan, "vm-and");
08443    }
08444    if (!res && vms->oldmessages) {
08445       res = (vms->oldmessages == 1) ?
08446          ast_play_and_wait(chan, "digits/un") ||
08447          ast_play_and_wait(chan, "vm-vecchio") ||
08448          ast_play_and_wait(chan, "vm-message") :
08449          /* 2 or more old messages */
08450          say_and_wait(chan, vms->oldmessages, chan->language) ||
08451          ast_play_and_wait(chan, "vm-vecchi") ||
08452          ast_play_and_wait(chan, "vm-messages");
08453    }
08454    return res;
08455 }
08456 
08457 /* POLISH syntax */
08458 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
08459 {
08460    /* Introduce messages they have */
08461    int res;
08462    div_t num;
08463 
08464    if (!vms->oldmessages && !vms->newmessages) {
08465       res = ast_play_and_wait(chan, "vm-no");
08466       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08467       return res;
08468    } else {
08469       res = ast_play_and_wait(chan, "vm-youhave");
08470    }
08471 
08472    if (vms->newmessages) {
08473       num = div(vms->newmessages, 10);
08474       if (vms->newmessages == 1) {
08475          res = ast_play_and_wait(chan, "digits/1-a");
08476          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
08477          res = res ? res : ast_play_and_wait(chan, "vm-message");
08478       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08479          if (num.rem == 2) {
08480             if (!num.quot) {
08481                res = ast_play_and_wait(chan, "digits/2-ie");
08482             } else {
08483                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
08484                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08485             }
08486          } else {
08487             res = say_and_wait(chan, vms->newmessages, chan->language);
08488          }
08489          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
08490          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08491       } else {
08492          res = say_and_wait(chan, vms->newmessages, chan->language);
08493          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
08494          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08495       }
08496       if (!res && vms->oldmessages)
08497          res = ast_play_and_wait(chan, "vm-and");
08498    }
08499    if (!res && vms->oldmessages) {
08500       num = div(vms->oldmessages, 10);
08501       if (vms->oldmessages == 1) {
08502          res = ast_play_and_wait(chan, "digits/1-a");
08503          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
08504          res = res ? res : ast_play_and_wait(chan, "vm-message");
08505       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08506          if (num.rem == 2) {
08507             if (!num.quot) {
08508                res = ast_play_and_wait(chan, "digits/2-ie");
08509             } else {
08510                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
08511                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08512             }
08513          } else {
08514             res = say_and_wait(chan, vms->oldmessages, chan->language);
08515          }
08516          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
08517          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08518       } else {
08519          res = say_and_wait(chan, vms->oldmessages, chan->language);
08520          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
08521          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08522       }
08523    }
08524 
08525    return res;
08526 }
08527 
08528 /* SWEDISH syntax */
08529 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
08530 {
08531    /* Introduce messages they have */
08532    int res;
08533 
08534    res = ast_play_and_wait(chan, "vm-youhave");
08535    if (res)
08536       return res;
08537 
08538    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08539       res = ast_play_and_wait(chan, "vm-no");
08540       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08541       return res;
08542    }
08543 
08544    if (vms->newmessages) {
08545       if ((vms->newmessages == 1)) {
08546          res = ast_play_and_wait(chan, "digits/ett");
08547          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
08548          res = res ? res : ast_play_and_wait(chan, "vm-message");
08549       } else {
08550          res = say_and_wait(chan, vms->newmessages, chan->language);
08551          res = res ? res : ast_play_and_wait(chan, "vm-nya");
08552          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08553       }
08554       if (!res && vms->oldmessages)
08555          res = ast_play_and_wait(chan, "vm-and");
08556    }
08557    if (!res && vms->oldmessages) {
08558       if (vms->oldmessages == 1) {
08559          res = ast_play_and_wait(chan, "digits/ett");
08560          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
08561          res = res ? res : ast_play_and_wait(chan, "vm-message");
08562       } else {
08563          res = say_and_wait(chan, vms->oldmessages, chan->language);
08564          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
08565          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08566       }
08567    }
08568 
08569    return res;
08570 }
08571 
08572 /* NORWEGIAN syntax */
08573 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
08574 {
08575    /* Introduce messages they have */
08576    int res;
08577 
08578    res = ast_play_and_wait(chan, "vm-youhave");
08579    if (res)
08580       return res;
08581 
08582    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08583       res = ast_play_and_wait(chan, "vm-no");
08584       res = res ? res : ast_play_and_wait(chan, "vm-messages");
08585       return res;
08586    }
08587 
08588    if (vms->newmessages) {
08589       if ((vms->newmessages == 1)) {
08590          res = ast_play_and_wait(chan, "digits/1");
08591          res = res ? res : ast_play_and_wait(chan, "vm-ny");
08592          res = res ? res : ast_play_and_wait(chan, "vm-message");
08593       } else {
08594          res = say_and_wait(chan, vms->newmessages, chan->language);
08595          res = res ? res : ast_play_and_wait(chan, "vm-nye");
08596          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08597       }
08598       if (!res && vms->oldmessages)
08599          res = ast_play_and_wait(chan, "vm-and");
08600    }
08601    if (!res && vms->oldmessages) {
08602       if (vms->oldmessages == 1) {
08603          res = ast_play_and_wait(chan, "digits/1");
08604          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
08605          res = res ? res : ast_play_and_wait(chan, "vm-message");
08606       } else {
08607          res = say_and_wait(chan, vms->oldmessages, chan->language);
08608          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
08609          res = res ? res : ast_play_and_wait(chan, "vm-messages");
08610       }
08611    }
08612 
08613    return res;
08614 }
08615 
08616 /* GERMAN syntax */
08617 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
08618 {
08619    /* Introduce messages they have */
08620    int res;
08621    res = ast_play_and_wait(chan, "vm-youhave");
08622    if (!res) {
08623       if (vms->newmessages) {
08624          if ((vms->newmessages == 1))
08625             res = ast_play_and_wait(chan, "digits/1F");
08626          else
08627             res = say_and_wait(chan, vms->newmessages, chan->language);
08628          if (!res)
08629             res = ast_play_and_wait(chan, "vm-INBOX");
08630          if (vms->oldmessages && !res)
08631             res = ast_play_and_wait(chan, "vm-and");
08632          else if (!res) {
08633             if ((vms->newmessages == 1))
08634                res = ast_play_and_wait(chan, "vm-message");
08635             else
08636                res = ast_play_and_wait(chan, "vm-messages");
08637          }
08638             
08639       }
08640       if (!res && vms->oldmessages) {
08641          if (vms->oldmessages == 1)
08642             res = ast_play_and_wait(chan, "digits/1F");
08643          else
08644             res = say_and_wait(chan, vms->oldmessages, chan->language);
08645          if (!res)
08646             res = ast_play_and_wait(chan, "vm-Old");
08647          if (!res) {
08648             if (vms->oldmessages == 1)
08649                res = ast_play_and_wait(chan, "vm-message");
08650             else
08651                res = ast_play_and_wait(chan, "vm-messages");
08652          }
08653       }
08654       if (!res) {
08655          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08656             res = ast_play_and_wait(chan, "vm-no");
08657             if (!res)
08658                res = ast_play_and_wait(chan, "vm-messages");
08659          }
08660       }
08661    }
08662    return res;
08663 }
08664 
08665 /* SPANISH syntax */
08666 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
08667 {
08668    /* Introduce messages they have */
08669    int res;
08670    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08671       res = ast_play_and_wait(chan, "vm-youhaveno");
08672       if (!res)
08673          res = ast_play_and_wait(chan, "vm-messages");
08674    } else {
08675       res = ast_play_and_wait(chan, "vm-youhave");
08676    }
08677    if (!res) {
08678       if (vms->newmessages) {
08679          if (!res) {
08680             if ((vms->newmessages == 1)) {
08681                res = ast_play_and_wait(chan, "digits/1M");
08682                if (!res)
08683                   res = ast_play_and_wait(chan, "vm-message");
08684                if (!res)
08685                   res = ast_play_and_wait(chan, "vm-INBOXs");
08686             } else {
08687                res = say_and_wait(chan, vms->newmessages, chan->language);
08688                if (!res)
08689                   res = ast_play_and_wait(chan, "vm-messages");
08690                if (!res)
08691                   res = ast_play_and_wait(chan, "vm-INBOX");
08692             }
08693          }
08694          if (vms->oldmessages && !res)
08695             res = ast_play_and_wait(chan, "vm-and");
08696       }
08697       if (vms->oldmessages) {
08698          if (!res) {
08699             if (vms->oldmessages == 1) {
08700                res = ast_play_and_wait(chan, "digits/1M");
08701                if (!res)
08702                   res = ast_play_and_wait(chan, "vm-message");
08703                if (!res)
08704                   res = ast_play_and_wait(chan, "vm-Olds");
08705             } else {
08706                res = say_and_wait(chan, vms->oldmessages, chan->language);
08707                if (!res)
08708                   res = ast_play_and_wait(chan, "vm-messages");
08709                if (!res)
08710                   res = ast_play_and_wait(chan, "vm-Old");
08711             }
08712          }
08713       }
08714    }
08715 return res;
08716 }
08717 
08718 /* BRAZILIAN PORTUGUESE syntax */
08719 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
08720    /* Introduce messages they have */
08721    int res;
08722    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08723       res = ast_play_and_wait(chan, "vm-nomessages");
08724       return res;
08725    } else {
08726       res = ast_play_and_wait(chan, "vm-youhave");
08727    }
08728    if (vms->newmessages) {
08729       if (!res)
08730          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08731       if ((vms->newmessages == 1)) {
08732          if (!res)
08733             res = ast_play_and_wait(chan, "vm-message");
08734          if (!res)
08735             res = ast_play_and_wait(chan, "vm-INBOXs");
08736       } else {
08737          if (!res)
08738             res = ast_play_and_wait(chan, "vm-messages");
08739          if (!res)
08740             res = ast_play_and_wait(chan, "vm-INBOX");
08741       }
08742       if (vms->oldmessages && !res)
08743          res = ast_play_and_wait(chan, "vm-and");
08744    }
08745    if (vms->oldmessages) {
08746       if (!res)
08747          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08748       if (vms->oldmessages == 1) {
08749          if (!res)
08750             res = ast_play_and_wait(chan, "vm-message");
08751          if (!res)
08752             res = ast_play_and_wait(chan, "vm-Olds");
08753       } else {
08754          if (!res)
08755             res = ast_play_and_wait(chan, "vm-messages");
08756          if (!res)
08757             res = ast_play_and_wait(chan, "vm-Old");
08758       }
08759    }
08760    return res;
08761 }
08762 
08763 /* FRENCH syntax */
08764 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
08765 {
08766    /* Introduce messages they have */
08767    int res;
08768    res = ast_play_and_wait(chan, "vm-youhave");
08769    if (!res) {
08770       if (vms->newmessages) {
08771          res = say_and_wait(chan, vms->newmessages, chan->language);
08772          if (!res)
08773             res = ast_play_and_wait(chan, "vm-INBOX");
08774          if (vms->oldmessages && !res)
08775             res = ast_play_and_wait(chan, "vm-and");
08776          else if (!res) {
08777             if ((vms->newmessages == 1))
08778                res = ast_play_and_wait(chan, "vm-message");
08779             else
08780                res = ast_play_and_wait(chan, "vm-messages");
08781          }
08782             
08783       }
08784       if (!res && vms->oldmessages) {
08785          res = say_and_wait(chan, vms->oldmessages, chan->language);
08786          if (!res)
08787             res = ast_play_and_wait(chan, "vm-Old");
08788          if (!res) {
08789             if (vms->oldmessages == 1)
08790                res = ast_play_and_wait(chan, "vm-message");
08791             else
08792                res = ast_play_and_wait(chan, "vm-messages");
08793          }
08794       }
08795       if (!res) {
08796          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08797             res = ast_play_and_wait(chan, "vm-no");
08798             if (!res)
08799                res = ast_play_and_wait(chan, "vm-messages");
08800          }
08801       }
08802    }
08803    return res;
08804 }
08805 
08806 /* DUTCH syntax */
08807 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
08808 {
08809    /* Introduce messages they have */
08810    int res;
08811    res = ast_play_and_wait(chan, "vm-youhave");
08812    if (!res) {
08813       if (vms->newmessages) {
08814          res = say_and_wait(chan, vms->newmessages, chan->language);
08815          if (!res) {
08816             if (vms->newmessages == 1)
08817                res = ast_play_and_wait(chan, "vm-INBOXs");
08818             else
08819                res = ast_play_and_wait(chan, "vm-INBOX");
08820          }
08821          if (vms->oldmessages && !res)
08822             res = ast_play_and_wait(chan, "vm-and");
08823          else if (!res) {
08824             if ((vms->newmessages == 1))
08825                res = ast_play_and_wait(chan, "vm-message");
08826             else
08827                res = ast_play_and_wait(chan, "vm-messages");
08828          }
08829             
08830       }
08831       if (!res && vms->oldmessages) {
08832          res = say_and_wait(chan, vms->oldmessages, chan->language);
08833          if (!res) {
08834             if (vms->oldmessages == 1)
08835                res = ast_play_and_wait(chan, "vm-Olds");
08836             else
08837                res = ast_play_and_wait(chan, "vm-Old");
08838          }
08839          if (!res) {
08840             if (vms->oldmessages == 1)
08841                res = ast_play_and_wait(chan, "vm-message");
08842             else
08843                res = ast_play_and_wait(chan, "vm-messages");
08844          }
08845       }
08846       if (!res) {
08847          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08848             res = ast_play_and_wait(chan, "vm-no");
08849             if (!res)
08850                res = ast_play_and_wait(chan, "vm-messages");
08851          }
08852       }
08853    }
08854    return res;
08855 }
08856 
08857 /* PORTUGUESE syntax */
08858 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
08859 {
08860    /* Introduce messages they have */
08861    int res;
08862    res = ast_play_and_wait(chan, "vm-youhave");
08863    if (!res) {
08864       if (vms->newmessages) {
08865          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08866          if (!res) {
08867             if ((vms->newmessages == 1)) {
08868                res = ast_play_and_wait(chan, "vm-message");
08869                if (!res)
08870                   res = ast_play_and_wait(chan, "vm-INBOXs");
08871             } else {
08872                res = ast_play_and_wait(chan, "vm-messages");
08873                if (!res)
08874                   res = ast_play_and_wait(chan, "vm-INBOX");
08875             }
08876          }
08877          if (vms->oldmessages && !res)
08878             res = ast_play_and_wait(chan, "vm-and");
08879       }
08880       if (!res && vms->oldmessages) {
08881          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08882          if (!res) {
08883             if (vms->oldmessages == 1) {
08884                res = ast_play_and_wait(chan, "vm-message");
08885                if (!res)
08886                   res = ast_play_and_wait(chan, "vm-Olds");
08887             } else {
08888                res = ast_play_and_wait(chan, "vm-messages");
08889                if (!res)
08890                   res = ast_play_and_wait(chan, "vm-Old");
08891             }
08892          }
08893       }
08894       if (!res) {
08895          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08896             res = ast_play_and_wait(chan, "vm-no");
08897             if (!res)
08898                res = ast_play_and_wait(chan, "vm-messages");
08899          }
08900       }
08901    }
08902    return res;
08903 }
08904 
08905 
08906 /* CZECH syntax */
08907 /* in czech there must be declension of word new and message
08908  * czech        : english        : czech      : english
08909  * --------------------------------------------------------
08910  * vm-youhave   : you have 
08911  * vm-novou     : one new        : vm-zpravu  : message
08912  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08913  * vm-novych    : 5-infinite new : vm-zprav   : messages
08914  * vm-starou   : one old
08915  * vm-stare     : 2-4 old 
08916  * vm-starych   : 5-infinite old
08917  * jednu        : one   - falling 4. 
08918  * vm-no        : no  ( no messages )
08919  */
08920 
08921 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08922 {
08923    int res;
08924    res = ast_play_and_wait(chan, "vm-youhave");
08925    if (!res) {
08926       if (vms->newmessages) {
08927          if (vms->newmessages == 1) {
08928             res = ast_play_and_wait(chan, "digits/jednu");
08929          } else {
08930             res = say_and_wait(chan, vms->newmessages, chan->language);
08931          }
08932          if (!res) {
08933             if ((vms->newmessages == 1))
08934                res = ast_play_and_wait(chan, "vm-novou");
08935             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08936                res = ast_play_and_wait(chan, "vm-nove");
08937             if (vms->newmessages > 4)
08938                res = ast_play_and_wait(chan, "vm-novych");
08939          }
08940          if (vms->oldmessages && !res)
08941             res = ast_play_and_wait(chan, "vm-and");
08942          else if (!res) {
08943             if ((vms->newmessages == 1))
08944                res = ast_play_and_wait(chan, "vm-zpravu");
08945             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08946                res = ast_play_and_wait(chan, "vm-zpravy");
08947             if (vms->newmessages > 4)
08948                res = ast_play_and_wait(chan, "vm-zprav");
08949          }
08950       }
08951       if (!res && vms->oldmessages) {
08952          res = say_and_wait(chan, vms->oldmessages, chan->language);
08953          if (!res) {
08954             if ((vms->oldmessages == 1))
08955                res = ast_play_and_wait(chan, "vm-starou");
08956             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08957                res = ast_play_and_wait(chan, "vm-stare");
08958             if (vms->oldmessages > 4)
08959                res = ast_play_and_wait(chan, "vm-starych");
08960          }
08961          if (!res) {
08962             if ((vms->oldmessages == 1))
08963                res = ast_play_and_wait(chan, "vm-zpravu");
08964             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08965                res = ast_play_and_wait(chan, "vm-zpravy");
08966             if (vms->oldmessages > 4)
08967                res = ast_play_and_wait(chan, "vm-zprav");
08968          }
08969       }
08970       if (!res) {
08971          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08972             res = ast_play_and_wait(chan, "vm-no");
08973             if (!res)
08974                res = ast_play_and_wait(chan, "vm-zpravy");
08975          }
08976       }
08977    }
08978    return res;
08979 }
08980 
08981 /* CHINESE (Taiwan) syntax */
08982 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08983 {
08984    int res;
08985    /* Introduce messages they have */
08986    res = ast_play_and_wait(chan, "vm-you");
08987 
08988    if (!res && vms->newmessages) {
08989       res = ast_play_and_wait(chan, "vm-have");
08990       if (!res)
08991          res = say_and_wait(chan, vms->newmessages, chan->language);
08992       if (!res)
08993          res = ast_play_and_wait(chan, "vm-tong");
08994       if (!res)
08995          res = ast_play_and_wait(chan, "vm-INBOX");
08996       if (vms->oldmessages && !res)
08997          res = ast_play_and_wait(chan, "vm-and");
08998       else if (!res) 
08999          res = ast_play_and_wait(chan, "vm-messages");
09000    }
09001    if (!res && vms->oldmessages) {
09002       res = ast_play_and_wait(chan, "vm-have");
09003       if (!res)
09004          res = say_and_wait(chan, vms->oldmessages, chan->language);
09005       if (!res)
09006          res = ast_play_and_wait(chan, "vm-tong");
09007       if (!res)
09008          res = ast_play_and_wait(chan, "vm-Old");
09009       if (!res)
09010          res = ast_play_and_wait(chan, "vm-messages");
09011    }
09012    if (!res && !vms->oldmessages && !vms->newmessages) {
09013       res = ast_play_and_wait(chan, "vm-haveno");
09014       if (!res)
09015          res = ast_play_and_wait(chan, "vm-messages");
09016    }
09017    return res;
09018 }
09019 
09020 /* Vietnamese syntax */
09021 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09022 {
09023    int res;
09024 
09025    /* Introduce messages they have */
09026    res = ast_play_and_wait(chan, "vm-youhave");
09027    if (!res) {
09028       if (vms->newmessages) {
09029          res = say_and_wait(chan, vms->newmessages, chan->language);
09030          if (!res)
09031             res = ast_play_and_wait(chan, "vm-INBOX");
09032          if (vms->oldmessages && !res)
09033             res = ast_play_and_wait(chan, "vm-and");
09034       }
09035       if (!res && vms->oldmessages) {
09036          res = say_and_wait(chan, vms->oldmessages, chan->language);
09037          if (!res)
09038             res = ast_play_and_wait(chan, "vm-Old");        
09039       }
09040       if (!res) {
09041          if (!vms->oldmessages && !vms->newmessages) {
09042             res = ast_play_and_wait(chan, "vm-no");
09043             if (!res)
09044                res = ast_play_and_wait(chan, "vm-message");
09045          }
09046       }
09047    }
09048    return res;
09049 }
09050 
09051 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09052 {
09053    char prefile[256];
09054    
09055    /* Notify the user that the temp greeting is set and give them the option to remove it */
09056    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09057    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09058       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09059       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09060          ast_play_and_wait(chan, "vm-tempgreetactive");
09061       }
09062       DISPOSE(prefile, -1);
09063    }
09064 
09065    /* Play voicemail intro - syntax is different for different languages */
09066    if (0) {
09067       return 0;
09068    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
09069       return vm_intro_cs(chan, vms);
09070    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
09071       static int deprecation_warning = 0;
09072       if (deprecation_warning++ % 10 == 0) {
09073          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09074       }
09075       return vm_intro_cs(chan, vms);
09076    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
09077       return vm_intro_de(chan, vms);
09078    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
09079       return vm_intro_es(chan, vms);
09080    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
09081       return vm_intro_fr(chan, vms);
09082    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
09083       return vm_intro_gr(chan, vms);
09084    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
09085       return vm_intro_he(chan, vms);
09086    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
09087       return vm_intro_it(chan, vms);
09088    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
09089       return vm_intro_nl(chan, vms);
09090    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
09091       return vm_intro_no(chan, vms);
09092    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
09093       return vm_intro_pl(chan, vms);
09094    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09095       return vm_intro_pt_BR(chan, vms);
09096    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
09097       return vm_intro_pt(chan, vms);
09098    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
09099       return vm_intro_multilang(chan, vms, "n");
09100    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
09101       return vm_intro_se(chan, vms);
09102    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
09103       return vm_intro_multilang(chan, vms, "n");
09104    } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
09105       return vm_intro_vi(chan, vms);
09106    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09107       return vm_intro_zh(chan, vms);
09108    } else {                                             /* Default to ENGLISH */
09109       return vm_intro_en(chan, vms);
09110    }
09111 }
09112 
09113 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09114 {
09115    int res = 0;
09116    /* Play instructions and wait for new command */
09117    while (!res) {
09118       if (vms->starting) {
09119          if (vms->lastmsg > -1) {
09120             if (skipadvanced)
09121                res = ast_play_and_wait(chan, "vm-onefor-full");
09122             else
09123                res = ast_play_and_wait(chan, "vm-onefor");
09124             if (!res)
09125                res = vm_play_folder_name(chan, vms->vmbox);
09126          }
09127          if (!res) {
09128             if (skipadvanced)
09129                res = ast_play_and_wait(chan, "vm-opts-full");
09130             else
09131                res = ast_play_and_wait(chan, "vm-opts");
09132          }
09133       } else {
09134          /* Added for additional help */
09135          if (skipadvanced) {
09136             res = ast_play_and_wait(chan, "vm-onefor-full");
09137             if (!res)
09138                res = vm_play_folder_name(chan, vms->vmbox);
09139             res = ast_play_and_wait(chan, "vm-opts-full");
09140          }
09141          /* Logic:
09142           * If the current message is not the first OR
09143           * if we're listening to the first new message and there are
09144           * also urgent messages, then prompt for navigation to the
09145           * previous message
09146           */
09147          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09148             res = ast_play_and_wait(chan, "vm-prev");
09149          }
09150          if (!res && !skipadvanced)
09151             res = ast_play_and_wait(chan, "vm-advopts");
09152          if (!res)
09153             res = ast_play_and_wait(chan, "vm-repeat");
09154          /* Logic:
09155           * If we're not listening to the last message OR
09156           * we're listening to the last urgent message and there are
09157           * also new non-urgent messages, then prompt for navigation
09158           * to the next message
09159           */
09160          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09161             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09162             res = ast_play_and_wait(chan, "vm-next");
09163          }
09164          if (!res) {
09165             int curmsg_deleted;
09166 #ifdef IMAP_STORAGE
09167             ast_mutex_lock(&vms->lock);
09168 #endif
09169             curmsg_deleted = vms->deleted[vms->curmsg];
09170 #ifdef IMAP_STORAGE
09171             ast_mutex_unlock(&vms->lock);
09172 #endif
09173             if (!curmsg_deleted) {
09174                res = ast_play_and_wait(chan, "vm-delete");
09175             } else {
09176                res = ast_play_and_wait(chan, "vm-undelete");
09177             }
09178             if (!res) {
09179                res = ast_play_and_wait(chan, "vm-toforward");
09180             }
09181             if (!res) {
09182                res = ast_play_and_wait(chan, "vm-savemessage");
09183             }
09184          }
09185       }
09186       if (!res) {
09187          res = ast_play_and_wait(chan, "vm-helpexit");
09188       }
09189       if (!res)
09190          res = ast_waitfordigit(chan, 6000);
09191       if (!res) {
09192          vms->repeats++;
09193          if (vms->repeats > 2) {
09194             res = 't';
09195          }
09196       }
09197    }
09198    return res;
09199 }
09200 
09201 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09202 {
09203    int res = 0;
09204    /* Play instructions and wait for new command */
09205    while (!res) {
09206       if (vms->lastmsg > -1) {
09207          res = ast_play_and_wait(chan, "vm-listen");
09208          if (!res)
09209             res = vm_play_folder_name(chan, vms->vmbox);
09210          if (!res)
09211             res = ast_play_and_wait(chan, "press");
09212          if (!res)
09213             res = ast_play_and_wait(chan, "digits/1");
09214       }
09215       if (!res)
09216          res = ast_play_and_wait(chan, "vm-opts");
09217       if (!res) {
09218          vms->starting = 0;
09219          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09220       }
09221    }
09222    return res;
09223 }
09224 
09225 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09226 {
09227    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
09228       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09229    } else {             /* Default to ENGLISH */
09230       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09231    }
09232 }
09233 
09234 
09235 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09236 {
09237    int cmd = 0;
09238    int duration = 0;
09239    int tries = 0;
09240    char newpassword[80] = "";
09241    char newpassword2[80] = "";
09242    char prefile[PATH_MAX] = "";
09243    unsigned char buf[256];
09244    int bytes = 0;
09245 
09246    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09247    if (ast_adsi_available(chan)) {
09248       bytes += adsi_logo(buf + bytes);
09249       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09250       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09251       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09252       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09253       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09254    }
09255 
09256    /* If forcename is set, have the user record their name */
09257    if (ast_test_flag(vmu, VM_FORCENAME)) {
09258       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09259       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09260          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09261          if (cmd < 0 || cmd == 't' || cmd == '#')
09262             return cmd;
09263       }
09264    }
09265 
09266    /* If forcegreetings is set, have the user record their greetings */
09267    if (ast_test_flag(vmu, VM_FORCEGREET)) {
09268       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09269       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09270          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09271          if (cmd < 0 || cmd == 't' || cmd == '#')
09272             return cmd;
09273       }
09274 
09275       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09276       if (ast_fileexists(prefile, NULL, NULL) < 1) {
09277          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09278          if (cmd < 0 || cmd == 't' || cmd == '#')
09279             return cmd;
09280       }
09281    }
09282 
09283    /*
09284     * Change the password last since new users will be able to skip over any steps this one comes before
09285     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
09286     */
09287    for (;;) {
09288       newpassword[1] = '\0';
09289       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09290       if (cmd == '#')
09291          newpassword[0] = '\0';
09292       if (cmd < 0 || cmd == 't' || cmd == '#')
09293          return cmd;
09294       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
09295       if (cmd < 0 || cmd == 't' || cmd == '#')
09296          return cmd;
09297       cmd = check_password(vmu, newpassword); /* perform password validation */
09298       if (cmd != 0) {
09299          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09300          cmd = ast_play_and_wait(chan, vm_invalid_password);
09301       } else {
09302          newpassword2[1] = '\0';
09303          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09304          if (cmd == '#')
09305             newpassword2[0] = '\0';
09306          if (cmd < 0 || cmd == 't' || cmd == '#')
09307             return cmd;
09308          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
09309          if (cmd < 0 || cmd == 't' || cmd == '#')
09310             return cmd;
09311          if (!strcmp(newpassword, newpassword2))
09312             break;
09313          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09314          cmd = ast_play_and_wait(chan, vm_mismatch);
09315       }
09316       if (++tries == 3)
09317          return -1;
09318       if (cmd != 0) {
09319          cmd = ast_play_and_wait(chan, vm_pls_try_again);
09320       }
09321    }
09322    if (pwdchange & PWDCHANGE_INTERNAL)
09323       vm_change_password(vmu, newpassword);
09324    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
09325       vm_change_password_shell(vmu, newpassword);
09326 
09327    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
09328    cmd = ast_play_and_wait(chan, vm_passchanged);
09329 
09330    return cmd;
09331 }
09332 
09333 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09334 {
09335    int cmd = 0;
09336    int retries = 0;
09337    int duration = 0;
09338    char newpassword[80] = "";
09339    char newpassword2[80] = "";
09340    char prefile[PATH_MAX] = "";
09341    unsigned char buf[256];
09342    int bytes = 0;
09343 
09344    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
09345    if (ast_adsi_available(chan)) {
09346       bytes += adsi_logo(buf + bytes);
09347       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
09348       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09349       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09350       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09351       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09352    }
09353    while ((cmd >= 0) && (cmd != 't')) {
09354       if (cmd)
09355          retries = 0;
09356       switch (cmd) {
09357       case '1': /* Record your unavailable message */
09358          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
09359          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09360          break;
09361       case '2':  /* Record your busy message */
09362          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
09363          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09364          break;
09365       case '3': /* Record greeting */
09366          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09367          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09368          break;
09369       case '4':  /* manage the temporary greeting */
09370          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
09371          break;
09372       case '5': /* change password */
09373          if (vmu->password[0] == '-') {
09374             cmd = ast_play_and_wait(chan, "vm-no");
09375             break;
09376          }
09377          newpassword[1] = '\0';
09378          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
09379          if (cmd == '#')
09380             newpassword[0] = '\0';
09381          else {
09382             if (cmd < 0)
09383                break;
09384             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
09385                break;
09386             }
09387          }
09388          cmd = check_password(vmu, newpassword); /* perform password validation */
09389          if (cmd != 0) {
09390             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
09391             cmd = ast_play_and_wait(chan, vm_invalid_password);
09392             if (!cmd) {
09393                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09394             }
09395             break;
09396          }
09397          newpassword2[1] = '\0';
09398          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
09399          if (cmd == '#')
09400             newpassword2[0] = '\0';
09401          else {
09402             if (cmd < 0)
09403                break;
09404 
09405             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
09406                break;
09407             }
09408          }
09409          if (strcmp(newpassword, newpassword2)) {
09410             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
09411             cmd = ast_play_and_wait(chan, vm_mismatch);
09412             if (!cmd) {
09413                cmd = ast_play_and_wait(chan, vm_pls_try_again);
09414             }
09415             break;
09416          }
09417 
09418          if (pwdchange & PWDCHANGE_INTERNAL) {
09419             vm_change_password(vmu, newpassword);
09420          }
09421          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
09422             vm_change_password_shell(vmu, newpassword);
09423          }
09424 
09425          ast_debug(1, "User %s set password to %s of length %d\n",
09426             vms->username, newpassword, (int) strlen(newpassword));
09427          cmd = ast_play_and_wait(chan, vm_passchanged);
09428          break;
09429       case '*': 
09430          cmd = 't';
09431          break;
09432       default: 
09433          cmd = 0;
09434          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09435          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09436          if (ast_fileexists(prefile, NULL, NULL)) {
09437             cmd = ast_play_and_wait(chan, "vm-tmpexists");
09438          }
09439          DISPOSE(prefile, -1);
09440          if (!cmd) {
09441             cmd = ast_play_and_wait(chan, "vm-options");
09442          }
09443          if (!cmd) {
09444             cmd = ast_waitfordigit(chan, 6000);
09445          }
09446          if (!cmd) {
09447             retries++;
09448          }
09449          if (retries > 3) {
09450             cmd = 't';
09451          }
09452          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09453       }
09454    }
09455    if (cmd == 't')
09456       cmd = 0;
09457    return cmd;
09458 }
09459 
09460 /*!
09461  * \brief The handler for 'record a temporary greeting'. 
09462  * \param chan
09463  * \param vmu
09464  * \param vms
09465  * \param fmtc
09466  * \param record_gain
09467  *
09468  * This is option 4 from the mailbox options menu.
09469  * This function manages the following promptings:
09470  * 1: play / record / review the temporary greeting. : invokes play_record_review().
09471  * 2: remove (delete) the temporary greeting.
09472  * *: return to the main menu.
09473  *
09474  * \return zero on success, -1 on error.
09475  */
09476 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09477 {
09478    int cmd = 0;
09479    int retries = 0;
09480    int duration = 0;
09481    char prefile[PATH_MAX] = "";
09482    unsigned char buf[256];
09483    int bytes = 0;
09484 
09485    if (ast_adsi_available(chan)) {
09486       bytes += adsi_logo(buf + bytes);
09487       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
09488       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09489       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09490       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09491       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09492    }
09493 
09494    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
09495    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09496    while ((cmd >= 0) && (cmd != 't')) {
09497       if (cmd)
09498          retries = 0;
09499       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09500       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
09501          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09502          if (cmd == -1) {
09503             break;
09504          }
09505          cmd = 't';  
09506       } else {
09507          switch (cmd) {
09508          case '1':
09509             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
09510             break;
09511          case '2':
09512             DELETE(prefile, -1, prefile, vmu);
09513             ast_play_and_wait(chan, "vm-tempremoved");
09514             cmd = 't';  
09515             break;
09516          case '*': 
09517             cmd = 't';
09518             break;
09519          default:
09520             cmd = ast_play_and_wait(chan,
09521                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
09522                   "vm-tempgreeting2" : "vm-tempgreeting");
09523             if (!cmd) {
09524                cmd = ast_waitfordigit(chan, 6000);
09525             }
09526             if (!cmd) {
09527                retries++;
09528             }
09529             if (retries > 3) {
09530                cmd = 't';
09531             }
09532             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
09533          }
09534       }
09535       DISPOSE(prefile, -1);
09536    }
09537    if (cmd == 't')
09538       cmd = 0;
09539    return cmd;
09540 }
09541 
09542 /*!
09543  * \brief Greek syntax for 'You have N messages' greeting.
09544  * \param chan
09545  * \param vms
09546  * \param vmu
09547  *
09548  * \return zero on success, -1 on error.
09549  */   
09550 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09551 {
09552    int cmd = 0;
09553 
09554    if (vms->lastmsg > -1) {
09555       cmd = play_message(chan, vmu, vms);
09556    } else {
09557       cmd = ast_play_and_wait(chan, "vm-youhaveno");
09558       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
09559          if (!cmd) {
09560             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
09561             cmd = ast_play_and_wait(chan, vms->fn);
09562          }
09563          if (!cmd)
09564             cmd = ast_play_and_wait(chan, "vm-messages");
09565       } else {
09566          if (!cmd)
09567             cmd = ast_play_and_wait(chan, "vm-messages");
09568          if (!cmd) {
09569             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09570             cmd = ast_play_and_wait(chan, vms->fn);
09571          }
09572       }
09573    } 
09574    return cmd;
09575 }
09576 
09577 /* Hebrew Syntax */
09578 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09579 {
09580    int cmd = 0;
09581 
09582    if (vms->lastmsg > -1) {
09583       cmd = play_message(chan, vmu, vms);
09584    } else {
09585       if (!strcasecmp(vms->fn, "INBOX")) {
09586          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
09587       } else {
09588          cmd = ast_play_and_wait(chan, "vm-nomessages");
09589       }
09590    }
09591    return cmd;
09592 }
09593 
09594 /*! 
09595  * \brief Default English syntax for 'You have N messages' greeting.
09596  * \param chan
09597  * \param vms
09598  * \param vmu
09599  *
09600  * \return zero on success, -1 on error.
09601  */
09602 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09603 {
09604    int cmd = 0;
09605 
09606    if (vms->lastmsg > -1) {
09607       cmd = play_message(chan, vmu, vms);
09608    } else {
09609       cmd = ast_play_and_wait(chan, "vm-youhave");
09610       if (!cmd) 
09611          cmd = ast_play_and_wait(chan, "vm-no");
09612       if (!cmd) {
09613          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09614          cmd = ast_play_and_wait(chan, vms->fn);
09615       }
09616       if (!cmd)
09617          cmd = ast_play_and_wait(chan, "vm-messages");
09618    }
09619    return cmd;
09620 }
09621 
09622 /*! 
09623  *\brief Italian syntax for 'You have N messages' greeting.
09624  * \param chan
09625  * \param vms
09626  * \param vmu
09627  *
09628  * \return zero on success, -1 on error.
09629  */
09630 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09631 {
09632    int cmd;
09633 
09634    if (vms->lastmsg > -1) {
09635       cmd = play_message(chan, vmu, vms);
09636    } else {
09637       cmd = ast_play_and_wait(chan, "vm-no");
09638       if (!cmd)
09639          cmd = ast_play_and_wait(chan, "vm-message");
09640       if (!cmd) {
09641          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09642          cmd = ast_play_and_wait(chan, vms->fn);
09643       }
09644    }
09645    return cmd;
09646 }
09647 
09648 /*! 
09649  * \brief Spanish 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_es(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-youhaveno");
09664       if (!cmd)
09665          cmd = ast_play_and_wait(chan, "vm-messages");
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 Portuguese 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_pt(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-no");
09690       if (!cmd) {
09691          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09692          cmd = ast_play_and_wait(chan, vms->fn);
09693       }
09694       if (!cmd)
09695          cmd = ast_play_and_wait(chan, "vm-messages");
09696    }
09697    return cmd;
09698 }
09699 
09700 /*! 
09701  * \brief Chinese (Taiwan)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_zh(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-you");
09716       if (!cmd) 
09717          cmd = ast_play_and_wait(chan, "vm-haveno");
09718       if (!cmd)
09719          cmd = ast_play_and_wait(chan, "vm-messages");
09720       if (!cmd) {
09721          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09722          cmd = ast_play_and_wait(chan, vms->fn);
09723       }
09724    }
09725    return cmd;
09726 }
09727 
09728 /*! 
09729  * \brief Vietnamese syntax for 'You have N messages' greeting.
09730  * \param chan
09731  * \param vms
09732  * \param vmu
09733  *
09734  * \return zero on success, -1 on error.
09735  */
09736 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09737 {
09738    int cmd = 0;
09739 
09740    if (vms->lastmsg > -1) {
09741       cmd = play_message(chan, vmu, vms);
09742    } else {
09743       cmd = ast_play_and_wait(chan, "vm-no");
09744       if (!cmd) {
09745          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
09746          cmd = ast_play_and_wait(chan, vms->fn);
09747       }
09748    }
09749    return cmd;
09750 }
09751 
09752 /*!
09753  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
09754  * \param chan The channel for the current user. We read the language property from this.
09755  * \param vms passed into the language-specific vm_browse_messages function.
09756  * \param vmu passed into the language-specific vm_browse_messages function.
09757  * 
09758  * The method to be invoked is determined by the value of language code property in the user's channel.
09759  * The default (when unable to match) is to use english.
09760  *
09761  * \return zero on success, -1 on error.
09762  */
09763 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
09764 {
09765    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
09766       return vm_browse_messages_es(chan, vms, vmu);
09767    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
09768       return vm_browse_messages_gr(chan, vms, vmu);
09769    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
09770       return vm_browse_messages_he(chan, vms, vmu);
09771    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
09772       return vm_browse_messages_it(chan, vms, vmu);
09773    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
09774       return vm_browse_messages_pt(chan, vms, vmu);
09775    } else if (!strncasecmp(chan->language, "vi", 2)) {  /* VIETNAMESE */
09776       return vm_browse_messages_vi(chan, vms, vmu);
09777    } else if (!strncasecmp(chan->language, "zh", 2)) {  /* CHINESE (Taiwan) */
09778       return vm_browse_messages_zh(chan, vms, vmu);
09779    } else {                                             /* Default to English syntax */
09780       return vm_browse_messages_en(chan, vms, vmu);
09781    }
09782 }
09783 
09784 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
09785          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
09786          int skipuser, int max_logins, int silent)
09787 {
09788    int useadsi = 0, valid = 0, logretries = 0;
09789    char password[AST_MAX_EXTENSION]="", *passptr;
09790    struct ast_vm_user vmus, *vmu = NULL;
09791 
09792    /* If ADSI is supported, setup login screen */
09793    adsi_begin(chan, &useadsi);
09794    if (!skipuser && useadsi)
09795       adsi_login(chan);
09796    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
09797    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
09798       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
09799       return -1;
09800    }
09801 
09802    /* Authenticate them and get their mailbox/password */
09803 
09804    while (!valid && (logretries < max_logins)) {
09805       /* Prompt for, and read in the username */
09806       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
09807          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
09808          return -1;
09809       }
09810       if (ast_strlen_zero(mailbox)) {
09811          if (chan->caller.id.number.valid && chan->caller.id.number.str) {
09812             ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
09813          } else {
09814             ast_verb(3, "Username not entered\n"); 
09815             return -1;
09816          }
09817       } else if (mailbox[0] == '*') {
09818          /* user entered '*' */
09819          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
09820          if (ast_exists_extension(chan, chan->context, "a", 1,
09821             S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09822             return -1;
09823          }
09824          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
09825          mailbox[0] = '\0';
09826       }
09827 
09828       if (useadsi)
09829          adsi_password(chan);
09830 
09831       if (!ast_strlen_zero(prefix)) {
09832          char fullusername[80] = "";
09833          ast_copy_string(fullusername, prefix, sizeof(fullusername));
09834          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
09835          ast_copy_string(mailbox, fullusername, mailbox_size);
09836       }
09837 
09838       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
09839       vmu = find_user(&vmus, context, mailbox);
09840       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
09841          /* saved password is blank, so don't bother asking */
09842          password[0] = '\0';
09843       } else {
09844          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
09845          if (ast_streamfile(chan, vm_password, chan->language)) {
09846             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
09847             return -1;
09848          }
09849          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
09850             ast_log(AST_LOG_WARNING, "Unable to read password\n");
09851             return -1;
09852          } else if (password[0] == '*') {
09853             /* user entered '*' */
09854             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
09855             if (ast_exists_extension(chan, chan->context, "a", 1,
09856                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
09857                mailbox[0] = '*';
09858                return -1;
09859             }
09860             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
09861             mailbox[0] = '\0';
09862             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
09863             vmu = NULL;
09864          }
09865       }
09866 
09867       if (vmu) {
09868          passptr = vmu->password;
09869          if (passptr[0] == '-') passptr++;
09870       }
09871       if (vmu && !strcmp(passptr, password))
09872          valid++;
09873       else {
09874          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
09875          if (!ast_strlen_zero(prefix))
09876             mailbox[0] = '\0';
09877       }
09878       logretries++;
09879       if (!valid) {
09880          if (skipuser || logretries >= max_logins) {
09881             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
09882             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
09883                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
09884                return -1;
09885             }
09886          } else {
09887             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
09888             if (useadsi)
09889                adsi_login(chan);
09890             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
09891                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
09892                return -1;
09893             }
09894          }
09895          if (ast_waitstream(chan, "")) /* Channel is hung up */
09896             return -1;
09897       }
09898    }
09899    if (!valid && (logretries >= max_logins)) {
09900       ast_stopstream(chan);
09901       ast_play_and_wait(chan, "vm-goodbye");
09902       return -1;
09903    }
09904    if (vmu && !skipuser) {
09905       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
09906    }
09907    return 0;
09908 }
09909 
09910 static int vm_execmain(struct ast_channel *chan, const char *data)
09911 {
09912    /* XXX This is, admittedly, some pretty horrendous code.  For some
09913       reason it just seemed a lot easier to do with GOTO's.  I feel
09914       like I'm back in my GWBASIC days. XXX */
09915    int res = -1;
09916    int cmd = 0;
09917    int valid = 0;
09918    char prefixstr[80] ="";
09919    char ext_context[256]="";
09920    int box;
09921    int useadsi = 0;
09922    int skipuser = 0;
09923    struct vm_state vms;
09924    struct ast_vm_user *vmu = NULL, vmus;
09925    char *context = NULL;
09926    int silentexit = 0;
09927    struct ast_flags flags = { 0 };
09928    signed char record_gain = 0;
09929    int play_auto = 0;
09930    int play_folder = 0;
09931    int in_urgent = 0;
09932 #ifdef IMAP_STORAGE
09933    int deleted = 0;
09934 #endif
09935 
09936    /* Add the vm_state to the active list and keep it active */
09937    memset(&vms, 0, sizeof(vms));
09938 
09939    vms.lastmsg = -1;
09940 
09941    memset(&vmus, 0, sizeof(vmus));
09942 
09943    ast_test_suite_event_notify("START", "Message: vm_execmain started");
09944    if (chan->_state != AST_STATE_UP) {
09945       ast_debug(1, "Before ast_answer\n");
09946       ast_answer(chan);
09947    }
09948 
09949    if (!ast_strlen_zero(data)) {
09950       char *opts[OPT_ARG_ARRAY_SIZE];
09951       char *parse;
09952       AST_DECLARE_APP_ARGS(args,
09953          AST_APP_ARG(argv0);
09954          AST_APP_ARG(argv1);
09955       );
09956 
09957       parse = ast_strdupa(data);
09958 
09959       AST_STANDARD_APP_ARGS(args, parse);
09960 
09961       if (args.argc == 2) {
09962          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09963             return -1;
09964          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09965             int gain;
09966             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09967                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09968                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09969                   return -1;
09970                } else {
09971                   record_gain = (signed char) gain;
09972                }
09973             } else {
09974                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09975             }
09976          }
09977          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09978             play_auto = 1;
09979             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
09980                /* See if it is a folder name first */
09981                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
09982                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09983                      play_folder = -1;
09984                   }
09985                } else {
09986                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
09987                }
09988             } else {
09989                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09990             }
09991             if (play_folder > 9 || play_folder < 0) {
09992                ast_log(AST_LOG_WARNING,
09993                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
09994                   opts[OPT_ARG_PLAYFOLDER]);
09995                play_folder = 0;
09996             }
09997          }
09998       } else {
09999          /* old style options parsing */
10000          while (*(args.argv0)) {
10001             if (*(args.argv0) == 's')
10002                ast_set_flag(&flags, OPT_SILENT);
10003             else if (*(args.argv0) == 'p')
10004                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
10005             else 
10006                break;
10007             (args.argv0)++;
10008          }
10009 
10010       }
10011 
10012       valid = ast_test_flag(&flags, OPT_SILENT);
10013 
10014       if ((context = strchr(args.argv0, '@')))
10015          *context++ = '\0';
10016 
10017       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10018          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10019       else
10020          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10021 
10022       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10023          skipuser++;
10024       else
10025          valid = 0;
10026    }
10027 
10028    if (!valid)
10029       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10030 
10031    ast_debug(1, "After vm_authenticate\n");
10032 
10033    if (vms.username[0] == '*') {
10034       ast_debug(1, "user pressed * in context '%s'\n", chan->context);
10035 
10036       /* user entered '*' */
10037       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10038          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10039          res = 0; /* prevent hangup */
10040          goto out;
10041       }
10042    }
10043 
10044    if (!res) {
10045       valid = 1;
10046       if (!skipuser)
10047          vmu = &vmus;
10048    } else {
10049       res = 0;
10050    }
10051 
10052    /* If ADSI is supported, setup login screen */
10053    adsi_begin(chan, &useadsi);
10054 
10055    ast_test_suite_assert(valid);
10056    if (!valid) {
10057       goto out;
10058    }
10059    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10060 
10061 #ifdef IMAP_STORAGE
10062    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10063    pthread_setspecific(ts_vmstate.key, &vms);
10064 
10065    vms.interactive = 1;
10066    vms.updated = 1;
10067    if (vmu)
10068       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10069    vmstate_insert(&vms);
10070    init_vm_state(&vms);
10071 #endif
10072    
10073    /* Set language from config to override channel language */
10074    if (!ast_strlen_zero(vmu->language))
10075       ast_string_field_set(chan, language, vmu->language);
10076 
10077    /* Retrieve urgent, old and new message counts */
10078    ast_debug(1, "Before open_mailbox\n");
10079    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10080    if (res < 0)
10081       goto out;
10082    vms.oldmessages = vms.lastmsg + 1;
10083    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10084    /* check INBOX */
10085    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10086    if (res < 0)
10087       goto out;
10088    vms.newmessages = vms.lastmsg + 1;
10089    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10090    /* Start in Urgent */
10091    in_urgent = 1;
10092    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10093    if (res < 0)
10094       goto out;
10095    vms.urgentmessages = vms.lastmsg + 1;
10096    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10097 
10098    /* Select proper mailbox FIRST!! */
10099    if (play_auto) {
10100       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10101       if (vms.urgentmessages) {
10102          in_urgent = 1;
10103          res = open_mailbox(&vms, vmu, 11);
10104       } else {
10105          in_urgent = 0;
10106          res = open_mailbox(&vms, vmu, play_folder);
10107       }
10108       if (res < 0)
10109          goto out;
10110 
10111       /* If there are no new messages, inform the user and hangup */
10112       if (vms.lastmsg == -1) {
10113          in_urgent = 0;
10114          cmd = vm_browse_messages(chan, &vms, vmu);
10115          res = 0;
10116          goto out;
10117       }
10118    } else {
10119       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10120          /* If we only have old messages start here */
10121          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10122          in_urgent = 0;
10123          play_folder = 1;
10124          if (res < 0)
10125             goto out;
10126       } else if (!vms.urgentmessages && vms.newmessages) {
10127          /* If we have new messages but none are urgent */
10128          in_urgent = 0;
10129          res = open_mailbox(&vms, vmu, NEW_FOLDER);
10130          if (res < 0)
10131             goto out;
10132       }
10133    }
10134 
10135    if (useadsi)
10136       adsi_status(chan, &vms);
10137    res = 0;
10138 
10139    /* Check to see if this is a new user */
10140    if (!strcasecmp(vmu->mailbox, vmu->password) && 
10141       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
10142       if (ast_play_and_wait(chan, "vm-newuser") == -1)
10143          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10144       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10145       if ((cmd == 't') || (cmd == '#')) {
10146          /* Timeout */
10147          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10148          res = 0;
10149          goto out;
10150       } else if (cmd < 0) {
10151          /* Hangup */
10152          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10153          res = -1;
10154          goto out;
10155       }
10156    }
10157 #ifdef IMAP_STORAGE
10158       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10159       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10160          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10161          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10162       }
10163       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10164       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10165          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10166          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10167       }
10168 #endif
10169 
10170    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10171    if (play_auto) {
10172       cmd = '1';
10173    } else {
10174       cmd = vm_intro(chan, vmu, &vms);
10175    }
10176    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10177 
10178    vms.repeats = 0;
10179    vms.starting = 1;
10180    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10181       /* Run main menu */
10182       switch (cmd) {
10183       case '1': /* First message */
10184          vms.curmsg = 0;
10185          /* Fall through */
10186       case '5': /* Play current message */
10187          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10188          cmd = vm_browse_messages(chan, &vms, vmu);
10189          break;
10190       case '2': /* Change folders */
10191          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10192          if (useadsi)
10193             adsi_folders(chan, 0, "Change to folder...");
10194 
10195          cmd = get_folder2(chan, "vm-changeto", 0);
10196          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10197          if (cmd == '#') {
10198             cmd = 0;
10199          } else if (cmd > 0) {
10200             cmd = cmd - '0';
10201             res = close_mailbox(&vms, vmu);
10202             if (res == ERROR_LOCK_PATH)
10203                goto out;
10204             /* If folder is not urgent, set in_urgent to zero! */
10205             if (cmd != 11) in_urgent = 0;
10206             res = open_mailbox(&vms, vmu, cmd);
10207             if (res < 0)
10208                goto out;
10209             play_folder = cmd;
10210             cmd = 0;
10211          }
10212          if (useadsi)
10213             adsi_status2(chan, &vms);
10214 
10215          if (!cmd) {
10216             cmd = vm_play_folder_name(chan, vms.vmbox);
10217          }
10218 
10219          vms.starting = 1;
10220          vms.curmsg = 0;
10221          break;
10222       case '3': /* Advanced options */
10223          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10224          cmd = 0;
10225          vms.repeats = 0;
10226          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10227             switch (cmd) {
10228             case '1': /* Reply */
10229                if (vms.lastmsg > -1 && !vms.starting) {
10230                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10231                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10232                      res = cmd;
10233                      goto out;
10234                   }
10235                } else {
10236                   cmd = ast_play_and_wait(chan, "vm-sorry");
10237                }
10238                cmd = 't';
10239                break;
10240             case '2': /* Callback */
10241                if (!vms.starting)
10242                   ast_verb(3, "Callback Requested\n");
10243                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10244                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10245                   if (cmd == 9) {
10246                      silentexit = 1;
10247                      goto out;
10248                   } else if (cmd == ERROR_LOCK_PATH) {
10249                      res = cmd;
10250                      goto out;
10251                   }
10252                } else {
10253                   cmd = ast_play_and_wait(chan, "vm-sorry");
10254                }
10255                cmd = 't';
10256                break;
10257             case '3': /* Envelope */
10258                if (vms.lastmsg > -1 && !vms.starting) {
10259                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10260                   if (cmd == ERROR_LOCK_PATH) {
10261                      res = cmd;
10262                      goto out;
10263                   }
10264                } else {
10265                   cmd = ast_play_and_wait(chan, "vm-sorry");
10266                }
10267                cmd = 't';
10268                break;
10269             case '4': /* Dialout */
10270                if (!ast_strlen_zero(vmu->dialout)) {
10271                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
10272                   if (cmd == 9) {
10273                      silentexit = 1;
10274                      goto out;
10275                   }
10276                } else {
10277                   cmd = ast_play_and_wait(chan, "vm-sorry");
10278                }
10279                cmd = 't';
10280                break;
10281 
10282             case '5': /* Leave VoiceMail */
10283                if (ast_test_flag(vmu, VM_SVMAIL)) {
10284                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10285                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10286                      res = cmd;
10287                      goto out;
10288                   }
10289                } else {
10290                   cmd = ast_play_and_wait(chan, "vm-sorry");
10291                }
10292                cmd = 't';
10293                break;
10294 
10295             case '*': /* Return to main menu */
10296                cmd = 't';
10297                break;
10298 
10299             default:
10300                cmd = 0;
10301                if (!vms.starting) {
10302                   cmd = ast_play_and_wait(chan, "vm-toreply");
10303                }
10304                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10305                   cmd = ast_play_and_wait(chan, "vm-tocallback");
10306                }
10307                if (!cmd && !vms.starting) {
10308                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
10309                }
10310                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10311                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
10312                }
10313                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10314                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
10315                }
10316                if (!cmd) {
10317                   cmd = ast_play_and_wait(chan, "vm-starmain");
10318                }
10319                if (!cmd) {
10320                   cmd = ast_waitfordigit(chan, 6000);
10321                }
10322                if (!cmd) {
10323                   vms.repeats++;
10324                }
10325                if (vms.repeats > 3) {
10326                   cmd = 't';
10327                }
10328                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10329             }
10330          }
10331          if (cmd == 't') {
10332             cmd = 0;
10333             vms.repeats = 0;
10334          }
10335          break;
10336       case '4': /* Go to the previous message */
10337          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10338          if (vms.curmsg > 0) {
10339             vms.curmsg--;
10340             cmd = play_message(chan, vmu, &vms);
10341          } else {
10342             /* Check if we were listening to new
10343                messages.  If so, go to Urgent messages
10344                instead of saying "no more messages"
10345             */
10346             if (in_urgent == 0 && vms.urgentmessages > 0) {
10347                /* Check for Urgent messages */
10348                in_urgent = 1;
10349                res = close_mailbox(&vms, vmu);
10350                if (res == ERROR_LOCK_PATH)
10351                   goto out;
10352                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
10353                if (res < 0)
10354                   goto out;
10355                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10356                vms.curmsg = vms.lastmsg;
10357                if (vms.lastmsg < 0) {
10358                   cmd = ast_play_and_wait(chan, "vm-nomore");
10359                }
10360             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10361                vms.curmsg = vms.lastmsg;
10362                cmd = play_message(chan, vmu, &vms);
10363             } else {
10364                cmd = ast_play_and_wait(chan, "vm-nomore");
10365             }
10366          }
10367          break;
10368       case '6': /* Go to the next message */
10369          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10370          if (vms.curmsg < vms.lastmsg) {
10371             vms.curmsg++;
10372             cmd = play_message(chan, vmu, &vms);
10373          } else {
10374             if (in_urgent && vms.newmessages > 0) {
10375                /* Check if we were listening to urgent
10376                 * messages.  If so, go to regular new messages
10377                 * instead of saying "no more messages"
10378                 */
10379                in_urgent = 0;
10380                res = close_mailbox(&vms, vmu);
10381                if (res == ERROR_LOCK_PATH)
10382                   goto out;
10383                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10384                if (res < 0)
10385                   goto out;
10386                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10387                vms.curmsg = -1;
10388                if (vms.lastmsg < 0) {
10389                   cmd = ast_play_and_wait(chan, "vm-nomore");
10390                }
10391             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10392                vms.curmsg = 0;
10393                cmd = play_message(chan, vmu, &vms);
10394             } else {
10395                cmd = ast_play_and_wait(chan, "vm-nomore");
10396             }
10397          }
10398          break;
10399       case '7': /* Delete the current message */
10400          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10401             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10402             if (useadsi)
10403                adsi_delete(chan, &vms);
10404             if (vms.deleted[vms.curmsg]) {
10405                if (play_folder == 0) {
10406                   if (in_urgent) {
10407                      vms.urgentmessages--;
10408                   } else {
10409                      vms.newmessages--;
10410                   }
10411                }
10412                else if (play_folder == 1)
10413                   vms.oldmessages--;
10414                cmd = ast_play_and_wait(chan, "vm-deleted");
10415             } else {
10416                if (play_folder == 0) {
10417                   if (in_urgent) {
10418                      vms.urgentmessages++;
10419                   } else {
10420                      vms.newmessages++;
10421                   }
10422                }
10423                else if (play_folder == 1)
10424                   vms.oldmessages++;
10425                cmd = ast_play_and_wait(chan, "vm-undeleted");
10426             }
10427             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10428                if (vms.curmsg < vms.lastmsg) {
10429                   vms.curmsg++;
10430                   cmd = play_message(chan, vmu, &vms);
10431                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10432                   vms.curmsg = 0;
10433                   cmd = play_message(chan, vmu, &vms);
10434                } else {
10435                   /* Check if we were listening to urgent
10436                      messages.  If so, go to regular new messages
10437                      instead of saying "no more messages"
10438                   */
10439                   if (in_urgent == 1) {
10440                      /* Check for new messages */
10441                      in_urgent = 0;
10442                      res = close_mailbox(&vms, vmu);
10443                      if (res == ERROR_LOCK_PATH)
10444                         goto out;
10445                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
10446                      if (res < 0)
10447                         goto out;
10448                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10449                      vms.curmsg = -1;
10450                      if (vms.lastmsg < 0) {
10451                         cmd = ast_play_and_wait(chan, "vm-nomore");
10452                      }
10453                   } else {
10454                      cmd = ast_play_and_wait(chan, "vm-nomore");
10455                   }
10456                }
10457             }
10458          } else /* Delete not valid if we haven't selected a message */
10459             cmd = 0;
10460 #ifdef IMAP_STORAGE
10461          deleted = 1;
10462 #endif
10463          break;
10464    
10465       case '8': /* Forward the current message */
10466          if (vms.lastmsg > -1) {
10467             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10468             if (cmd == ERROR_LOCK_PATH) {
10469                res = cmd;
10470                goto out;
10471             }
10472          } else {
10473             /* Check if we were listening to urgent
10474                messages.  If so, go to regular new messages
10475                instead of saying "no more messages"
10476             */
10477             if (in_urgent == 1 && vms.newmessages > 0) {
10478                /* Check for new messages */
10479                in_urgent = 0;
10480                res = close_mailbox(&vms, vmu);
10481                if (res == ERROR_LOCK_PATH)
10482                   goto out;
10483                res = open_mailbox(&vms, vmu, NEW_FOLDER);
10484                if (res < 0)
10485                   goto out;
10486                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10487                vms.curmsg = -1;
10488                if (vms.lastmsg < 0) {
10489                   cmd = ast_play_and_wait(chan, "vm-nomore");
10490                }
10491             } else {
10492                cmd = ast_play_and_wait(chan, "vm-nomore");
10493             }
10494          }
10495          break;
10496       case '9': /* Save message to folder */
10497          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10498          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10499             /* No message selected */
10500             cmd = 0;
10501             break;
10502          }
10503          if (useadsi)
10504             adsi_folders(chan, 1, "Save to folder...");
10505          cmd = get_folder2(chan, "vm-savefolder", 1);
10506          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10507          box = 0; /* Shut up compiler */
10508          if (cmd == '#') {
10509             cmd = 0;
10510             break;
10511          } else if (cmd > 0) {
10512             box = cmd = cmd - '0';
10513             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10514             if (cmd == ERROR_LOCK_PATH) {
10515                res = cmd;
10516                goto out;
10517 #ifndef IMAP_STORAGE
10518             } else if (!cmd) {
10519                vms.deleted[vms.curmsg] = 1;
10520 #endif
10521             } else {
10522                vms.deleted[vms.curmsg] = 0;
10523                vms.heard[vms.curmsg] = 0;
10524             }
10525          }
10526          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10527          if (useadsi)
10528             adsi_message(chan, &vms);
10529          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10530          if (!cmd) {
10531             cmd = ast_play_and_wait(chan, "vm-message");
10532             if (!cmd) 
10533                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10534             if (!cmd)
10535                cmd = ast_play_and_wait(chan, "vm-savedto");
10536             if (!cmd)
10537                cmd = vm_play_folder_name(chan, vms.fn);
10538          } else {
10539             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10540          }
10541          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10542             if (vms.curmsg < vms.lastmsg) {
10543                vms.curmsg++;
10544                cmd = play_message(chan, vmu, &vms);
10545             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10546                vms.curmsg = 0;
10547                cmd = play_message(chan, vmu, &vms);
10548             } else {
10549                /* Check if we were listening to urgent
10550                   messages.  If so, go to regular new messages
10551                   instead of saying "no more messages"
10552                */
10553                if (in_urgent == 1 && vms.newmessages > 0) {
10554                   /* Check for new messages */
10555                   in_urgent = 0;
10556                   res = close_mailbox(&vms, vmu);
10557                   if (res == ERROR_LOCK_PATH)
10558                      goto out;
10559                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
10560                   if (res < 0)
10561                      goto out;
10562                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10563                   vms.curmsg = -1;
10564                   if (vms.lastmsg < 0) {
10565                      cmd = ast_play_and_wait(chan, "vm-nomore");
10566                   }
10567                } else {
10568                   cmd = ast_play_and_wait(chan, "vm-nomore");
10569                }
10570             }
10571          }
10572          break;
10573       case '*': /* Help */
10574          if (!vms.starting) {
10575             cmd = ast_play_and_wait(chan, "vm-onefor");
10576             if (!strncasecmp(chan->language, "he", 2)) {
10577                cmd = ast_play_and_wait(chan, "vm-for");
10578             }
10579             if (!cmd)
10580                cmd = vm_play_folder_name(chan, vms.vmbox);
10581             if (!cmd)
10582                cmd = ast_play_and_wait(chan, "vm-opts");
10583             if (!cmd)
10584                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10585          } else
10586             cmd = 0;
10587          break;
10588       case '0': /* Mailbox options */
10589          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10590          if (useadsi)
10591             adsi_status(chan, &vms);
10592          break;
10593       default: /* Nothing */
10594          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10595          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10596          break;
10597       }
10598    }
10599    if ((cmd == 't') || (cmd == '#')) {
10600       /* Timeout */
10601       res = 0;
10602    } else {
10603       /* Hangup */
10604       res = -1;
10605    }
10606 
10607 out:
10608    if (res > -1) {
10609       ast_stopstream(chan);
10610       adsi_goodbye(chan);
10611       if (valid && res != OPERATOR_EXIT) {
10612          if (silentexit)
10613             res = ast_play_and_wait(chan, "vm-dialout");
10614          else 
10615             res = ast_play_and_wait(chan, "vm-goodbye");
10616       }
10617       if ((valid && res > 0) || res == OPERATOR_EXIT) {
10618          res = 0;
10619       }
10620       if (useadsi)
10621          ast_adsi_unload_session(chan);
10622    }
10623    if (vmu)
10624       close_mailbox(&vms, vmu);
10625    if (valid) {
10626       int new = 0, old = 0, urgent = 0;
10627       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10628       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10629       /* Urgent flag not passwd to externnotify here */
10630       run_externnotify(vmu->context, vmu->mailbox, NULL);
10631       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10632       queue_mwi_event(ext_context, urgent, new, old);
10633    }
10634 #ifdef IMAP_STORAGE
10635    /* expunge message - use UID Expunge if supported on IMAP server*/
10636    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10637    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10638       ast_mutex_lock(&vms.lock);
10639 #ifdef HAVE_IMAP_TK2006
10640       if (LEVELUIDPLUS (vms.mailstream)) {
10641          mail_expunge_full(vms.mailstream, NIL, EX_UID);
10642       } else 
10643 #endif
10644          mail_expunge(vms.mailstream);
10645       ast_mutex_unlock(&vms.lock);
10646    }
10647    /*  before we delete the state, we should copy pertinent info
10648     *  back to the persistent model */
10649    if (vmu) {
10650       vmstate_delete(&vms);
10651    }
10652 #endif
10653    if (vmu)
10654       free_user(vmu);
10655 
10656 #ifdef IMAP_STORAGE
10657    pthread_setspecific(ts_vmstate.key, NULL);
10658 #endif
10659    return res;
10660 }
10661 
10662 static int vm_exec(struct ast_channel *chan, const char *data)
10663 {
10664    int res = 0;
10665    char *tmp;
10666    struct leave_vm_options leave_options;
10667    struct ast_flags flags = { 0 };
10668    char *opts[OPT_ARG_ARRAY_SIZE];
10669    AST_DECLARE_APP_ARGS(args,
10670       AST_APP_ARG(argv0);
10671       AST_APP_ARG(argv1);
10672    );
10673    
10674    memset(&leave_options, 0, sizeof(leave_options));
10675 
10676    if (chan->_state != AST_STATE_UP)
10677       ast_answer(chan);
10678 
10679    if (!ast_strlen_zero(data)) {
10680       tmp = ast_strdupa(data);
10681       AST_STANDARD_APP_ARGS(args, tmp);
10682       if (args.argc == 2) {
10683          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10684             return -1;
10685          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
10686          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10687             int gain;
10688 
10689             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10690                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10691                return -1;
10692             } else {
10693                leave_options.record_gain = (signed char) gain;
10694             }
10695          }
10696          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10697             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10698                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10699          }
10700       }
10701    } else {
10702       char temp[256];
10703       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10704       if (res < 0)
10705          return res;
10706       if (ast_strlen_zero(temp))
10707          return 0;
10708       args.argv0 = ast_strdupa(temp);
10709    }
10710 
10711    res = leave_voicemail(chan, args.argv0, &leave_options);
10712    if (res == 't') {
10713       ast_play_and_wait(chan, "vm-goodbye");
10714       res = 0;
10715    }
10716 
10717    if (res == OPERATOR_EXIT) {
10718       res = 0;
10719    }
10720 
10721    if (res == ERROR_LOCK_PATH) {
10722       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10723       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10724       res = 0;
10725    }
10726 
10727    return res;
10728 }
10729 
10730 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10731 {
10732    struct ast_vm_user *vmu;
10733 
10734    if (!ast_strlen_zero(box) && box[0] == '*') {
10735       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
10736             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10737             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
10738             "\n\tand will be ignored.\n", box, context);
10739       return NULL;
10740    }
10741 
10742    AST_LIST_TRAVERSE(&users, vmu, list) {
10743       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10744          if (strcasecmp(vmu->context, context)) {
10745             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10746                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10747                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10748                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10749          }
10750          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10751          return NULL;
10752       }
10753       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10754          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10755          return NULL;
10756       }
10757    }
10758    
10759    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10760       return NULL;
10761    
10762    ast_copy_string(vmu->context, context, sizeof(vmu->context));
10763    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10764 
10765    AST_LIST_INSERT_TAIL(&users, vmu, list);
10766    
10767    return vmu;
10768 }
10769 
10770 static int append_mailbox(const char *context, const char *box, const char *data)
10771 {
10772    /* Assumes lock is already held */
10773    char *tmp;
10774    char *stringp;
10775    char *s;
10776    struct ast_vm_user *vmu;
10777    char *mailbox_full;
10778    int new = 0, old = 0, urgent = 0;
10779    char secretfn[PATH_MAX] = "";
10780 
10781    tmp = ast_strdupa(data);
10782 
10783    if (!(vmu = find_or_create(context, box)))
10784       return -1;
10785 
10786    populate_defaults(vmu);
10787 
10788    stringp = tmp;
10789    if ((s = strsep(&stringp, ","))) {
10790       if (!ast_strlen_zero(s) && s[0] == '*') {
10791          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
10792             "\n\tmust be reset in voicemail.conf.\n", box);
10793       }
10794       /* assign password regardless of validity to prevent NULL password from being assigned */
10795       ast_copy_string(vmu->password, s, sizeof(vmu->password));
10796    }
10797    if (stringp && (s = strsep(&stringp, ","))) {
10798       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10799    }
10800    if (stringp && (s = strsep(&stringp, ","))) {
10801       ast_copy_string(vmu->email, s, sizeof(vmu->email));
10802    }
10803    if (stringp && (s = strsep(&stringp, ","))) {
10804       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10805    }
10806    if (stringp && (s = strsep(&stringp, ","))) {
10807       apply_options(vmu, s);
10808    }
10809 
10810    switch (vmu->passwordlocation) {
10811    case OPT_PWLOC_SPOOLDIR:
10812       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10813       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10814    }
10815 
10816    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
10817    strcpy(mailbox_full, box);
10818    strcat(mailbox_full, "@");
10819    strcat(mailbox_full, context);
10820 
10821    inboxcount2(mailbox_full, &urgent, &new, &old);
10822    queue_mwi_event(mailbox_full, urgent, new, old);
10823 
10824    return 0;
10825 }
10826 
10827 AST_TEST_DEFINE(test_voicemail_vmuser)
10828 {
10829    int res = 0;
10830    struct ast_vm_user *vmu;
10831    /* language parameter seems to only be used for display in manager action */
10832    static const char options_string[] = "attach=yes|attachfmt=wav49|"
10833       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10834       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10835       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10836       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10837       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10838       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10839       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10840       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10841 #ifdef IMAP_STORAGE
10842    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10843       "imapfolder=INBOX|imapvmshareid=6000";
10844 #endif
10845 
10846    switch (cmd) {
10847    case TEST_INIT:
10848       info->name = "vmuser";
10849       info->category = "/apps/app_voicemail/";
10850       info->summary = "Vmuser unit test";
10851       info->description =
10852          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10853       return AST_TEST_NOT_RUN;
10854    case TEST_EXECUTE:
10855       break;
10856    }
10857 
10858    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10859       return AST_TEST_NOT_RUN;
10860    }
10861    ast_set_flag(vmu, VM_ALLOCED);
10862    populate_defaults(vmu);
10863 
10864    apply_options(vmu, options_string);
10865 
10866    if (!ast_test_flag(vmu, VM_ATTACH)) {
10867       ast_test_status_update(test, "Parse failure for attach option\n");
10868       res = 1;
10869    }
10870    if (strcasecmp(vmu->attachfmt, "wav49")) {
10871       ast_test_status_update(test, "Parse failure for attachftm option\n");
10872       res = 1;
10873    }
10874    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10875       ast_test_status_update(test, "Parse failure for serveremail option\n");
10876       res = 1;
10877    }
10878    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10879       ast_test_status_update(test, "Parse failure for emailsubject option\n");
10880       res = 1;
10881    }
10882    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10883       ast_test_status_update(test, "Parse failure for emailbody option\n");
10884       res = 1;
10885    }
10886    if (strcasecmp(vmu->zonetag, "central")) {
10887       ast_test_status_update(test, "Parse failure for tz option\n");
10888       res = 1;
10889    }
10890    if (!ast_test_flag(vmu, VM_DELETE)) {
10891       ast_test_status_update(test, "Parse failure for delete option\n");
10892       res = 1;
10893    }
10894    if (!ast_test_flag(vmu, VM_SAYCID)) {
10895       ast_test_status_update(test, "Parse failure for saycid option\n");
10896       res = 1;
10897    }
10898    if (!ast_test_flag(vmu, VM_SVMAIL)) {
10899       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10900       res = 1;
10901    }
10902    if (!ast_test_flag(vmu, VM_REVIEW)) {
10903       ast_test_status_update(test, "Parse failure for review option\n");
10904       res = 1;
10905    }
10906    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10907       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10908       res = 1;
10909    }
10910    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10911       ast_test_status_update(test, "Parse failure for messagewrap option\n");
10912       res = 1;
10913    }
10914    if (!ast_test_flag(vmu, VM_OPERATOR)) {
10915       ast_test_status_update(test, "Parse failure for operator option\n");
10916       res = 1;
10917    }
10918    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10919       ast_test_status_update(test, "Parse failure for envelope option\n");
10920       res = 1;
10921    }
10922    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10923       ast_test_status_update(test, "Parse failure for moveheard option\n");
10924       res = 1;
10925    }
10926    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10927       ast_test_status_update(test, "Parse failure for sayduration option\n");
10928       res = 1;
10929    }
10930    if (vmu->saydurationm != 5) {
10931       ast_test_status_update(test, "Parse failure for saydurationm option\n");
10932       res = 1;
10933    }
10934    if (!ast_test_flag(vmu, VM_FORCENAME)) {
10935       ast_test_status_update(test, "Parse failure for forcename option\n");
10936       res = 1;
10937    }
10938    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10939       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10940       res = 1;
10941    }
10942    if (strcasecmp(vmu->callback, "somecontext")) {
10943       ast_test_status_update(test, "Parse failure for callbacks option\n");
10944       res = 1;
10945    }
10946    if (strcasecmp(vmu->dialout, "somecontext2")) {
10947       ast_test_status_update(test, "Parse failure for dialout option\n");
10948       res = 1;
10949    }
10950    if (strcasecmp(vmu->exit, "somecontext3")) {
10951       ast_test_status_update(test, "Parse failure for exitcontext option\n");
10952       res = 1;
10953    }
10954    if (vmu->minsecs != 10) {
10955       ast_test_status_update(test, "Parse failure for minsecs option\n");
10956       res = 1;
10957    }
10958    if (vmu->maxsecs != 100) {
10959       ast_test_status_update(test, "Parse failure for maxsecs option\n");
10960       res = 1;
10961    }
10962    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10963       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10964       res = 1;
10965    }
10966    if (vmu->maxdeletedmsg != 50) {
10967       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10968       res = 1;
10969    }
10970    if (vmu->volgain != 1.3) {
10971       ast_test_status_update(test, "Parse failure for volgain option\n");
10972       res = 1;
10973    }
10974    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10975       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10976       res = 1;
10977    }
10978 #ifdef IMAP_STORAGE
10979    apply_options(vmu, option_string2);
10980 
10981    if (strcasecmp(vmu->imapuser, "imapuser")) {
10982       ast_test_status_update(test, "Parse failure for imapuser option\n");
10983       res = 1;
10984    }
10985    if (strcasecmp(vmu->imappassword, "imappasswd")) {
10986       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10987       res = 1;
10988    }
10989    if (strcasecmp(vmu->imapfolder, "INBOX")) {
10990       ast_test_status_update(test, "Parse failure for imappasswd option\n");
10991       res = 1;
10992    }
10993    if (strcasecmp(vmu->imapvmshareid, "6000")) {
10994       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10995       res = 1;
10996    }
10997 #endif
10998 
10999    free_user(vmu);
11000    return res ? AST_TEST_FAIL : AST_TEST_PASS;
11001 }
11002 
11003 static int vm_box_exists(struct ast_channel *chan, const char *data) 
11004 {
11005    struct ast_vm_user svm;
11006    char *context, *box;
11007    AST_DECLARE_APP_ARGS(args,
11008       AST_APP_ARG(mbox);
11009       AST_APP_ARG(options);
11010    );
11011    static int dep_warning = 0;
11012 
11013    if (ast_strlen_zero(data)) {
11014       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
11015       return -1;
11016    }
11017 
11018    if (!dep_warning) {
11019       dep_warning = 1;
11020       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
11021    }
11022 
11023    box = ast_strdupa(data);
11024 
11025    AST_STANDARD_APP_ARGS(args, box);
11026 
11027    if (args.options) {
11028    }
11029 
11030    if ((context = strchr(args.mbox, '@'))) {
11031       *context = '\0';
11032       context++;
11033    }
11034 
11035    if (find_user(&svm, context, args.mbox)) {
11036       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11037    } else
11038       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11039 
11040    return 0;
11041 }
11042 
11043 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11044 {
11045    struct ast_vm_user svm;
11046    AST_DECLARE_APP_ARGS(arg,
11047       AST_APP_ARG(mbox);
11048       AST_APP_ARG(context);
11049    );
11050 
11051    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11052 
11053    if (ast_strlen_zero(arg.mbox)) {
11054       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11055       return -1;
11056    }
11057 
11058    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11059    return 0;
11060 }
11061 
11062 static struct ast_custom_function mailbox_exists_acf = {
11063    .name = "MAILBOX_EXISTS",
11064    .read = acf_mailbox_exists,
11065 };
11066 
11067 static int vmauthenticate(struct ast_channel *chan, const char *data)
11068 {
11069    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11070    struct ast_vm_user vmus;
11071    char *options = NULL;
11072    int silent = 0, skipuser = 0;
11073    int res = -1;
11074    
11075    if (data) {
11076       s = ast_strdupa(data);
11077       user = strsep(&s, ",");
11078       options = strsep(&s, ",");
11079       if (user) {
11080          s = user;
11081          user = strsep(&s, "@");
11082          context = strsep(&s, "");
11083          if (!ast_strlen_zero(user))
11084             skipuser++;
11085          ast_copy_string(mailbox, user, sizeof(mailbox));
11086       }
11087    }
11088 
11089    if (options) {
11090       silent = (strchr(options, 's')) != NULL;
11091    }
11092 
11093    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11094       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11095       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11096       ast_play_and_wait(chan, "auth-thankyou");
11097       res = 0;
11098    } else if (mailbox[0] == '*') {
11099       /* user entered '*' */
11100       if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11101          res = 0; /* prevent hangup */
11102       }
11103    }
11104 
11105    return res;
11106 }
11107 
11108 static char *show_users_realtime(int fd, const char *context)
11109 {
11110    struct ast_config *cfg;
11111    const char *cat = NULL;
11112 
11113    if (!(cfg = ast_load_realtime_multientry("voicemail", 
11114       "context", context, SENTINEL))) {
11115       return CLI_FAILURE;
11116    }
11117 
11118    ast_cli(fd,
11119       "\n"
11120       "=============================================================\n"
11121       "=== Configured Voicemail Users ==============================\n"
11122       "=============================================================\n"
11123       "===\n");
11124 
11125    while ((cat = ast_category_browse(cfg, cat))) {
11126       struct ast_variable *var = NULL;
11127       ast_cli(fd,
11128          "=== Mailbox ...\n"
11129          "===\n");
11130       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11131          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11132       ast_cli(fd,
11133          "===\n"
11134          "=== ---------------------------------------------------------\n"
11135          "===\n");
11136    }
11137 
11138    ast_cli(fd,
11139       "=============================================================\n"
11140       "\n");
11141 
11142    ast_config_destroy(cfg);
11143 
11144    return CLI_SUCCESS;
11145 }
11146 
11147 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11148 {
11149    int which = 0;
11150    int wordlen;
11151    struct ast_vm_user *vmu;
11152    const char *context = "";
11153 
11154    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11155    if (pos > 4)
11156       return NULL;
11157    if (pos == 3)
11158       return (state == 0) ? ast_strdup("for") : NULL;
11159    wordlen = strlen(word);
11160    AST_LIST_TRAVERSE(&users, vmu, list) {
11161       if (!strncasecmp(word, vmu->context, wordlen)) {
11162          if (context && strcmp(context, vmu->context) && ++which > state)
11163             return ast_strdup(vmu->context);
11164          /* ignore repeated contexts ? */
11165          context = vmu->context;
11166       }
11167    }
11168    return NULL;
11169 }
11170 
11171 /*! \brief Show a list of voicemail users in the CLI */
11172 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11173 {
11174    struct ast_vm_user *vmu;
11175 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11176    const char *context = NULL;
11177    int users_counter = 0;
11178 
11179    switch (cmd) {
11180    case CLI_INIT:
11181       e->command = "voicemail show users";
11182       e->usage =
11183          "Usage: voicemail show users [for <context>]\n"
11184          "       Lists all mailboxes currently set up\n";
11185       return NULL;
11186    case CLI_GENERATE:
11187       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11188    }  
11189 
11190    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11191       return CLI_SHOWUSAGE;
11192    if (a->argc == 5) {
11193       if (strcmp(a->argv[3],"for"))
11194          return CLI_SHOWUSAGE;
11195       context = a->argv[4];
11196    }
11197 
11198    if (ast_check_realtime("voicemail")) {
11199       if (!context) {
11200          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11201          return CLI_SHOWUSAGE;
11202       }
11203       return show_users_realtime(a->fd, context);
11204    }
11205 
11206    AST_LIST_LOCK(&users);
11207    if (AST_LIST_EMPTY(&users)) {
11208       ast_cli(a->fd, "There are no voicemail users currently defined\n");
11209       AST_LIST_UNLOCK(&users);
11210       return CLI_FAILURE;
11211    }
11212    if (!context) {
11213       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11214    } else {
11215       int count = 0;
11216       AST_LIST_TRAVERSE(&users, vmu, list) {
11217          if (!strcmp(context, vmu->context)) {
11218             count++;
11219             break;
11220          }
11221       }
11222       if (count) {
11223          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11224       } else {
11225          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11226          AST_LIST_UNLOCK(&users);
11227          return CLI_FAILURE;
11228       }
11229    }
11230    AST_LIST_TRAVERSE(&users, vmu, list) {
11231       int newmsgs = 0, oldmsgs = 0;
11232       char count[12], tmp[256] = "";
11233 
11234       if (!context || !strcmp(context, vmu->context)) {
11235          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11236          inboxcount(tmp, &newmsgs, &oldmsgs);
11237          snprintf(count, sizeof(count), "%d", newmsgs);
11238          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11239          users_counter++;
11240       }
11241    }
11242    AST_LIST_UNLOCK(&users);
11243    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11244    return CLI_SUCCESS;
11245 }
11246 
11247 /*! \brief Show a list of voicemail zones in the CLI */
11248 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11249 {
11250    struct vm_zone *zone;
11251 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11252    char *res = CLI_SUCCESS;
11253 
11254    switch (cmd) {
11255    case CLI_INIT:
11256       e->command = "voicemail show zones";
11257       e->usage =
11258          "Usage: voicemail show zones\n"
11259          "       Lists zone message formats\n";
11260       return NULL;
11261    case CLI_GENERATE:
11262       return NULL;
11263    }
11264 
11265    if (a->argc != 3)
11266       return CLI_SHOWUSAGE;
11267 
11268    AST_LIST_LOCK(&zones);
11269    if (!AST_LIST_EMPTY(&zones)) {
11270       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11271       AST_LIST_TRAVERSE(&zones, zone, list) {
11272          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11273       }
11274    } else {
11275       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11276       res = CLI_FAILURE;
11277    }
11278    AST_LIST_UNLOCK(&zones);
11279 
11280    return res;
11281 }
11282 
11283 /*! \brief Reload voicemail configuration from the CLI */
11284 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11285 {
11286    switch (cmd) {
11287    case CLI_INIT:
11288       e->command = "voicemail reload";
11289       e->usage =
11290          "Usage: voicemail reload\n"
11291          "       Reload voicemail configuration\n";
11292       return NULL;
11293    case CLI_GENERATE:
11294       return NULL;
11295    }
11296 
11297    if (a->argc != 2)
11298       return CLI_SHOWUSAGE;
11299 
11300    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
11301    load_config(1);
11302    
11303    return CLI_SUCCESS;
11304 }
11305 
11306 static struct ast_cli_entry cli_voicemail[] = {
11307    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11308    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11309    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11310 };
11311 
11312 #ifdef IMAP_STORAGE
11313    #define DATA_EXPORT_VM_USERS(USER)              \
11314       USER(ast_vm_user, context, AST_DATA_STRING)        \
11315       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11316       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11317       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11318       USER(ast_vm_user, email, AST_DATA_STRING)       \
11319       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11320       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11321       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11322       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11323       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11324       USER(ast_vm_user, language, AST_DATA_STRING)       \
11325       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11326       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11327       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11328       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11329       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11330       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11331       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11332       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11333       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11334       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11335       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11336       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
11337       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
11338       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
11339       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11340 #else
11341    #define DATA_EXPORT_VM_USERS(USER)              \
11342       USER(ast_vm_user, context, AST_DATA_STRING)        \
11343       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
11344       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
11345       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
11346       USER(ast_vm_user, email, AST_DATA_STRING)       \
11347       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
11348       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
11349       USER(ast_vm_user, pager, AST_DATA_STRING)       \
11350       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
11351       USER(ast_vm_user, mailcmd, AST_DATA_STRING)        \
11352       USER(ast_vm_user, language, AST_DATA_STRING)       \
11353       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
11354       USER(ast_vm_user, callback, AST_DATA_STRING)       \
11355       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
11356       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
11357       USER(ast_vm_user, exit, AST_DATA_STRING)        \
11358       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
11359       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
11360       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
11361       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
11362       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
11363       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
11364       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11365 #endif
11366 
11367 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
11368 
11369 #define DATA_EXPORT_VM_ZONES(ZONE)        \
11370    ZONE(vm_zone, name, AST_DATA_STRING)      \
11371    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
11372    ZONE(vm_zone, msg_format, AST_DATA_STRING)
11373 
11374 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
11375 
11376 /*!
11377  * \internal
11378  * \brief Add voicemail user to the data_root.
11379  * \param[in] search The search tree.
11380  * \param[in] data_root The main result node.
11381  * \param[in] user The voicemail user.
11382  */
11383 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11384     struct ast_data *data_root, struct ast_vm_user *user)
11385 {
11386    struct ast_data *data_user, *data_zone;
11387    struct ast_data *data_state;
11388    struct vm_zone *zone = NULL;
11389    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11390    char ext_context[256] = "";
11391 
11392    data_user = ast_data_add_node(data_root, "user");
11393    if (!data_user) {
11394       return -1;
11395    }
11396 
11397    ast_data_add_structure(ast_vm_user, data_user, user);
11398 
11399    AST_LIST_LOCK(&zones);
11400    AST_LIST_TRAVERSE(&zones, zone, list) {
11401       if (!strcmp(zone->name, user->zonetag)) {
11402          break;
11403       }
11404    }
11405    AST_LIST_UNLOCK(&zones);
11406 
11407    /* state */
11408    data_state = ast_data_add_node(data_user, "state");
11409    if (!data_state) {
11410       return -1;
11411    }
11412    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11413    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11414    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11415    ast_data_add_int(data_state, "newmsg", newmsg);
11416    ast_data_add_int(data_state, "oldmsg", oldmsg);
11417 
11418    if (zone) {
11419       data_zone = ast_data_add_node(data_user, "zone");
11420       ast_data_add_structure(vm_zone, data_zone, zone);
11421    }
11422 
11423    if (!ast_data_search_match(search, data_user)) {
11424       ast_data_remove_node(data_root, data_user);
11425    }
11426 
11427    return 0;
11428 }
11429 
11430 static int vm_users_data_provider_get(const struct ast_data_search *search,
11431    struct ast_data *data_root)
11432 {
11433    struct ast_vm_user *user;
11434 
11435    AST_LIST_LOCK(&users);
11436    AST_LIST_TRAVERSE(&users, user, list) {
11437       vm_users_data_provider_get_helper(search, data_root, user);
11438    }
11439    AST_LIST_UNLOCK(&users);
11440 
11441    return 0;
11442 }
11443 
11444 static const struct ast_data_handler vm_users_data_provider = {
11445    .version = AST_DATA_HANDLER_VERSION,
11446    .get = vm_users_data_provider_get
11447 };
11448 
11449 static const struct ast_data_entry vm_data_providers[] = {
11450    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11451 };
11452 
11453 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
11454 {
11455    int new = 0, old = 0, urgent = 0;
11456 
11457    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11458 
11459    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11460       mwi_sub->old_urgent = urgent;
11461       mwi_sub->old_new = new;
11462       mwi_sub->old_old = old;
11463       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11464       run_externnotify(NULL, mwi_sub->mailbox, NULL);
11465    }
11466 }
11467 
11468 static void poll_subscribed_mailboxes(void)
11469 {
11470    struct mwi_sub *mwi_sub;
11471 
11472    AST_RWLIST_RDLOCK(&mwi_subs);
11473    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11474       if (!ast_strlen_zero(mwi_sub->mailbox)) {
11475          poll_subscribed_mailbox(mwi_sub);
11476       }
11477    }
11478    AST_RWLIST_UNLOCK(&mwi_subs);
11479 }
11480 
11481 static void *mb_poll_thread(void *data)
11482 {
11483    while (poll_thread_run) {
11484       struct timespec ts = { 0, };
11485       struct timeval wait;
11486 
11487       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11488       ts.tv_sec = wait.tv_sec;
11489       ts.tv_nsec = wait.tv_usec * 1000;
11490 
11491       ast_mutex_lock(&poll_lock);
11492       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11493       ast_mutex_unlock(&poll_lock);
11494 
11495       if (!poll_thread_run)
11496          break;
11497 
11498       poll_subscribed_mailboxes();
11499    }
11500 
11501    return NULL;
11502 }
11503 
11504 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11505 {
11506    ast_free(mwi_sub);
11507 }
11508 
11509 static int handle_unsubscribe(void *datap)
11510 {
11511    struct mwi_sub *mwi_sub;
11512    uint32_t *uniqueid = datap;
11513    
11514    AST_RWLIST_WRLOCK(&mwi_subs);
11515    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11516       if (mwi_sub->uniqueid == *uniqueid) {
11517          AST_LIST_REMOVE_CURRENT(entry);
11518          break;
11519       }
11520    }
11521    AST_RWLIST_TRAVERSE_SAFE_END
11522    AST_RWLIST_UNLOCK(&mwi_subs);
11523 
11524    if (mwi_sub)
11525       mwi_sub_destroy(mwi_sub);
11526 
11527    ast_free(uniqueid);  
11528    return 0;
11529 }
11530 
11531 static int handle_subscribe(void *datap)
11532 {
11533    unsigned int len;
11534    struct mwi_sub *mwi_sub;
11535    struct mwi_sub_task *p = datap;
11536 
11537    len = sizeof(*mwi_sub);
11538    if (!ast_strlen_zero(p->mailbox))
11539       len += strlen(p->mailbox);
11540 
11541    if (!ast_strlen_zero(p->context))
11542       len += strlen(p->context) + 1; /* Allow for seperator */
11543 
11544    if (!(mwi_sub = ast_calloc(1, len)))
11545       return -1;
11546 
11547    mwi_sub->uniqueid = p->uniqueid;
11548    if (!ast_strlen_zero(p->mailbox))
11549       strcpy(mwi_sub->mailbox, p->mailbox);
11550 
11551    if (!ast_strlen_zero(p->context)) {
11552       strcat(mwi_sub->mailbox, "@");
11553       strcat(mwi_sub->mailbox, p->context);
11554    }
11555 
11556    AST_RWLIST_WRLOCK(&mwi_subs);
11557    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11558    AST_RWLIST_UNLOCK(&mwi_subs);
11559    ast_free((void *) p->mailbox);
11560    ast_free((void *) p->context);
11561    ast_free(p);
11562    poll_subscribed_mailbox(mwi_sub);
11563    return 0;
11564 }
11565 
11566 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11567 {
11568    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11569 
11570    if (!uniqueid) {
11571       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11572       return;
11573    }
11574 
11575    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11576       ast_free(uniqueid);
11577       return;
11578    }
11579 
11580    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
11581       ast_free(uniqueid);
11582       return;
11583    }
11584 
11585    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11586    *uniqueid = u;
11587    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11588       ast_free(uniqueid);
11589    }
11590 }
11591 
11592 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11593 {
11594    struct mwi_sub_task *mwist;
11595    
11596    if (ast_event_get_type(event) != AST_EVENT_SUB)
11597       return;
11598 
11599    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
11600       return;
11601 
11602    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11603       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11604       return;
11605    }
11606    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
11607    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
11608    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
11609    
11610    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11611       ast_free(mwist);
11612    }
11613 }
11614 
11615 static void start_poll_thread(void)
11616 {
11617    int errcode;
11618    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11619       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11620       AST_EVENT_IE_END);
11621 
11622    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11623       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
11624       AST_EVENT_IE_END);
11625 
11626    if (mwi_sub_sub)
11627       ast_event_report_subs(mwi_sub_sub);
11628 
11629    poll_thread_run = 1;
11630 
11631    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11632       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11633    }
11634 }
11635 
11636 static void stop_poll_thread(void)
11637 {
11638    poll_thread_run = 0;
11639 
11640    if (mwi_sub_sub) {
11641       ast_event_unsubscribe(mwi_sub_sub);
11642       mwi_sub_sub = NULL;
11643    }
11644 
11645    if (mwi_unsub_sub) {
11646       ast_event_unsubscribe(mwi_unsub_sub);
11647       mwi_unsub_sub = NULL;
11648    }
11649 
11650    ast_mutex_lock(&poll_lock);
11651    ast_cond_signal(&poll_cond);
11652    ast_mutex_unlock(&poll_lock);
11653 
11654    pthread_join(poll_thread, NULL);
11655 
11656    poll_thread = AST_PTHREADT_NULL;
11657 }
11658 
11659 /*! \brief Manager list voicemail users command */
11660 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11661 {
11662    struct ast_vm_user *vmu = NULL;
11663    const char *id = astman_get_header(m, "ActionID");
11664    char actionid[128] = "";
11665 
11666    if (!ast_strlen_zero(id))
11667       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11668 
11669    AST_LIST_LOCK(&users);
11670 
11671    if (AST_LIST_EMPTY(&users)) {
11672       astman_send_ack(s, m, "There are no voicemail users currently defined.");
11673       AST_LIST_UNLOCK(&users);
11674       return RESULT_SUCCESS;
11675    }
11676    
11677    astman_send_ack(s, m, "Voicemail user list will follow");
11678    
11679    AST_LIST_TRAVERSE(&users, vmu, list) {
11680       char dirname[256];
11681 
11682 #ifdef IMAP_STORAGE
11683       int new, old;
11684       inboxcount(vmu->mailbox, &new, &old);
11685 #endif
11686       
11687       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11688       astman_append(s,
11689          "%s"
11690          "Event: VoicemailUserEntry\r\n"
11691          "VMContext: %s\r\n"
11692          "VoiceMailbox: %s\r\n"
11693          "Fullname: %s\r\n"
11694          "Email: %s\r\n"
11695          "Pager: %s\r\n"
11696          "ServerEmail: %s\r\n"
11697          "MailCommand: %s\r\n"
11698          "Language: %s\r\n"
11699          "TimeZone: %s\r\n"
11700          "Callback: %s\r\n"
11701          "Dialout: %s\r\n"
11702          "UniqueID: %s\r\n"
11703          "ExitContext: %s\r\n"
11704          "SayDurationMinimum: %d\r\n"
11705          "SayEnvelope: %s\r\n"
11706          "SayCID: %s\r\n"
11707          "AttachMessage: %s\r\n"
11708          "AttachmentFormat: %s\r\n"
11709          "DeleteMessage: %s\r\n"
11710          "VolumeGain: %.2f\r\n"
11711          "CanReview: %s\r\n"
11712          "CallOperator: %s\r\n"
11713          "MaxMessageCount: %d\r\n"
11714          "MaxMessageLength: %d\r\n"
11715          "NewMessageCount: %d\r\n"
11716 #ifdef IMAP_STORAGE
11717          "OldMessageCount: %d\r\n"
11718          "IMAPUser: %s\r\n"
11719 #endif
11720          "\r\n",
11721          actionid,
11722          vmu->context,
11723          vmu->mailbox,
11724          vmu->fullname,
11725          vmu->email,
11726          vmu->pager,
11727          vmu->serveremail,
11728          vmu->mailcmd,
11729          vmu->language,
11730          vmu->zonetag,
11731          vmu->callback,
11732          vmu->dialout,
11733          vmu->uniqueid,
11734          vmu->exit,
11735          vmu->saydurationm,
11736          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11737          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11738          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11739          vmu->attachfmt,
11740          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11741          vmu->volgain,
11742          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11743          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11744          vmu->maxmsg,
11745          vmu->maxsecs,
11746 #ifdef IMAP_STORAGE
11747          new, old, vmu->imapuser
11748 #else
11749          count_messages(vmu, dirname)
11750 #endif
11751          );
11752    }     
11753    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11754 
11755    AST_LIST_UNLOCK(&users);
11756 
11757    return RESULT_SUCCESS;
11758 }
11759 
11760 /*! \brief Free the users structure. */
11761 static void free_vm_users(void) 
11762 {
11763    struct ast_vm_user *current;
11764    AST_LIST_LOCK(&users);
11765    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11766       ast_set_flag(current, VM_ALLOCED);
11767       free_user(current);
11768    }
11769    AST_LIST_UNLOCK(&users);
11770 }
11771 
11772 /*! \brief Free the zones structure. */
11773 static void free_vm_zones(void)
11774 {
11775    struct vm_zone *zcur;
11776    AST_LIST_LOCK(&zones);
11777    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11778       free_zone(zcur);
11779    AST_LIST_UNLOCK(&zones);
11780 }
11781 
11782 static const char *substitute_escapes(const char *value)
11783 {
11784    char *current;
11785 
11786    /* Add 16 for fudge factor */
11787    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11788 
11789    ast_str_reset(str);
11790    
11791    /* Substitute strings \r, \n, and \t into the appropriate characters */
11792    for (current = (char *) value; *current; current++) {
11793       if (*current == '\\') {
11794          current++;
11795          if (!*current) {
11796             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11797             break;
11798          }
11799          switch (*current) {
11800          case '\\':
11801             ast_str_append(&str, 0, "\\");
11802             break;
11803          case 'r':
11804             ast_str_append(&str, 0, "\r");
11805             break;
11806          case 'n':
11807 #ifdef IMAP_STORAGE
11808             if (!str->used || str->str[str->used - 1] != '\r') {
11809                ast_str_append(&str, 0, "\r");
11810             }
11811 #endif
11812             ast_str_append(&str, 0, "\n");
11813             break;
11814          case 't':
11815             ast_str_append(&str, 0, "\t");
11816             break;
11817          default:
11818             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11819             break;
11820          }
11821       } else {
11822          ast_str_append(&str, 0, "%c", *current);
11823       }
11824    }
11825 
11826    return ast_str_buffer(str);
11827 }
11828 
11829 static int load_config(int reload)
11830 {
11831    struct ast_config *cfg, *ucfg;
11832    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11833    int res;
11834 
11835    ast_unload_realtime("voicemail");
11836    ast_unload_realtime("voicemail_data");
11837 
11838    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11839       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11840          return 0;
11841       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11842          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11843          ucfg = NULL;
11844       }
11845       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11846       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11847          ast_config_destroy(ucfg);
11848          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11849          return 0;
11850       }
11851    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11852       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
11853       return 0;
11854    } else {
11855       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11856       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11857          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
11858          ucfg = NULL;
11859       }
11860    }
11861 
11862    res = actual_load_config(reload, cfg, ucfg);
11863 
11864    ast_config_destroy(cfg);
11865    ast_config_destroy(ucfg);
11866 
11867    return res;
11868 }
11869 
11870 #ifdef TEST_FRAMEWORK
11871 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11872 {
11873    ast_unload_realtime("voicemail");
11874    ast_unload_realtime("voicemail_data");
11875    return actual_load_config(reload, cfg, ucfg);
11876 }
11877 #endif
11878 
11879 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11880 {
11881    struct ast_vm_user *current;
11882    char *cat;
11883    struct ast_variable *var;
11884    const char *val;
11885    char *q, *stringp, *tmp;
11886    int x;
11887    int tmpadsi[4];
11888    char secretfn[PATH_MAX] = "";
11889 
11890 #ifdef IMAP_STORAGE
11891    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11892 #endif
11893    /* set audio control prompts */
11894    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11895    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11896    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11897    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11898    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11899 
11900    /* Free all the users structure */  
11901    free_vm_users();
11902 
11903    /* Free all the zones structure */
11904    free_vm_zones();
11905 
11906    AST_LIST_LOCK(&users);  
11907 
11908    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11909    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11910 
11911    if (cfg) {
11912       /* General settings */
11913 
11914       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11915          val = "default";
11916       ast_copy_string(userscontext, val, sizeof(userscontext));
11917       /* Attach voice message to mail message ? */
11918       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
11919          val = "yes";
11920       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
11921 
11922       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11923          val = "no";
11924       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11925 
11926       volgain = 0.0;
11927       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11928          sscanf(val, "%30lf", &volgain);
11929 
11930 #ifdef ODBC_STORAGE
11931       strcpy(odbc_database, "asterisk");
11932       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11933          ast_copy_string(odbc_database, val, sizeof(odbc_database));
11934       }
11935       strcpy(odbc_table, "voicemessages");
11936       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11937          ast_copy_string(odbc_table, val, sizeof(odbc_table));
11938       }
11939 #endif      
11940       /* Mail command */
11941       strcpy(mailcmd, SENDMAIL);
11942       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11943          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11944 
11945       maxsilence = 0;
11946       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11947          maxsilence = atoi(val);
11948          if (maxsilence > 0)
11949             maxsilence *= 1000;
11950       }
11951       
11952       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11953          maxmsg = MAXMSG;
11954       } else {
11955          maxmsg = atoi(val);
11956          if (maxmsg < 0) {
11957             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11958             maxmsg = MAXMSG;
11959          } else if (maxmsg > MAXMSGLIMIT) {
11960             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11961             maxmsg = MAXMSGLIMIT;
11962          }
11963       }
11964 
11965       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11966          maxdeletedmsg = 0;
11967       } else {
11968          if (sscanf(val, "%30d", &x) == 1)
11969             maxdeletedmsg = x;
11970          else if (ast_true(val))
11971             maxdeletedmsg = MAXMSG;
11972          else
11973             maxdeletedmsg = 0;
11974 
11975          if (maxdeletedmsg < 0) {
11976             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11977             maxdeletedmsg = MAXMSG;
11978          } else if (maxdeletedmsg > MAXMSGLIMIT) {
11979             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11980             maxdeletedmsg = MAXMSGLIMIT;
11981          }
11982       }
11983 
11984       /* Load date format config for voicemail mail */
11985       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11986          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11987       }
11988 
11989       /* Load date format config for voicemail pager mail */
11990       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11991          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11992       }
11993 
11994       /* External password changing command */
11995       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11996          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11997          pwdchange = PWDCHANGE_EXTERNAL;
11998       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11999          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
12000          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
12001       }
12002  
12003       /* External password validation command */
12004       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
12005          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
12006          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
12007       }
12008 
12009 #ifdef IMAP_STORAGE
12010       /* IMAP server address */
12011       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
12012          ast_copy_string(imapserver, val, sizeof(imapserver));
12013       } else {
12014          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
12015       }
12016       /* IMAP server port */
12017       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
12018          ast_copy_string(imapport, val, sizeof(imapport));
12019       } else {
12020          ast_copy_string(imapport, "143", sizeof(imapport));
12021       }
12022       /* IMAP server flags */
12023       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
12024          ast_copy_string(imapflags, val, sizeof(imapflags));
12025       }
12026       /* IMAP server master username */
12027       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
12028          ast_copy_string(authuser, val, sizeof(authuser));
12029       }
12030       /* IMAP server master password */
12031       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
12032          ast_copy_string(authpassword, val, sizeof(authpassword));
12033       }
12034       /* Expunge on exit */
12035       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
12036          if (ast_false(val))
12037             expungeonhangup = 0;
12038          else
12039             expungeonhangup = 1;
12040       } else {
12041          expungeonhangup = 1;
12042       }
12043       /* IMAP voicemail folder */
12044       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12045          ast_copy_string(imapfolder, val, sizeof(imapfolder));
12046       } else {
12047          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12048       }
12049       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12050          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12051       }
12052       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12053          imapgreetings = ast_true(val);
12054       } else {
12055          imapgreetings = 0;
12056       }
12057       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12058          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12059       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12060          /* Also support greetingsfolder as documented in voicemail.conf.sample */
12061          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12062       } else {
12063          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12064       }
12065 
12066       /* There is some very unorthodox casting done here. This is due
12067        * to the way c-client handles the argument passed in. It expects a 
12068        * void pointer and casts the pointer directly to a long without
12069        * first dereferencing it. */
12070       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12071          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12072       } else {
12073          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12074       }
12075 
12076       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12077          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12078       } else {
12079          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12080       }
12081 
12082       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12083          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12084       } else {
12085          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12086       }
12087 
12088       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12089          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12090       } else {
12091          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12092       }
12093 
12094       /* Increment configuration version */
12095       imapversion++;
12096 #endif
12097       /* External voicemail notify application */
12098       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12099          ast_copy_string(externnotify, val, sizeof(externnotify));
12100          ast_debug(1, "found externnotify: %s\n", externnotify);
12101       } else {
12102          externnotify[0] = '\0';
12103       }
12104 
12105       /* SMDI voicemail notification */
12106       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12107          ast_debug(1, "Enabled SMDI voicemail notification\n");
12108          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12109             smdi_iface = ast_smdi_interface_find(val);
12110          } else {
12111             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12112             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12113          }
12114          if (!smdi_iface) {
12115             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12116          } 
12117       }
12118 
12119       /* Silence treshold */
12120       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
12121       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12122          silencethreshold = atoi(val);
12123       
12124       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
12125          val = ASTERISK_USERNAME;
12126       ast_copy_string(serveremail, val, sizeof(serveremail));
12127       
12128       vmmaxsecs = 0;
12129       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12130          if (sscanf(val, "%30d", &x) == 1) {
12131             vmmaxsecs = x;
12132          } else {
12133             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12134          }
12135       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12136          static int maxmessage_deprecate = 0;
12137          if (maxmessage_deprecate == 0) {
12138             maxmessage_deprecate = 1;
12139             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12140          }
12141          if (sscanf(val, "%30d", &x) == 1) {
12142             vmmaxsecs = x;
12143          } else {
12144             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12145          }
12146       }
12147 
12148       vmminsecs = 0;
12149       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12150          if (sscanf(val, "%30d", &x) == 1) {
12151             vmminsecs = x;
12152             if (maxsilence / 1000 >= vmminsecs) {
12153                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12154             }
12155          } else {
12156             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12157          }
12158       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12159          static int maxmessage_deprecate = 0;
12160          if (maxmessage_deprecate == 0) {
12161             maxmessage_deprecate = 1;
12162             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12163          }
12164          if (sscanf(val, "%30d", &x) == 1) {
12165             vmminsecs = x;
12166             if (maxsilence / 1000 >= vmminsecs) {
12167                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12168             }
12169          } else {
12170             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12171          }
12172       }
12173 
12174       val = ast_variable_retrieve(cfg, "general", "format");
12175       if (!val) {
12176          val = "wav";   
12177       } else {
12178          tmp = ast_strdupa(val);
12179          val = ast_format_str_reduce(tmp);
12180          if (!val) {
12181             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12182             val = "wav";
12183          }
12184       }
12185       ast_copy_string(vmfmts, val, sizeof(vmfmts));
12186 
12187       skipms = 3000;
12188       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12189          if (sscanf(val, "%30d", &x) == 1) {
12190             maxgreet = x;
12191          } else {
12192             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12193          }
12194       }
12195 
12196       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12197          if (sscanf(val, "%30d", &x) == 1) {
12198             skipms = x;
12199          } else {
12200             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12201          }
12202       }
12203 
12204       maxlogins = 3;
12205       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12206          if (sscanf(val, "%30d", &x) == 1) {
12207             maxlogins = x;
12208          } else {
12209             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12210          }
12211       }
12212 
12213       minpassword = MINPASSWORD;
12214       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12215          if (sscanf(val, "%30d", &x) == 1) {
12216             minpassword = x;
12217          } else {
12218             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
12219          }
12220       }
12221 
12222       /* Force new user to record name ? */
12223       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
12224          val = "no";
12225       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12226 
12227       /* Force new user to record greetings ? */
12228       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
12229          val = "no";
12230       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12231 
12232       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12233          ast_debug(1, "VM_CID Internal context string: %s\n", val);
12234          stringp = ast_strdupa(val);
12235          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12236             if (!ast_strlen_zero(stringp)) {
12237                q = strsep(&stringp, ",");
12238                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12239                   q++;
12240                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12241                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12242             } else {
12243                cidinternalcontexts[x][0] = '\0';
12244             }
12245          }
12246       }
12247       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12248          ast_debug(1, "VM Review Option disabled globally\n");
12249          val = "no";
12250       }
12251       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
12252 
12253       /* Temporary greeting reminder */
12254       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12255          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12256          val = "no";
12257       } else {
12258          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12259       }
12260       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12261       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12262          ast_debug(1, "VM next message wrap disabled globally\n");
12263          val = "no";
12264       }
12265       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
12266 
12267       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12268          ast_debug(1, "VM Operator break disabled globally\n");
12269          val = "no";
12270       }
12271       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
12272 
12273       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12274          ast_debug(1, "VM CID Info before msg disabled globally\n");
12275          val = "no";
12276       } 
12277       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
12278 
12279       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12280          ast_debug(1, "Send Voicemail msg disabled globally\n");
12281          val = "no";
12282       }
12283       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12284    
12285       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12286          ast_debug(1, "ENVELOPE before msg enabled globally\n");
12287          val = "yes";
12288       }
12289       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
12290 
12291       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12292          ast_debug(1, "Move Heard enabled globally\n");
12293          val = "yes";
12294       }
12295       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
12296 
12297       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12298          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12299          val = "no";
12300       }
12301       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
12302 
12303       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12304          ast_debug(1, "Duration info before msg enabled globally\n");
12305          val = "yes";
12306       }
12307       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
12308 
12309       saydurationminfo = 2;
12310       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12311          if (sscanf(val, "%30d", &x) == 1) {
12312             saydurationminfo = x;
12313          } else {
12314             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12315          }
12316       }
12317 
12318       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12319          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12320          val = "no";
12321       }
12322       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12323 
12324       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12325          ast_copy_string(dialcontext, val, sizeof(dialcontext));
12326          ast_debug(1, "found dialout context: %s\n", dialcontext);
12327       } else {
12328          dialcontext[0] = '\0';  
12329       }
12330       
12331       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12332          ast_copy_string(callcontext, val, sizeof(callcontext));
12333          ast_debug(1, "found callback context: %s\n", callcontext);
12334       } else {
12335          callcontext[0] = '\0';
12336       }
12337 
12338       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12339          ast_copy_string(exitcontext, val, sizeof(exitcontext));
12340          ast_debug(1, "found operator context: %s\n", exitcontext);
12341       } else {
12342          exitcontext[0] = '\0';
12343       }
12344       
12345       /* load password sounds configuration */
12346       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12347          ast_copy_string(vm_password, val, sizeof(vm_password));
12348       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12349          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12350       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12351          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12352       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12353          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12354       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12355          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12356       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12357          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12358       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12359          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12360       }
12361       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12362          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12363       }
12364       /* load configurable audio prompts */
12365       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12366          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12367       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12368          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12369       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12370          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12371       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12372          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12373       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12374          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12375 
12376       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
12377          val = "no";
12378       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
12379 
12380       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12381          val = "voicemail.conf";
12382       }
12383       if (!(strcmp(val, "spooldir"))) {
12384          passwordlocation = OPT_PWLOC_SPOOLDIR;
12385       } else {
12386          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12387       }
12388 
12389       poll_freq = DEFAULT_POLL_FREQ;
12390       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12391          if (sscanf(val, "%30u", &poll_freq) != 1) {
12392             poll_freq = DEFAULT_POLL_FREQ;
12393             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12394          }
12395       }
12396 
12397       poll_mailboxes = 0;
12398       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12399          poll_mailboxes = ast_true(val);
12400 
12401       memset(fromstring, 0, sizeof(fromstring));
12402       memset(pagerfromstring, 0, sizeof(pagerfromstring));
12403       strcpy(charset, "ISO-8859-1");
12404       if (emailbody) {
12405          ast_free(emailbody);
12406          emailbody = NULL;
12407       }
12408       if (emailsubject) {
12409          ast_free(emailsubject);
12410          emailsubject = NULL;
12411       }
12412       if (pagerbody) {
12413          ast_free(pagerbody);
12414          pagerbody = NULL;
12415       }
12416       if (pagersubject) {
12417          ast_free(pagersubject);
12418          pagersubject = NULL;
12419       }
12420       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12421          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12422       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12423          ast_copy_string(fromstring, val, sizeof(fromstring));
12424       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12425          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12426       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12427          ast_copy_string(charset, val, sizeof(charset));
12428       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12429          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12430          for (x = 0; x < 4; x++) {
12431             memcpy(&adsifdn[x], &tmpadsi[x], 1);
12432          }
12433       }
12434       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12435          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12436          for (x = 0; x < 4; x++) {
12437             memcpy(&adsisec[x], &tmpadsi[x], 1);
12438          }
12439       }
12440       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12441          if (atoi(val)) {
12442             adsiver = atoi(val);
12443          }
12444       }
12445       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12446          ast_copy_string(zonetag, val, sizeof(zonetag));
12447       }
12448       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12449          ast_copy_string(locale, val, sizeof(locale));
12450       }
12451       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12452          emailsubject = ast_strdup(substitute_escapes(val));
12453       }
12454       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12455          emailbody = ast_strdup(substitute_escapes(val));
12456       }
12457       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12458          pagersubject = ast_strdup(substitute_escapes(val));
12459       }
12460       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12461          pagerbody = ast_strdup(substitute_escapes(val));
12462       }
12463 
12464       /* load mailboxes from users.conf */
12465       if (ucfg) { 
12466          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12467             if (!strcasecmp(cat, "general")) {
12468                continue;
12469             }
12470             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12471                continue;
12472             if ((current = find_or_create(userscontext, cat))) {
12473                populate_defaults(current);
12474                apply_options_full(current, ast_variable_browse(ucfg, cat));
12475                ast_copy_string(current->context, userscontext, sizeof(current->context));
12476                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12477                   current->passwordlocation = OPT_PWLOC_USERSCONF;
12478                }
12479 
12480                switch (current->passwordlocation) {
12481                case OPT_PWLOC_SPOOLDIR:
12482                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12483                   read_password_from_file(secretfn, current->password, sizeof(current->password));
12484                }
12485             }
12486          }
12487       }
12488 
12489       /* load mailboxes from voicemail.conf */
12490       cat = ast_category_browse(cfg, NULL);
12491       while (cat) {
12492          if (strcasecmp(cat, "general")) {
12493             var = ast_variable_browse(cfg, cat);
12494             if (strcasecmp(cat, "zonemessages")) {
12495                /* Process mailboxes in this context */
12496                while (var) {
12497                   append_mailbox(cat, var->name, var->value);
12498                   var = var->next;
12499                }
12500             } else {
12501                /* Timezones in this context */
12502                while (var) {
12503                   struct vm_zone *z;
12504                   if ((z = ast_malloc(sizeof(*z)))) {
12505                      char *msg_format, *tzone;
12506                      msg_format = ast_strdupa(var->value);
12507                      tzone = strsep(&msg_format, "|,");
12508                      if (msg_format) {
12509                         ast_copy_string(z->name, var->name, sizeof(z->name));
12510                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12511                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12512                         AST_LIST_LOCK(&zones);
12513                         AST_LIST_INSERT_HEAD(&zones, z, list);
12514                         AST_LIST_UNLOCK(&zones);
12515                      } else {
12516                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12517                         ast_free(z);
12518                      }
12519                   } else {
12520                      AST_LIST_UNLOCK(&users);
12521                      return -1;
12522                   }
12523                   var = var->next;
12524                }
12525             }
12526          }
12527          cat = ast_category_browse(cfg, cat);
12528       }
12529 
12530       AST_LIST_UNLOCK(&users);
12531 
12532       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12533          start_poll_thread();
12534       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12535          stop_poll_thread();;
12536 
12537       return 0;
12538    } else {
12539       AST_LIST_UNLOCK(&users);
12540       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12541       return 0;
12542    }
12543 }
12544 
12545 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12546 {
12547    int res = -1;
12548    char dir[PATH_MAX];
12549    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12550    ast_debug(2, "About to try retrieving name file %s\n", dir);
12551    RETRIEVE(dir, -1, mailbox, context);
12552    if (ast_fileexists(dir, NULL, NULL)) {
12553       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12554    }
12555    DISPOSE(dir, -1);
12556    return res;
12557 }
12558 
12559 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12560    struct ast_config *pwconf;
12561    struct ast_flags config_flags = { 0 };
12562 
12563    pwconf = ast_config_load(secretfn, config_flags);
12564    if (pwconf) {
12565       const char *val = ast_variable_retrieve(pwconf, "general", "password");
12566       if (val) {
12567          ast_copy_string(password, val, passwordlen);
12568          ast_config_destroy(pwconf);
12569          return;
12570       }
12571       ast_config_destroy(pwconf);
12572    }
12573    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12574 }
12575 
12576 static int write_password_to_file(const char *secretfn, const char *password) {
12577    struct ast_config *conf;
12578    struct ast_category *cat;
12579    struct ast_variable *var;
12580    int res = -1;
12581 
12582    if (!(conf = ast_config_new())) {
12583       ast_log(LOG_ERROR, "Error creating new config structure\n");
12584       return res;
12585    }
12586    if (!(cat = ast_category_new("general", "", 1))) {
12587       ast_log(LOG_ERROR, "Error creating new category structure\n");
12588       ast_config_destroy(conf);
12589       return res;
12590    }
12591    if (!(var = ast_variable_new("password", password, ""))) {
12592       ast_log(LOG_ERROR, "Error creating new variable structure\n");
12593       ast_config_destroy(conf);
12594       ast_category_destroy(cat);
12595       return res;
12596    }
12597    ast_category_append(conf, cat);
12598    ast_variable_append(cat, var);
12599    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12600       res = 0;
12601    } else {
12602       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12603    }
12604 
12605    ast_config_destroy(conf);
12606    return res;
12607 }
12608 
12609 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12610 {
12611    char *context;
12612    char *args_copy;
12613    int res;
12614 
12615    if (ast_strlen_zero(data)) {
12616       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12617       return -1;
12618    }
12619 
12620    args_copy = ast_strdupa(data);
12621    if ((context = strchr(args_copy, '@'))) {
12622       *context++ = '\0';
12623    } else {
12624       context = "default";
12625    }
12626 
12627    if ((res = sayname(chan, args_copy, context) < 0)) {
12628       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12629       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12630       if (!res) {
12631          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12632       }
12633    }
12634 
12635    return res;
12636 }
12637 
12638 #ifdef TEST_FRAMEWORK
12639 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12640 {
12641    return 0;
12642 }
12643 
12644 static struct ast_frame *fake_read(struct ast_channel *ast)
12645 {
12646    return &ast_null_frame;
12647 }
12648 
12649 AST_TEST_DEFINE(test_voicemail_vmsayname)
12650 {
12651    char dir[PATH_MAX];
12652    char dir2[PATH_MAX];
12653    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12654    static const char TEST_EXTENSION[] = "1234";
12655 
12656    struct ast_channel *test_channel1 = NULL;
12657    int res = -1;
12658 
12659    static const struct ast_channel_tech fake_tech = {
12660       .write = fake_write,
12661       .read = fake_read,
12662    };
12663 
12664    switch (cmd) {
12665    case TEST_INIT:
12666       info->name = "vmsayname_exec";
12667       info->category = "/apps/app_voicemail/";
12668       info->summary = "Vmsayname unit test";
12669       info->description =
12670          "This tests passing various parameters to vmsayname";
12671       return AST_TEST_NOT_RUN;
12672    case TEST_EXECUTE:
12673       break;
12674    }
12675 
12676    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12677         NULL, NULL, 0, 0, "TestChannel1"))) {
12678       goto exit_vmsayname_test;
12679    }
12680 
12681    /* normally this is done in the channel driver */
12682    test_channel1->nativeformats = AST_FORMAT_GSM;
12683    test_channel1->writeformat = AST_FORMAT_GSM;
12684    test_channel1->rawwriteformat = AST_FORMAT_GSM;
12685    test_channel1->readformat = AST_FORMAT_GSM;
12686    test_channel1->rawreadformat = AST_FORMAT_GSM;
12687    test_channel1->tech = &fake_tech;
12688 
12689    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12690    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12691    if (!(res = vmsayname_exec(test_channel1, dir))) {
12692       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12693       if (ast_fileexists(dir, NULL, NULL)) {
12694          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12695          res = -1;
12696          goto exit_vmsayname_test;
12697       } else {
12698          /* no greeting already exists as expected, let's create one to fully test sayname */
12699          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12700             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12701             goto exit_vmsayname_test;
12702          }
12703          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12704          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12705          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12706          if ((res = symlink(dir, dir2))) {
12707             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12708             goto exit_vmsayname_test;
12709          }
12710          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12711          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12712          res = vmsayname_exec(test_channel1, dir);
12713 
12714          /* TODO: there may be a better way to do this */
12715          unlink(dir2);
12716          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12717          rmdir(dir2);
12718          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12719          rmdir(dir2);
12720       }
12721    }
12722 
12723 exit_vmsayname_test:
12724 
12725    if (test_channel1) {
12726       ast_hangup(test_channel1);
12727    }
12728 
12729    return res ? AST_TEST_FAIL : AST_TEST_PASS;
12730 }
12731 
12732 AST_TEST_DEFINE(test_voicemail_msgcount)
12733 {
12734    int i, j, res = AST_TEST_PASS, syserr;
12735    struct ast_vm_user *vmu;
12736    struct vm_state vms;
12737 #ifdef IMAP_STORAGE
12738    struct ast_channel *chan = NULL;
12739 #endif
12740    struct {
12741       char dir[256];
12742       char file[256];
12743       char txtfile[256];
12744    } tmp[3];
12745    char syscmd[256];
12746    const char origweasels[] = "tt-weasels";
12747    const char testcontext[] = "test";
12748    const char testmailbox[] = "00000000";
12749    const char testspec[] = "00000000@test";
12750    FILE *txt;
12751    int new, old, urgent;
12752    const char *folders[3] = { "Old", "Urgent", "INBOX" };
12753    const int folder2mbox[3] = { 1, 11, 0 };
12754    const int expected_results[3][12] = {
12755       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12756       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
12757       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
12758       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
12759    };
12760 
12761    switch (cmd) {
12762    case TEST_INIT:
12763       info->name = "test_voicemail_msgcount";
12764       info->category = "/apps/app_voicemail/";
12765       info->summary = "Test Voicemail status checks";
12766       info->description =
12767          "Verify that message counts are correct when retrieved through the public API";
12768       return AST_TEST_NOT_RUN;
12769    case TEST_EXECUTE:
12770       break;
12771    }
12772 
12773    /* Make sure the original path was completely empty */
12774    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12775    if ((syserr = ast_safe_system(syscmd))) {
12776       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12777          syserr > 0 ? strerror(syserr) : "unable to fork()");
12778       return AST_TEST_FAIL;
12779    }
12780 
12781 #ifdef IMAP_STORAGE
12782    if (!(chan = ast_dummy_channel_alloc())) {
12783       ast_test_status_update(test, "Unable to create dummy channel\n");
12784       return AST_TEST_FAIL;
12785    }
12786 #endif
12787 
12788    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
12789       !(vmu = find_or_create(testcontext, testmailbox))) {
12790       ast_test_status_update(test, "Cannot create vmu structure\n");
12791       ast_unreplace_sigchld();
12792 #ifdef IMAP_STORAGE
12793       chan = ast_channel_unref(chan);
12794 #endif
12795       return AST_TEST_FAIL;
12796    }
12797 
12798    populate_defaults(vmu);
12799    memset(&vms, 0, sizeof(vms));
12800 
12801    /* Create temporary voicemail */
12802    for (i = 0; i < 3; i++) {
12803       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12804       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12805       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12806 
12807       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12808          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12809             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12810          if ((syserr = ast_safe_system(syscmd))) {
12811             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12812                syserr > 0 ? strerror(syserr) : "unable to fork()");
12813             ast_unreplace_sigchld();
12814 #ifdef IMAP_STORAGE
12815             chan = ast_channel_unref(chan);
12816 #endif
12817             return AST_TEST_FAIL;
12818          }
12819       }
12820 
12821       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12822          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12823          fclose(txt);
12824       } else {
12825          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12826          res = AST_TEST_FAIL;
12827          break;
12828       }
12829       open_mailbox(&vms, vmu, folder2mbox[i]);
12830       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12831 
12832       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12833       for (j = 0; j < 3; j++) {
12834          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
12835          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12836             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12837                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12838             res = AST_TEST_FAIL;
12839          }
12840       }
12841 
12842       new = old = urgent = 0;
12843       if (ast_app_inboxcount(testspec, &new, &old)) {
12844          ast_test_status_update(test, "inboxcount returned failure\n");
12845          res = AST_TEST_FAIL;
12846       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12847          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12848             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12849          res = AST_TEST_FAIL;
12850       }
12851 
12852       new = old = urgent = 0;
12853       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12854          ast_test_status_update(test, "inboxcount2 returned failure\n");
12855          res = AST_TEST_FAIL;
12856       } else if (old != expected_results[i][6 + 0] ||
12857             urgent != expected_results[i][6 + 1] ||
12858                new != expected_results[i][6 + 2]    ) {
12859          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12860             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12861          res = AST_TEST_FAIL;
12862       }
12863 
12864       new = old = urgent = 0;
12865       for (j = 0; j < 3; j++) {
12866          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12867             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12868                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12869             res = AST_TEST_FAIL;
12870          }
12871       }
12872    }
12873 
12874    for (i = 0; i < 3; i++) {
12875       /* This is necessary if the voicemails are stored on an ODBC/IMAP
12876        * server, in which case, the rm below will not affect the
12877        * voicemails. */
12878       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12879       DISPOSE(tmp[i].dir, 0);
12880    }
12881 
12882    if (vms.deleted) {
12883       ast_free(vms.deleted);
12884    }
12885    if (vms.heard) {
12886       ast_free(vms.heard);
12887    }
12888 
12889 #ifdef IMAP_STORAGE
12890    chan = ast_channel_unref(chan);
12891 #endif
12892 
12893    /* And remove test directory */
12894    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12895    if ((syserr = ast_safe_system(syscmd))) {
12896       ast_test_status_update(test, "Unable to clear test directory: %s\n",
12897          syserr > 0 ? strerror(syserr) : "unable to fork()");
12898    }
12899 
12900    return res;
12901 }
12902 
12903 AST_TEST_DEFINE(test_voicemail_notify_endl)
12904 {
12905    int res = AST_TEST_PASS;
12906    char testcontext[] = "test";
12907    char testmailbox[] = "00000000";
12908    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12909    char attach[256], attach2[256];
12910    char buf[256] = ""; /* No line should actually be longer than 80 */
12911    struct ast_channel *chan = NULL;
12912    struct ast_vm_user *vmu, vmus = {
12913       .flags = 0,
12914    };
12915    FILE *file;
12916    struct {
12917       char *name;
12918       enum { INT, FLAGVAL, STATIC, STRPTR } type;
12919       void *location;
12920       union {
12921          int intval;
12922          char *strval;
12923       } u;
12924    } test_items[] = {
12925       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12926       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12927       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12928       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12929       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12930       { "attach2", STRPTR, attach2, .u.strval = "" },
12931       { "attach", STRPTR, attach, .u.strval = "" },
12932    };
12933    int which;
12934 
12935    switch (cmd) {
12936    case TEST_INIT:
12937       info->name = "test_voicemail_notify_endl";
12938       info->category = "/apps/app_voicemail/";
12939       info->summary = "Test Voicemail notification end-of-line";
12940       info->description =
12941          "Verify that notification emails use a consistent end-of-line character";
12942       return AST_TEST_NOT_RUN;
12943    case TEST_EXECUTE:
12944       break;
12945    }
12946 
12947    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12948    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12949 
12950    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12951       !(vmu = find_or_create(testcontext, testmailbox))) {
12952       ast_test_status_update(test, "Cannot create vmu structure\n");
12953       return AST_TEST_NOT_RUN;
12954    }
12955 
12956    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12957       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12958       return AST_TEST_NOT_RUN;
12959    }
12960 
12961    populate_defaults(vmu);
12962    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12963 #ifdef IMAP_STORAGE
12964    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12965 #endif
12966 
12967    file = tmpfile();
12968    for (which = 0; which < ARRAY_LEN(test_items); which++) {
12969       /* Kill previous test, if any */
12970       rewind(file);
12971       if (ftruncate(fileno(file), 0)) {
12972          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12973          res = AST_TEST_FAIL;
12974          break;
12975       }
12976 
12977       /* Make each change, in order, to the test mailbox */
12978       if (test_items[which].type == INT) {
12979          *((int *) test_items[which].location) = test_items[which].u.intval;
12980       } else if (test_items[which].type == FLAGVAL) {
12981          if (ast_test_flag(vmu, test_items[which].u.intval)) {
12982             ast_clear_flag(vmu, test_items[which].u.intval);
12983          } else {
12984             ast_set_flag(vmu, test_items[which].u.intval);
12985          }
12986       } else if (test_items[which].type == STATIC) {
12987          strcpy(test_items[which].location, test_items[which].u.strval);
12988       } else if (test_items[which].type == STRPTR) {
12989          test_items[which].location = test_items[which].u.strval;
12990       }
12991 
12992       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12993       rewind(file);
12994       while (fgets(buf, sizeof(buf), file)) {
12995          if (
12996 #ifdef IMAP_STORAGE
12997          buf[strlen(buf) - 2] != '\r'
12998 #else
12999          buf[strlen(buf) - 2] == '\r'
13000 #endif
13001          || buf[strlen(buf) - 1] != '\n') {
13002             res = AST_TEST_FAIL;
13003          }
13004       }
13005    }
13006    fclose(file);
13007    return res;
13008 }
13009 
13010 AST_TEST_DEFINE(test_voicemail_load_config)
13011 {
13012    int res = AST_TEST_PASS;
13013    struct ast_vm_user *vmu;
13014    struct ast_config *cfg;
13015    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
13016    int fd;
13017    FILE *file;
13018    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
13019 
13020    switch (cmd) {
13021    case TEST_INIT:
13022       info->name = "test_voicemail_load_config";
13023       info->category = "/apps/app_voicemail/";
13024       info->summary = "Test loading Voicemail config";
13025       info->description =
13026          "Verify that configuration is loaded consistently. "
13027          "This is to test regressions of ASTERISK-18838 where it was noticed that "
13028          "some options were loaded after the mailboxes were instantiated, causing "
13029          "those options not to be set correctly.";
13030       return AST_TEST_NOT_RUN;
13031    case TEST_EXECUTE:
13032       break;
13033    }
13034 
13035    /* build a config file by hand... */
13036    if ((fd = mkstemp(config_filename)) < 0) {
13037       return AST_TEST_FAIL;
13038    }
13039    if (!(file = fdopen(fd, "w"))) {
13040       close(fd);
13041       unlink(config_filename);
13042       return AST_TEST_FAIL;
13043    }
13044    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13045    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13046    fputs("00000002 => 9999,Mrs. Test\n", file);
13047    fclose(file);
13048 
13049    if (!(cfg = ast_config_load(config_filename, config_flags))) {
13050       res = AST_TEST_FAIL;
13051       goto cleanup;
13052    }
13053 
13054    load_config_from_memory(1, cfg, NULL);
13055    ast_config_destroy(cfg);
13056 
13057 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13058    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13059    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13060 
13061    AST_LIST_LOCK(&users);
13062    AST_LIST_TRAVERSE(&users, vmu, list) {
13063       if (!strcmp(vmu->mailbox, "00000001")) {
13064          if (0); /* trick to get CHECK to work */
13065          CHECK(vmu, callback, "othercontext")
13066          CHECK(vmu, locale, "nl_NL.UTF-8")
13067          CHECK(vmu, zonetag, "central")
13068       } else if (!strcmp(vmu->mailbox, "00000002")) {
13069          if (0); /* trick to get CHECK to work */
13070          CHECK(vmu, callback, "somecontext")
13071          CHECK(vmu, locale, "de_DE.UTF-8")
13072          CHECK(vmu, zonetag, "european")
13073       }
13074    }
13075    AST_LIST_UNLOCK(&users);
13076 
13077 #undef CHECK
13078 
13079    /* restore config */
13080    load_config(1); /* this might say "Failed to load configuration file." */
13081 
13082 cleanup:
13083    unlink(config_filename);
13084    return res;
13085 }
13086 
13087 #endif /* defined(TEST_FRAMEWORK) */
13088 
13089 static int reload(void)
13090 {
13091    return load_config(1);
13092 }
13093 
13094 static int unload_module(void)
13095 {
13096    int res;
13097 
13098    res = ast_unregister_application(app);
13099    res |= ast_unregister_application(app2);
13100    res |= ast_unregister_application(app3);
13101    res |= ast_unregister_application(app4);
13102    res |= ast_unregister_application(sayname_app);
13103    res |= ast_custom_function_unregister(&mailbox_exists_acf);
13104    res |= ast_manager_unregister("VoicemailUsersList");
13105    res |= ast_data_unregister(NULL);
13106 #ifdef TEST_FRAMEWORK
13107    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13108    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13109    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13110    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13111    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13112 #endif
13113    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13114    ast_uninstall_vm_functions();
13115    ao2_ref(inprocess_container, -1);
13116 
13117    if (poll_thread != AST_PTHREADT_NULL)
13118       stop_poll_thread();
13119 
13120    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13121    ast_unload_realtime("voicemail");
13122    ast_unload_realtime("voicemail_data");
13123 
13124    free_vm_users();
13125    free_vm_zones();
13126    return res;
13127 }
13128 
13129 static int load_module(void)
13130 {
13131    int res;
13132    my_umask = umask(0);
13133    umask(my_umask);
13134 
13135    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13136       return AST_MODULE_LOAD_DECLINE;
13137    }
13138 
13139    /* compute the location of the voicemail spool directory */
13140    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13141    
13142    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13143       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
13144    }
13145 
13146    if ((res = load_config(0)))
13147       return res;
13148 
13149    res = ast_register_application_xml(app, vm_exec);
13150    res |= ast_register_application_xml(app2, vm_execmain);
13151    res |= ast_register_application_xml(app3, vm_box_exists);
13152    res |= ast_register_application_xml(app4, vmauthenticate);
13153    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13154    res |= ast_custom_function_register(&mailbox_exists_acf);
13155    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
13156 #ifdef TEST_FRAMEWORK
13157    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13158    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13159    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13160    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13161    res |= AST_TEST_REGISTER(test_voicemail_load_config);
13162 #endif
13163 
13164    if (res)
13165       return res;
13166 
13167    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13168    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13169 
13170    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
13171    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13172    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13173 
13174    return res;
13175 }
13176 
13177 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
13178 {
13179    int cmd = 0;
13180    char destination[80] = "";
13181    int retries = 0;
13182 
13183    if (!num) {
13184       ast_verb(3, "Destination number will be entered manually\n");
13185       while (retries < 3 && cmd != 't') {
13186          destination[1] = '\0';
13187          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13188          if (!cmd)
13189             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13190          if (!cmd)
13191             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13192          if (!cmd) {
13193             cmd = ast_waitfordigit(chan, 6000);
13194             if (cmd)
13195                destination[0] = cmd;
13196          }
13197          if (!cmd) {
13198             retries++;
13199          } else {
13200 
13201             if (cmd < 0)
13202                return 0;
13203             if (cmd == '*') {
13204                ast_verb(3, "User hit '*' to cancel outgoing call\n");
13205                return 0;
13206             }
13207             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
13208                retries++;
13209             else
13210                cmd = 't';
13211          }
13212          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13213       }
13214       if (retries >= 3) {
13215          return 0;
13216       }
13217       
13218    } else {
13219       if (option_verbose > 2)
13220          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13221       ast_copy_string(destination, num, sizeof(destination));
13222    }
13223 
13224    if (!ast_strlen_zero(destination)) {
13225       if (destination[strlen(destination) -1 ] == '*')
13226          return 0; 
13227       if (option_verbose > 2)
13228          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13229       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13230       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13231       chan->priority = 0;
13232       return 9;
13233    }
13234    return 0;
13235 }
13236 
13237 /*!
13238  * \brief The advanced options within a message.
13239  * \param chan
13240  * \param vmu 
13241  * \param vms
13242  * \param msg
13243  * \param option
13244  * \param record_gain
13245  *
13246  * Provides handling for the play message envelope, call the person back, or reply to message. 
13247  *
13248  * \return zero on success, -1 on error.
13249  */
13250 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)
13251 {
13252    int res = 0;
13253    char filename[PATH_MAX];
13254    struct ast_config *msg_cfg = NULL;
13255    const char *origtime, *context;
13256    char *name, *num;
13257    int retries = 0;
13258    char *cid;
13259    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13260 
13261    vms->starting = 0; 
13262 
13263    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13264 
13265    /* Retrieve info from VM attribute file */
13266    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13267    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13268    msg_cfg = ast_config_load(filename, config_flags);
13269    DISPOSE(vms->curdir, vms->curmsg);
13270    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
13271       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13272       return 0;
13273    }
13274 
13275    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13276       ast_config_destroy(msg_cfg);
13277       return 0;
13278    }
13279 
13280    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13281 
13282    context = ast_variable_retrieve(msg_cfg, "message", "context");
13283    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13284       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13285    switch (option) {
13286    case 3: /* Play message envelope */
13287       if (!res)
13288          res = play_message_datetime(chan, vmu, origtime, filename);
13289       if (!res)
13290          res = play_message_callerid(chan, vms, cid, context, 0);
13291 
13292       res = 't';
13293       break;
13294 
13295    case 2:  /* Call back */
13296 
13297       if (ast_strlen_zero(cid))
13298          break;
13299 
13300       ast_callerid_parse(cid, &name, &num);
13301       while ((res > -1) && (res != 't')) {
13302          switch (res) {
13303          case '1':
13304             if (num) {
13305                /* Dial the CID number */
13306                res = dialout(chan, vmu, num, vmu->callback);
13307                if (res) {
13308                   ast_config_destroy(msg_cfg);
13309                   return 9;
13310                }
13311             } else {
13312                res = '2';
13313             }
13314             break;
13315 
13316          case '2':
13317             /* Want to enter a different number, can only do this if there's a dialout context for this user */
13318             if (!ast_strlen_zero(vmu->dialout)) {
13319                res = dialout(chan, vmu, NULL, vmu->dialout);
13320                if (res) {
13321                   ast_config_destroy(msg_cfg);
13322                   return 9;
13323                }
13324             } else {
13325                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13326                res = ast_play_and_wait(chan, "vm-sorry");
13327             }
13328             ast_config_destroy(msg_cfg);
13329             return res;
13330          case '*':
13331             res = 't';
13332             break;
13333          case '3':
13334          case '4':
13335          case '5':
13336          case '6':
13337          case '7':
13338          case '8':
13339          case '9':
13340          case '0':
13341 
13342             res = ast_play_and_wait(chan, "vm-sorry");
13343             retries++;
13344             break;
13345          default:
13346             if (num) {
13347                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13348                res = ast_play_and_wait(chan, "vm-num-i-have");
13349                if (!res)
13350                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
13351                if (!res)
13352                   res = ast_play_and_wait(chan, "vm-tocallnum");
13353                /* Only prompt for a caller-specified number if there is a dialout context specified */
13354                if (!ast_strlen_zero(vmu->dialout)) {
13355                   if (!res)
13356                      res = ast_play_and_wait(chan, "vm-calldiffnum");
13357                }
13358             } else {
13359                res = ast_play_and_wait(chan, "vm-nonumber");
13360                if (!ast_strlen_zero(vmu->dialout)) {
13361                   if (!res)
13362                      res = ast_play_and_wait(chan, "vm-toenternumber");
13363                }
13364             }
13365             if (!res) {
13366                res = ast_play_and_wait(chan, "vm-star-cancel");
13367             }
13368             if (!res) {
13369                res = ast_waitfordigit(chan, 6000);
13370             }
13371             if (!res) {
13372                retries++;
13373                if (retries > 3) {
13374                   res = 't';
13375                }
13376             }
13377             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13378             break; 
13379             
13380          }
13381          if (res == 't')
13382             res = 0;
13383          else if (res == '*')
13384             res = -1;
13385       }
13386       break;
13387       
13388    case 1:  /* Reply */
13389       /* Send reply directly to sender */
13390       if (ast_strlen_zero(cid))
13391          break;
13392 
13393       ast_callerid_parse(cid, &name, &num);
13394       if (!num) {
13395          ast_verb(3, "No CID number available, no reply sent\n");
13396          if (!res)
13397             res = ast_play_and_wait(chan, "vm-nonumber");
13398          ast_config_destroy(msg_cfg);
13399          return res;
13400       } else {
13401          struct ast_vm_user vmu2;
13402          if (find_user(&vmu2, vmu->context, num)) {
13403             struct leave_vm_options leave_options;
13404             char mailbox[AST_MAX_EXTENSION * 2 + 2];
13405             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13406 
13407             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13408             
13409             memset(&leave_options, 0, sizeof(leave_options));
13410             leave_options.record_gain = record_gain;
13411             res = leave_voicemail(chan, mailbox, &leave_options);
13412             if (!res)
13413                res = 't';
13414             ast_config_destroy(msg_cfg);
13415             return res;
13416          } else {
13417             /* Sender has no mailbox, can't reply */
13418             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13419             ast_play_and_wait(chan, "vm-nobox");
13420             res = 't';
13421             ast_config_destroy(msg_cfg);
13422             return res;
13423          }
13424       } 
13425       res = 0;
13426 
13427       break;
13428    }
13429 
13430 #ifndef IMAP_STORAGE
13431    ast_config_destroy(msg_cfg);
13432 
13433    if (!res) {
13434       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13435       vms->heard[msg] = 1;
13436       res = wait_file(chan, vms, vms->fn);
13437    }
13438 #endif
13439    return res;
13440 }
13441 
13442 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13443          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13444          signed char record_gain, struct vm_state *vms, char *flag)
13445 {
13446    /* Record message & let caller review or re-record it, or set options if applicable */
13447    int res = 0;
13448    int cmd = 0;
13449    int max_attempts = 3;
13450    int attempts = 0;
13451    int recorded = 0;
13452    int msg_exists = 0;
13453    signed char zero_gain = 0;
13454    char tempfile[PATH_MAX];
13455    char *acceptdtmf = "#";
13456    char *canceldtmf = "";
13457    int canceleddtmf = 0;
13458 
13459    /* Note that urgent and private are for flagging messages as such in the future */
13460 
13461    /* barf if no pointer passed to store duration in */
13462    if (duration == NULL) {
13463       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13464       return -1;
13465    }
13466 
13467    if (!outsidecaller)
13468       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13469    else
13470       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13471 
13472    cmd = '3';  /* Want to start by recording */
13473 
13474    while ((cmd >= 0) && (cmd != 't')) {
13475       switch (cmd) {
13476       case '1':
13477          if (!msg_exists) {
13478             /* In this case, 1 is to record a message */
13479             cmd = '3';
13480             break;
13481          } else {
13482             /* Otherwise 1 is to save the existing message */
13483             ast_verb(3, "Saving message as is\n");
13484             if (!outsidecaller) 
13485                ast_filerename(tempfile, recordfile, NULL);
13486             ast_stream_and_wait(chan, "vm-msgsaved", "");
13487             if (!outsidecaller) {
13488                /* Saves to IMAP server only if imapgreeting=yes */
13489                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13490                DISPOSE(recordfile, -1);
13491             }
13492             cmd = 't';
13493             return res;
13494          }
13495       case '2':
13496          /* Review */
13497          ast_verb(3, "Reviewing the message\n");
13498          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13499          break;
13500       case '3':
13501          msg_exists = 0;
13502          /* Record */
13503          if (recorded == 1) 
13504             ast_verb(3, "Re-recording the message\n");
13505          else  
13506             ast_verb(3, "Recording the message\n");
13507          
13508          if (recorded && outsidecaller) {
13509             cmd = ast_play_and_wait(chan, INTRO);
13510             cmd = ast_play_and_wait(chan, "beep");
13511          }
13512          recorded = 1;
13513          /* 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 */
13514          if (record_gain)
13515             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13516          if (ast_test_flag(vmu, VM_OPERATOR))
13517             canceldtmf = "0";
13518          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13519          if (strchr(canceldtmf, cmd)) {
13520          /* need this flag here to distinguish between pressing '0' during message recording or after */
13521             canceleddtmf = 1;
13522          }
13523          if (record_gain)
13524             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13525          if (cmd == -1) {
13526             /* User has hung up, no options to give */
13527             if (!outsidecaller) {
13528                /* user was recording a greeting and they hung up, so let's delete the recording. */
13529                ast_filedelete(tempfile, NULL);
13530             }     
13531             return cmd;
13532          }
13533          if (cmd == '0') {
13534             break;
13535          } else if (cmd == '*') {
13536             break;
13537 #if 0
13538          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13539             /* Message is too short */
13540             ast_verb(3, "Message too short\n");
13541             cmd = ast_play_and_wait(chan, "vm-tooshort");
13542             cmd = ast_filedelete(tempfile, NULL);
13543             break;
13544          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13545             /* Message is all silence */
13546             ast_verb(3, "Nothing recorded\n");
13547             cmd = ast_filedelete(tempfile, NULL);
13548             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13549             if (!cmd)
13550                cmd = ast_play_and_wait(chan, "vm-speakup");
13551             break;
13552 #endif
13553          } else {
13554             /* If all is well, a message exists */
13555             msg_exists = 1;
13556             cmd = 0;
13557          }
13558          break;
13559       case '4':
13560          if (outsidecaller) {  /* only mark vm messages */
13561             /* Mark Urgent */
13562             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13563                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13564                res = ast_play_and_wait(chan, "vm-marked-urgent");
13565                strcpy(flag, "Urgent");
13566             } else if (flag) {
13567                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13568                res = ast_play_and_wait(chan, "vm-urgent-removed");
13569                strcpy(flag, "");
13570             } else {
13571                ast_play_and_wait(chan, "vm-sorry");
13572             }
13573             cmd = 0;
13574          } else {
13575             cmd = ast_play_and_wait(chan, "vm-sorry");
13576          }
13577          break;
13578       case '5':
13579       case '6':
13580       case '7':
13581       case '8':
13582       case '9':
13583       case '*':
13584       case '#':
13585          cmd = ast_play_and_wait(chan, "vm-sorry");
13586          break;
13587 #if 0 
13588 /*  XXX Commented out for the moment because of the dangers of deleting
13589     a message while recording (can put the message numbers out of sync) */
13590       case '*':
13591          /* Cancel recording, delete message, offer to take another message*/
13592          cmd = ast_play_and_wait(chan, "vm-deleted");
13593          cmd = ast_filedelete(tempfile, NULL);
13594          if (outsidecaller) {
13595             res = vm_exec(chan, NULL);
13596             return res;
13597          }
13598          else
13599             return 1;
13600 #endif
13601       case '0':
13602          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13603             cmd = ast_play_and_wait(chan, "vm-sorry");
13604             break;
13605          }
13606          if (msg_exists || recorded) {
13607             cmd = ast_play_and_wait(chan, "vm-saveoper");
13608             if (!cmd)
13609                cmd = ast_waitfordigit(chan, 3000);
13610             if (cmd == '1') {
13611                ast_filerename(tempfile, recordfile, NULL);
13612                ast_play_and_wait(chan, "vm-msgsaved");
13613                cmd = '0';
13614             } else if (cmd == '4') {
13615                if (flag) {
13616                   ast_play_and_wait(chan, "vm-marked-urgent");
13617                   strcpy(flag, "Urgent");
13618                }
13619                ast_play_and_wait(chan, "vm-msgsaved");
13620                cmd = '0';
13621             } else {
13622                ast_play_and_wait(chan, "vm-deleted");
13623                DELETE(tempfile, -1, tempfile, vmu);
13624                cmd = '0';
13625             }
13626          }
13627          return cmd;
13628       default:
13629          /* If the caller is an ouside caller, and the review option is enabled,
13630             allow them to review the message, but let the owner of the box review
13631             their OGM's */
13632          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13633             return cmd;
13634          if (msg_exists) {
13635             cmd = ast_play_and_wait(chan, "vm-review");
13636             if (!cmd && outsidecaller) {
13637                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13638                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
13639                } else if (flag) {
13640                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13641                }
13642             }
13643          } else {
13644             cmd = ast_play_and_wait(chan, "vm-torerecord");
13645             if (!cmd)
13646                cmd = ast_waitfordigit(chan, 600);
13647          }
13648          
13649          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13650             cmd = ast_play_and_wait(chan, "vm-reachoper");
13651             if (!cmd)
13652                cmd = ast_waitfordigit(chan, 600);
13653          }
13654 #if 0
13655          if (!cmd)
13656             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13657 #endif
13658          if (!cmd)
13659             cmd = ast_waitfordigit(chan, 6000);
13660          if (!cmd) {
13661             attempts++;
13662          }
13663          if (attempts > max_attempts) {
13664             cmd = 't';
13665          }
13666       }
13667    }
13668    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13669       /* Hang up or timeout, so delete the recording. */
13670       ast_filedelete(tempfile, NULL);
13671    }
13672 
13673    if (cmd != 't' && outsidecaller)
13674       ast_play_and_wait(chan, "vm-goodbye");
13675 
13676    return cmd;
13677 }
13678 
13679 /* This is a workaround so that menuselect displays a proper description
13680  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13681  */
13682 
13683 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
13684       .load = load_module,
13685       .unload = unload_module,
13686       .reload = reload,
13687       .nonoptreq = "res_adsi,res_smdi",
13688       );

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